Avnology ID
Self-Hosting

TLS via Traefik ACME

Let's Encrypt (HTTP-01) and DNS-01 wildcard setup.

TLS via Traefik ACME

Traefik handles certificate issuance and renewal from Let's Encrypt. Two modes are supported:

  • HTTP-01 challenge (default) -- simplest, works on any DNS provider, issues one cert per hostname.
  • DNS-01 challenge -- required for wildcard certs, needs an API-capable DNS provider.

HTTP-01 (default)

The shipped deploy/docker/traefik/traefik.yml is pre-configured for HTTP-01:

certificatesResolvers:
  letsencrypt:
    acme:
      email: [email protected]   # set via ACME_EMAIL env var
      storage

Requirements:

  • DNS records (see DNS setup) resolving to your Traefik host.
  • TCP ports 80 and 443 reachable from the public internet.
  • Set [email protected] in .env.production before docker compose up.

Traefik issues one cert per hostname on first request. Initial requests to a freshly-added hostname take ~10s while the challenge completes.

Rate limits

Let's Encrypt enforces 50 certs / domain / week and 5 duplicates / week. For production deployments set LETSENCRYPT_STAGING=true first to test against the staging CA, then flip it off to get real certs.

DNS-01 (wildcard)

Required if you want *.your-company.com covering every subdomain with a single cert, or if your Traefik host doesn't have inbound 80 exposed.

Supported providers (shipped resolver plugins):

  • Cloudflare (CF_API_EMAIL, CF_API_KEY or CF_DNS_API_TOKEN)
  • Route 53 (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION, AWS_HOSTED_ZONE_ID)
  • Google Cloud DNS (GCE_SERVICE_ACCOUNT_FILE)
  • DigitalOcean (DO_AUTH_TOKEN)

Swap the resolver in deploy/docker/traefik/traefik.yml:

certificatesResolvers:
  letsencrypt:
    acme:
      email: [email protected]
      storage: /letsencrypt/acme.json
      dnsChallenge:
        provider: cloudflare
        delayBeforeCheck: 15

Then request a wildcard in service labels:

labels:
  - "traefik.http.routers.api.tls.certresolver=letsencrypt"
  - "traefik.http.routers.api.tls.domains[0].main=your-company.com"
  - "traefik.http.routers.api.tls.domains[0].sans=*.your-company.com"

Cert storage

Certs live in the traefik-letsencrypt named volume. Back it up in your normal volume-backup cadence (see Backup). Losing acme.json triggers reissuance on next request, burning one of your weekly cert allowances.

Monitoring expiry

Traefik exposes cert expiry metrics at its internal :9100/metrics endpoint, scraped by Grafana. The shipped dashboard (grafana/dashboards/tls.json) alerts 30 days before expiry.