← Start of tutorial

WIUNIX ACADEMY

Set up Traefik with Let’s Encrypt and HTTP/3 on Ubuntu and Debian

Page 5 of 6. Review the earlier steps and prerequisites before continuing.

YOUR SYSTEM. YOUR STEPS.

Where are you working?

Choose your OS and version. The prerequisites, commands and troubleshooting follow your environment.

Ubuntu 26.10Reviewed against official documentation ·

USE YOUR OWN VALUES

Configure this tutorial

Set the example values for your environment; related explanations and copy panels update together. Never enter passwords, private keys or tokens. Values stay in this tab and are not sent to the server or share links. Check actual DNS, certificates and network policy before running commands.

Different IP addresses, domains or ports? Open the settings below, change the values and select “Apply to tutorial”, then copy the updated commands.

Set values for my environment11 editable values

Check private and public API boundaries

With the tunnel open, the first two local requests run on your workstation. Open http://127.0.0.1:8080/dashboard/ there. Inspect route status without sharing the API response or operational details. The last HTTPS request targets the public app: it must not return Traefik API JSON. With this harmless file origin it returns 404. For a real app, distinguish its own /api path from Traefik API; a generic 200/404 alone is not enough.

Bash
curl --fail --silent --show-error --noproxy '*' 'http://127.0.0.1:8080/api/http/routers'
curl --silent --show-error --head --noproxy '*' 'http://127.0.0.1:8080/dashboard/'
curl --silent --show-error --noproxy '*' --output /dev/null --write-out 'Public API path status: %{http_code}\n' 'https://app.example.com/api/http/routers'
18

LET’S BUILD

Monitor renewal, expiry and state

Traefik renews certificates internally; do not add Certbot or a second writer to its ACME file. Current defaults track a 90-day duration and begin renewal 30 days before expiry; CA profiles/lifetimes are changing, so monitor actual notAfter, issuance errors and current resolver guidance. Keep HTTP-01 port 80 reachable and state persistent/writable at 0600. Monitor from outside for trusted TLS, expected body and remaining lifetime, with an actionable alert well before your organization’s recovery deadline. This guide has not waited for a real public renewal and does not claim one.

Bash

Dedicated Linux host

sudo stat -c '%a %U:%G %n' '/var/lib/traefik-academy' '/var/lib/traefik-academy/acme-production.json'
sudo journalctl -u traefik-academy.service --since '24 hours ago' --no-pager
bash -o pipefail <<'CERT'
openssl s_client -connect 'app.example.com:443' -servername 'app.example.com' -verify_hostname 'app.example.com' -verify_return_error </dev/null 2>/dev/null | openssl x509 -noout -issuer -dates -checkend 1209600
CERT
How do I know it worked?

Protected state, no renewal errors; external certificate expires later than the example 14-day alert threshold.

Certificate failure: DNS, reachability or permissions

First inspect the exact ACME error. Wrong A/AAAA, restrictive CAA, blocked inbound TCP80, another listener, wrong clock or unwritable state are distinct causes. Correct that cause and allow the next normal attempt; respect the CA retry time. Do not erase ACME state or repeatedly restart against production. The probes below are host diagnostics; repeat public DNS/HTTP checks from outside as well.

Bash
dig A app.example.com
dig AAAA app.example.com
dig CAA app.example.com
timedatectl status
sudo ss -lntp 'sport = :80'
sudo -u traefik-academy test -w '/var/lib/traefik-academy/acme-production.json'
sudo journalctl -u traefik-academy.service --since '15 minutes ago' --no-pager

502, 404 or a disabled router

502 usually points to an unavailable/wrong upstream. Compare the local exact marker to the file-provider URL and origin port; both services share this host’s loopback. A 404 may instead mean the Host does not match or the route failed to load. Check private router status and logs before changing DNS. After a failed dynamic edit restore the last reviewed root-owned route file; do not expose the origin or disable TLS to hide the error.

Bash
sudo systemctl is-active academy-traefik-origin.service traefik-academy.service
curl --fail --silent --show-error --noproxy '*' 'http://127.0.0.1:8081/health.txt'
curl --fail --silent --show-error --noproxy '*' 'http://127.0.0.1:8080/api/http/routers'
sudo journalctl -u traefik-academy.service --since '10 minutes ago' --no-pager

HTTPS works but HTTP/3 does not

Separate missing client support from blocked UDP. Verify HTTP3 in client Features, actual UDP443 listener, TLS-enabled router and firewall/NAT path. An advertised h3 header with a failed --http3-only request is a failed QUIC test, even if browser fallback keeps the site reachable. No separate UDP entrypoint should claim 443 because HTTP3 already owns it. Do not add experimental flags from v2 examples.

Bash
sudo ss -lnup 'sport = :443'
sudo journalctl -u traefik-academy.service --since '10 minutes ago' --no-pager

If you later choose Docker discovery

This guide deliberately uses only the file provider. Docker discovery requires access to a privileged management API; mounting docker.sock with :ro does not authorize only read-only API requests and is not a security boundary. Before adopting it, design a separately reviewed authenticated and authorized/filtered daemon-access path, restrict discovery with exposedByDefault=false and explicit per-service enablement/network/port labels, and avoid publishing the Docker API. A socket proxy alone is not proof of safe permissions. Do not add a raw socket mount to this tutorial as a shortcut.

Explore all tutorials ↗