← Start of tutorial

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.

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
09

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.

Bash

Dedicated Linux host

sudoedit '/etc/traefik-academy/dynamic/routes.yml'
How do I know it worked?

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.

yaml · /etc/traefik-academy/dynamic/routes.yml
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
10

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.

Bash

Dedicated Linux host

test ! -e /etc/systemd/system/traefik-academy.service &&
sudoedit /etc/systemd/system/traefik-academy.service
How do I know it worked?

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.

ini · /etc/systemd/system/traefik-academy.service
[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
11

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.

Bash

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
How do I know it worked?

Exact origin marker; proxy active; no YAML/provider/ACME errors; admin/origin only loopback, web TCP80/TCP443 and QUIC UDP443.

12

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.

Bash

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'
How do I know it worked?

Staging certificate has correct SAN and staging issuer, marker is academy-traefik-origin-ok, HTTP redirects to the same HTTPS hostname.

Explore all tutorials ↗