SSD Nodes Learn Hosting plans →
Przewodniki Matt ConnorAutor: Matt Connor · Zaktualizowano 2026-08-28

Traefik v3: wiele aplikacji w jednym Docker Compose

Konfiguracja pięciu usług za jednym adresem IP z Traefik v3. Instrukcja routingu Host, automatyczne certyfikaty TLS oraz rozwiązanie błędu uprawnień pliku acme.json.

Jeden adres IP, pięć aplikacji, jeden port 443

Twój serwer VPS posiada jeden publiczny adres IPv4 oraz jeden port TCP 443. Chcesz uruchomić na nim Gitea, kopię testową aplikacji, wewnętrzny panel sterowania, stronę statusu oraz odbiornik webhooków – pięć nazw hostów na jednej maszynie. Reverse proxy to proces, który przejmuje porty :80 oraz :443, odczytuje nagłówek Host w każdym żądaniu i przekazuje je do właściwego kontenera. Traefik realizuje to zadanie, automatycznie uzyskując i odnawiając certyfikaty dla każdej nazwy hosta bez konieczności ręcznego uruchamiania certbot. Nginx oraz Caddy również obsłużą te pięć nazw hostów, więc jeśli wybór nie został jeszcze dokonany, warto porównać te trzy proxy pod kątem obsługi certyfikatów i kosztu konfiguracji dla każdej aplikacji przed przypisaniem wszystkiego do jednego z nich.

Tym, co odróżnia Traefik od bloku server {} w nginx, jest źródło konfiguracji. W przypadku nginx edytujesz plik i przeładowujesz usługę, a cykl życia certyfikatu pozostaje oddzielnym zadaniem – jest to proces, który wykonujesz, gdy generujesz certyfikaty Let's Encrypt za pomocą certbot na nginx, gdzie timer odnowienia działa całkowicie niezależnie od serwera WWW. Dostawca Docker w Traefik monitoruje strumień zdarzeń Docker i odczytuje etykiety (labels) z Twoich kontenerów: uruchom kontener z etykietą reguły Host(), a stanie się on dostępny w ciągu sekundy; zatrzymaj go, a trasa zniknie. To również stanowi pułapkę. Konfiguracja zawarta w etykietach znajduje się w pięciu miejscach jednocześnie, a błędna etykieta nie generuje komunikatu o błędzie – kontener po prostu nie jest kierowany do ruchu, a Traefik nie zgłasza żadnej informacji.

Cztery pojęcia

  • Entrypoints to gniazda nasłuchujące. Zdefiniujesz dwa: web na :80 oraz websecure na :443.
  • Routers dopasowują żądanie (Host(...)) i przypisują je do usługi. Certyfikaty są żądane dla każdego routera przez tls.certresolver.
  • Services to backend, czyli kontener oraz port, na którym nasłuchuje on wewnątrz sieci Docker.
  • Middlewares znajdują się między routerem a usługą: uwierzytelnianie basic auth, listy dozwolonych adresów IP, nadpisywanie nagłówków, przekierowania.

Te cztery pojęcia to nazwy Traefik dla zadań, które w innym przypadku wykonywałbyś ręcznie: router to server_name, usługa to cel proxy_pass, a middleware to dyrektywy nagłówków i autoryzacji, które ustawiasz samodzielnie, gdy budujesz blok serwera reverse proxy nginx linia po linii.

Konfiguracja statyczna (entrypoints, providers, ACME) jest przekazywana w wierszu poleceń Traefik lub w traefik.yml, a jej zmiana wymaga restartu Traefik. Konfiguracja dynamiczna (routers, services, middlewares) pochodzi z etykiet kontenerów i jest przeładowywana w locie. Mylenie tych dwóch rodzajów konfiguracji to najczęstsza przyczyna sytuacji, w której "flaga nie działa".

Plik compose

Wspólna sieć Docker o nazwie proxy stanowi fundament. Traefik uzyskuje dostęp do kontenera tylko wtedy, gdy oba znajdują się w tej samej sieci.

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, a następnie docker compose logs -f traefik. Każda kolejna aplikacja jest kopią bloku gitea z własną nazwą routera, własnym Host() oraz własnym portem wewnętrznym. Instalacja Nextcloud działająca w Docker z TLS i kopiami zapasowymi jest dodawana w ten sam sposób: należy usunąć publikowane porty, podłączyć ją do proxy i pozwolić etykietom routera obsłużyć nazwę hosta oraz certyfikat.

Pięć szczegółów ma tutaj kluczowe znaczenie.

exposedByDefault=false sprawia, że kontener jest niewidoczny dla Traefik, dopóki nie otrzyma etykiety traefik.enable=true. Jeśli zostanie pominięty, dla każdego uruchomionego kontenera, w tym tymczasowego postgres użytego do testów, zostanie wygenerowana trasa.

providers.docker.network=proxy informuje Traefik, której sieci użyć, gdy kontener jest podłączony do kilku z nich. Pominięcie tego parametru może spowodować, że Traefik wybierze błędny adres IP kontenera, co objawi się błędem 502, wyglądającym jak awaria aplikacji.

loadbalancer.server.port=3000 to port wewnątrz kontenera; Gitea nasłuchuje tam na porcie 3000. Należy zauważyć, że żaden kontener aplikacji nie publikuje portu – robi to wyłącznie Traefik.

Przekierowanie w punkcie wejścia web zmienia żądania czystego tekstu na kod 308 do HTTPS. Port 80 pozostaje otwarty: jest wymagany przez wyzwanie ACME HTTP oraz dla użytkowników wpisujących samą nazwę hosta.

Podwojony znak $$ w hashu basic-auth to ucieczka znaków w Compose, a nie błąd. Należy wygenerować go za pomocą htpasswd -nbB admin 'your-password' (pakiet apache2-utils), a następnie podwoić każdy znak $.

Certyfikat i pułapka pliku acme.json

tlschallenge=true wybiera TLS-ALPN-01: Let's Encrypt łączy się z serwerem na porcie 443, a Traefik odpowiada na wyzwanie wewnątrz handshake'u TLS. Alternatywą jest HTTP-01 na porcie 80; zamień linię tlschallenge na liście command: w Traefik na te dwie:

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

Obie metody działają. Wymagają one, aby publiczny rekord DNS dla nazwy hosta wskazywał już na Twój VPS, ponieważ urząd certyfikacji musi rozwiązać nazwę i połączyć się z zewnątrz. Najpierw utwórz rekord A (oraz AAAA), potwierdź go za pomocą dig +short git.example.com, a następnie uruchom Traefik.

Oto pułapka, która kosztuje wielu użytkowników cały wieczór. Traefik przechowuje klucz konta ACME oraz wszystkie wydane certyfikaty w jednym pliku acme.json. Jeśli plik ten jest dostępny do odczytu dla grupy lub wszystkich użytkowników, Traefik wyświetli komunikat zbliżony do poniższego i zatrzyma się:

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

Poprawnym rozwiązaniem jest metoda opisana powyżej: zamontuj katalog (bind-mount) i pozwól Traefikowi samodzielnie utworzyć plik z odpowiednimi uprawnieniami. Jeśli utworzyłeś acme.json za pomocą touch, Twoja maska umask prawdopodobnie ustawiła uprawnienia na 644. Napraw to na hoście:

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

Wykonuj kopię zapasową tego katalogu wraz z wolumenami aplikacji. Utrata danych jest możliwa do naprawienia, ponieważ certyfikaty zostaną wydane ponownie, jednak ponowne wystawienie pięciu certyfikatów jednocześnie może doprowadzić do przekroczenia limitów (rate limits).

Podczas testów używaj środowiska staging CA. Odkomentuj linię caserver, skonfiguruj wszystkie trasy, a następnie zakomentuj ją ponownie i usuń acme.json, aby wymusić pobranie nowych certyfikatów produkcyjnych. Produkcyjne środowisko Let's Encrypt pozwala na pięć duplikatów certyfikatów tygodniowo dla tego samego zestawu nazw hostów i nakłada ograniczenia w przypadku powtarzających się nieudanych walidacji dla tej samej nazwy. Środowisko staging wydaje niezaufane certyfikaty, co powoduje ostrzeżenia w przeglądarce – takie ostrzeżenie jest sygnałem, że konfiguracja działa, a limity są znacznie łagodniejsze.

Dashboard to panel sterowania, a nie demonstracja

Większość szybkich startów ustawia --api.insecure=true, co udostępnia dashboard na porcie 8080 bez uwierzytelniania. Na serwerze z publicznym adresem IP ujawnia to topologię routingu, nazwy hostów, nazwy middleware oraz porty backendów każdemu, kto przeskanuje maszynę.

Etykiety w usłudze traefik powyżej stanowią alternatywę: dashboard jest kierowany jak każda inna aplikacja, pod prawdziwą nazwą hosta, przez TLS, za basicauth. service=api@internal to element łączący router z wbudowanym API Traefik. Zabezpiecz go dodatkowo, stosując listę dozwolonych adresów IP, przetwarzaną od lewej do prawej. Jeśli adres biurowy jest dynamiczny, ustaw zakres na podsieć przydzielaną przez samodzielnie hostowany na tym samym VPS serwer WireGuard VPN i uzyskuj dostęp do dashboardu wyłącznie przez tunel:

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

Jedno współdzielone hasło basicauth przestaje być wystarczające, gdy pięć aplikacji wymaga własnych kont. W tym samym miejscu middleware można umieścić forwardauth, który przekazuje decyzję do Authentik, samodzielnie hostowanego serwera single sign-on. Dzięki temu dashboard oraz każda inna trasa znajdują się za jednym logowaniem, które można unieważnić w jednym miejscu.

Gniazdo Docker jako root

/var/run/docker.sock to API, które może utworzyć kontener montujący / z hosta. Dostęp do niego jest równoznaczny z uprawnieniami root na maszynie, a Traefik potrzebuje go do odczytu etykiet.

Zachowaj :ro przy montowaniu, ale miej świadomość, co to daje: czyni to plik gniazda tylko do odczytu. Nie blokuje to żądań POST do Docker API przesyłanych przez to gniazdo. Prawdziwym zabezpieczeniem jest nieudostępnianie gniazda bezpośrednio dla Traefik i umieszczenie pomiędzy nimi proxy filtrującego:

  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

Usuń wolumen gniazda z konfiguracji Traefik i skieruj dostawcę na proxy:

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

Traefik zachowuje dostęp do odczytu kontenerów i sieci, tracąc jednocześnie możliwość tworzenia jakichkolwiek zasobów.

Firewall, porty i reguła, w której wszyscy popełniają błąd

Dwa otwarte porty oraz SSH:

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

Opublikowane porty Docker omijają ufw. Docker wstawia własne reguły iptables, które są przetwarzane przed łańcuchami ufw. W efekcie kontener uruchomiony z ports: ["3000:3000"] jest dostępny z Internetu, nawet jeśli w ufw ustawiono regułę deny. Obrona musi mieć charakter strukturalny, a nie polegać na konfiguracji firewalla: publikuj porty wyłącznie z poziomu Traefik, a wszystkim pozostałym kontenerom nadawaj networks: [proxy] i nic więcej. Jeśli usługa musi mieć dostęp do hosta, należy powiązać ją z interfejsem zwrotnym (loopback), "127.0.0.1:3000:3000".

Rozwiązywanie problemów: najczęstsze błędy

404 page not found, serwowane przez Traefik. Brak dopasowania routera. W kolejności prawdopodobieństwa: kontener nie posiada traefik.enable=true (z ustawionym exposedByDefault=false); reguła Host() nie pasuje do wpisanej nazwy; nazwa routera w jednej etykiecie różni się od nazwy w innej (routers.gitea.rule oraz routers.gitea.entrypoints muszą być tym samym słowem); lub nazwa hosta została ujęta w cudzysłów zamiast w backticki. Traefik v3 wymaga użycia backticków wewnątrz matcherów.

502 Bad Gateway. Router został dopasowany, ale backend jest nieosiągalny. Prawie zawsze oznacza to, że kontener nie znajduje się w sieci proxy, sprawdź docker inspect -f '{{json .NetworkSettings.Networks}}' gitea. Drugą przyczyną jest błędny loadbalancer.server.port: podano port opublikowany lub aplikacja nasłuchuje na innym porcie. Dziennik wskazuje próbę połączenia: dial tcp 172.18.0.5:8080: connect: connection refused.

Przeglądarka wyświetla ostrzeżenie, a certyfikat jest wystawiony dla TRAEFIK DEFAULT CERT. Certyfikat dla tej nazwy hosta nie istnieje, a Traefik użył własnego, samopodpisanego certyfikatu zastępczego. Przeczytaj komunikaty ACME:

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

Rekord DNS nie wskazuje jeszcze na serwer. Popraw rekord, odczekaj czas TTL i zrestartuj Traefik.

Invalid response from http://git.example.com/.well-known/acme-challenge/... podczas wyzwania HTTP: port 80 nie jest dostępny dla Traefik z zewnątrz. Zazwyczaj jest to firewall na poziomie dostawcy VPS, a nie ufw.

Certyfikaty nie są wystawiane, a DNS znajduje się w Cloudflare z włączoną "pomarańczową chmurką". Cloudflare kończy połączenie TLS na swojej krawędzi, przez co wyzwanie TLS-ALPN-01 nie może zostać ukończone. Ustaw rekord na DNS-only na czas wystawiania certyfikatu lub przełącz się na wyzwanie DNS-01 przy użyciu tokena API. DNS-01 to również jedyna metoda pozwalająca na wystawianie certyfikatów typu wildcard.

Pętla przekierowań. Inny komponent przed Traefik kończy połączenie TLS i przesyła czysty tekst na port :80; przekierowanie punktu wejścia odsyła ruch z powrotem na HTTPS. Usuń jedno z dwóch przekierowań.

Utrzymanie ciągłości działania

Jednostka Docker musi być włączona przy starcie systemu (systemctl is-enabled docker), a restart: unless-stopped przywraca stos po restarcie. Aby uzyskać bezpośrednią kontrolę, mała jednostka systemd uruchamiająca docker compose -f /srv/edge/compose.yml up -d z flagą RemainAfterExit=yes zapewnia systemctl status edge oraz kontrolę kolejności startu.

Należy przypiąć wersję obrazu Traefik (traefik:v3.5, nigdy latest). Aktualizacja z wersji v2 do v3 zmieniła składnię reguł oraz nazwy dostawców, a automatyczna aktualizacja latest bez ostrzeżenia przeładuje konfigurację, której nie będzie w stanie obsłużyć. Aktualizację należy przeprowadzać świadomie: zapoznać się z notami migracyjnymi, zmienić tag, wykonać docker compose up -d traefik i monitorować dziennik zdarzeń. Jeśli nadal używany jest tag v2, przewodnik migracji Traefik z v2 do v3 szczegółowo opisuje każdą zmianę nazw, tryb kompatybilności oraz procedurę wycofania zmian, która pozwala zachować certyfikaty.

Należy tworzyć kopie zapasowe ./letsencrypt oraz wolumenów danych każdej aplikacji. Traefik nie przechowuje żadnego innego stanu, którego nie można odtworzyć z pliku compose.

Co ulega awarii przy skalowaniu

Pierwszą barierą nie jest przepustowość, lecz pojedyncza maszyna: jedna instancja Traefik na jednym VPS stanowi pojedynczy punkt awarii dla pięciu aplikacji, a acme.json to pamięć oparta na plikach płaskich; zapisywanie do niej przez dwie instancje Traefik spowoduje uszkodzenie danych. Skalowanie poziome wymaga przeniesienia przechowywania certyfikatów poza plik lub terminacji TLS w innym miejscu.

Drugą barierą są połączenia długotrwałe. Zdarzenia przesyłane przez serwer (Server-sent events), duże przesyłanie plików i wolni klienci napotykają limity czasu odpowiedzi punktu wejścia; --entryPoints.websecure.transport.respondingTimeouts.readTimeout oraz jego odpowiedniki writeTimeout i idleTimeout to parametry służące do ich regulacji. WebSockets przechodzą bez dodatkowej konfiguracji.

Trzecią barierą jest dysk. --accesslog=true zapisuje dane do stdout, a sterownik json-file w Dockerze przechowuje je bezterminowo, o ile nie zostanie nałożony limit. Należy ustawić logging.options.max-size w usłudze Traefik lub zapisywać dziennik dostępu do pliku i stosować jego rotację.

Nic z powyższych nie wymaga orkiestratora. Wymagany jest jedynie serwer pod pełną kontrolą, z publicznym adresem IP oraz otwartymi portami 80 i 443; pojedynczy mały VPS stanowi pełną listę zależności.

FAQ

Czy nadal potrzebuję certbot, jeśli używam Traefik?

Nie. Mechanizm ACME resolver w Traefik żąda i odnawia certyfikaty dla każdej nazwy hosta, którą obsługuje, przechowując je w acme.json. Certbot pozostaje właściwym narzędziem, gdy nginx lub inny serwer samodzielnie kończy połączenie TLS; uruchamianie obu narzędzi dla tych samych nazw hostów jedynie wyczerpuje limity zapytań Let's Encrypt.

Dlaczego mój kontener zwraca błąd 404 przez Traefik?

Błąd 404 serwowany przez Traefik oznacza, że żaden router nie dopasował się do żądania. Sprawdź, czy kontener posiada etykietę traefik.enable=true (wymaganą, gdy ustawiono exposedByDefault=false), czy wartość Host() zgadza się z wpisaną nazwą oraz czy nazwa routera jest identyczna we wszystkich etykietach danej aplikacji. Traefik v3 wymaga również użycia grawisów wewnątrz wyrażeń dopasowujących, zamiast cudzysłowów.

Jaka jest różnica między błędem 404 a 502 w tym przypadku?

Błąd 404 oznacza, że routing nie nastąpił; błąd 502 oznacza, że router dopasował się, ale backend odrzucił połączenie. Typowe przyczyny błędu 502 to kontener niepodłączony do sieci proxy oraz loadbalancer.server.port wskazujący na port opublikowany, zamiast na port, na którym aplikacja nasłuchuje wewnątrz kontenera. Dziennik dostępu (access log) wskazuje dokładny adres, z którym próbował połączyć się Traefik.

Czy montowanie gniazda Docker w trybie tylko do odczytu jest wystarczające?

Flaga :ro sprawia, że plik gniazda jest tylko do odczytu, ale nie ogranicza samego API; żądania POST nadal są przez nie przesyłane, a dostęp do Docker API jest równoważny z uprawnieniami root na hoście. Bezpieczniejszym rozwiązaniem jest kontener docker-socket-proxy przedstawiony powyżej, który udostępnia Traefik jedynie odczyt informacji o kontenerach i sieciach, całkowicie blokując operacje zapisu.

Czy Traefik może wystawić certyfikat typu wildcard?

Tylko przy użyciu wyzwania DNS-01, z tokenem API dla dostawcy DNS. Metody TLS-ALPN-01 oraz HTTP-01 weryfikują pojedynczą nazwę hosta i nie mogą wygenerować certyfikatu typu wildcard. DNS-01 jest również rozwiązaniem, gdy sieć CDN, taka jak Cloudflare, kończy połączenie TLS przed Twoim VPS, co uniemożliwia poprawne zakończenie pozostałych dwóch typów wyzwań.