Hello, I’m setting up a docker-compose project and struggling with traefik and acme to configure a certificate for my backend service in the following docker-compose file. When starting the compose up traefik generates the proper certificate for the ‘frontend’ server but doesn’t proceed to set anything up for the ‘backend service’. Any help would be greatly appreciated.
networks:
web: {}
volumes:
db-data:
uploads-data:
traefik-acme:
services:
traefik:
image: traefik:v3.0
command:
- --providers.docker=true
- --api.dashboard=false
- --providers.docker.network=web
# Entrypoints
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
# Redirect HTTP → HTTPS
- --entrypoints.web.http.redirections.entryPoint.to=websecure
- --entrypoints.web.http.redirections.entryPoint.scheme=https
# ACME HTTP-01 challenge (no Cloudflare)
- --certificatesresolvers.le.acme.httpchallenge=true
- --certificatesresolvers.le.acme.httpchallenge.entrypoint=web
- --certificatesresolvers.le.acme.email=${LETSENCRYPT_EMAIL}
- --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json
# TEMP: increase log verbosity
- --certificatesresolvers.le.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
- --log.level=DEBUG
- --accesslog=true
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- traefik-acme:/letsencrypt
networks: [web]
restart: unless-stopped
db:
image: postgres:16
environment:
POSTGRES_DB: images
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
# optional: set PGDATA for clarity
# PGDATA: /var/lib/postgresql/data/pgdata
volumes:
- db-data:/var/lib/postgresql/data
networks: [web]
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
interval: 10s
timeout: 5s
retries: 10
# Do NOT expose to the Internet in prod
# ports:
# - "127.0.0.1:5432:5432"
backend:
build:
context: .
dockerfile: Dockerfile.prod.backend
environment:
DATABASE_URL: ${DATABASE_URL} # e.g. postgres://USER:PASS@db:5432/images?sslmode=disable
JWT_SECRET: ${JWT_SECRET}
CORS_ALLOWED_ORIGINS: https://app.${DOMAIN}
PUBLIC_BASE_URL: https://api.${DOMAIN} # for absolute URLs to uploads
ADMIN_PASSWORD: ${ADMIN_PASSWORD}
volumes:
- uploads-data:/app/uploads # persist uploads
depends_on:
db:
condition: service_healthy
labels:
- traefik.enable=true
- traefik.http.routers.api.rule=Host(`api.${DOMAIN}`)
- traefik.http.routers.api.entrypoints=websecure
- traefik.http.routers.api.tls=true
- traefik.http.routers.api.tls.certresolver=le
- traefik.http.services.api.loadbalancer.server.port=8080
networks: [web]
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:8080/"]
interval: 10s
timeout: 3s
retries: 10
frontend:
build:
context: .
dockerfile: Dockerfile.prod.frontend # builds static files, served by nginx
environment:
# if your build uses this at build-time, prefer build args instead
REACT_APP_API_URL: https://api.${DOMAIN}
labels:
- traefik.enable=true
- traefik.http.routers.front.rule=Host(`app.${DOMAIN}`)
- traefik.http.routers.front.entrypoints=websecure
- traefik.http.routers.front.tls=true
- traefik.http.routers.front.tls.certresolver=le
- traefik.http.services.front.loadbalancer.server.port=80
networks: [web]
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost/"]
interval: 30s
timeout: 5s
retries: 5