SSD Nodes Learn
Mga Gabay Matt ConnorNi Matt Connor · Na-update 2026-07-24

Traefik v3 Docker Compose guide para sa 5 apps

Matutong mag-setup ng Traefik v3 sa Docker Compose para sa 5 apps gamit ang isang IP. Iwasan ang acme.json error at i-automate ang Let's Encrypt TLS.

Isang IP, limang app, isang port 443

Ang VPS mo ay may iisang public IPv4 address at iisang TCP port 443. Gusto mong magpatakbo ng Gitea, staging copy ng iyong app, internal dashboard, status page, at webhook receiver sa iisang server — limang hostname, isang box. Ang reverse proxy ang prosesong nagmamay-ari ng :80 at :443, nagbabasa ng Host header sa bawat request, at ipinapasa ito sa tamang container. Ginagawa ito ng Traefik, at kumukuha at nagre-renew ito ng certificate para sa bawat hostname nang hindi mo na kailangang patakbuhin ang certbot nang manual.

Ang pagkakaiba ng Traefik sa isang nginx server {} block ay kung saan nanggagaling ang configuration nito. Sa nginx, mag-e-edit ka ng file at magre-reload, at ang certificate lifecycle ay mananatiling hiwalay na gawain — ito ang workflow na sinusunod kapag nag-i-issue ng Let's Encrypt certificates gamit ang certbot sa nginx, kung saan ang renewal timer ay nasa labas ng web server. Binabantayan ng Docker provider ng Traefik ang Docker event stream at binabasa ang mga labels mula sa iyong mga container: mag-start ng container na may Host() rule label at magiging routable na ito sa loob ng isang segundo; i-stop ito at mawawala ang route. Ito rin ang panganib. Ang configuration na nasa loob ng mga label ay nakakalat sa limang lugar nang sabay-sabay, at ang maling label ay walang error message — hindi lang basta ma-ro-route ang container, at walang sasabihin ang Traefik.

Ang apat na noun

  • Entrypoints ay mga listening socket. Magtatakda ka ng dalawa: web sa :80 at websecure sa :443.
  • Routers ang nagma-match ng request (Host(...)) at nag-a-attach nito sa isang service. Ang mga certificate ay hinihiling bawat router gamit ang tls.certresolver.
  • Services ang backend — isang container at ang port na pinakikinggan nito sa loob ng Docker network.
  • Middlewares ang nasa pagitan ng router at service: basic auth, IP allow-lists, header rewrites, at redirects.

Ang static configuration (entrypoints, providers, ACME) ay ipinapasa sa command line ng Traefik o sa traefik.yml, at kailangang i-restart ang Traefik para mabago ito. Ang dynamic configuration (routers, services, middlewares) ay nanggagaling sa mga container label at may hot-reload. Ang pagpapalit sa dalawang ito ang karaniwang dahilan kung bakit "walang nangyayari sa flag ko".

Ang compose file

Isang shared Docker network na may pangalang proxy ang nagsisilbing backbone. Makakaabot lang ang Traefik sa isang container kung pareho silang nasa network na ito.

name: edge

networks:
  proxy:
    name: proxy

services:
  traefik:
    image: traefik:v3.5
    restart: unless-stopped
    command:
      - --providers.docker=true
      - --providers.docker.exposedByDefault=false
      - --providers.docker.network=proxy
      - --entryPoints.web.address=:80
      - --entryPoints.websecure.address=:443
      - --entryPoints.web.http.redirections.entryPoint.to=websecure
      - --entryPoints.web.http.redirections.entryPoint.scheme=https
      - --certificatesresolvers.le.acme.email=you@example.com
      - --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json
      - --certificatesresolvers.le.acme.tlschallenge=true
      # while you iterate, point at staging so a mistake costs nothing:
      # - --certificatesresolvers.le.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
      - --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
    labels:
      - traefik.enable=true
      - traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)
      - traefik.http.routers.dashboard.entrypoints=websecure
      - traefik.http.routers.dashboard.tls.certresolver=le
      - traefik.http.routers.dashboard.service=api@internal
      - traefik.http.routers.dashboard.middlewares=dashboard-auth
      - traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$apr1$$REPLACE$$THIS

  gitea:
    image: gitea/gitea:1  # major-only pin keeps this demo copy-pasteable; pin an exact release in production
    restart: unless-stopped
    volumes:
      - ./gitea:/data
    networks:
      - proxy
    labels:
      - traefik.enable=true
      - traefik.http.routers.gitea.rule=Host(`git.example.com`)
      - traefik.http.routers.gitea.entrypoints=websecure
      - traefik.http.routers.gitea.tls.certresolver=le
      - traefik.http.services.gitea.loadbalancer.server.port=3000

docker compose up -d, pagkatapos ay docker compose logs -f traefik. Ang bawat karagdagang app ay kopya ng gitea block na may sariling router name, sariling Host(), at sariling internal port. Ang isang Nextcloud install running in Docker with TLS and backups ay pwedeng ilagay sa parehong paraan — alisin ang mga published ports nito, i-attach sa proxy, at hayaan ang router labels na humawak sa hostname at certificate.

May limang detalye na mahalagang tandaan.

Ginagawang invisible ng exposedByDefault=false ang isang container sa Traefik hangga't wala itong traefik.enable=true. Kapag hindi ito isinama, lahat ng container na i-uumpisa mo — kasama na ang pansamantalang postgres na ginamit mo para mag-check — ay magkakaroon ng generated route.

Sinasabi ng providers.docker.network=proxy sa Traefik kung anong network ang dapat gamitin kapag ang isang container ay naka-attach sa maraming network. Kapag hindi ito isinama, maaaring maling container IP ang piliin ng Traefik, na magreresulta sa 502 error na mukhang application fault.

Ang loadbalancer.server.port=3000 ay ang port sa loob ng container; ang Gitea ay nakikinig sa 3000 doon. Pansinin na walang app container ang nagpu-publish ng port — ang Traefik lang ang gumagawa nito.

Ang redirect sa web entrypoint ay ginagawang 308 redirect sa HTTPS ang mga plaintext request. Mananatiling bukas ang Port 80: kailangan ito ng ACME HTTP challenge, pati na rin ng mga user na nagta-type ng bare hostname.

Ang dobleng $$ sa basic-auth hash ay Compose escaping, hindi typo. I-generate ito gamit ang htpasswd -nbB admin 'your-password' (package apache2-utils), pagkatapos ay i-double ang bawat $.

Ang certificate, at ang acme.json trap

Pinipili ng tlschallenge=true ang TLS-ALPN-01: kumokonekta ang Let's Encrypt sa iyong box sa port 443 at sumasagot ang Traefik sa challenge sa loob ng TLS handshake. Ang alternatibo ay HTTP-01, sa port 80 — palitan ang tlschallenge line sa command: list ng Traefik gamit ang dalawang ito:

      - --certificatesresolvers.le.acme.httpchallenge=true
      - --certificatesresolvers.le.acme.httpchallenge.entrypoint=web

Gumagana ang alinman sa dalawa. Parehong kailangan na ang public DNS para sa hostname ay nakaturo na sa iyong VPS — nire-resolve ng certificate authority ang pangalan at kumokonekta mula sa labas. I-create muna ang A (at AAAA) record, i-confirm gamit ang dig +short git.example.com, bago i-start ang Traefik.

Narito ang trap na nag-aaksaya ng oras ng maraming tao. Itinatago ng Traefik ang ACME account key nito at ang bawat issued certificate sa isang acme.json. Kung ang file na ito ay group-readable o world-readable, maglalabas ang Traefik ng error na katulad nito at hihinto:

error: unable to get ACME account: permissions 644 for /letsencrypt/acme.json are too open, please use 600

Ang tamang fix ay ang nabanggit sa itaas: i-bind-mount ang directory at hayaan ang Traefik na gumawa ng file gamit ang tamang mode. Kung ginawa mo ang acme.json gamit ang touch, ginawa itong 644 ng iyong umask. I-repair ito sa host:

chmod 600 ./letsencrypt/acme.json
docker compose restart traefik

I-back up ang directory na iyan kasama ang iyong app volumes. Maaaring mabuhay kahit mawala ito — dahil pwedeng mag-reissue ng certificates — pero ang sabay-sabay na pag-reissue ng limang hostname ay maglalantad sa iyo sa rate limits.

Gamitin ang staging CA habang nagte-test. I-uncomment ang caserver line, siguraduhing gumagana ang bawat route, pagkatapos ay i-comment muli ito at i-delete ang acme.json para makakuha ng bagong production certificates. Pinapayagan ng production Let's Encrypt ang limang duplicate certificates bawat linggo para sa magkaparehong set ng hostnames, at nililimitahan ang paulit-ulit na failed validations para sa iisang pangalan. Naglalabas ang staging ng mga untrusted certificates — magbibigay ng warning ang iyong browser, at ang warning na iyon ang senyales na gumana ito — na may mas maluwag na limits.

Ang dashboard ay isang control surface, hindi isang demo

Karamihan sa mga quickstart ay nagse-set ng --api.insecure=true, kung saan ang dashboard ay naka-serve sa port 8080 nang walang authentication. Mapanganib ito sa isang server na may public IP dahil ibibigay nito ang iyong routing topology, hostnames, middleware names, at backend ports sa sinumang mag-scan dito.

Ang mga label sa traefik service sa itaas ang alternatibo: ang dashboard ay naka-route gaya ng ibang app, gamit ang totoong hostname, sa pamamagitan ng TLS, at nasa likod ng basicauth. Ang service=api@internal ang nag-uugnay sa router sa built-in API ng Traefik. Mas higpitan pa ito sa pamamagitan ng paggamit ng IP allow-list, na inaayos mula kaliwa pakanan. Kung ang office address mo ay dynamic, i-set ang range sa subnet na ibinibigay ng isang WireGuard VPN na self-hosted sa parehong VPS at i-access ang dashboard sa pamamagitan lamang ng tunnel:

- traefik.http.middlewares.office.ipallowlist.sourcerange=10.0.0.7/32
- traefik.http.routers.dashboard.middlewares=office,dashboard-auth

Ang Docker socket ay root

Ang /var/run/docker.sock ay isang API na kayang gumawa ng container na nagmo-mount ng / mula sa host. Ang access dito ay katumbas ng root access sa machine, at kailangan ito ng Traefik para mabasa ang mga label.

Panatilihin ang :ro sa mount, pero dapat malinaw ang epekto nito: ginagawa nitong read-only ang socket file. Hindi nito pinipigilan ang mga POST request sa Docker API na dumadaan dito. Ang tunay na mitigation ay ang hindi pagbibigay ng socket sa Traefik, at paglalagay ng filtering proxy sa pagitan nito:

  dockerproxy:
    image: tecnativa/docker-socket-proxy   # pin the current tag
    restart: unless-stopped
    environment:
      CONTAINERS: 1
      NETWORKS: 1
      POST: 0
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - proxy

Tanggalin ang socket volume sa Traefik at ituro ang provider sa proxy:

--providers.docker.endpoint=tcp://dockerproxy:2375

Mananatili ang read access ng Traefik sa mga container at network, pero mawawala ang kakayahan nitong gumawa ng kahit ano.

Firewall, ports, at ang rule na madalas mali ang lahat

Dalawang bukas na port, plus SSH:

sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

Bypassed ng mga published ports ng Docker ang ufw. Naglalagay ang Docker ng sarili nitong iptables rules. Mas nauuna ang mga ito sa ufw chains, kaya ang container na sinimulan gamit ang ports: ["3000:3000"] ay maaari pa ring ma-access mula sa internet kahit may ufw deny rule. Ang problema ay nasa structure, hindi sa firewall configuration: i-publish lamang ang mga port mula sa Traefik, at bigyan ang lahat ng ibang container ng networks: [proxy] at wala nang iba. Kung kailangang ma-access talaga ang host, i-bind ito sa loopback — "127.0.0.1:3000:3000".

Troubleshooting: errors you will actually see

404 page not found, served by Traefik. No router matched. In order of likelihood: the container lacks traefik.enable=true (with exposedByDefault=false set); the Host() rule does not match the name you typed; the router name in one label differs from the router name in another (routers.gitea.rule and routers.gitea.entrypoints must be the same word); or you wrapped the hostname in quotes instead of backticks. Traefik v3 requires backticks inside matchers.

502 Bad Gateway. A router matched and the backend was unreachable. Almost always the container is not on the proxy network — check docker inspect -f '{{json .NetworkSettings.Networks}}' gitea. The other candidate is a wrong loadbalancer.server.port: you gave it a published port, or the app listens elsewhere. The log names the attempt: dial tcp 172.18.0.5:8080: connect: connection refused.

The browser warns, and the certificate is issued to TRAEFIK DEFAULT CERT. No certificate exists for that hostname and Traefik served its self-signed placeholder. Read the ACME lines:

unable to obtain ACME certificate for domains "git.example.com" ...
acme: error: 400 ... DNS problem: NXDOMAIN looking up A for git.example.com

DNS is not pointing at the box yet. Fix the record, wait out the TTL, restart Traefik.

Invalid response from http://git.example.com/.well-known/acme-challenge/... on the HTTP challenge: port 80 is not reaching Traefik from outside — usually a provider-level firewall in front of the VPS, not ufw.

Certificates never issue, and your DNS is on Cloudflare with the orange cloud on. Cloudflare terminates TLS at its edge and TLS-ALPN-01 cannot complete through it. Set the record to DNS-only while issuing, or switch to the DNS-01 challenge with an API token. DNS-01 is also the only challenge that issues wildcards.

Redirect loop. Something in front of Traefik already terminates TLS and forwards plaintext to :80; the entrypoint redirect sends it back to HTTPS. Remove one of the two redirects.

Pagpapanatili sa pagtakbo

Dapat naka-boot-enabled ang unit ng Docker (systemctl is-enabled docker), at ibabalik ng restart: unless-stopped ang stack pagkatapos ng reboot. Para sa mas eksaktong kontrol, ang isang maliit na systemd unit na nagpapatakbo ng docker compose -f /srv/edge/compose.yml up -d gamit ang RemainAfterExit=yes ay nagbibigay sa iyo ng systemctl status edge at control sa ordering.

I-pin ang Traefik tag (traefik:v3.5, huwag gagamit ng latest). Binago ng v2-to-v3 upgrade ang syntax ng rule at ang mga pangalan ng provider. Ang isang unattended na latest ay magre-reload ng config na hindi na nito naiintindihan. Gawin ang upgrade nang may pag-iingat: basahin ang migration notes, i-update ang tag, docker compose up -d traefik, at i-monitor ang log. Kung nasa v2 tag ka pa, ang Traefik v2 to v3 migration guide ang magtuturo sa bawat rename, ang compatibility mode, at ang rollback na nagpapanatili sa iyong mga certificate.

I-back up ang ./letsencrypt at ang data volume ng bawat app. Walang ibang state ang Traefik na hindi kayang i-rebuild mula sa compose file.

Ano ang nagiging sanhi ng failure sa scale

Ang unang limitasyon ay hindi throughput, kundi ang single box: ang isang Traefik sa isang VPS ay single point of failure para sa limang apps. Ang acme.json ay flat-file storage — magdudulot ng corruption kung dalawang Traefik instance ang sabay na magsusulat dito. Ang scaling out ay nangangahulugang ilipat ang certificate storage mula sa file, o i-terminate ang TLS sa ibang lugar.

Ang ikalawa ay ang long-lived connections. Ang server-sent events, malalaking upload, at slow clients ay maaaring mag-timeout sa entrypoint; ang --entryPoints.websecure.transport.respondingTimeouts.readTimeout at ang mga sibling nitong writeTimeout at idleTimeout ang nagsisilbing knobs para rito. Ang WebSockets ay gumagana nang walang extra configuration.

Ang ikatlo ay ang disk. Ang --accesslog=true ay nagsusulat sa stdout, at pananatilihin iyon ng Docker's json-file driver habambuhay maliban kung may limitasyon. I-set ang logging.options.max-size sa Traefik service, o isulat ang access log sa isang file at i-rotate ito.

Hindi kailangan ng mga ito ang orchestrator. Ang kailangan ay isang server na kontrolado mo, na may real IP at bukas ang ports 80 at 443 sa mundo — isang maliit na VPS lang ang buong dependency list.

FAQ

Kailangan ko pa ba ng certbot kung gagamit ako ng Traefik?

Hindi. Ang ACME resolver ng Traefik ang nagre-request at nagre-renew ng certificate para sa bawat hostname na nire-route nito, at iniimbak ang mga ito sa acme.json. Ang Certbot ang tamang tool kung ang nginx o ibang server ang mismong nagte-terminate ng TLS; ang pagtakbo ng parehong tool sa iisang hostname ay magdudulot lamang ng paglampas sa Let's Encrypt rate limits.

Bakit nagbabalik ng 404 ang container ko sa pamamagitan ng Traefik?

Ang 404 mula sa Traefik ay nangangahulugang walang router na tumugma sa request. Siguraduhin na ang container ay may traefik.enable=true (mandatory kapag naka-set na ang exposedByDefault=false), na ang value ng Host() ay tugma sa tinype mong pangalan, at ang router name ay pareho sa lahat ng label para sa app na iyon. Sa Traefik v3, backticks ang dapat gamitin sa loob ng matcher, hindi quotes.

Ano ang pagkakaiba ng 404 at 502 dito?

Ang 404 ay nangangahulugang hindi naganap ang routing; ang 502 ay nangangahulugang may tumugmang router pero tinanggihan ng backend ang connection. Ang mga karaniwang sanhi ng 502 ay container na hindi naka-attach sa proxy network, at loadbalancer.server.port na nakaturo sa published port sa halip na sa port na pinakikinggan ng app sa loob ng container. Nakasaad sa access log ang eksaktong address na tinawagan ng Traefik.

Sapat na ba ang pag-mount ng Docker socket bilang read-only?

Ginagawang read-only ng :ro flag ang socket file, hindi ang API sa likod nito — ang mga POST request ay dadaan pa rin dito, at ang access sa Docker API ay katumbas ng root access sa host. Ang mas ligtas na setup ay ang docker-socket-proxy container na ipinapakita sa itaas, na nagbibigay lamang ng container at network reads sa Traefik at tuluyang bina-block ang mga writes.

Kayang mag-issue ng Traefik ng wildcard certificate?

Sa pamamagitan lamang ng DNS-01 challenge, gamit ang API token para sa iyong DNS provider. Ang TLS-ALPN-01 at HTTP-01 ay nagve-validate lamang ng iisang hostname at hindi makakagawa ng wildcard. Ang DNS-01 din ang solusyon kapag ang isang CDN tulad ng Cloudflare ang nagte-terminate ng TLS sa harap ng iyong VPS at hindi natatapos ang dalawang iba pang challenges.