Docker Compose Guide: Multi-Container Application Management
Define and manage multi-container applications with Docker Compose: service dependencies, environment variables, health checks, scaling, and production deployment. Comprehensive guide.
Caddy web server installation, Caddyfile configuration, automatic Let's Encrypt SSL, reverse proxy, static site hosting, PHP-FPM integration, and performance settings.
Caddy is a modern web server that stands out with automatic HTTPS certificate management. Thanks to its Let's Encrypt integration, it automatically obtains, renews, and configures SSL certificates.
Caddy's standout features:
Caddy requires root privileges to use ports 80 and 443. This is automatically managed when running as a systemd service.
# Add Caddy GPG key and repository
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
# Install Caddy
sudo apt update
sudo apt install caddy -y
# Check service status
sudo systemctl status caddy
# Download latest version
VERSION=$(curl -s https://api.github.com/repos/caddyserver/caddy/releases/latest | grep tag_name | cut -d '"' -f 4)
curl -L "https://github.com/caddyserver/caddy/releases/download/${VERSION}/caddy_${VERSION#v}_linux_amd64.tar.gz" -o caddy.tar.gz
# Extract and install
tar -xzf caddy.tar.gz
sudo mv caddy /usr/local/bin/
sudo chmod +x /usr/local/bin/caddy
# Check version
caddy version
# Install xcaddy
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
# Build with additional modules
xcaddy build \
--with github.com/caddy-dns/cloudflare \
--with github.com/greenpau/caddy-security
# Single site
example.com {
root * /var/www/html
file_server
}
# Multiple sites
example.com {
respond "Hello example.com!"
}
api.example.com {
reverse_proxy localhost:3000
}
# Global settings at the top of Caddyfile
{
# Admin API port
admin localhost:2019
# Email (for Let's Encrypt)
email admin@example.com
# ACME server (staging for testing)
# acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
# HTTP port
http_port 80
# HTTPS port
https_port 443
# Log format
log {
output file /var/log/caddy/access.log
format json
}
}
Caddy automatically obtains a Let's Encrypt certificate for your domain:
# Just write the domain name - HTTPS is automatic!
example.com {
root * /var/www/html
file_server
encode gzip
}
# www redirect
www.example.com {
redir https://example.com{uri} permanent
}
# Wildcard certificate (requires DNS challenge)
*.example.com {
tls {
dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}
@api host api.example.com
handle @api {
reverse_proxy localhost:3000
}
}
Caddy automatically renews certificates. No manual intervention required. Certificates are stored in ~/.local/share/caddy/ or /var/lib/caddy/.
app.example.com {
reverse_proxy localhost:3000
}
# Multiple upstreams (load balancing)
api.example.com {
reverse_proxy {
to localhost:3001 localhost:3002 localhost:3003
lb_policy round_robin
health_uri /health
health_interval 30s
health_timeout 5s
}
}
app.example.com {
reverse_proxy localhost:3000 {
# Forward headers
header_up Host {upstream_hostport}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
# Timeouts
transport http {
dial_timeout 5s
response_header_timeout 30s
}
# Retry
lb_retries 3
lb_try_duration 5s
}
# Security headers
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Content-Type-Options nosniff
X-Frame-Options DENY
Referrer-Policy strict-origin-when-cross-origin
-Server
}
}
static.example.com {
root * /var/www/static
file_server {
# Directory listing
browse
# Hide hidden files
hide .git .env
}
encode gzip zstd
# Cache headers
header /assets/* Cache-Control "public, max-age=31536000, immutable"
header /*.html Cache-Control "no-cache"
# Error pages
handle_errors {
rewrite * /errors/{err.status_code}.html
file_server
}
}
# SPA (Single Page Application)
app.example.com {
root * /var/www/app
encode gzip
try_files {path} /index.html
file_server
}
php.example.com {
root * /var/www/php
encode gzip
# Route PHP files to PHP-FPM
php_fastcgi unix//run/php/php8.2-fpm.sock
# Or via TCP
# php_fastcgi localhost:9000
file_server
# For WordPress
@notStatic {
not path /wp-content/* /wp-includes/*
not file
}
rewrite @notStatic /index.php
}
# Full WordPress configuration
wordpress.example.com {
root * /var/www/wordpress
encode gzip
php_fastcgi unix//run/php/php8.2-fpm.sock
@disallowed path /xmlrpc.php /wp-admin/install.php
respond @disallowed 403
@static path /wp-content/* /wp-includes/*
handle @static {
file_server
}
handle {
try_files {path} /index.php
php_fastcgi unix//run/php/php8.2-fpm.sock
}
}
# docker-compose.yml
services:
caddy:
image: caddy:2-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp" # HTTP/3
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
- ./html:/var/www/html:ro
environment:
- CLOUDFLARE_API_TOKEN=${CLOUDFLARE_API_TOKEN}
app:
build: ./app
expose:
- "3000"
volumes:
caddy_data:
caddy_config:
# Docker Caddyfile
example.com {
reverse_proxy app:3000
}
Caddy supports dynamic configuration via REST API:
# View current configuration
curl http://localhost:2019/config/
# Update configuration
curl -X POST http://localhost:2019/load \
-H "Content-Type: application/json" \
-d @caddy.json
# Reload Caddy (zero downtime)
caddy reload --config /etc/caddy/Caddyfile
# Validate configuration
caddy validate --config /etc/caddy/Caddyfile
# Format Caddyfile
caddy fmt --overwrite /etc/caddy/Caddyfile
{
servers {
max_header_size 1MB
timeouts {
read_body 10s
read_header 10s
write 30s
idle 2m
}
}
}
example.com {
# Compression
encode {
gzip 6
zstd
minimum_length 1024
}
reverse_proxy localhost:3000
}
Caddy meets modern web server needs with automatic HTTPS management and simple configuration. It requires much less configuration compared to Nginx or Apache while offering all the features needed for production environments. It's an ideal choice especially for those who want to automate Let's Encrypt certificate management.
Caddy stores certificates by default in '/var/lib/caddy/.local/share/caddy/' on Linux. This directory is automatically created when running as a systemd service. When using Docker, you need to persistently mount the 'caddy_data' volume, otherwise a new certificate is obtained on every restart.
Let's Encrypt applies a limit of 5 certificates per week for the same domain. During testing, use the staging environment with 'acme_ca https://acme-staging-v02.api.letsencrypt.org/directory'. Staging certificates are marked as untrusted by browsers but have no rate limit. Remove the staging line when switching to production.
Caddy offers automatic HTTPS management and much simpler configuration. Nginx has more customization options and wider community support. Caddy is written in Go and comes with HTTP/3 support by default. Nginx is written in C and may perform better under very high traffic.
You can reload with zero downtime using 'sudo systemctl reload caddy' or 'caddy reload --config /etc/caddy/Caddyfile'. It's recommended to validate the configuration first with 'caddy validate'. If an invalid configuration is loaded, Caddy rolls back to the previous configuration.
Wildcard certificates require a DNS challenge. If using Cloudflare, add the 'caddy-dns/cloudflare' module and set your API token. Use the 'tls { dns cloudflare {env.CF_API_TOKEN} }' directive in Caddyfile. You may need to create a custom build with xcaddy including the DNS provider module.
By default, the Admin API only listens on localhost:2019. Don't expose this port externally in production. If remote management is needed, use an SSH tunnel instead of 'admin 0.0.0.0:2019'. Alternatively, you can completely disable the API with 'admin off'.
Define and manage multi-container applications with Docker Compose: service dependencies, environment variables, health checks, scaling, and production deployment. Comprehensive guide.
Build CI/CD pipelines with GitHub Actions: Docker build and push, SSH server deployment, test automation, secrets management, and workflow optimization.
Ansible installation, inventory file, playbook writing, role structure, variables, encrypted vault, ad-hoc commands, and server configuration automation. Step-by-step guide.