webhookd on AWS User Guide
Overview
This guide covers deploying and operating webhookd on AWS from the cloudimg AWS Marketplace listing. webhookd is a minimalist, powerful open source HTTP server that turns ordinary shell scripts into webhook endpoints. You drop an executable script into a directory and it becomes callable over HTTP, with the script's standard output streamed back to the caller in real time, either as chunked transfer encoding or as Server-Sent Events. Query parameters and HTTP headers from the request are passed to the script as environment variables, so a hook can react to whatever triggered it.
Use webhookd to trigger a deployment, run a maintenance job, kick off a backup, restart a service, or drive any automation from an HTTP call, from a CI/CD pipeline, a chatbot, a monitoring alert, or another service's outgoing webhook.
The image installs the pinned, checksum verified webhookd 1.22.0 official release binary (a single Go binary) and runs it under systemd as a dedicated non root webhookd user, bound to the loopback interface on 127.0.0.1:8080. nginx terminates HTTPS on port 443 in front of it and reverse proxies to it with response buffering disabled, so streaming and Server-Sent Events output reaches the client the instant the script produces it.
Secure by default. HTTP Basic authentication is mandatory and enforced by webhookd itself. A request with no credentials, or with the wrong password, is refused with HTTP 401 and no hook is ever run. The shipped AMI bakes no credential: on the very first boot the image generates a unique per instance username and password, hashed with bcrypt into an htpasswd file, and writes them to a root owned 0600 file. A per instance self signed TLS certificate for nginx is generated on the same first boot, so no default certificate ships inside the image.
What is included:
-
webhookd 1.22.0 (single Go binary), run under systemd as
webhookd.serviceas a dedicated non root user, bound to127.0.0.1:8080 -
nginx terminating TLS on :443 with a per instance self signed certificate regenerated on first boot, reverse proxying to webhookd with streaming and Server-Sent Events output forwarded unbuffered
-
An unauthenticated
/healthzload balancer probe and a301redirect to HTTPS on port 80 -
Mandatory HTTP Basic authentication: a unique per instance credential generated on first boot into
/etc/webhookd/htpasswd(bcrypt) with the plaintext written only to/root/webhookd-credentials.txt(0600root) -
One ready to call example hook,
echo, that streams a few lines so you can see the streaming behaviour immediately -
Ubuntu 24.04 LTS base with the latest security patches applied at build time and unattended security updates enabled
-
24/7 cloudimg support
Connecting to your instance
webhookd is a headless service: you administer it over SSH and call your hooks over HTTPS. SSH in as the default login user for the operating system variant you launched.
| OS variant | SSH login user | Example |
|---|---|---|
| Ubuntu 24.04 | ubuntu |
ssh -i your-key.pem ubuntu@<public-ip> |
The instance security group must allow TCP 22 (admin), TCP 443 (the webhook API over HTTPS) and TCP 80 (the HTTP to HTTPS redirect and the health probe) from the networks your callers and administrators connect from.
Prerequisites
-
An AWS account subscribed to the webhookd listing on AWS Marketplace, and an EC2 key pair in the target region
-
A client that can make authenticated HTTPS requests (curl, a CI/CD runner, a chatbot, or another service's webhook sender)
Recommended instance type: m5.large (2 vCPU, 8 GB RAM) is ample for a webhook server. webhookd is lightweight; hooks run as short lived child processes, so size the instance for whatever your hook scripts actually do.
Step 1: Launch from AWS Marketplace
Subscribe to webhookd on AWS Marketplace (published by cloudimg), then launch the Ubuntu 24.04 AMI with your key pair and a security group that opens ports 22, 80 and 443 as described above. First boot generates the per instance credential and TLS certificate and starts the service, which takes a few seconds after the instance reaches the running state.
Step 2: Confirm the service is running
SSH in and confirm webhookd and nginx are active and listening. webhookd owns the loopback port 127.0.0.1:8080; nginx owns the public ports 80 and 443.
sudo systemctl start webhookd nginx
sleep 2
systemctl is-active webhookd nginx

Step 3: Retrieve your per instance credentials
The unique username, password and endpoint for this instance are written to a root owned file on first boot. Read them over SSH:
$ sudo cat /root/webhookd-credentials.txt
webhookd.user=cloudimg
webhookd.pass=<your-per-instance-password>
webhookd.host=<public-ip>
webhookd.url=https://<public-ip>/
webhookd.example_hook=https://<public-ip>/echo
The password is unique to this instance and never leaves it. Keep this file readable only by root.
Step 4: Authentication is mandatory
webhookd refuses any request that does not carry valid HTTP Basic credentials, so no unauthenticated caller can ever run a hook. A request with no credentials is refused with HTTP 401:
curl -sk -o /dev/null -w 'anonymous request without credentials: HTTP %{http_code}\n' https://localhost/echo

The image ships a fail closed htpasswd with zero user entries, so if first boot has not yet completed every hook request is refused rather than left open.
Step 5: Call the example hook
The image includes a self signed certificate, so pass -k to curl (or install a CA signed certificate for your domain in production). Call the bundled echo hook with your per instance credentials and its output streams back line by line:
$ curl -k -u cloudimg:<your-per-instance-password> https://<public-ip>/echo?name=cloudimg
cloudimg webhookd example hook
hook_name=echo method=GET authenticated_user=
hello, cloudimg
step 1/3 working...
step 2/3 working...
step 3/3 working...
done: hook completed successfully
For real time Server-Sent Events, ask for them with an Accept header:
$ curl -k -u cloudimg:<your-per-instance-password> -H 'Accept: text/event-stream' https://<public-ip>/echo

You can prove the full authentication round trip on the instance itself, without printing any password, with the bundled proof script. It confirms that anonymous and wrong credentials are refused with 401 while the correct per instance credential returns 200 and runs the hook, on both the loopback backend and the nginx TLS edge:
sudo /usr/local/sbin/webhookd-roundtrip.sh
Step 6: Add your own hook
To publish a new webhook, drop an executable script into the hooks directory and call it by name. A script at /opt/webhookd/scripts/deploy.sh becomes the endpoint https://<public-ip>/deploy.
$ sudo tee /opt/webhookd/scripts/deploy.sh >/dev/null <<'SCRIPT'
#!/usr/bin/env bash
echo "deploying ${ref:-main}..."
# your deployment commands here
echo "done"
SCRIPT
$ sudo chmod +x /opt/webhookd/scripts/deploy.sh
$ sudo chown webhookd:webhookd /opt/webhookd/scripts/deploy.sh
$ curl -k -u cloudimg:<your-per-instance-password> "https://<public-ip>/deploy?ref=release-1"
Request query parameters (for example ?ref=release-1) and HTTP headers are passed to your script as environment variables, so a hook can act on the payload that triggered it.
Step 7: Operating webhookd
Rotate the password at any time and restart the service to pick it up:
$ sudo htpasswd -B /etc/webhookd/htpasswd cloudimg
$ sudo systemctl restart webhookd
Configuration lives in /etc/webhookd/webhookd.env (listen address, hook directory, htpasswd path, hook timeout, default streaming mode). To replace the self signed certificate with a CA signed certificate for your domain in production, drop your certificate and key into /etc/nginx/certs/ as cert.pem and key.pem and run sudo systemctl reload nginx.
The service runs under systemd, so it restarts automatically and logs cleanly:
systemctl status webhookd --no-pager --lines 0
Security notes
- No credential is baked into the image. The per instance username, password (bcrypt in
/etc/webhookd/htpasswd) and self signed TLS certificate are all generated on first boot and are unique to this instance.

-
HTTP Basic authentication is enforced by webhookd itself, and nginx terminates TLS in front of it. There is no unauthenticated path to running a hook.
-
Restrict the security group so that only the networks that need to call your hooks can reach ports 443 and 80, and only your administrators can reach port 22.
-
Your hook scripts run as the non root
webhookduser. Treat every hook as remotely triggerable code and keep the scripts under your own review.
Support
webhookd is free and open source software distributed under the MIT license. This image repackages the unmodified upstream release with cloudimg support services. cloudimg is not affiliated with or endorsed by the webhookd project or Nicolas Carlier.
For help with this image, contact cloudimg support. For questions about webhookd itself, see the upstream project at github.com/ncarlier/webhookd.