Most of my personal services I run within my Tailscale network, and so I do not tend to bother with HTTPS (since provisioning TLS certificates for multiple services running on a single host via Tailscale is a pain).
However, for external services that need to be accessed publicly, I use Traefik as a reverse proxy and for managing and provisioning TLS certificates.
Running Traefik
Run Traefik using Docker. Create a docker-compose.yml file in a directory called traefik:
services:
reverse-proxy:
image: traefik:v2.6
container_name: traefik
command:
- "--providers.docker"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.myresolver.acme.email=CHANGEME"
- "--certificatesresolvers.myresolver.acme.storage=/etc/traefik/acme/acme.json"
- "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"
ports:
- "80:80"
- "443:443"
volumes:
- ./acme:/etc/traefik/acme # To persist certificate info
- /var/run/docker.sock:/var/run/docker.sock # To access the host Docker daemon
networks:
- net
restart: always
networks:
net:
driver: bridge
Rate-limiting
Sometimes it is useful to rate-limit access to services via Traefik. For example, I recently experienced bots scraping my Gitea instance, causing the host to be partially overloaded.
Rate-limit rules can be set using standard Traefik labels in the compose definition for the relevant service. For example, in my Gitea docker-compose.yml file:
...
- traefik.http.routers.gitea.middlewares=gitea-ratelimit
- traefik.http.middlewares.gitea-ratelimit.ratelimit.average=2
- traefik.http.middlewares.gitea-ratelimit.ratelimit.period=1s
- traefik.http.middlewares.gitea-ratelimit.ratelimit.burst=5
The bridge network
This setup creates a bridge network into which we will add other services. This allows Traefik to route traffic to them.
The directory name (traefik) is important (in my case) since my services rely on the fact that the network created is called traefik_net.
Adding other services
When adding other services, follow these steps:
- Set-up DNS such that your chosen domain name points to the server
- Create a
docker-compose.ymlfor the service with Traefik labels (see below) - Bring up the service
An example docker-compose.yml for a Traefik-connected service:
services:
example:
image: example
networks:
- traefik_net
labels:
- traefik.http.routers.servicename.rule=Host(`domain.com`)
- traefik.http.routers.servicename.tls=true
- traefik.http.routers.servicename.tls.certresolver=myresolver
networks:
traefik_net:
external: true
Change domain.com to the real domain (or subdomain) and change servicename to a unique name in your Traefik network.
When you bring the service up, Traefik will detect it and then auto-provision and renew the TLS certificates.