Docker Compose automatisch starten na reboot
Zorg dat uw Docker Compose containers automatisch opstarten na een reboot. Leer waarom restart: always nodig is en wanneer u een systemd unit moet gebruiken voor afhankelijkheden.
Het korte antwoord
Docker Compose-services starten bij het opstarten wanneer aan twee voorwaarden tegelijkertijd is voldaan. De Docker-daemon moet als systeemservice zijn ingeschakeld en elke service in het bestand moet een restart-policy van unless-stopped of always hebben. Voeg restart: unless-stopped toe aan elke service, voer eenmaal docker compose up -d uit en de containers starten na een reboot automatisch opnieuw op. Voor de standaardgevallen is niets anders vereist.
U heeft alleen een systemd-unit nodig wanneer de volgorde van belang is: een stack die afhankelijk is van een aangekoppelde schijf, een VPN-interface of een netwerkshare die nog niet gereed is op het moment dat de Docker-daemon start. Dat is een reëel scenario en de tweede helft van deze handleiding behandelt dit. Als u nog bekend moet raken met servicedefinities en volumes, begin dan bij de basis van Docker Compose op een VPS en keer daarna terug.
Het restart-beleid instellen in compose.yaml
Het beleid wordt per service op één regel gedefinieerd. Er is geen globale schakelaar; een service die u vergeet, blijft na een herstart uitgeschakeld terwijl de rest van de stack opstart.
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 vervolgens het beleid uit van de actieve container:
docker compose up -d
docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app)Dit voert unless-stopped uit. Als het no weergeeft, is het bestand wel bewerkt, maar is de container nooit opnieuw aangemaakt.
Dit is de meest voorkomende fout. Het restart-beleid wordt opgeslagen in de container, niet in het YAML-bestand. Het bewerken van compose.yaml verandert niets aan een container die al bestaat. docker compose restart helpt ook niet, omdat dit de bestaande container stopt en start zonder de configuratie aan te passen. Alleen docker compose up -d vergelijkt het bestand met de actieve containers, detecteert de wijziging in het beleid en maakt de containers opnieuw aan.
Voor een container die u op dit moment niet opnieuw wilt aanmaken, wijzigt u het beleid direct:
docker update --restart unless-stopped my-containerBewerk daarnaast nog steeds het YAML-bestand. docker update wijzigt de actieve container, en de volgende docker compose up -d zal het bestand inlezen en de oude waarde terugzetten.
Wat elke restart-waarde precies doet
Docker definieert vier waarden. Het verschil tussen deze waarden wordt pas duidelijk wanneer de machine opnieuw opstart of de daemon wordt herstart.
nois de standaardinstelling. De container wordt onder geen enkele omstandigheid automatisch herstart.alwaysherstart de container telkens wanneer deze stopt. Als u de container handmatig heeft gestopt, komt deze toch weer terug zodra de Docker-daemon start. Dit is vaak onverwacht: een container die u vorige week bewust heeft gestopt, draait weer na een reboot.unless-stoppedgedraagt zich alsalways, met als verschil dat een handmatig gestopte container gestopt blijft na een herstart van de daemon. Dit is de gewenste waarde voor een service die u af en toe offline haalt voor onderhoud.on-failureherstart de container alleen wanneer deze afsluit met een exitcode die niet nul is. U kunt het aantal pogingen beperken, zoals inrestart: on-failure:3.
Voor een stack die simpelweg moet draaien wanneer de server actief is, is unless-stopped de juiste standaard. Kies alleen voor always wanneer u een container wilt die niet in een gestopte status blijft staan.
Waarom restart: on-failure niet werkt na een reboot
Veel gebruikers kiezen voor on-failure omdat dit voorzichtig klinkt, maar ontdekken vervolgens dat elke container na de eerste reboot is gestopt. De reden hiervoor ligt in de definitie. on-failure reageert op slechts één gebeurtenis: het containerproces dat afsluit met een foutcode.
Een reboot is geen fout. Wanneer de host afsluit, stopt systemd docker.service en de daemon stopt elke container opzettelijk. De container is niet gefaald, dus het beleid heeft geen aanleiding om actie te ondernemen. Bij het opstarten kijkt de daemon naar containers die hervat moeten worden, en een on-failure-container die netjes is gestopt, hoort daar niet bij. Deze blijft in de status exited staan.
U kunt dit direct waarnemen. Stel restart: on-failure in op een service, voer docker compose up -d uit, voer een reboot uit en voer vervolgens het volgende commando uit:
docker compose ps -aDe service wordt vermeld met een status van Exited en een statusmelding zoals Exited (0) 2 minutes ago. Er is niets defect en er wordt geen fout gelogd, wat de diagnose lastig maakt. Het beleid heeft precies uitgevoerd wat de definitie voorschrijft.
on-failure is nog steeds nuttig. Het is geschikt voor een container die een taak uitvoert en kan crashen, waarbij u een beperkt aantal pogingen wilt en geen oneindige herstartlus. Het is echter het verkeerde hulpmiddel om een langlopende service actief te houden na een reboot.
Restart policies werken alleen als de Docker service start bij het opstarten
Restart policies worden afgedwongen door de Docker daemon. Als de daemon niet start, wordt er niets afgedwongen. Controleer dit:
systemctl is-enabled docker
systemctl is-enabled containerdBeide zouden enabled moeten weergeven. De pakketten uit de officiële repository van Docker schakelen deze in tijdens de installatie, dus op een nieuwe server is dit meestal in orde. Als een van beide disabled weergeeft, herstel dit dan:
sudo systemctl enable --now docker containerdEr is hier een valkuil die het begrijpen waard is. Ubuntu levert ook docker.socket, die de daemon op verzoek start zodra er voor het eerst contact wordt gemaakt met de Docker API. Mensen zien dat docker.socket is ingeschakeld, gaan ervan uit dat de daemon gedekt is en schakelen docker.service uit om geheugen te besparen. Bij het opstarten roept niets de API aan, dus de socket wordt nooit aangeraakt, de daemon start nooit en er komt geen container online totdat u uw eerste docker commando typt. Socket-activatie is geen vervanging voor het inschakelen van docker.service.
Wanneer een systemd unit de betere oplossing is
Restart-policies hebben geen inzicht in de opstartvolgorde van de rest van het systeem. De daemon start en brengt uw containers zo snel mogelijk online. Als uw stack een map bind-mount vanaf een apart volume, een NFS (network file system) share of een versleutelde schijf, kunnen de containers starten voordat dat pad bestaat. Docker maakt in dat geval zonder waarschuwing een lege map aan op het mount-point en start de container daarin, waardoor uw database zonder data opstart.
Schrijf een systemd unit wanneer een van de volgende 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 op dezelfde manier werken als voor elke andere service op de server. Of u wilt dat de stack tijdens het afsluiten netjes wordt gestopt in plaats van dat deze samen met de daemon wordt beëindigd. Als systemd units nieuw voor u zijn, behandelt het schrijven van een systemd service en timer het bestandsformaat in meer detail.
Het schrijven van de systemd-unit
Plaats de stack in een vast pad buiten een home-directory. /srv/myapp is een goede keuze, omdat een unit die opstart voordat iemand inlogt, geen toegang hoeft te hebben tot /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 echter correct: Type=oneshot met RemainAfterExit=yes betekent dat de unit het commando heeft uitgevoerd, het commando is voltooid en systemd de unit als actief gemarkeerd houdt zodat ExecStop wordt uitgevoerd bij het afsluiten.
Elke regel heeft een functie. Requires=docker.service zorgt ervoor dat de unit direct faalt in plaats van docker compose uit te voeren tegen een dode socket. After= bepaalt de volgorde, omdat Requires= dat op zichzelf niet doet. RequiresMountsFor= zorgt ervoor dat systemd de mount-unit voor dat pad ophaalt en erop wacht; dit is de voornaamste reden om een unit te gebruiken in plaats van een restart-policy. TimeoutStartSec=0 voorkomt dat systemd de starttaak afbreekt terwijl een groot image nog wordt binnengehaald.
Een opmerking over het combineren van beide mechanismen. De documentatie van Docker raadt het mengen van restart-policies met een host-procesbeheerder af. Die waarschuwing betreft een procesbeheerder die het containerproces zelf superviseert en herstart terwijl de daemon hetzelfde probeert te doen. Een Type=oneshot-unit superviseert niets, dus het behouden van restart: unless-stopped in het compose-bestand naast deze unit is prima, en is precies wat u wilt. systemd regelt de volgorde bij het opstarten, en de daemon regelt een container die crasht om drie uur 's nachts.
De unit ziet er anders uit wanneer het proces dat u actief houdt een simpel langlopend proces is in plaats van een stack, omdat er dan geen daemon onder zit en systemd's eigen Restart= de supervisie moet uitvoeren; dsh headless draaien achter systemd is een uitgewerkt voorbeeld van die vorm, inclusief de toegewezen gebruiker en de journal.
Verifiëren met een echte reboot
Er is geen vervanging voor de echte test. systemctl restart docker test de volgorde van mounts niet, en docker compose down gevolgd door docker compose up -d test niets met betrekking tot het opstartproces zelf.
sudo rebootWacht, maak opnieuw verbinding en controleer in deze volgorde:
uptime
systemctl is-active docker
docker compose psuptime bevestigt dat u kijkt naar een machine die daadwerkelijk opnieuw is opgestart. docker compose ps, uitgevoerd vanuit de stack-directory, hoort elke service weer te geven als running met een uptime die dicht bij die van de machine ligt. Een service die Exited toont, is de service die u moet onderzoeken.
Als iets niet is opgestart, bevat het daemon-logboek het opstartvenster:
journalctl -u docker.service -b --no-pager | tail -50Voor een stack die wordt beheerd door een unit, toont journalctl -u myapp.service -b --no-pager de exacte docker compose-output vanaf het opstarten, inclusief een mislukte image-pull of een ontbrekend .env-bestand. De reboot die u plant, is de reboot die u in de gaten houdt, dus laat de unit u informeren over de reboots die u niet ziet: een OnFailure=-regel die verwijst naar een zelfgehoste ntfy-server zet een stack die niet is opgestart om in een push-notificatie, in plaats van iets dat u pas dagen later ontdekt.
Zaken die het automatisch opstarten stilletjes verstoren
Containers die met docker compose run zijn aangemaakt, krijgen nooit het restart-beleid uit het bestand mee. Compose behandelt deze als eenmalige containers. Als een service zijn beleid lijkt te negeren, controleer dan of deze is gestart met run in plaats van up.
Een relatief pad in een volume of in een env_file-item wordt opgelost ten opzichte van de directory van het compose-bestand. Dat werkt vanuit uw shell, en het werkt vanuit een unit die WorkingDirectory instelt. Het mislukt vanuit een unit zonder deze instelling, omdat de werkdirectory dan / is.
Rootless Docker is een apart geval. De daemon draait als een user service, en een user service stopt wanneer de laatste sessie voor die gebruiker eindigt. Schakel dit in voor de gebruiker en sta toe dat het proces blijft draaien zonder dat er iemand is ingelogd:
systemctl --user enable docker
sudo loginctl enable-linger $USERZonder enable-linger sluit de rootless daemon af wanneer u uitlogt en gaan de containers mee, wat er precies zo uitziet als een defect restart-beleid.
Nog één laatste punt. Automatische beveiligingsupdates kunnen een server op een vast uur herstarten; dit is alleen wenselijk als uw stack vanzelf weer opkomt. Het instellen hiervan op een nieuwe machine hoort bij de rest van het werk in het eerste uur, zoals beschreven in de eerste tien minuten op een nieuwe VPS.
FAQ
Wat is het verschil tussen restart: always en restart: unless-stopped?
Beide herstarten de container wanneer deze uit zichzelf stopt. Ze verschillen wanneer u een container handmatig stopt. Bij always start de container opnieuw zodra de Docker daemon start; een reboot maakt uw handmatige stopactie dus ongedaan. Bij unless-stopped onthoudt de daemon dat de container bewust is gestopt en laat deze met rust. Gebruik unless-stopped tenzij u specifiek een container wilt die niet uitgeschakeld blijft.
Ik heb restart: unless-stopped toegevoegd, maar de container start na een reboot nog steeds niet. Hoe komt dit?
Het beleid is gekoppeld aan de container, niet aan het bestand. Een bestaande container wordt niet bijgewerkt door het YAML-bestand aan te passen. Voer docker compose up -d uit zodat Compose de container opnieuw aanmaakt en bevestig dit met docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app). Als dit no toont, is de container aangemaakt vóór uw wijziging. De andere veelvoorkomende oorzaak is dat docker.service niet is ingeschakeld; dit kunt u controleren met systemctl is-enabled docker.
Heb ik een systemd unit nodig als ik al restart policies gebruik?
Meestal niet. Een restart policy volstaat voor een stack die alleen het netwerk nodig heeft, wat voor de meeste stacks geldt. Voeg een unit toe wanneer de containers afhankelijk zijn van iets dat nog niet gereed is wanneer de Docker daemon start, zoals een externe schijf, een versleuteld volume, een NFS-share of een VPN-interface. De unit biedt volgordelijkheid via After= en RequiresMountsFor=, wat een restart policy niet kan afdwingen.
Hoe stop ik een stack permanent zonder dat deze na een reboot terugkeert?
Bij unless-stopped is docker compose stop voldoende, omdat een handmatig gestopte container niet wordt hervat wanneer de daemon herstart. Bij always is een stopactie niet genoeg en keert de container na een reboot terug. Voer ofwel docker compose down uit, wat de containers verwijdert, of wijzig eerst het beleid met docker update --restart no my-container. Als een systemd unit de stack beheert, voer dan ook sudo systemctl disable myapp.service uit, anders zal de unit de stack opnieuw starten.