notes.dt.in.th

The most boring thing about setting up a web server on a new cloud server for me is the initial configuration. From installing and configuring nginx to setting up Let's Encrypt with certbot1. There are so many steps involved and so much boilerplate code in the config2.

This has changed now that Caddy exists. It is an open source web server written in Go that has built-in Let's Encrypt support. And it has the simplest config file format I've ever seen for a web server:

Built with Go, it is available as a standalone binary. Packages for popular package managers are available. And there is also a ready-to-use Docker image.

Usage with Docker Compose

Here's a basic docker-compose file that runs Caddy:

# ingress/docker-compose.yml
version: '3.7'
services:
  caddy:
    image: caddy
    restart: unless-stopped
    ports:
      - '80:80'
      - '443:443'
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - ./public:/var/www/html
      - caddy_data:/data
      - caddy_config:/config
volumes:
  caddy_data:
  caddy_config:

Now when I run docker-compose up -d, Caddy starts up and:

  • Listens to port 80 and 443.
  • Obtains a Let's Encrypt certificate for domain vps.dt.in.th.
  • Sets up HTTP to HTTPS redirection on port 80.
  • Serves files from public folder (mounted to /var/www/html inside container).
  • Proxies requests to /api/ to the api hostname.

Configuring Docker networks for reverse proxy

Now, other projects I run on this VPS have their own docker-compose file. We need to make the container join Caddy's Docker network for Caddy to be able to look it up via hostname.

This can be done by joining an external network and setting a network alias for that network.

# api/docker-compose.yml
version: '3.8'
services:
  server:
    restart: unless-stopped
    build: .
    networks:
      default:
      ingress:
        aliases:
          - api
networks:
  ingress:
    external: true
    name: ingress_default

Footnotes

  1. A commenter recommended me try out acme.sh should I need to use nginx again.

  2. Boilerplate include but not limited to:

    • Creating a file in sites-available.
    • Setting up a server block.
    • Setting up redirect from HTTP to HTTPS.
    • Setting up the path to the SSL certificate file.
    • Setting up HTTP headers for the reverse proxy, such as Host, X-Real-IP, X-Forwarded-For.
    • Setting up WebSocket support.
    • Symlinking the file to sites-enabled.