304 North Cardinal St.
Dorchester Center, MA 02124
In this post, I am going to show you how to deploy Caddy 2.3 in the Docker Swarm Cluster using docker-compose to act as a Reverse Proxy and Load Balancer for the micro-services.
Caddy is a powerful, enterprise-ready, open-source web server with automatic HTTPS written in Go. Caddy is the only webserver to use HTTPS automatically and by default.
Please make sure you should fulfill the below requirements before proceeding to the actual deployment.
The Caddy web server is an open-source web server written in Go. It uses the Go standard library for its HTTP functionality and supports HTTPS natively.
Matthew Holt began developing Caddy in December 2014 and released it in April 2015. Since then it has been worked on by over 200 other developers.
Please find Caddy’s official GitHub repo below
Caddy simplifies your infrastructure. It takes care of TLS certificate renewals, OCSP stapling, static file serving, reverse proxying, Kubernetes ingress, and more.
Its modular architecture means you can do more with a single, static binary that compiles for any platform.
Caddy runs great in containers because it has no dependencies-not even libc. Run Caddy practically anywhere.
Follow the below links if you want to know more about Caddy
Caddy Server has best-in-class security features. With regards to protocols and cipher suites, Caddy uses TLS 1.0-1.2 and prefers ECDHE ECDSA with AES-256 GCM SHA-384, although a dozen different ciphers are supported.
Caddy has also been used by Cloudflare as a platform to serve an experimental TLS 1.3 implementation.
Caddy is not vulnerable to a number of widespread CVEs including Heartbleed, DROWN, POODLE, and BEAST. In addition, Caddy uses TLS_FALLBACK_SCSV to prevent protocol downgrade attacks.
Caddy is the only web server to use HTTPS automatically and by default.
Best-in-class security features include…
A variety of web technologies can be served by Caddy.
Caddy is both a flexible, efficient static file server and a powerful, scalable reverse proxy or load balancer.
Use it to serve your static site with compression, template evaluation, Markdown rendering, and more.
tuneit.me is real example for serving static files (Jekyll) using Caddy.
Please go through my previous post to set up the Jekyll development environment on Windows 10. Generate static content to upload it to docker swarm cluster; serve the content through Caddy.
Most of Caddy’s feature implementations are found in Go’s library.
Some enhancements are available as middleware and exposed through directives in the Caddyfile (a text file used to configure Caddy).
Caddy’s capabilities include…
Containers are fast to deploy and make efficient use of system resources. Developers get application portability and programmable image management and the operations team gets standard run time units of deployment and management.
With all the known benefits of containers, there is one common misperception that the containers are ephemeral, which means if we restart the container or in case of any issues with it, we lose all the data for that particular container. They are only good for stateless micro-service applications and that it’s not possible to containerize stateful applications.
I am going to use GlusterFS to overcome the ephemeral behavior of Containers.
I already set up a replicated GlusterFS volume to have data replicated throughout the cluster if I would like to have some persistent data.
The below diagram explains how the replicated volume works.
Volume will be mounted on all the nodes, and when a file is written to the
/mntpartition, data will be replicated to all the nodes in the Cluster
In case any one of the nodes fails, the application automatically starts on another node without losing any data and that’s the beauty of the replicated volume.
Persistent application state or data needs to survive application restarts and outages. We are storing the data or state in GlusterFS and had periodic backups performed on it.
We will use a backup of the volume to spin a new application container anywhere else in case of unexpected issues occur in the current environment.
I am going to persistent
/var/log/caddy directories of Caddy for disorder recovery.
/data folder stores SSL certificate information of all the sites mentioned in Caddyfile,
/config folder stores caddy configuration and
/var/log/caddy folder stores caddy logs.
Create folders in
/mnt directory to persistent Caddy data folders.
sudo mkdir -p caddydata
sudo mkdir -p caddyconfig
sudo mkdir -p caddylog
Make sure you perisist static site folders as well if you want to serve file server using Caddy, like
Please watch the below video for Glusterfs Installation
Let’s create a Docker network for our Caddy proxy to share with other stack or containers. This network is necessary so that we can use it with applications that are run using Docker Compose. Let’s call this network as
docker network create -d overlay caddy
When the Caddy stack starts, we will add it to the
caddy network. Then we can add additional stacks or containers to this network later for Caddy to serve the stacks to the outside world.
Now let’s create a folder called caddy in /opt file location where I am going to put all the micro-services configurations individually (nothing but .yml files in different folders under /opt directory).
Now on-wards everything executed on the master node.
sudo mkdir -p caddy
Now create 2 files,
Caddyfile (a text file used to configure Caddy) and
caddy.yml (caddy docker compose).
sudo touch Caddyfile
sudo touch caddy.yml
caddy.yml file in nano or any text editor of your choice (I use nano because it’s easy to use)
sudo nano caddy.yml
Caddyfile is a configuration file for Caddy that’s human-readable and easy to write; it is perfect for most common and manual configurations.
A local file server with template evaluation
localhost templates file_server
Learn more about Caddyfile
Here is the
docker compose file for Caddy.
Copy and paste the below code in
version: "3.7" services: caddy: image: tuneitme/caddy ports: - "80:80" - "443:443" networks: - caddy volumes: - ./Caddyfile:/etc/caddy/Caddyfile - /mnt/caddydata:/data - /mnt/caddyconfig:/config - /mnt/caddylogs:/var/log/caddy - /mnt/blog:/etc/caddy/html/blog environment: ACME_AGREE: 'true' deploy: placement: constraints: - node.role == manager replicas: 1 update_config: parallelism: 2 delay: 10s restart_policy: condition: on-failure volumes: caddydata: driver: "local" caddyconfig: driver: "local" caddylogs: driver: "local" blog: driver: "local" networks: caddy: external: true
Here I am using a custom caddy docker container built with custom plugins, like Cloudflare DNS, Caddy Auth Portal, etc…
Please find the custom caddy docker image below.
Now it’s time to deploy our docker-compose file above,
caddy.yml using the below command
docker stack deploy --compose-file caddy.yml caddy
In the above command, you have to replace
caddy.yml with your docker-compose file name and caddy with whatever name you want to call this particular application
As mentioned earlier I named my docker-compose as
caddy.yml and named my application stack as
Check the status of the stack by using
docker stack ps caddy
caddy stack logs using
docker service logs caddy_caddy
One thing we observe is that it automatically re-directs to
https with Letsencrypt generated certificate. The information is stored in
I will be using this
caddy stackas a reverse proxy / load balancer for the applications I am going to deploy to Docker Swarm Cluster.
Also I use docker network
caddyto access the applications externally.
In the coming posts, I will show you all how I migrated my blog TUNEIT.ME from WordPress to Jekyll using Caddy as a reverse proxy.
I will deploy/run WordPress, Nextcloud, Rocker Chat, Dolibarr ERP, CRM, Metabase, Flarum, etc…to our Docker Swarm Cluster behind Caddy
Stay tuned for other deployments… 🙂