WIUNIX ACADEMY
Set up Traefik with Let’s Encrypt and HTTP/3 on Ubuntu and Debian
Page 3 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.
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
LET’S BUILD
Open the dynamic routing file
Use a watched directory rather than replacing a lone bind-mounted file. This host-service design has no container bind mount. File-provider changes normally reload automatically; static entrypoints and ACME changes need a controlled service restart. Never edit generated ACME JSON manually.
Dedicated Linux host
sudoedit '/etc/traefik-academy/dynamic/routes.yml'
Editor open for the dynamic file below.
Dynamic routes: app and private admin
FILE CONTENT: only the exact application Host reaches the marker over TLS. The API/dashboard rule is limited to the loopback admin entrypoint. Untrusted clients cannot choose a backend through a supplied URL. Forwarded headers are not blindly trusted. If replacing the marker with a remote origin, use authenticated verified HTTPS and a separately reviewed route; do not set insecureSkipVerify.
http:
routers:
app:
rule: "Host(`app.example.com`)"
entryPoints: [websecure]
service: marker
tls:
certResolver: le
options: modern
admin:
rule: "PathPrefix(`/api`) || PathPrefix(`/dashboard`)"
entryPoints: [admin]
service: api@internal
services:
marker:
loadBalancer:
servers:
- url: "http://127.0.0.1:8081"
tls:
options:
modern:
minVersion: VersionTLS12
LET’S BUILD
Open the restricted proxy service
Grant only the capability needed for ports below 1024. The process runs as the dedicated non-login user, reads root-controlled configuration, and can write only its ACME directory. Do not give it a Docker socket, root account or membership in privileged groups.
Dedicated Linux host
test ! -e /etc/systemd/system/traefik-academy.service &&
sudoedit /etc/systemd/system/traefik-academy.service
Editor open for the new proxy unit.
Proxy service file
FILE CONTENT: systemd protects the filesystem and retains only CAP_NET_BIND_SERVICE. ReadWritePaths does not bypass ordinary ownership; the state directory must still belong to the service user. Network isolation is intentionally not enabled because DNS, ACME, public listeners and the same-host origin are needed.
[Unit]
Description=Academy Traefik HTTPS and HTTP3 edge
Wants=network-online.target
After=network-online.target academy-traefik-origin.service
[Service]
Type=simple
User=traefik-academy
Group=traefik-academy
ExecStart=/usr/local/bin/traefik --configFile=/etc/traefik-academy/traefik.yml
Restart=on-failure
RestartSec=5s
UMask=0077
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/traefik-academy
[Install]
WantedBy=multi-user.target
LET’S BUILD
Start and inspect staging
Apply readable configuration permissions and validate systemd unit syntax before starting. This parser check does not validate Traefik YAML; use the process startup logs and private API route status for that. Starting now contacts the staging CA and may create an account/order under its terms: review the current subscriber agreement before your own public exercise. Do not retry issuance in a restart loop. Keep port 80 reachable for future challenges.
Dedicated Linux host
sudo chown root:traefik-academy '/etc/traefik-academy/traefik.yml' '/etc/traefik-academy/dynamic/routes.yml'
sudo chmod 0640 '/etc/traefik-academy/traefik.yml' '/etc/traefik-academy/dynamic/routes.yml'
sudo systemd-analyze verify /etc/systemd/system/academy-traefik-origin.service /etc/systemd/system/traefik-academy.service
sudo systemctl daemon-reload
sudo systemctl enable --now academy-traefik-origin.service
curl --fail --silent --show-error --noproxy '*' 'http://127.0.0.1:8081/health.txt'
sudo systemctl enable --now traefik-academy.service
sudo systemctl status traefik-academy.service --no-pager
sudo journalctl -u traefik-academy.service --since '10 minutes ago' --no-pager
sudo ss -lntup
Exact origin marker; proxy active; no YAML/provider/ACME errors; admin/origin only loopback, web TCP80/TCP443 and QUIC UDP443.
LET’S BUILD
Verify the staging certificate without trusting it
From an independent client inspect the certificate subject/SAN/issuer/dates. Staging roots are intentionally untrusted. Only for this non-secret marker, the second probe temporarily skips trust to prove routing; do not enter credentials, install staging roots, bypass browser warnings or use this flag for production. Require the certificate to contain app.example.com and a staging issuer; a self-signed TRAEFIK DEFAULT CERT means issuance has not succeeded. The untrusted chain probe itself does not prove successful trusted TLS.
Independent external client
openssl s_client -connect 'app.example.com:443' -servername 'app.example.com' </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -dates -ext subjectAltName
curl --insecure --fail --silent --show-error --noproxy '*' 'https://app.example.com/health.txt'
curl --silent --show-error --head --noproxy '*' 'http://app.example.com/health.txt'
Staging certificate has correct SAN and staging issuer, marker is academy-traefik-origin-ok, HTTP redirects to the same HTTPS hostname.