Sign inSign up

cplieger/docker-caddy

By cplieger

Updated about 4 hours ago

Caddy with Cloudflare DNS-01 and CrowdSec bouncer plugins, built with xcaddy

Image
Networking
Security
Web servers
0

9.6K

cplieger/docker-caddy repository overview

docker-caddy

Caddy reverse proxy and web server, custom-built with xcaddy to bundle the Cloudflare DNS-01 plugin and the CrowdSec HTTP bouncer.

What it does

Caddy is a modern, automatic-HTTPS reverse proxy and web server. This image rebuilds it from upstream's official builder with two extra plugins so you can:

  • Issue ACME certificates via Cloudflare DNS-01: for wildcard certs and internal-only services (see Plugins for details).
  • Block IPs flagged by CrowdSec: community-driven threat intel applied at the reverse-proxy layer, before requests reach your backends.

All of Caddy's standard features work as documented.

Why this design
  • Built from the official builder. The binary matches upstream Caddy exactly; plugins are compiled in with xcaddy, the upstream-prescribed mechanism.

  • Distroless runtime. The final stage is gcr.io/distroless/static: no shell, no package manager, no OS packages to patch or scan. There is no shell to docker exec into, and docker exec can only run the shipped binaries (caddy, /probe); debugging is otherwise via logs, metrics, and the admin API.

  • Upstream contract preserved. The default Caddyfile, welcome page, MIME map and state directories are copied from the upstream runtime image, so upstream changes to them keep flowing in with ordinary image updates. The XDG_* env that makes /data the certificate store is declared by hand, because COPY moves files and not image ENV metadata.

  • Multi-arch, built natively. amd64 and arm64 each compile on matching hardware, with no emulation.

  • Config reload without a restart. Send the running process SIGUSR1 (docker kill -s USR1 caddy) and Caddy reloads from the --config file and --adapter this image's CMD already records. For a deploy script that needs a rejected Caddyfile on the caller's own exit status, use docker exec caddy caddy reload --config /etc/caddy/Caddyfile --adapter caddyfile. Either way a rejected Caddyfile leaves the previous config running.

  • Watch mode available, off by default. Upstream documents --watch as a local-development feature, so the shipped command is upstream's. To opt in, override the command on your compose service:

    command: ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile", "--watch"]
    

    Know what that turns on: one save that fails to ADAPT stops the watcher for the lifetime of the container, and later valid edits are then ignored with no healthcheck and no metric reporting it; the watcher logger emits unable to load latest config once, carrying the adapter error, at the moment it dies. Recovery is a container restart; an explicit reload applies the current file but does not revive the watcher. Validate before saving with docker exec caddy caddy validate --adapter caddyfile --config /etc/caddy/Caddyfile. Separately, a single-file bind mount pins an inode, so an editor that saves by rename leaves the container reading the old bytes; save in place, or mount the directory (./caddy:/etc/caddy). That defeats every reload path, not only the watcher.

Pull

docker pull cplieger/docker-caddy:latest

Also published to ghcr.io/cplieger/docker-caddy with identical images and tags. Release versions are tagged vX.Y.Z alongside latest.

Quick start

# Example compose for docker-caddy. See the README for all configuration options and hardening.
services:
  caddy:
    image: ghcr.io/cplieger/docker-caddy:latest
    container_name: caddy
    restart: unless-stopped

    environment:
      # Set these in a gitignored .env file; never commit live tokens.
      CLOUDFLARE_API_TOKEN: "${CLOUDFLARE_API_TOKEN:-}"  # used by the DNS-01 plugin
      CROWDSEC_BOUNCER_KEY: "${CROWDSEC_BOUNCER_KEY:-}"  # used by the CrowdSec bouncer

    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"  # HTTP/3

    volumes:
      - "./caddy:/etc/caddy:ro"  # put Caddyfile.example or Caddyfile.plugins.example at ./caddy/Caddyfile; both lock the admin API to loopback
      - "./data:/data"  # persist issued certs + ACME state

Documentation

License

Apache-2.0. See LICENSE. The image carries the license text of every bundled component under /usr/share/licenses/.

The image packages Caddy (Apache-2.0, source at https://github.com/caddyserver/caddy), built with xcaddy from the official caddy:2.11-builder image the Dockerfile pins by digest, together with the caddy-dns/cloudflare and caddy-crowdsec-bouncer plugins (both Apache-2.0) at the versions the Dockerfile's --with flags name. The build applies no patches. One linked module, hslatman/ipstore, publishes no license file and carries the Apache-2.0 header in every source file; the packager supplies its license text under licenses/ in this repository, and it ships in the image beside the others.

Tag summary

Content type

Image

Digest

sha256:b7529c68d

Size

23 MB

Last updated

about 4 hours ago

docker pull cplieger/docker-caddy