Using Caddy to replace Traefik and Nginx

written 2022-09-09

In my pevious post I explained how to use Traefik2 to get a site up and running. There is an alternative with much less configuration called Caddy that allows for the same features as Treafik2 with its caddy_docker_proxy plugin.

Setup the caddy reverse proxy

Similar to Traefik, the external Network has to be defined with: docker network create caddy To get the Docker Reverse Proxy (think Traefik) up and running I have prepared following docker-compose file:

docker-compose caddy-docker-proxy

version: "3.7"
services:
  caddy:
    image: lucaslorentz/caddy-docker-proxy:ci-alpine
    ports:
      - 8080:80 # change to 80 in Production
      - 8443:443 #change to 443 in Production
    environment:
      - CADDY_INGRESS_NETWORKS=caddy
    networks:
      - caddy
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - caddy_data:/data
      - /var/static_files/:/srv/static/ # Drop all your Static files here to be served by Caddy
    restart: unless-stopped

networks:
  caddy:
    external: true

volumes:
  caddy_data: {}

From here it is just docker-compose up -d and you are good. You might have noticed the /var_static_files/ path at the volumes directive. Here, all Files to be served by Caddy have to be stored. You can change the paths to your convenience.

Configure the service

Like Traefik2 the configuration is done via flags at the container level:

docker-compose example-for-django

version: '3'
services:
  postgres:
    image: docker.io/postgres:14

    environment:
      POSTGRES_USER: django_db
      POSTGRES_PASSWORD: ${django_db_pw}
    volumes:
    - database:/var/lib/postgresql/data
    networks:
      - internal

  web:
    build: .
    container_name: django_webserver
    volumes:
      - /var/static_files/django_webserver/static:/static-files # check that you use a diffrent folder for each of your projects
      - /var/static_files/django_webserver/media:/media-volume
    labels:
      caddy: localhost
      caddy.handle_path_0: /static/*
      caddy.handle_path_0.0_root: "* /srv/static/django_webserver/static/" # These have to correspond with the caddy file
      caddy.handle_path_0.1_file_server: ""
      caddy.handle_path_1: /media/*
      caddy.handle_path_1.0_root: "* /srv/static/django_webserver/media/"
      caddy.handle_path_1.1_file_server: ""
      caddy.reverse_proxy: "{{ upstreams 8000 }}"
    depends_on:
      - postgres
    networks:
      - caddy
      - internal

networks:
  caddy:
    external: True
  internal:
    external: False

volumes:
  database:

The flags will be handed over to caddy and a config will be generated from them. The numbers represent the ordering of the directives. To check the resulting Caddyfile you can docker exec <caddy_container_name> cat /config/caddy/Caddyfile.autosave:

localhost {
	handle_path /media/* {
		root * /srv/static/django_webserver/static/
		file_server
	}
	handle_path /static/* {
		root * /srv/static/django_webserver/media/
		file_server
	}
	reverse_proxy 172.19.0.3:8000
}

Conclusion

With this configuration, we get static file serving. Previously we needed nginx for that. We get HTTPS Redirect by default. We get a Lets Encrypt Certificate (for localhost caddy will provide a certificate). But with much less configuration than needed for Traefik2. Configuration via labels feels a bit weird at time, but much easier than Traefik2. The static file handling feels a bit nasty too, since they are leaving the compose context; in the end they are all stored on the same machine, or storage, or something — the CloudAge… — so I guess it’s not a problem.

There is no comment system. If you want to contact me about this article, you can do so via e-mail or Mastodon.