SSD Nodes Learn Hosting plans →
Mga Gabay Matt ConnorNi Matt Connor · Na-update 2026-08-31

Traefik sa Docker Compose: 5 app, isang IP

Patakbuhin ang limang app sa isang IP gamit ang Traefik v3 at Docker Compose. Alamin ang Host routing, automatic Let's Encrypt TLS, at acme.json error na pumipigil sa startup.

Isang IP, limang app, isang port na 443

May iisang public IPv4 address at iisang TCP port na 443 ang VPS mo. Gusto mong patakbuhin dito ang Gitea, staging copy ng app mo, internal dashboard, status page, at webhook receiver—limang hostname sa iisang server. 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. Awtomatiko rin nitong kinukuha at nire-renew ang certificate para sa bawat hostname, kaya hindi mo kailangang manu-manong magpatakbo ng certbot. Kayang i-terminate ng Nginx at Caddy ang parehong limang hostname, kaya kung bukas pa ang pagpili, mainam na ikumpara ang tatlong proxy batay sa paghawak ng certificate at gastos sa configuration ng bawat app bago mo ikonekta ang lahat sa isa sa mga ito.

Ang kaibahan ng Traefik sa isang nginx server {} block ay kung saan nagmumula ang configuration nito. Sa nginx, nag-e-edit ka ng file at nagre-reload, habang hiwalay na gawain ang lifecycle ng certificate. Ito ang workflow na sinusunod mo kapag nag-iisyu ka ng Let's Encrypt certificate gamit ang certbot sa nginx, kung saan nasa labas mismo ng web server ang renewal timer. Sinusubaybayan ng Docker provider ng Traefik ang Docker event stream at binabasa ang labels ng mga container. Kapag nagsimula ang container na may Host() rule label, maaari na itong ma-route sa loob ng isang segundo. Kapag itinigil ito, nawawala ang route. Iyan din ang panganib. Kapag nasa labels ang configuration, nakakalat ito sa limang magkahiwalay na lugar. Tahimik na nabibigo ang maling label: hindi lang naro-route ang container, at walang ipinapakitang mensahe ang Traefik.

Ang apat na noun

  • Entrypoints ang mga listening socket. Magde-define ka ng dalawa: web sa :80 at websecure sa :443.
  • Routers ang nagma-match sa isang request (Host(...)) at nag-a-attach dito ng isang service. Per router hinihiling ang mga certificate, 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 apat na noun na ito ang mga pangalan ng Traefik para sa mga konfigurasyong kung hindi ay mano-mano mong isusulat: ang router ay isang server_name, ang service ay isang proxy_pass target, at ang middlewares ang mga header at auth directive na ikaw mismo ang nagse-set kapag gumagawa ng nginx reverse proxy server block nang paisa-isa.

Ipinapasa ang static configuration (entrypoints, providers, ACME) sa command line ng Traefik o sa traefik.yml, at kailangan mong i-restart ang Traefik kapag binago ito. Dumarating ang dynamic configuration (routers, services, middlewares) mula sa container labels at awtomatiko itong nire-reload. Ang pagkalito sa dalawang ito ang karaniwang sanhi ng “walang epekto ang flag ko.”

Ang compose file

Ang isang shared Docker network na pinangalanang proxy ang backbone. Maaabot lamang ng Traefik ang isang container kung nakakonekta rin ito sa network na iyon.

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 block na gitea, na may sarili nitong router name, sariling Host(), at sariling internal port. Ang isang Nextcloud installation na tumatakbo sa Docker, na may TLS at backups ay isinasama sa parehong paraan: alisin ang published ports nito, ikonekta ito sa proxy, at hayaang ang router labels ang mag-handle ng hostname at certificate.

May limang detalyeng mahalaga rito.

Ginagawang invisible ng exposedByDefault=false ang isang container sa Traefik hanggang magkaroon ito ng traefik.enable=true. Kapag iniwan itong wala, magkakaroon ng route ang bawat container na sisimulan mo, kabilang ang pansamantalang postgres na pinatakbo mo para mag-check ng isang bagay.

Sinasabi ng providers.docker.network=proxy sa Traefik kung aling network ang gagamitin kapag nakakonekta ang isang container sa maraming network. Kapag inalis ito, maaaring maling container IP ang piliin ng Traefik. Magreresulta ito sa 502 na mukhang problema sa application.

Ang loadbalancer.server.port=3000 ay ang port sa loob ng container; nakikinig ang Gitea sa port 3000 doon. Pansinin na walang app container ang nag-publish ng port. Si Traefik lamang ang nagpa-publish nito.

Ang redirect sa web entrypoint ay nagko-convert ng mga plaintext request sa 308 papunta sa HTTPS. Nananatiling bukas ang port 80 dahil kailangan ito ng ACME HTTP challenge. Kailangan din ito ng mga user na nagta-type ng hostname nang walang scheme.

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

Ang certificate at ang trap sa acme.json

tlschallenge=true ang pumipili sa TLS-ALPN-01: kumokonekta ang Let's Encrypt sa server mo sa 443, at sinasagot ng Traefik ang challenge sa loob ng TLS handshake. Ang alternatibo ay HTTP-01 sa port 80. Palitan ang linyang tlschallenge sa listahan ng command: ng Traefik ng dalawang ito:

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

Parehong gumagana ang mga ito. Pareho ring nangangailangan na nakaturo na ang public DNS ng hostname sa iyong VPS, at nalulutas ng certificate authority ang pangalan at nakakakonekta mula sa labas. Gumawa muna ng A (at AAAA) record. Kumpirmahin ito gamit ang dig +short git.example.com, saka simulan ang Traefik.

Ngayon, narito ang trap na kumakain ng isang buong gabi. Iniimbak ng Traefik ang ACME account key at lahat ng ini-issue nitong certificate sa isang acme.json. Kung mababasa ang file na iyon ng group o ng lahat ng user, magpi-print ang Traefik ng linyang halos ganito at hihinto:

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

Ang tamang pag-aayos ay ang nasa itaas: i-bind-mount ang directory at hayaang ang Traefik mismo ang gumawa ng file gamit ang tamang mode. Kung ginawa mo ang acme.json gamit ang touch, ginawa ito ng umask mo bilang 644. Ayusin ito sa host:

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

I-back up ang directory na iyon kasama ng app volumes mo. Maaayos pa kung mawala ito dahil mai-issue muli ang mga certificate, pero kapag limang hostname ang sabay-sabay na kailangang i-issue muli, maaari mong maabot ang rate limits.

Gamitin ang staging CA habang nag-i-iterate. I-uncomment ang linyang caserver, gawing gumagana ang lahat ng route, saka i-comment out ang linyang iyon at i-delete ang acme.json upang bagong hilingin ang production certificates. Pinapahintulutan ng production Let's Encrypt ang limang duplicate certificate bawat linggo para sa eksaktong parehong set ng hostname, at nililimitahan nito ang paulit-ulit na nabigong validation para sa parehong pangalan. Ang staging ay nag-i-issue ng mga certificate na hindi pinagkakatiwalaan. Magpapakita ng warning ang browser mo, at ang warning na iyon ang senyas na gumana ito. Mas maluwag ang mga limit nito.

Ang dashboard ay control surface, hindi demo

Itinatakda ng karamihan sa quickstart ang --api.insecure=true, na naghahain ng dashboard sa port 8080 nang walang authentication. Sa server na may public IP, ibinibigay nito sa sinumang nag-scan ang routing topology, mga hostname, pangalan ng middleware, at backend port.

Ang mga label sa traefik service sa itaas ang alternatibo: niruruta ang dashboard tulad ng ibang app, gamit ang totoong hostname, sa TLS, at nasa likod ng basicauth. Ang service=api@internal ang nag-uugnay sa router sa built-in API ng Traefik. Mas patibayin pa ito sa pamamagitan ng pag-chain ng IP allow-list, na inilalapat mula kaliwa pakanan. Kung dynamic ang address ng opisina ninyo, itakda ang range sa subnet na ibinibigay ng WireGuard VPN na sarili mong naka-host sa parehong VPS at i-access ang dashboard sa tunnel lamang:

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

Hindi na sapat ang depensa para sa isang shared basicauth password kapag limang app na ang nangangailangan ng kani-kanilang account. Sa parehong middleware slot, maaari kang gumamit ng forwardauth na ipinapasa ang desisyon sa Authentik, isang self-hosted single sign-on server, kaya ang dashboard at lahat ng katabing route ay nasa likod ng iisang login na maaari mong i-revoke sa iisang lugar.

Ang Docker socket ay katumbas ng root access

Ang /var/run/docker.sock ay isang API na maaaring lumikha ng container na nagmo-mount ng / mula sa host. Katumbas ng root access sa machine ang pag-access dito, at kailangan ito ng Traefik upang mabasa ang mga label.

Panatilihin ang :ro sa mount, ngunit maging malinaw sa pakinabang nito: ginagawa nitong read-only ang socket file. Hindi nito pinipigilan ang mga POST request sa Docker API na dumaan dito. Ang tunay na mitigation ay huwag kailanman ibigay sa Traefik ang socket, at maglagay ng filtering proxy sa pagitan:

  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

Alisin ang socket volume mula sa Traefik at ituro ang provider sa proxy:

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

Mananatili sa Traefik ang read access sa mga container at network, ngunit mawawala ang kakayahan nitong lumikha ng anuman.

Firewall, mga port, at ang rule na madalas napagkakamalian

Dalawang port ang bukas, kasama ang SSH:

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

Nilalampasan ng mga published port ng Docker ang ufw. Naglalagay ang Docker ng sarili nitong iptables rules na sinusuri bago ang mga chain ng ufw. Dahil dito, maaabot mula sa internet ang container na sinimulan gamit ang ports: ["3000:3000"] kahit may ufw deny rule dito. Structural ang depensa, hindi ito nakukuha sa firewall configuration: sa Traefik lamang mag-publish ng mga port, at bigyan ang lahat ng iba pang container ng networks: [proxy] at wala nang iba. Kung talagang kailangang maabot ng isang serbisyo ang host, i-bind ito sa loopback gamit ang "127.0.0.1:3000:3000".

Pag-troubleshoot: mga error na aktuwal mong makikita

404 page not found, na inihatid ng Traefik. Walang router na tumugma. Batay sa posibilidad, maaaring walang traefik.enable=true ang container (na nakatakda ang exposedByDefault=false); hindi tumutugma ang Host() rule sa pangalang inilagay mo; magkaiba ang router name sa isang label at sa isa pa (dapat pareho ang salita sa routers.gitea.rule at routers.gitea.entrypoints); o nilagyan mo ng quotes ang hostname sa halip na backticks. Nangangailangan ang Traefik v3 ng backticks sa loob ng matchers.

502 Bad Gateway. May router na tumugma, pero hindi maabot ang backend. Halos palaging wala ang container sa proxy network; tingnan ang docker inspect -f '{{json .NetworkSettings.Networks}}' gitea. Ang isa pang posibilidad ay maling loadbalancer.server.port: binigyan mo ito ng published port, o sa ibang port nakikinig ang app. Tinutukoy ng log ang ginawang pagtatangka: dial tcp 172.18.0.5:8080: connect: connection refused.

Nagpapakita ng babala ang browser, at para sa TRAEFIK DEFAULT CERT ang certificate. Walang certificate para sa hostname na iyon, kaya inihatid ng Traefik ang self-signed placeholder nito. Basahin ang mga ACME line:

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

Hindi pa tumuturo sa server ang DNS. Ayusin ang record, hintayin ang TTL, at i-restart ang Traefik.

Invalid response from http://git.example.com/.well-known/acme-challenge/... sa HTTP challenge: hindi naaabot ng mga koneksyon mula sa labas ang Traefik sa port 80. Karaniwan itong firewall sa antas ng provider na nasa harap ng VPS, hindi ufw.

Hindi kailanman nai-issue ang mga certificate, at nasa Cloudflare ang DNS mo na naka-on ang orange cloud. Tine-terminate ng Cloudflare ang TLS sa edge nito, kaya hindi makumpleto ang TLS-ALPN-01 sa pamamagitan nito. Itakda sa DNS-only ang record habang nag-i-issue, o lumipat sa DNS-01 challenge gamit ang API token. Ang DNS-01 lang din ang challenge na nag-iisyu ng wildcards.

Redirect loop. May component sa harap ng Traefik na nagte-terminate na ng TLS at nagpapasa ng plaintext sa :80; ibinabalik naman ito ng entrypoint redirect sa HTTPS. Alisin ang isa sa dalawang redirect.

Panatilihing gumagana

Dapat naka-enable sa boot ang Docker unit (systemctl is-enabled docker), at ibinabalik ng restart: unless-stopped ang stack pagkatapos ng reboot. Para sa tahasang kontrol, gumawa ng maliit na systemd unit na nagpapatakbo ng docker compose -f /srv/edge/compose.yml up -d gamit ang RemainAfterExit=yes. Nagbibigay ito ng systemctl status edge at kontrol sa pagkakasunod-sunod.

I-pin ang Traefik tag (traefik:v3.5, hindi latest). Binago ng upgrade mula v2 patungong v3 ang syntax ng mga rule at mga pangalan ng provider. Kusang ire-reload ng hindi sinusubaybayang latest ang configuration na hindi na nito nauunawaan. Isagawa ang upgrade nang planado: basahin ang migration notes, i-update ang tag, docker compose up -d traefik, at monitorin ang log. Kung gumagamit ka pa rin ng v2 tag, ipinapakita ng gabay sa migration ng Traefik v2 patungong v3 ang bawat pagpapalit ng pangalan, ang compatibility mode, at 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 mo kayang buuin muli mula sa compose file.

Ano ang nasisira kapag lumalaki ang scale

Ang unang limitasyon ay hindi throughput kundi ang iisang server: ang isang Traefik sa iisang VPS ay single point of failure para sa limang app, at ang acme.json ay flat-file storage. Masisira ito kapag dalawang Traefik instance ang sabay na sumulat dito. Ang pag-scale out ay nangangahulugang ilipat ang certificate storage mula sa file, o mag-terminate ng TLS sa ibang lugar.

Ang ikalawang limitasyon ay ang mga long-lived connection. Nahaharap ang server-sent events, malalaking upload, at mababagal na client sa mga timeout ng entrypoint sa pagtugon; ang --entryPoints.websecure.transport.respondingTimeouts.readTimeout at ang mga kaugnay nitong writeTimeout at idleTimeout ang mga setting para rito. Dumadaan ang WebSockets nang walang karagdagang configuration.

Ang ikatlong limitasyon ay disk space. Sumusulat ang --accesslog=true sa stdout, at pinananatili ito ng Docker's json-file driver magpakailanman maliban kung lalagyan ng limit. Itakda ang logging.options.max-size sa Traefik service, o isulat ang access log sa isang file at i-rotate ito.

Hindi kailangan ng alinman dito ng orchestrator. Kailangan lamang nito ng server na kontrolado mo, may totoong IP, at bukas sa buong internet ang ports 80 at 443. Ang isang maliit na VPS ang buong listahan ng dependency.

FAQ

Kailangan ko pa ba ang certbot kung nagpapatakbo ako ng Traefik?

Hindi. Humihingi at nagre-renew ng certificate ang ACME resolver ng Traefik para sa bawat hostname na niruruta nito, at iniimbak ang lahat sa acme.json. Nanatiling tamang tool ang Certbot kapag nginx o ibang server mismo ang nagte-terminate ng TLS. Ang pagpapatakbo sa dalawang ito para sa parehong hostname ay kumokonsumo lamang ng rate limit ng Let's Encrypt.

Bakit nagbabalik ng 404 ang container ko kapag dumadaan sa Traefik?

Ang 404 na inihatid ng Traefik ay nangangahulugang walang router na tumugma sa request. Tiyaking may traefik.enable=true ang container. Mandatory ito kapag nakatakda ang exposedByDefault=false. Tiyakin ding tumutugma ang value ng Host() sa pangalang iyong inilagay, at pareho ang router name sa bawat 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?

Ibig sabihin ng 404 ay hindi naganap ang routing. Ibig sabihin ng 502 ay may router na tumugma ngunit tinanggihan ng backend ang connection. Karaniwang sanhi ng 502 ang container na hindi nakakonekta sa proxy network, at ang loadbalancer.server.port na tumuturo sa published port sa halip na sa port na pinakikinggan ng app sa loob ng container. Tinutukoy ng access log ang eksaktong address na dina- dial 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. Dumaraan pa rin dito ang POST requests, at katumbas ng root access sa host ang Docker API access. Mas matibay na setup ang docker-socket-proxy container na ipinakita sa itaas. Binibigyan lamang nito ang Traefik ng access para magbasa ng container at network, at ganap nitong bina-block ang mga write.

Makakapag-issue ba ang Traefik ng wildcard certificate?

Maaari lamang sa pamamagitan ng DNS-01 challenge, gamit ang API token para sa iyong DNS provider. Bawat isa sa TLS-ALPN-01 at HTTP-01 ay nagva-validate ng iisang hostname at hindi makakagawa ng wildcard. DNS-01 din ang dapat gamitin kapag isang CDN gaya ng Cloudflare ang nagte-terminate ng TLS sa harap ng iyong VPS at hindi nakukumpleto ang dalawa pang challenge.