Deploying a Personal Website with Docker and Caddy

These notes record how I set up a static website with Docker and gave it HTTPS automatically. The whole process was much simpler than I expected, mostly thanks to the Caddy web server.

Why Caddy

In the past, enabling HTTPS meant requesting a certificate yourself, configuring Nginx, and writing a cron job to renew the certificate — tedious work. What attracts me most about Caddy is that, by default, it automatically obtains certificates from Let's Encrypt and renews them before they expire, with almost no extra configuration.

Directory layout

I organised the whole site like this:

ztsj-site/
├── docker-compose.yml
├── Caddyfile
└── site/
    └── index.html

Writing the Caddyfile

The Caddyfile syntax is very intuitive — declare the domain, point to the site root, and enable gzip:

example.com {
    root * /srv
    file_server
    encode gzip
}
As long as DNS already points to the server and port 80 is open, Caddy will obtain the certificate automatically once it starts.

Orchestrating with Docker Compose

Run Caddy inside a container, mount the config file and site directory, and use a volume to persist the certificates:

services:
  caddy:
    image: caddy:2
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - ./site:/srv:ro
      - caddy_data:/data
      - caddy_config:/config
volumes:
  caddy_data:
  caddy_config:

Start and verify

A single command starts everything:

docker compose up -d

Checking the logs afterwards, you can see Caddy complete the ACME challenge and issue the certificate. Open the domain in a browser and the padlock is already green.

Pitfalls I hit

Summary

With Docker plus Caddy, going from nothing to a website with HTTPS took only about fifteen minutes of hands-on work. Containerization keeps the environment clean and reproducible, and this approach scales to more complex services later.

← Back to home