-
Notifications
You must be signed in to change notification settings - Fork 0
CGI ‐ Common Gateway Interface
CGI allows you to build lightweight endpoints in Bash (or any language with a shebang), without needing a full application server. It’s useful for tasks like returning dynamic JSON, invoking CLI tools, or integrating with your local filesystem.
Make a directory for scripts:
mkdir caddy/cgi
Build the Caddy image with additions:
- Add the caddy-cgi module.
- Copy our scripts into the image.
caddy/Dockerfile
FROM caddy:2-builder AS builder
RUN xcaddy build \
--with github.com/aksdb/caddy-cgi/v2
# Final lightweight image
FROM caddy:2
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
# Copy our scripts into the image
COPY ./cgi /cgi
# Copy our Caddyfile into the image
COPY Caddyfile /etc /caddy/Caddyfile
You may want to add bash
and jq
or some other scripting language such as
Python. Add a line such as:
RUN apk add --no-cache bash jq
Build the image:
docker compose build caddy
Mount the cgi
scripts directory in the Compose override file (which affects
development only):
compose.override.yaml
caddy:
volumes:
- ./caddy/cgi:/cgi:ro
caddy/Caddyfile
cgi /my_route myscript.sh
Make sure scripts are executable.
chmod +x caddy/cgi/myscript.sh
Lastly, recreate the Caddy container:
docker compose up -d --force-recreate caddy
Output goes to:
-
stdout
goes to the response. -
stderr
goes to Caddy's logs.
The stdout
output must include headers:
caddy/cgi/myscript.sh
#!/bin/sh
echo "Content-Type: text/plain"
echo
echo 'Hello'
CGI will respond with HTTP status 200 even if the script fails, and the body will be whatever had been output up to the point of failure.
If you want failures to respond correctly (500 Internal Server Error), and the details logged, use this wrapper script:
caddy/cgi/entry.sh
Click to expand
#!/bin/sh
set -euo pipefail
TMP=$(mktemp)
trap 'rm -f "$TMP"' EXIT
handle_error() {
code=$?
echo "Error $code in $1 on line $2" >&2
echo "Status: 500 Internal Server Error"
echo "Content-Type: text/plain"
echo
echo "Internal Server Error"
exit 0
}
trap 'handle_error "$TARGET" $LINENO' ERR
TARGET=$1
shift
# --- Source logic script in subshell, redirecting output ---
(
source "$TARGET" "$@"
) >"$TMP"
# --- Only reached on success ---
cat "$TMP"
And update your route:
cgi /my_route entry.sh myscript.sh {
dir /cgi
}
Note
The entry.sh
must be executable, but the scripts it runs (myscript.sh) do
not.
Important
If your script is bash
, the entry.sh
should be changed to bash.