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

Docker Compose: automatyczne uruchamianie po restarcie

Dowiedz się, jak skonfigurować restart policy w pliku compose.yaml, aby kontenery startowały po reboocie. Wyjaśniamy różnice między restart: always a tworzeniem jednostek systemd.

Krótka odpowiedź

Usługi Docker Compose uruchamiają się podczas startu systemu, gdy spełnione są jednocześnie dwa warunki. Demon Docker musi być włączony jako usługa systemowa, a każda usługa w pliku musi posiadać politykę restartu ustawioną na unless-stopped lub always. Dodaj restart: unless-stopped do każdej usługi, uruchom jednorazowo docker compose up -d, a kontenery będą automatycznie wznawiane po restarcie systemu. W typowych przypadkach nic więcej nie jest wymagane.

Jednostka systemd jest potrzebna tylko wtedy, gdy kolejność uruchamiania ma znaczenie: w przypadku stosu usług zależnego od zamontowanego dysku, interfejsu VPN lub zasobu sieciowego, który nie jest gotowy w momencie startu demona Docker. Jest to realny scenariusz, a druga część tego przewodnika go omawia. Jeśli dopiero zapoznajesz się z definicjami usług i wolumenami, zacznij od podstaw Docker Compose na VPS i wróć tutaj później.

Ustawienie polityki restartu w compose.yaml

Polityka jest definiowana w jednej linii dla każdej usługi. Nie istnieje przełącznik globalny, więc usługa, dla której pominięto konfigurację, pozostanie wyłączona po restarcie, podczas gdy reszta stosu zostanie uruchomiona.

services:
  app:
    image: nginx:1.27
    restart: unless-stopped
    ports:
      - "8080:80"
  db:
    image: postgres:16
    restart: unless-stopped
    environment:
      POSTGRES_PASSWORD: change-me
    volumes:
      - dbdata:/var/lib/postgresql/data

volumes:
  dbdata:

Zastosuj zmiany, a następnie odczytaj politykę z działającego kontenera:

docker compose up -d
docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app)

Polecenie to wyświetli unless-stopped. Jeśli wyświetli no, oznacza to, że plik został edytowany, ale kontener nie został odtworzony.

Jest to najczęstsza przyczyna awarii. Polityka restartu jest przechowywana w kontenerze, a nie w pliku YAML. Edycja compose.yaml nie zmienia niczego w istniejącym kontenerze. Polecenie docker compose restart również nie pomaga, ponieważ zatrzymuje i uruchamia ten sam obiekt kontenera bez ingerencji w jego konfigurację. Tylko docker compose up -d porównuje plik z uruchomionymi kontenerami, wykrywa zmianę polityki i odtwarza je.

W przypadku kontenera, którego nie chcesz teraz odtwarzać, zmień politykę bezpośrednio:

docker update --restart unless-stopped my-container

Nadal należy edytować również plik YAML. Polecenie docker update zmienia działający kontener, a kolejne docker compose up -d odczyta plik i przywróci starą wartość.

Działanie poszczególnych wartości restart

Docker definiuje cztery wartości, a różnice między nimi ujawniają się dopiero podczas restartu maszyny lub demona.

  • no to wartość domyślna. Kontener nigdy nie jest restartowany automatycznie, w żadnych okolicznościach.
  • always restartuje kontener za każdym razem, gdy zostanie zatrzymany. Jeśli kontener został zatrzymany ręcznie, uruchomi się ponownie przy następnym starcie demona Docker. Jest to często zaskakujące: kontener, który został celowo zatrzymany w zeszłym tygodniu, działa ponownie po restarcie systemu.
  • unless-stopped zachowuje się podobnie do always, z tą różnicą, że kontener zatrzymany ręcznie pozostaje zatrzymany po restarcie demona. Jest to wartość zalecana dla usług, które są okresowo wyłączane w celach konserwacyjnych.
  • on-failure restartuje kontener tylko wtedy, gdy zakończył on działanie z kodem wyjścia innym niż zero. Można ograniczyć liczbę prób, jak w restart: on-failure:3.

Dla stosu, który powinien działać zawsze, gdy serwer jest uruchomiony, unless-stopped jest właściwą wartością domyślną. Wartość always należy wybierać tylko wtedy, gdy wymagane jest, aby kontener nie pozostawał w stanie zatrzymanym.

Dlaczego restart: on-failure nie przetrwa restartu systemu

Wiele osób wybiera on-failure, ponieważ brzmi to bezpiecznie, a następnie odkrywa, że wszystkie kontenery są zatrzymane po pierwszym restarcie systemu. Przyczyna tkwi w definicji. on-failure reaguje tylko na jedno zdarzenie: zakończenie procesu kontenera z kodem błędu.

Restart systemu nie jest błędem. Gdy host się wyłącza, systemd zatrzymuje docker.service, a demon celowo wyłącza każdy kontener. Kontener nie uległ awarii, więc polityka nie ma na co reagować. Podczas uruchamiania systemu demon sprawdza kontenery, które powinien wznowić, a kontener on-failure, który został poprawnie zatrzymany, do nich nie należy. Pozostaje on w stanie exited.

Można to sprawdzić bezpośrednio. Ustaw restart: on-failure w usłudze, uruchom docker compose up -d, zrestartuj system, a następnie wykonaj:

docker compose ps -a

Usługa jest wyświetlana ze stanem Exited i statusem typu Exited (0) 2 minutes ago. Nic nie jest uszkodzone i nic nie jest logowane jako błąd, co utrudnia diagnostykę. Polityka zadziałała dokładnie zgodnie z opisem.

on-failure jest nadal przydatne. Pasuje do kontenera wykonującego zadanie, które może ulec awarii, gdzie wymagana jest ograniczona liczba ponownych prób bez pętli restartów. Jest to niewłaściwe narzędzie do utrzymywania długo działającej usługi po restarcie systemu.

Polityki restartu działają tylko wtedy, gdy usługa Docker uruchamia się podczas startu systemu

Polityki restartu są wymuszane przez daemon Docker. Jeśli daemon nie zostanie uruchomiony, żadna polityka nie zadziała. Sprawdź to:

systemctl is-enabled docker
systemctl is-enabled containerd

Oba polecenia powinny zwrócić enabled. Pakiety z oficjalnego repozytorium Docker włączają je podczas instalacji, więc na nowym serwerze zazwyczaj jest to poprawne. Jeśli którekolwiek z nich zwróci disabled, napraw to:

sudo systemctl enable --now docker containerd

Istnieje pułapka, którą warto zrozumieć. Ubuntu dostarcza również docker.socket, który uruchamia daemona na żądanie, gdy tylko jakikolwiek proces odwoła się do API Docker. Użytkownicy widzą, że docker.socket jest włączony, zakładają, że daemon jest zabezpieczony i wyłączają docker.service, aby zaoszczędzić pamięć. Podczas startu systemu nikt nie wywołuje API, więc gniazdo nie jest używane, daemon nie startuje, a żaden kontener nie zostanie uruchomiony, dopóki nie wpiszesz pierwszego polecenia docker. Aktywacja przez gniazdo (socket activation) nie zastępuje włączenia docker.service.

Kiedy jednostka systemd jest lepszym rozwiązaniem

Polityki restartu nie uwzględniają kolejności uruchamiania względem reszty systemu. Demon uruchamia się i włącza kontenery najszybciej, jak to możliwe. Jeśli stos (stack) korzysta z montowania typu bind-mount z osobnego wolumenu, udziału NFS (network file system) lub zaszyfrowanego dysku, kontenery mogą wystartować, zanim dana ścieżka będzie dostępna. Docker utworzy pusty katalog w punkcie montowania i uruchomi kontener, co spowoduje, że baza danych wystartuje bez danych.

Należy utworzyć jednostkę systemd, jeśli występuje którykolwiek z poniższych przypadków. Stos wymaga wcześniejszego przygotowania punktu montowania, interfejsu VPN lub innej jednostki. Wymagane jest, aby systemctl stop myapp i systemctl start myapp działały tak samo, jak dla każdej innej usługi w systemie. Można również chcieć, aby stos był poprawnie zatrzymywany podczas wyłączania systemu, zamiast być ubijanym wraz z demonem. Jeśli jednostki systemd są nowością, tworzenie serwisu i timera systemd zawiera bardziej szczegółowy opis formatu plików.

Tworzenie jednostki systemd

Umieść stos w stałej ścieżce poza katalogiem domowym. /srv/myapp jest dobrym wyborem, ponieważ jednostka uruchamiana przed zalogowaniem użytkownika nie powinna odczytywać /home.

Utwórz /etc/systemd/system/myapp.service:

[Unit]
Description=myapp docker compose stack
Requires=docker.service
After=docker.service network-online.target
Wants=network-online.target
RequiresMountsFor=/srv/myapp/data

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/srv/myapp
ExecStart=/usr/bin/docker compose up -d --remove-orphans
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=0

[Install]
WantedBy=multi-user.target

Włącz i uruchom jednostkę:

sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service
systemctl status myapp.service

Poprawnie działająca jednostka wyświetla Active: active (exited). Przy pierwszym kontakcie może to wyglądać na błąd. Jest to jednak poprawne: Type=oneshot z RemainAfterExit=yes oznacza, że jednostka wykonała swoje polecenie, polecenie zakończyło działanie, a systemd utrzymuje jednostkę jako aktywną, aby ExecStop wykonało się podczas zamykania systemu.

Każda linia ma swoje uzasadnienie. Requires=docker.service oznacza, że jednostka kończy działanie natychmiast po wystąpieniu błędu, zamiast uruchamiać docker compose dla nieaktywnego gniazda. After= ustala kolejność, ponieważ samo Requires= tego nie zapewnia. RequiresMountsFor= sprawia, że systemd uwzględnia jednostkę montowania dla tej ścieżki i oczekuje na nią; jest to główny powód stosowania jednostki zamiast polityki restartu. TimeoutStartSec=0 zapobiega przerwaniu zadania uruchamiania przez systemd w trakcie pobierania dużego obrazu.

Uwaga dotycząca łączenia obu mechanizmów. Dokumentacja Docker odradza łączenie polityk restartu z zewnętrznym menedżerem procesów hosta. Ostrzeżenie to dotyczy menedżera nadzorującego bezpośrednio proces kontenera i restartującego go w czasie, gdy daemon próbuje robić to samo. Jednostka Type=oneshot nie nadzoruje niczego, więc pozostawienie restart: unless-stopped w pliku compose obok tej jednostki jest dopuszczalne i pożądane. systemd zarządza kolejnością podczas startu systemu, a daemon obsługuje kontenery, które ulegną awarii w nocy.

Jednostka wygląda inaczej, gdy utrzymywanym procesem jest zwykły proces działający w tle, a nie stos, ponieważ wtedy pod spodem nie ma daemona i to systemd poprzez Restart= musi pełnić rolę nadzorcy; uruchamianie dsh bez interfejsu za pomocą systemd to przykład takiego rozwiązania, uwzględniający dedykowanego użytkownika oraz dziennik zdarzeń.

Weryfikacja poprzez pełny restart

Nic nie zastąpi rzeczywistego testu. systemctl restart docker nie sprawdza kolejności montowania, a docker compose down połączone z docker compose up -d w ogóle nie testuje procesu uruchamiania systemu.

sudo reboot

Należy odczekać, połączyć się ponownie i sprawdzić stan w następującej kolejności:

uptime
systemctl is-active docker
docker compose ps

uptime potwierdza, że maszyna faktycznie została zrestartowana. docker compose ps, uruchomione z katalogu stosu, powinno wyświetlić każdą usługę jako running z czasem pracy zbliżonym do czasu pracy maszyny. Usługa oznaczona jako Exited wymaga sprawdzenia.

Jeśli któryś element nie został uruchomiony, dziennik demona obejmuje okres startu systemu:

journalctl -u docker.service -b --no-pager | tail -50

W przypadku stosu zarządzanego przez jednostkę, journalctl -u myapp.service -b --no-pager pokazuje dokładne wyjście docker compose z momentu startu, w tym błędy pobierania obrazu lub brakujący plik .env. Zaplanowany restart to ten, który obserwujesz, więc pozwól jednostce informować o tych, których nie monitorujesz: linia OnFailure= skierowana na własny serwer ntfy zamienia stos, który nie uruchomił się poprawnie, w powiadomienie push, zamiast być problemem wykrytym dopiero po kilku dniach.

Przyczyny cichego niepowodzenia automatycznego startu

Kontenery utworzone za pomocą docker compose run nigdy nie przejmują polityki restartu z pliku. Compose traktuje je jako kontenery jednorazowe. Jeśli usługa zdaje się ignorować swoją politykę, sprawdź, czy została uruchomiona za pomocą run zamiast up.

Ścieżka względna w wolumenie lub we wpisie env_file jest rozwiązywana względem katalogu pliku compose. Działa to z poziomu powłoki oraz z jednostki, która ustawia WorkingDirectory. Operacja kończy się niepowodzeniem w jednostce bez tego ustawienia, ponieważ katalogiem roboczym jest wtedy /.

Rootless Docker to odrębny przypadek. Demon działa jako usługa użytkownika, a usługa użytkownika zatrzymuje się, gdy kończy się ostatnia sesja tego użytkownika. Włącz usługę dla użytkownika i zezwól jej na działanie, gdy nikt nie jest zalogowany:

systemctl --user enable docker
sudo loginctl enable-linger $USER

Bez enable-linger demon rootless wyłącza się po wylogowaniu, a wraz z nim kontenery, co wygląda dokładnie jak uszkodzona polityka restartu.

Ostatnia kwestia. Automatyczne aktualizacje bezpieczeństwa mogą zrestartować serwer o określonej godzinie, co jest korzystne tylko wtedy, gdy stos usług uruchamia się samodzielnie. Konfigurację tego mechanizmu na nowej maszynie należy wykonać w ramach prac wykonywanych w pierwszej kolejności, opisanych w pierwsze dziesięć minut na nowym VPS.

FAQ

Jaka jest różnica między restart: always a restart: unless-stopped?

Obie opcje restartują kontener, gdy ten zatrzyma się samoczynnie. Różnica pojawia się po ręcznym zatrzymaniu kontenera. W przypadku always kontener uruchomi się ponownie przy następnym starcie Docker daemon, więc restart systemu anuluje ręczne zatrzymanie. W przypadku unless-stopped daemon zapamiętuje, że kontener został zatrzymany celowo i nie ingeruje w jego stan. Należy stosować unless-stopped, chyba że wymagane jest, aby kontener nigdy nie pozostawał w stanie zatrzymanym.

Dodałem restart: unless-stopped, ale kontener nadal nie startuje po restarcie systemu. Dlaczego?

Polityka jest przypisana do kontenera, a nie do pliku, więc edycja YAML nie aktualizuje istniejącego kontenera. Należy wykonać docker compose up -d, aby Compose odtworzył kontener, a następnie potwierdzić stan za pomocą docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app). Jeśli wynik polecenia zawiera no, oznacza to, że kontener został utworzony przed wprowadzeniem zmian. Inną częstą przyczyną jest brak włączonej usługi docker.service, co można sprawdzić za pomocą systemctl is-enabled docker.

Czy potrzebuję jednostki systemd, jeśli używam polityk restartu?

Zazwyczaj nie. Polityka restartu jest wystarczająca dla stosów wymagających jedynie sieci, co dotyczy większości przypadków. Jednostkę należy dodać, gdy kontenery zależą od zasobów niedostępnych w momencie startu Docker daemon, takich jak dysk zewnętrzny, zaszyfrowany wolumen, udział NFS lub interfejs VPN. Jednostka pozwala na określenie kolejności za pomocą After= oraz RequiresMountsFor=, czego polityka restartu nie umożliwia.

Jak trwale zatrzymać stos, aby nie uruchamiał się ponownie po restarcie systemu?

W przypadku unless-stopped wystarczy docker compose stop, ponieważ kontener zatrzymany ręcznie nie jest wznawiany po restarcie daemona. W przypadku always samo zatrzymanie nie wystarczy i kontener powróci po restarcie systemu. Należy wykonać docker compose down, co usunie kontenery, lub najpierw zmienić politykę za pomocą docker update --restart no my-container. Jeśli stos jest zarządzany przez jednostkę systemd, należy również wykonać sudo systemctl disable myapp.service, w przeciwnym razie jednostka uruchomi go ponownie.