When running services in Docker, storing secrets (like API keys) directly in environment variables can expose them when inspecting the container. A safer approach is to use Docker Secrets, which keeps sensitive values outside of your runtime environment.
This guide shows how to integrate Docker Secrets with Caddy, making secrets available as environment variables inside the container without exposing them.
Define Your docker-compose.yml
We’ll configure a caddy service that builds from a custom Dockerfile and mounts a secret called api_key.
name: caddy
services:
caddy:
build: .
container_name: caddy
restart: unless-stopped
cap_add:
- NET_ADMIN
secrets:
- api_key
ports:
- "80:80"
- "443:443"
volumes:
- data:/data
- config:/config
networks:
- caddy
volumes:
data:
config:
networks:
caddy:
name: caddy
external: true
secrets:
api_key:
environment: API_KEY
This declares the api_key secret, which we’ll inject into the container. It content comes from the host environment variable API_KEY.
Create a Custom Dockerfile
Caddy itself doesn’t automatically load secrets from /run/secrets/, so we need a small helper script to export them as environment variables.
FROM caddy:2-alpine
COPY ./export_secrets /usr/sbin/export_secrets
RUN chmod +x /usr/sbin/export_secrets
ENTRYPOINT ["/usr/sbin/export_secrets"]
CMD ["caddy"]
In our Dockerfile we copy the script, make it executable, and set it as the container entrypoint.
Write the export_secrets Script
This script loops over all files in /run/secrets/, converts their filenames to uppercase, and exports them as environment variables before launching Caddy.
#!/bin/sh
set -e
# Export all secrets in /run/secrets as uppercase env vars
for secret_file in /run/secrets/*; do
if [ -f "$secret_file" ]; then
secret_name=$(basename "$secret_file")
env_var_name=$(echo "$secret_name" | tr '[:lower:]' '[:upper:]')
export "$env_var_name"="$(cat "$secret_file")"
fi
done
# Execute the container’s main process
exec "$@"
For example, a file named /run/secrets/api_key becomes the environment variable API_KEY.
Use the Secret in Caddyfile
Now you can reference the secret as an environment variable in your Caddyfile:
{
...
some_module {
api_key {env.API_KEY}
}
...
}
Run the Container
Start the container by providing the secret, add a space before the secret to not safe this command into your bash/zsh history:
$ API_KEY='super_secret!' docker compose up -d
Or use any Secret Manager with a CLI to get the secret.
Of course, this would be even nicer if implemented as a Caddy module, but this is out of scope right now.