Docker Secrets x Caddy

Hi,

I saw a bunch of complicated guides that involved custom scripts and changing the container’s entrypoint, and it all felt like way too much work. Instead of making Caddy read an exposed environment variable, you can just use Docker Secrets to mount your token as a file and tell Caddy to read that file directly.

The Goal

In my case I want to use Docker Secrets for DESEC_TOKEN so it’s not exposed as an environment variable.

1. The Secret File

First, I removed the token from my .env file and put it in a new file on my host named desec_token.txt. The file contains only the token string.

desec_token.txt

YourDeSECTokenHere

2. The docker-compose.yml

In my docker-compose.yml, I told the caddy service to use this file and defined it in the top-level secrets: block.

services:
  caddy:
    build: .
    container_name: caddy
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - data:/data
      - config:/config
    networks:
      - proxy
    secrets:
      - desec_token

volumes:
  data:
  config:

networks:
  proxy:
    external: true

secrets:
  desec_token:
    file: ./desec_token.txt

3. The Caddyfile

This is the most important part. Docker mounts the secret at /run/secrets/desec_token (using the key from the compose file).

I modified my (dns_challenge) snippet to use the {file.} placeholder to read the token directly from that file.

(dns_challenge) {
	tls {$ACME_EMAIL} {
		propagation_delay 60s
		propagation_timeout 300s
		dns desec {
			token {file./run/secrets/desec_token}
		}
	}
}

Now, when I run docker inspect caddy, the token is nowhere to be found.

This can also be applied to other services that have sensitive values such as API key, database passwords and so on.

1 Like

Thank you for sharing :folded_hands:

A {file.} placeholder is definitely a better way to go :+1: