Caddy takes a modular approach to building Docker images, allowing users to include only the modules they need. This repository aims to provide flexibility and convenience to run Caddy with specific combinations of modules by providing pre-built images according to the needs and preferences of the users.
All custom images are updated automatically when a new version of Caddy is released using the official Caddy Docker image. This is done by using GitHub Actions to build and push the images for all Caddy supported platforms to Docker Hub, GitHub Packages and Quay container registries. In addition, since the update cycle of many modules is faster than Caddy's, all custom images are periodically re-built with the latest version of their respective modules on the first day of every month. Those who are already running Caddy's latest version can force the update by re-creating the container (i.e. running docker compose up --force-recreate
if using Docker Compose).
All commits and tags are signed with a GPG key to ensure their integrity and authenticity, and 2FA is enabled in the accounts involved in the management of this repository and the container registries.
If you are looking for a specific custom build not available yet in this repository, please open a new Issue with your request. To make sure no broken or unsafe builds are created, the requested modules should be properly maintained and listed in the Caddy's download page. Additional information and instructions can be found by clicking on the name of the Caddy images and modules listed below.
- caddy-cloudflare: includes Cloudflare DNS and IPs modules.
- caddy-cloudflare-crowdsec: includes Cloudflare DNS/IPs and CrowdSec Bouncer modules.
- caddy-cloudflare-ddns: includes Cloudflare Dynamic DNS and IPs modules.
- caddy-cloudflare-ddns-crowdsec: includes Cloudflare Dynamic DNS/IPs and CrowdSec Bouncer modules.
- caddy-cloudflare-ddns-crowdsec-geoip: includes Cloudflare Dynamic DNS/IPs, CrowdSec Bouncer and GeoIP Filter modules.
- caddy-cloudflare-ddns-crowdsec-geoip-security: includes Cloudflare Dynamic DNS/IPs, CrowdSec Bouncer, GeoIP Filter and Caddy Security modules.
- caddy-cloudflare-ddns-crowdsec-geoip-security-dockerproxy: includes Cloudflare Dynamic DNS/IPs, CrowdSec Bouncer, GeoIP Filter, Caddy Security and Docker Proxy modules.
- caddy-cloudflare-ddns-security: includes Cloudflare Dynamic DNS/IPs and Caddy Security modules.
- caddy-crowdsec: includes CrowdSec Bouncer module.
- caddy-crowdsec-geoip: includes CrowdSec Bouncer and GeoIP Filter modules.
- caddy-crowdsec-geoip-ratelimit-security-dockerproxy: includes CrowdSec Bouncer, GeoIP Filter, Rate Limit, Caddy Security and Docker Proxy modules.
- caddy-duckdns: includes DuckDNS DNS module.
- caddy-duckdns-crowdsec: includes DuckDNS DNS and CrowdSec Bouncer modules.
- caddy-duckdns-ddns: includes DuckDNS Dynamic DNS module.
- caddy-duckdns-ddns-crowdsec: includes DuckDNS Dynamic DNS and CrowdSec Bouncer modules.
- caddy-duckdns-ddns-crowdsec-geoip-security: includes DuckDNS Dynamic DNS, CrowdSec Bouncer, GeoIP Filter and Caddy Security modules.
- caddy-duckdns-ddns-crowdsec-geoip-security-dockerproxy: includes DuckDNS Dynamic DNS, CrowdSec Bouncer, GeoIP Filter, Caddy Security and Docker Proxy modules.
- caddy-eventsexec: includes Events Exec module.
- caddy-gandi: includes Gandi DNS module.
- caddy-netcup: includes Netcup DNS module.
- caddy-netcup-ddns: includes Netcup Dynamic DNS module.
- caddy-porkbun-dockerproxy: includes Porkbun DNS and Docker Proxy modules.
- caddy-ratelimit-dockerproxy-sablier: includes Rate Limit, Docker Proxy and Sablier modules.
- Cloudflare DNS: for Cloudflare DNS-01 ACME validation support | caddy-dns/cloudflare
- Cloudflare IPs: to retrieve Cloudflare's current IP ranges | WeidiDeng/caddy-cloudflare-ip
- DuckDNS: for DuckDNS DNS-01 ACME validation support | caddy-dns/duckdns
- Gandi DNS: for Gandi DNS-01 ACME validation support | caddy-dns/gandi
- Netcup DNS: for Netcup DNS-01 ACME validation support | caddy-dns/netcup
- Porkbun DNS: for Porkbun DNS-01 ACME validation support | caddy-dns/porkbun
- Dynamic DNS: updates the DNS records with the public IP address of your instance | mholt/caddy-dynamicdns
- CrowdSec Bouncer: blocks malicious traffic based on CrowdSec decisions | hslatman/caddy-crowdsec-bouncer
- Rate Limit: implements both internal and distributed HTTP rate limiting | mholt/caddy-ratelimit
- Caddy Security: to add different authentication methods including MFA/2FA support | greenpau/caddy-security
- Docker Proxy: enables Caddy to be used for Docker containers via labels | lucaslorentz/caddy-docker-proxy
- Sablier: to start your containers on demand and shut them down automatically | acouvreur/sablier
- GeoIP Filter to allow or block traffic from specific regions based on Maxmind GeoLite2 database | porech/caddy-maxmind-geolocation
- Events Exec: implements an event handler that allows to execute commands on the system | mholt/caddy-events-exec
Since all images from this repository are built off the official Caddy Docker image, the same volumes and/or bind mounts, ports mapping, environment variables, etc. can be used with this container. Please refer to the official Caddy Docker image and docs for more information on using Caddy.
Docker builds for all Caddy supported platforms are available at the following container registries:
- Docker Hub >
docker pull serfriz/<caddy-build-name>:latest
- GitHub Packages >
docker pull ghcr.io/serfriz/<caddy-build-name>:latest
- Quay >
docker pull quay.io/serfriz/<caddy-build-name>:latest
To pull a specific build, replace <caddy-build-name>
with the desired one. For example, to pull the caddy-cloudflare
build from Docker Hub, use docker pull serfriz/caddy-cloudflare:latest
.
The following tags are available for all images:
latest
<version>
(eg:2.7.4
, including:2.7
,2
, etc.)
Simply create the container using the docker run
command, or a docker-compose.yml
file including the necessary environment variables depending on the modules used. The following blocks contain examples for both methods using <caddy-build-name>
as the image name (replace it with the desired Caddy build name), and including all environment variables required by the modules listed above (some may not apply to your specific build).
docker run --rm -it \
--name caddy \ # feel free to choose your own container name
--restart unless-stopped \ # run container unless stopped by user (optional)
-p 80:80 \ # HTTP port
-p 443:443 \ # HTTPS port
-p 443:443/udp \ # HTTP/3 port (optional)
-v caddy-data:/data \ # volume mount for certificates data
-v caddy-config:/config \ # volume mount for configuration data
-v $PWD/Caddyfile:/etc/caddy/Caddyfile \ # to use your own Caddyfile
-v $PWD/log:/var/log \ # bind mount for the log directory (optional)
-v $PWD/srv:/srv \ # bind mount to serve static sites or files (optional)
-e CLOUDFLARE_API_TOKEN=<token-value> \ # Cloudflare API token (if applicable)
-e DUCKDNS_API_TOKEN=<token-value> \ # DuckDNS API token (if applicable)
-e CROWDSEC_API_KEY=<key-value> \ # CrowdSec API key (if applicable)
-e GANDI_BEARER_TOKEN=<token-value> \ # Gandi API token (if applicable)
-e NETCUP_CUSTOMER_NUMBER=<number-value> \ # Netcup customer number (if applicable)
-e NETCUP_API_KEY=<key-value> \ # Netcup API key (if applicable)
-e NETCUP_API_PASSWORD=<password-value> \ # Netcup API password (if applicable)
-e PORKBUN_API_KEY=<key-value> \ # Porkbun API key (if applicable)
-e PORKBUN_API_SECRET_KEY=<secret-key-value> \ # Porkbun API secret key (if applicable)
serfriz/<caddy-build-name>:latest # replace with the desired Caddy build name
The volume and bind mounts can be adjusted to meet to your needs, $PWD
is used to reference the current working directory, but you can replace it with your preferred path. The environment variables are only required if the modules used in the build require them.
The default Caddyfile that is included inside the Docker container is just a placeholder to serve a static Caddy welcome page with some useful instructions. So you will most likely want to mount your own $PWD/Caddyfile
to configure Caddy according to your needs (the file must already exist in the specified path before creating the container).
The restart policy can be adjusted to your needs. The policy unless-stopped
ensures the container is always running (even at boot) unless it is explicitly stopped by the user.
version: "3.7"
services:
caddy:
image: serfriz/<caddy-build-name>:latest # replace with the desired Caddy build name
container_name: caddy # feel free to choose your own container name
restart: "unless-stopped" # run container unless stopped by user (optional)
ports:
- "80:80" # HTTP port
- "443:443" # HTTPS port
- "443:443/udp" # HTTP/3 port (optional)
volumes:
- caddy-data:/data # volume mount for certificates data
- caddy-config:/config # volume mount for configuration data
- $PWD/Caddyfile:/etc/caddy/Caddyfile # to use your own Caddyfile
- $PWD/log:/var/log # bind mount for the log directory (optional)
- $PWD/srv:/srv # bind mount to serve static sites or files (optional)
environment:
- CLOUDFLARE_API_TOKEN=<token-value> # Cloudflare API token (if applicable)
- DUCKDNS_API_TOKEN=<token-value> # DuckDNS API token (if applicable)
- CROWDSEC_API_KEY=<key-value> # CrowdSec API key (if applicable)
- GANDI_BEARER_TOKEN=<token-value> # Gandi API token (if applicable)
- NETCUP_CUSTOMER_NUMBER=<number-value> # Netcup customer number (if applicable)
- NETCUP_API_KEY=<key-value> # Netcup API key (if applicable)
- NETCUP_API_PASSWORD=<password-value> # Netcup API password (if applicable)
- PORKBUN_API_KEY=<key-value> # Porkbun API key (if applicable)
- PORKBUN_API_SECRET_KEY=<secret-key-value> # Porkbun API secret key (if applicable)
volumes:
caddy-data:
external: true
caddy-config:
Defining the data volume as external ensures that docker compose down
does not delete the volume, but you may need to create it first using docker volume create caddy-data
. This doesn't apply to bind mounts if you opt to use them instead of volumes.
Caddy does not require a full restart when the Caddyfile
is modified. Caddy comes with a caddy reload command which can be used to reload its configuration with zero downtime.
When running Caddy in Docker, the recommended way to trigger a config reload is by executing the caddy reload
command in the running container. First, you'll need to determine your container ID or name. Then, pass the container ID to docker exec. The working directory is set to /etc/caddy so Caddy can find your Caddyfile
without additional arguments.
caddy_container_id=$(docker ps | grep caddy | awk '{print $1;}') # use your container name if different from 'caddy'
docker exec -w /etc/caddy $caddy_container_id caddy reload
It is possible to create an alias for the caddy reload
command to make it more convenient to use by adding the following line to your ~/.bashrc
or ~/.zshrc
file:
alias caddy-reload="docker exec -w /etc/caddy $(docker ps | grep caddy | awk '{print $1;}') caddy reload"
Once you have added the alias to the appropriate file, you will need to source it for the changes to take effect. You can do this by running source ~/.bashrc
or source ~/.zshrc
in your terminal. After this, you will be able to use the caddy-reload
alias in your terminal sessions.
This section aims to provide some basic information on how to configure Caddy with the modules included in the custom builds, but it is not intended to be a comprehensive guide. All the examples are based on the official Caddyfile syntax and the modules' documentation.
To make use of the different modules that provide DNS-01 ACME validation support at the server level, set the global acme_dns directive in your Caddyfile
using your DNS provider's name and the respective environment variable for the API token. The example shows the use case for Cloudflare DNS with the rest of the DNS providers commented out.
{
acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN} # for Cloudflare
# acme_dns duckdns {env.DUCKDNS_API_TOKEN} # for DuckDNS
# acme_dns gandi {env.GANDI_BEARER_TOKEN} # for Gandi
# acme_dns netcup { # for Netcup
# customer_number {env.NETCUP_CUSTOMER_NUMBER}
# api_key {env.NETCUP_API_KEY}
# api_password {env.NETCUP_API_PASSWORD}
# }
# acme_dns porkbun { # for Porkbun
# api_key {env.PORKBUN_API_KEY}
# api_secret_key {env.PORKBUN_API_SECRET_KEY}
# }
}
Alternatively, you can use the tls
directive at each site. See the caddy-dns/cloudflare module for additional details.
my.domain.tld {
tls {
dns cloudflare {env.CLOUDFLARE_API_TOKEN} # for Cloudflare
# dns duckdns {env.DUCKDNS_API_TOKEN} # for DuckDNS
# dns gandi {env.GANDI_BEARER_TOKEN} # for Gandi
# dns netcup { # for Netcup
# customer_number {env.NETCUP_CUSTOMER_NUMBER}
# api_key {env.NETCUP_API_KEY}
# api_password {env.NETCUP_API_PASSWORD}
# }
# dns porkbun { # for Porkbun
# api_key {env.PORKBUN_API_KEY}
# api_secret_key {env.PORKBUN_API_SECRET_KEY}
# }
}
}
You can generate a Cloudflare API token via the Cloudflare web dashboard through the following steps:
- Login to your Cloudflare Dashboard
- Go to Account Profile > API Tokens
- Click "Create token" (Use the "Create Custom Token" option)
- Grant the following permissions:
Zone > Zone > Read
Zone > DNS > Edit
- Copy the token and use it as the
CLOUDFLARE_API_TOKEN
environment variable.
To generate a DuckDNS API token, login to your DuckDNS account, copy the token, and use it as the DUCKDNS_API_TOKEN
environment variable. You can recreate the token by clicking on the three vertical lines in the top right corner next to your logged in email, and selecting the recreate token option.
To generate a Netcup API token follow the steps from the Netcup API docs. Use the NETCUP_CUSTOMER_NUMBER
, NETCUP_API_KEY
and NETCUP_API_PASSWORD
environment variables in the Docker Compose/Run and Caddyfile
configuration.
To restrict access to your server only to Cloudflare's IP ranges, add the trusted_proxies directive to the global options, under servers, in your Caddyfile
. For additional details, refer to trusted_proxies/cloudflare documentation and WeidiDeng/caddy-cloudflare-ip repository.
{
servers {
trusted_proxies cloudflare {
interval 12h
timeout 15s
}
}
}
To keep your DNS records updated with the public IP address of your instance, add the dynamic_dns directive to the global options in your Caddyfile
. This module regularly queries a service for your public IP address and updates the DNS records via your DNS provider's API whenever it changes. For additional details and advanced configuration examples refer to mholt/caddy-dynamicdns repository. The example shows the use case for Cloudflare DNS with the rest of the DNS providers commented out.
{
dynamic_dns {
provider cloudflare {env.CLOUDFLARE_API_TOKEN} # for Cloudflare
# provider duckdns {env.DUCKDNS_API_TOKEN} # for DuckDNS
# dns netcup { # for Netcup
# customer_number {env.NETCUP_CUSTOMER_NUMBER}
# api_key {env.NETCUP_API_KEY}
# api_password {env.NETCUP_API_PASSWORD}
# }
domains {
domain.tld
}
}
}
Using the option dynamic_domains, it can also be configured to scan through the domains configured in the Caddyfile
and try to manage those DNS records.
CrowdSec is a free and open source security automation tool that uses local logs and a set of scenarios to infer malicious intent. In addition to operating locally, an optional community integration is also available, through which crowd-sourced IP reputation lists are distributed.
To make use of the CrowdSec Bouncer module, set the global crowdsec directive in your Caddyfile
, and include it in every site you want to protect. For advanced usage, refer to the hslatman/caddy-crowdsec-bouncer repository.
{
debug # makes Caddy logs more detailed (optional)
order crowdsec first # forces the CrowdSec directive to be executed first
crowdsec {
api_url http://localhost:8080 # it should point to your CrowdSec API (it can be a remote URL)
api_key {env.CROWDSEC_API_KEY}
}
}
my.domain.tld {
crowdsec
log {
output file /var/log/access.log # the path should match the bind mount of the log directory
}
}
To register the Caddy CrowdSec Bouncer to your API, you need to run the command below on the server where the CrowdSec API is installed, and use the generated API key CROWDSEC_API_KEY
as environment variable when creating the Caddy container.
sudo cscli bouncers add caddy-bouncer
For additional details, refer to the CrowdSec documentation.
The rate_limit HTTP handler module lets you define rate limit zones, which have a unique name of your choosing. If a rate limit is exceeded, an HTTP error with status 429 will be returned. This error can be handled using the conventional error handling routes in your config.
Additional information and Caddyfile
configuration examples can be found in the mholt/caddy-ratelimit repository.
This plugin implements different authentication methods: Form-Based, Basic, Local, LDAP, OpenID Connect, OAuth 2.0, SAML, including MFA/2FA with App Authenticators and Yubico (formerly caddy-auth-portal
). It is also an authorization plugin for HTTP request authorization based on JWT/PASETO tokens (formerly caddy-authorize
, caddy-auth-jwt
), and manages credentials for various integrations.
Please, refer to the official greenpau/caddy-security documentation for additional details. Some configuration examples can also be found in the authp/authp.github.io repository.
The plugin scans Docker metadata, looking for labels indicating that the service or container should be served by Caddy. Then, it generates an in-memory Caddyfile
with site entries and proxies pointing to each Docker service by their DNS name or container IP. Every time a Docker object changes, the plugin updates the Caddyfile
and triggers Caddy to gracefully reload with zero downtime.
Additional information and Caddyfile
configuration examples can be found in the lucaslorentz/caddy-docker-proxy repository.
Sablier is a free and open-source software that can scale your workloads on demand following different strategies. Your workloads can be a docker container, a kubernetes deployment and more (see providers for the full list). This plugin provides an integration with Caddy.
Additional information and Caddyfile
configuration examples can be found in the acouvreur/sablier documentation.
Allows Caddy to filter traffic based on the client's IP address location. This module needs access to the Maxmind GeoLite2 database which can be downloaded for free after creating an account. Additional information is available on Maxmind official website. You will specifically need the GeoLite2-Country.mmdb file, or the GeoLite2-City.mmdb if you're matching on subdivisions and metro codes.
Information and examples about the usage of this module can be found on the on the Caddy website's plugin page and the porech/caddy-maxmind-geolocation repository.
It can be used to execute commands on the system based on specific events, such as when a certificate is renewed. This is configured in the Caddyfile
using the standard events directive along with the events.handlers.exec module. Additional information and examples can be found in the mholt/caddy-events-exec repository. Please be mindful of any security implications of the commands you run and how you configure this module.
Feel free to contribute, request additional Caddy images with your preferred modules, and make things better by opening an Issue or Pull Request.
Software under GPL-3.0 ensures users' freedom to use, modify, and distribute it while keeping the source code accessible. It promotes transparency, collaboration, and knowledge sharing. Users agree to comply with the GPL-3.0 license terms and provide the same freedom to others.