Context
In order to expose containerized apps and make them available from the public internet with HTTPS/TLS enabled, a reverse proxy is a powerful and essential tool. It acts as a gateway that routes external requests to the appropriate internal services, all while handling encryption, domain mapping, and other network-layer concerns.
graph LR subgraph public internet LE[Let's Encrypt] C1[client 1] C2[client n] end subgraph services RP[reverse proxy] S1[containerized service 1] S2[containerized service n] end LE <--> RP C1 <--> RP C2 <--> RP RP <--> S1 RP <--> S2
Let’s Encrypt is a free, automated, and open Certificate Authority (CA). Let’s Encrypt fundamentally changed the internet by making HTTPS encryption more accessible, affordable, and widespread. Before its launch in 2016, SSL/TLS certificates (which enable HTTPS) were costly and complex to manage, making them less accessible for many website owners.
There are several tools available for setting up a reverse proxy, including Caddy, go-doxy, HAProxy, Jauth, Nginx Proxy Manager and Traefik.
In this post, we’ll walk through a setup using Traefik.
Traefik
My preferred way to manage containers is docker-compose, therfore the following descriptions are oriented by that.
docker-compose.yml
version: '3' services: traefik: image: traefik:latest ports: - "80:80" #HTTP port - "443:443" #HTTPS port - "8080:8080" #Web UI (enabled by --api.insecure=true) volumes: - /var/run/docker.sock:/var/run/docker.sock - ./data/traefik.yml:/etc/traefik/traefik.yml:ro - ./data/acme.json:/etc/traefik/acme.json - ./logs:/log environment: - TZ=Europe/Berlin networks: - nw-proxy-exposed networks: nw-proxy-exposed: external: true
data/traefik.yml
################################################################ # Global configuration ################################################################ global: checkNewVersion: true sendAnonymousUsage: false ################################################################ # EntryPoints configuration ################################################################ entryPoints: web: address: :80 http: redirections: entryPoint: to: websecure scheme: https websecure: address: :443 ################################################################ # Traefik logs configuration ################################################################ log: level: DEBUG filePath: log/traefik.log ################################################################ # Access logs configuration ################################################################ accessLog: filePath: log/access.log ################################################################ # API and dashboard configuration ################################################################ api: insecure: false dashboard: true ################################################################ # Docker configuration backend ################################################################ providers: docker: endpoint: "unix:///var/run/docker.sock" exposedByDefault: false ################################################################ # Let's Encrypt¶ ################################################################ certificatesResolvers: myresolver: acme: email: web@lammel.ch storage: /etc/traefik/acme.json httpChallenge: # used during the challenge entryPoint: web
data/acme.json
This file needs to be created without any content. It will be used as storage.
whoami (service 1)
The Docker service traefik/whoami is a tiny web application designed for testing and debugging in reverse proxy setups like Traefik.
Tiny Go webserver that prints OS information and HTTP request to output.
docker-compose.yml
Traefik uses Docker labels to configure and discover services. In my setup, five specific labels are required to ensure the service is properly detected and routed by the reverse proxy.
It’s important to note that the service must be on the same Docker network as the reverse proxy to allow proper communication and routing.
version: '3' services: whoami: image: traefik/whoami hostname: "whoami" labels: - traefik.enable=true - traefik.http.routers.whoami-traefik.rule=Host(`service.lammel.ch`) || Host(`whoami.service.lammel.ch`) - traefik.http.routers.whoami-traefik.service=whoami-traefik - traefik.http.routers.whoami-traefik.tls=true - traefik.http.routers.whoami-traefik.tls.certresolver=myresolver networks: - nw-proxy-exposed networks: nw-proxy-exposed: external: true
stirling-pdf (service 2)
Stirling-PDF is a free, open-source, locally hosted web application designed for comprehensive PDF manipulation.
docker-compose.yml
For this particular service, an additional label is required to specify the internal port the application is running on. Since Stirling-PDF listens on port 8080, the label traefik.http.services.pdf-service.loadbalancer.server.port=8080 must be set accordingly.
An interesting point: once the service is routed through the reverse proxy, there’s no need to expose the container port to the host system—Traefik handles the internal communication entirely within the Docker network.
version: '3.3' services: stirling-pdf: image: stirlingtools/stirling-pdf:latest container_name: stirling-pdf restart: unless-stopped labels: - traefik.enable=true - traefik.http.routers.pdf-service.rule=Host(`pdf.service.lammel.ch`) - traefik.http.routers.pdf-service.service=pdf-service - traefik.http.routers.pdf-service.tls=true - traefik.http.routers.pdf-service.tls.certresolver=myresolver - traefik.http.services.pdf-service.loadbalancer.server.port=8080 volumes: - ./StirlingPDF/trainingData:/usr/share/tessdata - ./StirlingPDF/extraConfigs:/configs - ./StirlingPDF/customFiles:/customFiles/ - ./StirlingPDF/logs:/logs/ - ./StirlingPDF/pipeline:/pipeline/ environment: - DOCKER_ENABLE_SECURITY=false - LANGS=en_GB networks: - nw-proxy-nonexposed networks: nw-proxy-nonexposed: external: true