Traefik not generating certificate for subdomain

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

What do the logs tell you? Did you check with docker inspect if the env vars were successfully replaced?

Give your Docker network in compose file a name, or it will be prefixed with the project name. Compare to simple Traefik example.

Thank you for the response as it ultimately led to me finding the simple problem I overlooked. That being said, the backend service wasn't healthy so traefik wasn't attempting to generate certs for it. After finding that and fixing the problem, traefik now properly generates the certificates needed.