Caddy 2.3: Powerful, open source web server with automatic HTTPS

Caddy is a powerful, enterprise-ready, open-source web server with automatic HTTPS written in Go. Caddy is the only web server to use HTTPS automatically and by default.

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.

  1. Docker Swarm Cluster with GlusterFS as persistent tool.

Caddy Introduction

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 Github Repo

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.

Caddy Moving Parts

Follow the below links if you want to know more about Caddy

Caddy Server


Caddy Security

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…

  • Caddy obtains and renews TLS certificates for your sites automatically, it even staples OCSP responses.
  • Caddy’s novel certificate management features are the most mature and reliable in its class.
  • Caddy offers greater memory safety than other servers because it’s written in Go
  • A hardened TLS stack powered by the Go standard library serves a significant portion of all Internet traffic.

Caddy Capabilities or Features

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. 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…

  • HTTP/1.1 (plaintext HTTP), HTTP/2 (default for HTTPS connections), and as of 2.0 beta 17, experimental support for HTTP/3
  • HTTPS, either automatically enabled and managed, or manually configured
  • TLS 1.3 (including temporary support for older protocols)
  • Server Name Indication (SNI)
  • OCSP stapling
  • Multiple sites on the same port
  • Serve static files (uses sendfile where possible)
  • Reverse proxy (HTTP or WebSockets)
  • Load balancing with health checks
  • FastCGI proxy
  • Templates (similar to Server Side Includes)
  • Markdown rendering
  • Gzip compression
  • URL rewriting
  • Redirects
  • File browsing
  • Access, error, and process logs
  • Experimental QUIC support

Persist Caddy Data

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 /mnt partition, data will be replicated to all the nodes in the Cluster

GlusterFS Replicated Volume

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 /data/config and /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.

cd /mnt
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 /mnt/blog:/etc/caddy/html/blog

Please watch the below video for Glusterfs Installation

Prepare Caddy Environment

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 caddy.

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.

cd /opt
sudo mkdir -p caddy
cd 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

Open 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



Learn more about Caddyfile

Caddy Docker Compose

Here is the docker compose file for Caddy.

Copy and paste the below code in caddy.yml file.

version: "3.7"

    image: tuneitme/caddy
      - "80:80"
      - "443:443"
      - caddy
      - ./Caddyfile:/etc/caddy/Caddyfile
      - /mnt/caddydata:/data
      - /mnt/caddyconfig:/config
      - /mnt/caddylogs:/var/log/caddy
      - /mnt/blog:/etc/caddy/html/blog
      ACME_AGREE: 'true'
          - node.role == manager
      replicas: 1
        parallelism: 2
        delay: 10s
        condition: on-failure

    driver: "local"
    driver: "local"
    driver: "local"
    driver: "local"
    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.

Tuneit Caddy Docker Image

Deploy Caddy Stack using Docker Compose

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 caddy

Check the status of the stack by using docker stack ps caddy

Check 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 /data directory.

I will be using this caddy stack as a reverse proxy / load balancer for the applications I am going to deploy to Docker Swarm Cluster.

Also I use docker network caddy to 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… 🙂

Leave a Reply