Docker Compose automatisch starten na een reboot
Laat Docker Compose-services automatisch terugkomen na een reboot. Lees waarom on-failure niet volstaat en wanneer u een systemd-unit nodig hebt.
Het korte antwoord
Docker Compose-services starten bij het opstarten wanneer twee voorwaarden tegelijk gelden. De Docker-daemon moet als systeemservice zijn ingeschakeld en elke service in het bestand moet een restartbeleid van unless-stopped of always hebben. Voeg restart: unless-stopped toe aan elke service, voer docker compose up -d eenmalig uit en de containers starten na een herstart automatisch opnieuw. Voor het algemene geval is niets anders vereist.
U hebt alleen een systemd-unit nodig wanneer de volgorde van belang is: bijvoorbeeld voor een stack die afhankelijk is van een gekoppelde schijf, een VPN-interface of een netwerkshare die nog niet gereed is wanneer de Docker-daemon start. Dat geval komt voor en wordt in de tweede helft van deze handleiding behandeld. Als u nog bezig bent met service-definities en volumes, begin dan met de basisprincipes van Docker Compose op een VPS en kom daarna terug.
Stel het restart-beleid in in compose.yaml
Het beleid staat één keer per service. Er is geen globale instelling. Een service die u vergeet, blijft daarom na het opnieuw opstarten uitgeschakeld terwijl de rest van de stack wordt gestart.
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:Pas het beleid toe en lees het daarna uit van de actieve container:
docker compose up -d
docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app)Dit geeft unless-stopped weer. Als no wordt weergegeven, is het bestand bewerkt, maar is de container nooit opnieuw aangemaakt.
Dit is de meest voorkomende fout. Het restart-beleid wordt opgeslagen op de container, niet in het YAML-bestand. Als u compose.yaml bewerkt, verandert dat niets aan een container die al bestaat. docker compose restart helpt ook niet, omdat hiermee hetzelfde containerobject wordt gestopt en gestart zonder de configuratie te wijzigen. Alleen docker compose up -d vergelijkt het bestand met de actieve containers, stelt vast dat het beleid is gewijzigd en maakt de containers opnieuw aan.
Als u een container nu niet opnieuw wilt aanmaken, wijzigt u het beleid direct:
docker update --restart unless-stopped my-containerBewerk het YAML-bestand ook. docker update wijzigt de actieve container, en de volgende docker compose up -d leest het bestand en zet de oude waarde terug.
Wat elke herstartwaarde daadwerkelijk doet
Docker definieert vier waarden. Het verschil ertussen wordt alleen zichtbaar wanneer de machine opnieuw opstart of de daemon opnieuw wordt gestart.
nois de standaardwaarde. De container wordt onder geen enkele omstandigheid automatisch opnieuw gestart.alwaysstart de container opnieuw zodra deze stopt. Als u de container handmatig hebt gestopt, wordt deze alsnog opnieuw gestart zodra de Docker-daemon de volgende keer start. Dat is vaak verrassend: een container die u vorige week bewust hebt gestopt, draait na een herstart weer.unless-stoppedwerkt hetzelfde alsalways, behalve dat een handmatig gestopte container gestopt blijft wanneer de daemon opnieuw wordt gestart. Gebruik deze waarde voor een service die u soms voor onderhoud uitschakelt.on-failurestart de container alleen opnieuw wanneer deze wordt beëindigd met een exitcode die niet nul is. U kunt het aantal pogingen beperken, zoals inrestart: on-failure:3.
Voor een stack die actief moet zijn zolang de server actief is, is unless-stopped de juiste standaardwaarde. Kies always alleen wanneer u wilt voorkomen dat een container uitgeschakeld blijft.
Waarom herstarten: on-failure blijft niet actief na opnieuw opstarten
Veel mensen kiezen on-failure omdat dit voorzichtig klinkt. Daarna merken zij dat elke container na de eerste herstart is gestopt. De reden staat in de definitie. on-failure reageert maar op één gebeurtenis: het containerproces wordt beëindigd met een foutcode.
Een herstart is geen fout. Wanneer de host wordt afgesloten, stopt systemd docker.service en stopt de daemon elke container bewust. De container is niet mislukt, dus het beleid hoeft niet te reageren. Bij het opnieuw opstarten controleert de daemon welke containers moeten worden hervat. Een on-failure-container die correct is gestopt, hoort daar niet bij. Deze blijft de status exited houden.
U kunt dit rechtstreeks controleren. Stel restart: on-failure in voor een service, voer docker compose up -d uit, start de host opnieuw op en voer daarna het volgende uit:
docker compose ps -aDe service wordt weergegeven met de status Exited en een status zoals Exited (0) 2 minutes ago. Er is niets defect en er wordt niets als fout geregistreerd. Daardoor is dit moeilijk te diagnosticeren. Het beleid heeft precies gedaan wat het voorschrijft.
on-failure is nog steeds nuttig. Het past bij een container die een taak uitvoert en kan crashen, waarbij u een beperkt aantal nieuwe pogingen wilt zonder een herstartlus. Het is niet het juiste hulpmiddel om een langlopende service na een herstart actief te houden.
Herstartbeleid werkt alleen als de Docker-service bij het opstarten wordt gestart
Herstartbeleid wordt afgedwongen door de Docker-daemon. Als de daemon niet start, wordt er niets afgedwongen. Controleer dit:
systemctl is-enabled docker
systemctl is-enabled containerdBeide opdrachten moeten enabled afdrukken. De pakketten uit de officiële repository van Docker schakelen deze bij de installatie in. Op een nieuwe server slaagt deze controle daarom meestal. Als een van beide disabled afdrukt, herstelt u dit:
sudo systemctl enable --now docker containerdHierbij is een belangrijk aandachtspunt. Ubuntu levert ook docker.socket, dat de daemon op verzoek start zodra iets voor het eerst met de Docker API communiceert. Men ziet dat docker.socket is ingeschakeld, neemt aan dat de daemon daarmee is afgedekt en schakelt docker.service uit om geheugen te besparen. Bij het opstarten roept niets de API aan. De socket wordt dan nooit gebruikt, de daemon start niet en geen enkele container wordt gestart totdat u uw eerste opdracht docker invoert. Socketactivering vervangt niet het inschakelen van docker.service.
Wanneer een systemd-unit de betere oplossing is
Herstartbeleid houdt geen rekening met de volgorde waarin de rest van het systeem beschikbaar komt. De daemon start en start uw containers zo snel mogelijk. Als uw stack een directory bind-mount vanuit een afzonderlijk volume, een NFS-share (network file system) of een versleutelde schijf gebruikt, kunnen de containers starten voordat dat pad bestaat. Docker maakt zonder problemen een lege directory op het mountpunt en start de container met die directory. Uw database start dan zonder gegevens.
Schrijf een systemd-unit als een van deze situaties van toepassing is. De stack vereist dat een mount, een VPN-interface of een andere unit eerst gereed is. U wilt dat systemctl stop myapp en systemctl start myapp werken zoals bij elke andere service op het systeem. Of u wilt dat de stack tijdens het afsluiten netjes wordt gestopt, in plaats van samen met de daemon te worden beëindigd. Als systemd-units nieuw voor u zijn, behandelt een systemd-service en timer schrijven de bestandsindeling uitgebreider.
De systemd-unit schrijven
Plaats de stack op een vast pad buiten een home-directory. /srv/myapp is een goede keuze, omdat een unit die wordt uitgevoerd voordat iemand zich aanmeldt niets te zoeken heeft in /home.
Maak /etc/systemd/system/myapp.service aan:
[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.targetSchakel de unit in en start deze:
sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service
systemctl status myapp.serviceEen gezonde unit toont Active: active (exited). Dat ziet er de eerste keer vreemd uit. Het is correct: Type=oneshot met RemainAfterExit=yes betekent dat de unit zijn opdracht heeft uitgevoerd, dat de opdracht is voltooid en dat systemd de unit als actief gemarkeerd houdt, zodat ExecStop bij het afsluiten wordt uitgevoerd.
Elke regel heeft een functie. Requires=docker.service betekent dat de unit snel faalt in plaats van docker compose uit te voeren tegen een niet-beschikbare socket. After= stelt de volgorde in, omdat Requires= dat op zichzelf niet doet. RequiresMountsFor= zorgt ervoor dat systemd de mount-unit voor dat pad inlaadt en daarop wacht. Dat is precies de reden om een unit te gebruiken in plaats van een restartbeleid. TimeoutStartSec=0 voorkomt dat systemd de starttaak beëindigt terwijl er nog een grote image wordt opgehaald.
Een opmerking over het combineren van de twee mechanismen. De documentatie van Docker raadt af om restartbeleid te combineren met een processmanager op de host. Die waarschuwing gaat over een processmanager die toezicht houdt op het containerproces zelf en dit opnieuw start terwijl de daemon hetzelfde probeert te doen. Een Type=oneshot-unit houdt geen toezicht op een proces. Daarom is het goed om restart: unless-stopped naast deze unit in het compose-bestand te behouden. Dat is ook wat u nodig hebt. systemd handelt de volgorde tijdens het opstarten af en de daemon handelt een container af die om 03:00 crasht.
Controleren met een echte herstart
Er is geen vervanging voor de echte test. systemctl restart docker test de volgorde waarin bestandssystemen worden aangekoppeld niet, en docker compose down gevolgd door docker compose up -d test niets over het opstarten.
sudo rebootWacht, maak opnieuw verbinding en controleer dit in deze volgorde:
uptime
systemctl is-active docker
docker compose psuptime bevestigt dat u werkt op een machine die daadwerkelijk opnieuw is opgestart. docker compose ps, uitgevoerd vanuit de stackmap, moet elke service vermelden als running, met een uptime die ongeveer gelijk is aan die van de machine. Een service met de status Exited moet u nader onderzoeken.
Als iets niet is gestart, bevat het daemonlog de periode rond het opstarten:
journalctl -u docker.service -b --no-pager | tail -50Voor een stack die door een unit wordt beheerd, toont journalctl -u myapp.service -b --no-pager de exacte uitvoer van docker compose tijdens het opstarten, inclusief een mislukte image-pull of een ontbrekend bestand .env.
Zaken die automatisch starten ongemerkt verstoren
Containers die met docker compose run zijn gemaakt, krijgen nooit het restartbeleid uit het bestand. Compose behandelt ze als eenmalige containers. Als een service het beleid lijkt te negeren, controleert u of deze met run in plaats van up is gestart.
Een relatief pad in een volume of in een item van env_file wordt bepaald ten opzichte van de map van het compose-bestand. Dat werkt vanuit uw shell en vanuit een unit die WorkingDirectory instelt. Het werkt niet vanuit een unit zonder deze instelling, omdat de werkmap dan / is.
Rootless Docker is een apart geval. De daemon draait als een gebruikersservice. Een gebruikersservice stopt wanneer de laatste sessie van die gebruiker wordt beëindigd. Schakel deze in voor de gebruiker en zorg dat deze blijft draaien als niemand is aangemeld:
systemctl --user enable docker
sudo loginctl enable-linger $USERZonder enable-linger wordt de rootless daemon afgesloten wanneer u zich afmeldt. De containers worden dan ook gestopt. Dit ziet er precies uit als een defect restartbeleid.
Tot slot. Automatische beveiligingsupdates kunnen een server op een vast tijdstip opnieuw opstarten. Dat is alleen nuttig als uw stack daarna automatisch terugkomt. Het instellen hiervan op een nieuwe machine hoort bij de overige werkzaamheden van het eerste uur in de eerste tien minuten op een nieuwe VPS.
FAQ
Wat is het verschil tussen restart: always en restart: unless-stopped?
Beide starten de container opnieuw wanneer deze zelf stopt. Het verschil ontstaat nadat u een container handmatig hebt gestopt. Met always start de container opnieuw wanneer de Docker-daemon de volgende keer start. Een herstart van het systeem maakt uw handmatige stop dus ongedaan. Met unless-stopped onthoudt de daemon dat de container opzettelijk is gestopt en laat hij deze gestopt. Gebruik unless-stopped, tenzij u specifiek wilt dat een container gestopt blijft.
Ik heb restart: unless-stopped toegevoegd, maar de container start na een herstart nog steeds niet. Waarom?
Het beleid is gekoppeld aan de container, niet aan het bestand. Een bestaande container wordt niet bijgewerkt wanneer u YAML bewerkt. Voer docker compose up -d uit zodat Compose de container opnieuw maakt. Controleer dit vervolgens met docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app). Als dit no weergeeft, bestond de container al voordat u de wijziging aanbracht. Een andere veelvoorkomende oorzaak is dat docker.service niet is ingeschakeld. U kunt dit controleren met systemctl is-enabled docker.
Heb ik een systemd-unit nodig als ik al restart policies gebruik?
Meestal niet. Een restart policy is voldoende voor een stack die alleen het netwerk nodig heeft. Dat geldt voor de meeste stacks. Voeg een unit toe wanneer de containers afhankelijk zijn van iets dat nog niet beschikbaar is wanneer de Docker-daemon start, zoals een externe schijf, een versleuteld volume, een NFS-share of een VPN-interface. De unit regelt de volgorde via After= en RequiresMountsFor=. Een restart policy kan deze afhankelijkheden niet uitdrukken.
Hoe stop ik een stack permanent zonder dat deze bij de volgende herstart terugkomt?
Met unless-stopped is docker compose stop voldoende, omdat een handmatig gestopte container niet opnieuw wordt gestart wanneer de daemon opnieuw start. Met always is stoppen niet voldoende en komt de container na een herstart terug. Voer docker compose down uit om de containers te verwijderen, of wijzig eerst het beleid met docker update --restart no my-container. Als een systemd-unit de stack beheert, voert u ook sudo systemctl disable myapp.service uit. Anders start de unit de stack opnieuw.