Streaming & Messaging AWS

webhookd on AWS User Guide

| Product: webhookd on AWS

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.service as a dedicated non root user, bound to 127.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 /healthz load balancer probe and a 301 redirect 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 (0600 root)

  • 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

The webhookd and nginx services reporting active under systemd, the installed webhookd version, and the loopback 127.0.0.1:8080 backend with nginx listening on ports 80 and 443

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

A request with no credentials and a request with the wrong password both refused with HTTP 401, so no unauthenticated call can ever run a hook

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

An authenticated request running the bundled example hook and streaming its output back line by line, demonstrating webhookd turning a shell script into a live HTTP endpoint

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.

The per instance credential stored only in a root owned 0600 file with a single bcrypt user entry, generated on first boot and never baked into the image, value redacted

  • 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 webhookd user. 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.