A lightweight, secure, and flexible Docker image for Caddy server based on Alpine Linux. This setup provides an easy way to deploy Caddy with custom plugins, configurations, and static content.
This Docker setup comes with several powerful features that make it ideal for both development and production environments:
- Based on lightweight Alpine Linux
- Support for custom Caddy plugins
- Automatic HTTPS with Let's Encrypt
- Security-focused configuration
- Volume persistence for configurations and certificates
- Easy customization through environment variables
- Support for multiple architectures (amd64, arm64, armv7, armv6)
The fastest way to get started is using docker-compose:
# Clone the repository
git clone https://github.com/hhftechnology/alpine-caddy
cd alpine-caddy
# Start the server
docker compose up -d
Visit http://localhost to see the default welcome page.
The repository follows a clear structure for easy navigation:
π Project Root
βββ Dockerfile # Base Dockerfile
βββ docker-compose.yml # Docker Compose configuration
βββ rootfs # Root filesystem overlay
βββ etc
β βββ caddy # Caddy configuration
β β βββ Caddyfile # Default Caddyfile
β βββ entrypoint.d # Startup scripts
βββ usr/local/bin # Executables
βββ var/www # Default web root
The default Caddyfile provides a secure starting point:
{
admin off
persist_config off
}
:80 {
root * /var/www
file_server
log {
output stdout
format json
}
}
Create your own Caddyfile by mounting a volume:
services:
caddy:
volumes:
- ./my-caddyfile:/etc/caddy/Caddyfile
The image supports several environment variables:
USER
: The user to run Caddy (default: "caddy")PLUGINS
: Array of Caddy plugins to installXDG_CONFIG_HOME
: Config directory (default: /config)XDG_DATA_HOME
: Data directory (default: /data)
You can add Caddy plugins in two ways:
- Using environment variables in docker-compose.yml:
services:
caddy:
environment:
- PLUGINS=["github.com/gamalan/caddy-tlsredis", "github.com/greenpau/caddy-auth-jwt"]
- Creating a custom Dockerfile:
FROM hhftechnology/alpine-caddy:latest
ENV PLUGINS=( \
"github.com/gamalan/caddy-tlsredis" \
"github.com/greenpau/caddy-auth-jwt" \
)
RUN bash -c '/usr/local/bin/caddy-install.sh'
Host a static website with automatic HTTPS:
example.com {
root * /var/www
file_server
encode gzip
tls your@email.com
}
Set up a reverse proxy to your backend services:
api.example.com {
reverse_proxy localhost:8080
tls your@email.com
}
Host a PHP website using FastCGI:
example.com {
root * /var/www
php_fastcgi php-fpm:9000
file_server
tls your@email.com
}
Docker Compose configuration:
services:
caddy:
image: hhftechnology/alpine-caddy
ports:
- "80:80"
- "443:443"
volumes:
- ./www:/var/www
depends_on:
- php-fpm
php-fpm:
image: php:8-fpm-alpine
volumes:
- ./www:/var/www
Host multiple sites with different configurations:
site1.example.com {
root * /var/www/site1
file_server
tls your@email.com
}
site2.example.com {
root * /var/www/site2
file_server
tls your@email.com
}
Enable WebSocket proxying:
ws.example.com {
reverse_proxy /ws localhost:8080
tls your@email.com
}
The image includes several security enhancements:
- Runs as non-root user (caddy)
- Admin API disabled by default
- Configuration persistence disabled
- Minimal base image
- Regular security updates
Additional security recommendations:
- Use specific versions instead of 'latest' tag
- Regularly update the image
- Implement rate limiting for production
- Use secure headers
Example secure headers configuration:
example.com {
header {
# Enable HTTP Strict Transport Security (HSTS)
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
# Prevent clickjacking attacks
X-Frame-Options "SAMEORIGIN"
# Help prevent XSS attacks
X-XSS-Protection "1; mode=block"
# Prevent MIME-sniffing
X-Content-Type-Options "nosniff"
# Referrer policy
Referrer-Policy "strict-origin-when-cross-origin"
# Remove Server header
-Server
}
root * /var/www
file_server
}
Optimize your Caddy server for production:
- Enable compression:
encode gzip
- Configure caching:
header Cache-Control "public, max-age=3600"
- Use HTTP/3:
{
servers {
protocol {
experimental_http3
}
}
}
Enable structured logging for better observability:
{
log {
output stdout
format json
level INFO
}
}
For production monitoring, consider adding Prometheus metrics:
metrics.example.com {
metrics
basicauth {
metrics-user JDJhJDE0JE91S1FrN0Z0VEsyR2xnUmZJMnQuL2VtT29qYWZ3WWFzYkY1bVI1Qi9JS1RBc2hXWEdpT0ph
}
}
Common issues and solutions:
-
Permission errors:
- Check volume permissions
- Ensure correct user ownership
-
SSL certificate issues:
- Verify DNS records
- Check domain accessibility
- Ensure ports 80/443 are accessible
-
Plugin installation failures:
- Verify plugin compatibility
- Check for build dependencies
- Examine build logs
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Submit a pull request
This project is licensed under the MIT License.
For support:
- Open an issue on GitHub
- Join our community discussions
- Check the documentation
Remember to always check for the latest version and updates on our GitHub repository.