homelab-blueprint/docker-compose-files/caddy/README.md
2026-06-15 11:19:21 +02:00

4.7 KiB

Caddy

Docker deployment for Caddy with the Cloudflare DNS module. Run all commands from the repository root.

Appdata Location

Create appdata/ in the project root, not inside docker-compose-files/:

homelab-blueprint/
|-- appdata/
|   `-- caddy/
|       |-- config/
|       `-- data/
|-- docker-compose-files/
`-- docker-compose.caddy.yml

The relative volume paths in the generated Compose file resolve from the project root.

Installation

  1. Create .env if it does not exist:

    cp -n .env.sample .env
    
  2. Set the required values in .env:

    TZ=Africa/Johannesburg
    CADDY_TLS_EMAIL=admin@example.com
    CADDY_BASE_DOMAIN=home.example.com
    CLOUDFLARE_DNS_TOKEN=<restricted-cloudflare-api-token>
    
    CADDY_LAN_PREFIX=10.0.1
    CADDY_OFFICE_PREFIX=10.0.10
    CADDY_IOT_PREFIX=10.0.30
    DNS_BIND_IP=10.0.10.5
    
    CADDY_MEM_LIMIT=256m
    DOCKERLOGGING_MAXFILE=3
    DOCKERLOGGING_MAXSIZE=10m
    

    The Cloudflare token needs zone read and DNS edit permissions.

  3. Create appdata and render the templates:

    mkdir -p appdata/caddy/data appdata/caddy/config
    
    ./substitute_env.sh docker-compose-files/caddy/Caddyfile_template docker-compose-files/caddy/Caddyfile .env
    ./substitute_env.sh docker-compose-files/caddy/template.yaml docker-compose.caddy.yml .env
    

    Edit Caddyfile_template, not the generated Caddyfile. docker-compose.caddy.yml contains the rendered token and is ignored by Git.

  4. Validate and start Caddy:

    docker compose -f docker-compose.caddy.yml config --quiet
    docker compose -f docker-compose.caddy.yml build
    docker compose -f docker-compose.caddy.yml run --rm caddy caddy validate --config /etc/caddy/Caddyfile
    docker compose -f docker-compose.caddy.yml up -d
    
  5. Verify the container:

    docker compose -f docker-compose.caddy.yml ps
    docker compose -f docker-compose.caddy.yml logs --tail=100 caddy
    

Apply Configuration Changes

After changing .env or template.yaml, render the Compose file and recreate the container:

./substitute_env.sh docker-compose-files/caddy/template.yaml docker-compose.caddy.yml .env
docker compose -f docker-compose.caddy.yml config --quiet
docker compose -f docker-compose.caddy.yml up -d --build

After changing Caddyfile_template, render, validate, and reload it:

./substitute_env.sh docker-compose-files/caddy/Caddyfile_template docker-compose-files/caddy/Caddyfile .env
docker compose -f docker-compose.caddy.yml exec caddy caddy validate --config /etc/caddy/Caddyfile
docker compose -f docker-compose.caddy.yml exec caddy caddy reload --config /etc/caddy/Caddyfile

Initial Setup

In your internal DNS server:

  1. Add an A record named caddy pointing to the Caddy host at 10.0.15.5.
  2. Add a wildcard CNAME named * pointing to caddy.home.example.com.

Replace home.example.com with CADDY_BASE_DOMAIN.

Network Requirements

  • Caddy uses host networking and listens on ports 80 and 443.
  • Allow Caddy to reach every upstream IP and port in Caddyfile_template.
  • Allow outbound DNS and HTTPS for Cloudflare DNS-01 certificate issuance.
  • Exclude the Caddy host from DNS redirection so it can query public resolvers.
  • The admin API listens only on localhost:2019.

Troubleshooting

Validate the generated Caddyfile and inspect container logs:

docker compose -f docker-compose.caddy.yml run --rm caddy caddy validate --config /etc/caddy/Caddyfile
docker compose -f docker-compose.caddy.yml logs --tail=100 caddy

If certificate issuance fails, confirm the Cloudflare token permissions and ensure Caddy's public DNS queries are not redirected to internal DNS.

Raspberry Pi OS Memory Limits

If Docker reports the following warning, Caddy still starts, but Docker ignores CADDY_MEM_LIMIT:

Your kernel does not support memory limit capabilities or the cgroup is not mounted. Limitation discarded.

Enable memory cgroups by appending these parameters to the existing single line in /boot/firmware/cmdline.txt:

cgroup_enable=memory cgroup_memory=1

Older Raspberry Pi OS releases may use /boot/cmdline.txt. Do not add a new line to either file. Reboot and verify:

sudo reboot
docker info | grep -i warning
docker stats --no-stream caddy

Useful Commands

# Confirm the Cloudflare module is installed
docker compose -f docker-compose.caddy.yml exec caddy caddy list-modules | grep dns.providers.cloudflare

# Rebuild after image or Dockerfile updates
docker compose -f docker-compose.caddy.yml build --pull --no-cache
docker compose -f docker-compose.caddy.yml up -d

Backup

Back up appdata/caddy/data and appdata/caddy/config. The data directory contains certificates and ACME state.