Traefik v3 routing HTTPS traffic to Docker containers with Docker Compose
Docker

Traefik Docker Compose: v3 Reverse Proxy with Auto HTTPS

Every application you self-host wants something from you: its own port, its own domain name and, if you don’t want browsers to show a scary warning, its own HTTPS certificate. Do that by hand for three or four apps and you end up juggling Nginx config files, certbot timers and a list of port numbers you have to remember. Traefik turns this around. It watches Docker, reads a few labels you attach to each container, and builds the routing and the Let’s Encrypt certificates by itself. Start a container with the right labels and, a few seconds later, it answers on its own domain over HTTPS. Stop it, and the route disappears.

In this traefik docker compose tutorial we set up Traefik v3 as the single entry point of a fresh VPS. We route domains to containers using Docker labels, get free certificates automatically, redirect every HTTP request to HTTPS and put the Traefik dashboard behind a password. Traefik is open source (MIT license), developed by Traefik Labs, with documentation at doc.traefik.io and code at github.com/traefik/traefik . Every command below is copy-pasteable; you only need to replace example.com with your own domain.

What Traefik is and why you want a reverse proxy

A few terms first, because the rest of the article builds on them.

A port is a numbered door on a server. Web browsers knock on door 80 for plain HTTP and on door 443 for HTTPS. Only one program can listen on a given port at a time, which is exactly why running several web apps on one server needs some kind of traffic director.

A container is an application packaged with everything it needs, running isolated from the rest of the system. Docker is the tool that runs containers, and Docker Compose lets you describe them in a YAML file instead of typing long commands every time.

A reverse proxy is a program that sits in front of your apps. It is the only thing listening on ports 80 and 443. It looks at the domain the visitor asked for (the Host header of the request) and forwards the request to the right app behind it. Your apps never need to be reachable directly from the internet, and the reverse proxy becomes the one place where HTTPS certificates live.

Traefik is a reverse proxy built for containers. Classic proxies such as Nginx read a configuration file that you edit by hand and reload. Traefik connects to the Docker API, notices when containers start or stop, and reads their labels: small key=value annotations stored on a container, such as traefik.http.routers.blog.rule=Host(`blog.example.com`). That is the whole trick: the routing configuration lives next to the app it belongs to, inside that app’s own compose file.

The four words you need: entrypoints, routers, middlewares, services

The Traefik documentation uses four concepts everywhere. Once they click, every label in this tutorial makes sense:

  • Entrypoint: a port Traefik listens on. We create two: web on port 80 and websecure on port 443.
  • Router: a rule that matches incoming requests, usually by domain, for example Host(`whoami.example.com`). A router also says which entrypoint it listens on and whether it uses TLS.
  • Middleware: an optional step that changes the request on its way through: ask for a password, redirect, add security headers.
  • Service: where the request finally goes, meaning the IP address and port of a container. With Docker, Traefik discovers the IP for you; you only tell it the port.

Traefik also separates static configuration from dynamic configuration. Static configuration describes how Traefik itself starts (entrypoints, the Docker provider, the certificate resolver); we pass it as command-line flags, and changing it requires a container restart. Dynamic configuration (routers, middlewares, services) is read live from container labels, with no restart at all.

How a request reaches your containerInternetbrowsersLet's EncryptACME CADreamServer VPS (Debian 13 / Ubuntu 24.04)ufwSSH :22host services onlypublished by Docker: 80, 443Traefik v3web :80 -> HTTPSwebsecure :443routers + TLSmiddlewaresACMEdocker.sockread-onlyacme.jsoncertificatesOnly Traefik publishes portsDocker network: proxywhoamiwhoami.example.comblogblog.example.comstatusstatus.example.comyour appapp.example.comapps publish no ports, they only join proxyNamed volumes: app data survives updates
Figure 1: Traefik is the only container with published ports. It routes each domain to a container on the proxy network, while ufw only protects host services such as SSH (ports published by Docker bypass ufw).

What you need

  • A VPS running Debian 13 or Ubuntu 24.04 with a public IPv4 address. 1 vCPU and 1 GB of RAM are plenty for Traefik plus a few small apps. A KVM VPS is the easiest choice for Docker; inside an unprivileged LXC container, Docker only works if nesting is enabled for that container, and some features can still misbehave.
  • A user with sudo rights and SSH access.
  • A domain where you can edit DNS records. In the examples we use traefik.example.com for the dashboard and whoami.example.com for a test app.
  • An email address for your Let’s Encrypt account.
  • Ports 80 and 443 free on the host. If Apache or Nginx were installed directly on the VPS, stop and disable them first (see troubleshooting).
  • An up-to-date system:
sudo apt update && sudo apt full-upgrade -y

On Ubuntu, reboot if the file /var/run/reboot-required exists afterwards. Debian does not always create that file, so on Debian simply reboot if the upgrade installed a new linux-image package.

Traefik Docker Compose setup, step by step

The whole procedure has seven steps. The numbers in the diagram match the step headings below, so you can always see where you are.

Traefik setup in 7 steps1234567InstallDockerPointDNSNetwork+ acme.jsonTraefikcompose.yamlDashboardpasswordStart+ verifyAdd appwith labelsNumbers match the Step 1 to Step 7 headings below
Figure 2: The seven steps of this tutorial, from Step 1 (install Docker) to Step 7 (first app behind Traefik). Each number matches a step heading.

Step 1: Install Docker Engine and the Compose plugin

The Docker packages in the default Debian and Ubuntu repositories tend to lag behind, so we use Docker’s official repository. The block below works on both distributions: it reads /etc/os-release to find out whether you are on Debian or Ubuntu ($ID) and which release you run ($VERSION_CODENAME, which is trixie on Debian 13 and noble on Ubuntu 24.04), then writes the repository in Docker’s current deb822 format to /etc/apt/sources.list.d/docker.sources.

sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
. /etc/os-release
sudo curl -fsSL "https://download.docker.com/linux/${ID}/gpg" -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
sudo tee /etc/apt/sources.list.d/docker.sources > /dev/null <<EOF
Types: deb
URIs: https://download.docker.com/linux/${ID}
Suites: ${VERSION_CODENAME}
Components: stable
Signed-By: /etc/apt/keyrings/docker.asc
EOF
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

The only difference between the two distributions is the repository URL (.../linux/debian or .../linux/ubuntu), and the variables take care of it. Check that both pieces answer:

sudo docker version --format '{{.Server.Version}}'
sudo docker compose version

You should see a Docker Engine version number and a line like Docker Compose version v2.x.x. In this tutorial we run Docker commands with sudo. You can add your user to the docker group to skip it, but be aware that membership in that group is equivalent to root access on the machine.

Step 2: Point your DNS records to the VPS

Let’s Encrypt only issues a certificate for a domain if it can reach your server under that name. So, before Traefik starts, create an A record (a DNS entry that maps a name to an IPv4 address) for each hostname you plan to use, in the DNS panel of your domain provider:

NameTypeValue
traefik.example.comAyour VPS IPv4 address
whoami.example.comAyour VPS IPv4 address

If your VPS also has IPv6 and you add AAAA records, make sure IPv6 really works on the server: when an AAAA record exists, Let’s Encrypt prefers to validate over IPv6. Check that the records resolve from the VPS:

getent ahostsv4 whoami.example.com | head -n 1

The first column must be your VPS address. If the output is empty or shows an old IP, wait a few minutes before going on. Requesting certificates for a name that does not point to you yet is the most common cause of failed validations, and repeated failures count against Let’s Encrypt rate limits.

Step 3: Create the proxy network and acme.json

Traefik must share a Docker network with every app it forwards traffic to. We create one network called proxy, outside any compose project, so every future app can join it:

sudo docker network create proxy

Next, the folder for Traefik’s files. We keep it in /opt/traefik and give your user ownership, so you can edit files without sudo:

sudo mkdir -p /opt/traefik/letsencrypt
sudo chown -R "$USER": /opt/traefik
touch /opt/traefik/letsencrypt/acme.json
chmod 600 /opt/traefik/letsencrypt/acme.json

acme.json is where Traefik stores your Let’s Encrypt account and every certificate together with its private key. Traefik refuses to use the file if its permissions are more open than 600 (read and write for the owner only), which is why we set them explicitly.

Step 4: Write the Traefik compose.yaml

First, a small .env file. Docker Compose reads it automatically and replaces ${VARIABLE} placeholders in the compose file, so your personal values stay out of the YAML:

cd /opt/traefik
cat > .env <<'EOF'
ACME_EMAIL=you@example.com
TRAEFIK_DOMAIN=traefik.example.com
EOF
chmod 600 .env

Replace both values with yours. Now create the compose file with nano /opt/traefik/compose.yaml (the older name docker-compose.yml works too) and paste:

services:
  traefik:
    image: traefik:v3
    container_name: traefik
    restart: unless-stopped
    command:
      - --providers.docker=true
      - --providers.docker.exposedbydefault=false
      - --providers.docker.network=proxy
      - --entrypoints.web.address=:80
      - --entrypoints.web.http.redirections.entrypoint.to=websecure
      - --entrypoints.web.http.redirections.entrypoint.scheme=https
      - --entrypoints.websecure.address=:443
      - --entrypoints.websecure.http.tls.certresolver=le
      - --certificatesresolvers.le.acme.email=${ACME_EMAIL}
      - --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json
      - --certificatesresolvers.le.acme.httpchallenge=true
      - --certificatesresolvers.le.acme.httpchallenge.entrypoint=web
      - --api.dashboard=true
      - --log.level=INFO
      - --accesslog=true
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./letsencrypt:/letsencrypt
    networks:
      - proxy
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.dashboard.rule=Host(`${TRAEFIK_DOMAIN}`)"
      - "traefik.http.routers.dashboard.entrypoints=websecure"
      - "traefik.http.routers.dashboard.service=api@internal"
      - "traefik.http.routers.dashboard.middlewares=dashboard-auth"
      - "traefik.http.middlewares.dashboard-auth.basicauth.users=${DASHBOARD_AUTH}"

networks:
  proxy:
    external: true

Note that there is no version: key at the top: current Docker Compose v2 no longer needs it. Here is what each part does:

  • image: traefik:v3: the current major version tag. It follows every v3.x release, so a later docker compose pull brings fixes without jumping to a future v4 with breaking changes. Check the Traefik releases page for the newest version and its notes; if you prefer full control, pin an exact v3.x tag instead.
  • providers.docker=true: tells Traefik to read containers and their labels from Docker.
  • providers.docker.exposedbydefault=false: a container is ignored unless it carries traefik.enable=true. Without this, every container you start would get a public route, databases included.
  • providers.docker.network=proxy: when a container sits on several networks, Traefik uses the proxy network to reach it.
  • entrypoints.web and entrypoints.websecure: the two doors, ports 80 and 443.
  • entrypoints.web.http.redirections...: every request arriving on port 80 is permanently redirected to HTTPS. The Let’s Encrypt HTTP challenge still works, because Traefik answers it with a higher priority than the redirect.
  • entrypoints.websecure.http.tls.certresolver=le: every router on port 443 gets TLS and a certificate from the resolver named le by default.
  • certificatesresolvers.le.acme.*: the Let’s Encrypt setup: your email, where certificates are stored, and the HTTP-01 challenge on the web entrypoint (Let’s Encrypt fetches a token over port 80 to prove you control the domain).
  • api.dashboard=true: enables the web dashboard. We never expose it directly, only through the protected router defined in the labels.
  • ports: the only place in the whole setup where ports are published to the internet.
  • /var/run/docker.sock:ro: lets Traefik watch Docker. More on what that implies in the security notes.
  • logging: caps Docker logs at three files of 10 MB, so access logs never fill the disk. We set it per service, so there is no need to touch Docker’s global daemon.json.
  • The labels on Traefik itself create the dashboard router: api@internal is Traefik’s built-in dashboard service, and dashboard-auth is the password middleware we fill in during the next step.

Tip: while you are testing, add - --certificatesresolvers.le.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory to the command list. The staging server issues untrusted test certificates but has much more generous limits, so mistakes do not lock you out of production. Before going live, remove the line, empty acme.json (see troubleshooting) and start again.

Step 5: Protect the dashboard with a password

The dashboard shows every router, service and middleware Traefik knows about. That is useful to you and equally useful to an attacker, so we protect it with HTTP basic authentication. Traefik expects the user and a hashed password in htpasswd format. Install the tool that generates it (the package is called apache2-utils on both Debian and Ubuntu) and append the result to .env:

sudo apt install -y apache2-utils
cd /opt/traefik
echo "DASHBOARD_AUTH='$(htpasswd -nB admin)'" >> .env

htpasswd asks for the password twice and produces a line like admin:$2y$05$Qe.... The -B flag uses bcrypt, a deliberately slow hash that resists brute force. Now look at the file:

cat .env

The last line must look like DASHBOARD_AUTH='admin:$2y$05$...', wrapped in single quotes. They matter: the hash contains $ characters, and inside single quotes Compose takes them literally instead of treating them as variables. If you ever paste a hash directly into compose.yaml instead, you have to double every $ as $$.

Step 6: Start Traefik and verify the certificates

Time to start the proxy:

cd /opt/traefik
sudo docker compose up -d
sudo docker compose ps

Compose pulls the image and starts the container. The ps output should show it running with both ports published (trimmed here):

NAME      IMAGE        SERVICE   STATUS         PORTS
traefik   traefik:v3   traefik   Up 8 seconds   0.0.0.0:80->80/tcp, [::]:80->80/tcp, 0.0.0.0:443->443/tcp, [::]:443->443/tcp

As soon as it sees the dashboard router, Traefik asks Let’s Encrypt for a certificate for traefik.example.com. Follow the logs to see whether it worked:

sudo docker compose logs -f traefik

With log.level=INFO Traefik is fairly quiet; problems appear as lines marked ERR, often containing Unable to obtain ACME certificate. Access log lines appear as requests arrive. Press Ctrl+C to stop following; the container keeps running.

Now open https://traefik.example.com/dashboard/ in your browser (keep the trailing slash). You should get a padlock, a login prompt, and after entering admin and your password, the dashboard. You can confirm the same from the command line:

curl -sI https://traefik.example.com/dashboard/ | head -n 1
curl -sI -u admin https://traefik.example.com/dashboard/ | head -n 1

The first command should print HTTP/2 401: TLS worked (otherwise curl would stop with a certificate error) and a password is required. The second asks for the password and should print HTTP/2 200.

Step 7: Add an application with Docker labels

Now the part that makes Traefik pleasant to live with. We deploy traefik/whoami, a tiny web server that prints details about each request, in its own folder and its own compose project, completely separate from Traefik:

sudo mkdir -p /opt/whoami
sudo chown "$USER": /opt/whoami
cd /opt/whoami
nano compose.yaml

Paste:

services:
  whoami:
    image: traefik/whoami
    container_name: whoami
    restart: unless-stopped
    networks:
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`whoami.example.com`)"
      - "traefik.http.routers.whoami.entrypoints=websecure"
      - "traefik.http.routers.whoami.tls.certresolver=le"
      - "traefik.http.services.whoami.loadbalancer.server.port=80"

networks:
  proxy:
    external: true

We use the image without a tag (meaning :latest) because this is a throwaway test container; for real apps prefer a version tag. Line by line, the labels say: Traefik, please handle this container; create a router named whoami that matches the domain whoami.example.com; listen on the HTTPS entrypoint; get a certificate from le (already the default from Step 4, but writing it makes the file self-explanatory); and send the traffic to port 80 inside the container. Notice what is missing: there is no ports: section. The container is reachable only through Traefik over the proxy network.

Start it and test both HTTP and HTTPS:

sudo docker compose up -d
curl -sI http://whoami.example.com | grep -iE '^(HTTP|location)'
curl -s https://whoami.example.com

The first test should show a permanent redirect to HTTPS, for example:

HTTP/1.1 308 Permanent Redirect
Location: https://whoami.example.com/

Depending on the Traefik version you may see 301 Moved Permanently instead; for browsers both mean “always use HTTPS”. The second test returns something similar to:

Hostname: 3f2a1c9d8e7b
IP: 127.0.0.1
IP: 172.18.0.3
RemoteAddr: 172.18.0.2:41234
GET / HTTP/1.1
Host: whoami.example.com
X-Forwarded-For: 203.0.113.25
X-Forwarded-Proto: https
X-Real-Ip: 203.0.113.25

X-Forwarded-Proto: https and X-Forwarded-For (your own IP) prove the request went through Traefik. In the dashboard, under HTTP Routers, you will now see whoami@docker with a TLS shield next to it. No reload, no restart of Traefik: it noticed the new container on its own.

Adding more sites: the label pattern to remember

Every new app follows the same five rules: join the proxy network, set traefik.enable=true, give the router a unique name and a Host() rule, put it on websecure with the le resolver, and tell Traefik the port the app listens on inside the container. Here is a real app, Uptime Kuma, listening on port 3001:

services:
  uptime-kuma:
    image: louislam/uptime-kuma:2
    restart: unless-stopped
    volumes:
      - kuma_data:/app/data
    networks:
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.kuma.rule=Host(`status.example.com`)"
      - "traefik.http.routers.kuma.entrypoints=websecure"
      - "traefik.http.routers.kuma.tls.certresolver=le"
      - "traefik.http.services.kuma.loadbalancer.server.port=3001"

volumes:
  kuma_data:

networks:
  proxy:
    external: true

As always, check the project’s releases for the newest major tag. WebSockets, which Uptime Kuma uses for its live dashboard, pass through Traefik without extra configuration.

A few variations you will need sooner or later:

  • Several domains on one app: Host(`example.com`) || Host(`www.example.com`). Traefik requests one certificate covering both names.
  • Apps with a database: put only the web container on proxy. The database joins a second, private network shared with the app and never touches proxy. An excerpt:
services:
  app:
    networks: [proxy, internal]
  db:
    image: postgres:17
    networks: [internal]

networks:
  proxy:
    external: true
  internal:
  • Reusable security headers: add these labels to the Traefik container in /opt/traefik/compose.yaml and run sudo docker compose up -d there to recreate it:
      - "traefik.http.middlewares.secure-headers.headers.stsSeconds=31536000"
      - "traefik.http.middlewares.secure-headers.headers.contentTypeNosniff=true"
      - "traefik.http.middlewares.secure-headers.headers.referrerPolicy=strict-origin-when-cross-origin"

Any app can then use them with traefik.http.routers.kuma.middlewares=secure-headers@docker. Be careful with stsSeconds (HSTS): once a browser sees it, it refuses plain HTTP for that domain for a year, so enable it only when HTTPS works reliably.

How much does Traefik cost in resources?

Traefik is written in Go and ships as a single binary. It uses more memory than a bare Nginx, because it keeps the Docker state, the routing tables and the certificate manager in memory, but the difference rarely matters on a modern VPS.

Approximate idle RAM of common reverse proxies5 HTTPS sites on a small VPS, values are approximateNginx + certbot~10 MBHAProxy~15 MBCaddy~35 MBTraefik v3~50 MB0102030405060RAM in MB (approximate, idle)Reverse proxyOrder of magnitude only, real numbers vary with traffic and version
Figure 3: Approximate idle memory of common reverse proxies serving five HTTPS sites. Traefik uses more than Nginx, but around 50 MB is a small share of even a 1 GB VPS.

What you buy with those extra megabytes is time: adding a site means a few labels in the app’s own file instead of a new server block, a certbot run and a reload. Under real traffic, memory grows with the number of open connections for every proxy in the chart, so treat these numbers as an order of magnitude, not a benchmark. CPU use at idle is negligible for all of them.

Troubleshooting and common mistakes

404 page not found from Traefik. The request reached Traefik but no router matched it. Usually traefik.enable=true is missing, the domain in Host() has a typo, or the app container is not running. Open the dashboard, look under HTTP Routers, and check errors with sudo docker compose logs traefik | grep -i err in /opt/traefik.

Bad Gateway (502) or Gateway Timeout (504). A router matched, but Traefik cannot talk to the container. Either the port label is wrong (it must be the port the app listens on inside the container) or the container is not on the proxy network. Check with:

sudo docker inspect whoami --format '{{json .NetworkSettings.Networks}}'

The output must contain "proxy".

The browser warns about a certificate named TRAEFIK DEFAULT CERT. Traefik could not get a Let’s Encrypt certificate and fell back to its self-signed one. The logs show why: DNS not pointing to the VPS yet, port 80 not reachable, a broken AAAA record or a rate limit after too many failed attempts. Fix the cause, then run sudo docker compose restart traefik.

permissions 644 for /letsencrypt/acme.json are too open. Run chmod 600 /opt/traefik/letsencrypt/acme.json and restart Traefik.

client version 1.24 is too old in the logs. You are running an older Traefik image that predates the API changes in recent Docker Engine releases. Pull the current v3 image: sudo docker compose pull && sudo docker compose up -d.

The dashboard keeps asking for the password. The hash was mangled, usually because the $ signs were interpolated. Check what Traefik actually received:

sudo docker inspect traefik --format '{{ index .Config.Labels "traefik.http.middlewares.dashboard-auth.basicauth.users" }}'

It must start with admin:$2y$. If not, fix the single quotes in .env (Step 5) and run sudo docker compose up -d again.

bind: address already in use on port 80 or 443. Another web server runs on the host. Find it and disable it (the service names are the same on Debian and Ubuntu):

sudo ss -tlnp | grep -E ':(80|443) '
sudo systemctl disable --now nginx    # or apache2

Switching from the staging server to production. Remove the caserver line from compose.yaml, then empty the stored test certificates so Traefik requests real ones:

cd /opt/traefik
sudo docker compose down
sudo truncate -s 0 letsencrypt/acme.json
sudo docker compose up -d

truncate keeps the 600 permissions. The external proxy network survives docker compose down.

Security notes

  • The Docker socket is root access. Whoever controls Traefik’s container can control Docker, and through it the whole host. The :ro flag only stops writes to the socket file itself, not API calls. Keep Traefik updated, never enable --api.insecure, and on sensitive servers consider putting a Docker socket proxy container in between that only allows read-only API endpoints.
  • Docker bypasses ufw. Ports published with ports: are handled by Docker’s own firewall rules, before ufw ever sees the packet. That is why app containers in this setup publish nothing. If you need a port for local debugging, bind it to localhost only: "127.0.0.1:8080:80".
  • Use ufw for host services. Debian 13 does not ship ufw by default; Ubuntu 24.04 has it installed but inactive. Allow SSH before enabling it, otherwise you lock yourself out (if SSH listens on a different port, allow that port instead):
sudo apt install -y ufw
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose

The 80 and 443 rules mostly document intent, since Docker handles those ports itself.

  • Lock the dashboard down further with an IP allowlist. Add the first label below to Traefik and replace the existing middlewares label with the second one (use your own IP):
      - "traefik.http.middlewares.dashboard-ip.ipallowlist.sourcerange=203.0.113.10/32"
      - "traefik.http.routers.dashboard.middlewares=dashboard-ip,dashboard-auth"

Or remove the dashboard router labels completely if you do not need the dashboard.

  • acme.json and .env are secrets. They contain private keys and the password hash. Back up the folder and copy the archive off the server:
sudo tar czf ~/traefik-backup.tar.gz -C /opt traefik
sudo chown "$USER": ~/traefik-backup.tar.gz

Because it was created with sudo, the archive is root-owned until you chown it; afterwards scp can copy it to your computer. Store it somewhere private.

  • Update regularly. Read the release notes, then:
cd /opt/traefik
sudo docker compose pull
sudo docker compose up -d

So, is Traefik the right front door for your VPS?

If you run more than one containerized app on a server, yes. You set Traefik up once, and from then on every new service needs nothing but five labels in its own compose file: HTTPS, renewals, redirects and routing follow automatically. If you run a single static site, plain Nginx or Caddy is simpler; if your configuration lives outside Docker, a classic proxy may fit better.

You can follow this tutorial on a VPS from our Romanian data center in a few minutes. And if you would rather have someone else build and maintain the proxy, the certificates and the containers behind them, our server administration team can set it all up for you and keep it updated.

FAQ

Can Traefik issue wildcard certificates?

Yes, but only through the DNS-01 challenge, which requires API access to your DNS provider. The HTTP challenge used in this tutorial issues one certificate per set of hostnames, which is enough for most setups. The Traefik documentation lists the supported DNS providers and their variables.

Do my apps have to be in the same compose file as Traefik?

No, and they are better kept separate. As long as an app joins the external proxy network and carries the right labels, Traefik finds it, no matter which folder or compose project it comes from.

How are certificates renewed?

Automatically. Traefik checks the stored certificates and renews them well before they expire, then stores the new ones in acme.json. Your only job is to keep that file persistent and ports 80 and 443 reachable.

Traefik, Caddy or Nginx Proxy Manager?

All three are good. Caddy is configured through a Caddyfile and is lighter; Nginx Proxy Manager gives you a web UI on top of Nginx. Traefik wins when you want the routing defined next to each app, as code, and discovered automatically from Docker.

Trusted By & Member Of

We are proud members of leading internet infrastructure organizations.

RIPE NCC MANRS PeeringDB RoTLD DSIX SBIX 4IXP LOCIX Euro-IX RIPE NCC MANRS PeeringDB RoTLD DSIX SBIX 4IXP LOCIX Euro-IX