⚠️ CRITICAL SECURITY NOTICE
Use Caddy v2.11.1 or later - 6 CVEs fixed in this release. Do NOT use older versions.
✅ Official Docker Image Available
caddy- Docker Official Image (500M+ pulls)
For Docker installation, see Docker.
Create a directory to store your configuration and compose files.
mkdir -p /opt/caddy
cd /opt/caddy
Define a container for Caddy with specific version pinning:
version: '3.8'
services:
caddy:
image: caddy:2.11.1 # ⚠️ PIN SPECIFIC VERSION - DO NOT USE :latest
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp" # HTTP/3 QUIC
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
cap_add:
- NET_ADMIN # Improves HTTP/3 performance
networks:
- caddy-network
networks:
caddy-network:
driver: bridge
volumes:
caddy_data:
caddy_config:
⚠️ IMPORTANT: Never use
:latesttag in production. Always pin specific versions for reproducibility and security.
Create a basic Caddyfile configuration:
cat > Caddyfile << 'EOF'
# Global options
{
admin off # Disable admin API in production
email your@email.com # ACME email
}
# Site configuration
example.com {
root * /srv
file_server
# Security headers
header {
X-Content-Type-Options nosniff
X-Frame-Options DENY
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
}
# Logging
log {
output stdout
format json
}
}
EOF
Start the containers in the background.
docker compose up -d
# Check container status
docker compose ps
# View logs
docker compose logs -f caddy
# Check Caddy version
docker compose exec caddy caddy version
# Should show: v2.11.1
version: '3.8'
services:
caddy:
image: caddy:2.11.1
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data # Certificate storage
- caddy_config:/config
cap_add:
- NET_ADMIN
networks:
- caddy-network
networks:
caddy-network:
driver: bridge
volumes:
caddy_data:
caddy_config:
{
admin off
email admin@example.com
}
example.com {
reverse_proxy app:8080
# Security headers
header {
X-Content-Type-Options nosniff
X-Frame-Options DENY
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
}
log {
output stdout
format json
}
}
Create a Dockerfile to add custom Caddy modules:
FROM caddy:2.11.1-builder AS builder
RUN xcaddy build \
--with github.com/caddyserver/nginx-adapter \
--with github.com/hairyhenderson/caddy-teapot-module
FROM caddy:2.11.1
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
Build and run:
docker build -t my-caddy:2.11.1 .
docker compose up -d
# View logs
docker compose logs -f caddy
# Reload configuration
docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile
# Check status
docker compose exec caddy caddy status
# Enter container
docker compose exec caddy sh
# Update Caddy version
docker compose pull caddy
docker compose up -d caddy
:latest in production:ro)/data volume for TLS certificatescaddy reload for config changes| Issue | Solution |
|---|---|
| Certificate renewal fails | Check port 80 is accessible, verify DNS |
| Container exits immediately | Check logs: docker compose logs caddy |
| HTTP/3 not working | Ensure UDP 443 is open, NET_ADMIN capability |
| Config not loading | Validate: docker compose exec caddy caddy validate |
Deploying Caddy in containers for production? Our consulting covers:
Get expert help: office@linux-server-admin.com | Contact Page