SSD Nodes Learn 8GB di RAM — $66/anno
Guide Matt ConnorDi Matt Connor · Aggiornato 2026-08-02

Docker Compose: avvio automatico dopo il riavvio

Scopri come riavviare gli stack Docker Compose dopo un reboot: policy restart, perché on-failure non basta e quando conviene usare un'unità systemd.

La risposta breve

I servizi Docker Compose si avviano all'avvio del sistema quando valgono contemporaneamente due condizioni. Il daemon Docker deve essere abilitato come servizio di sistema e ogni servizio nel file deve avere una policy di riavvio unless-stopped o always. Aggiungete restart: unless-stopped a ogni servizio ed eseguite docker compose up -d una volta: dopo un riavvio, i container si riavvieranno automaticamente. Nel caso comune non serve altro.

Serve un'unità systemd solo quando l'ordine di avvio è importante: per uno stack che dipende da un disco montato, da un'interfaccia VPN o da una condivisione di rete che non è pronta quando viene avviato il daemon Docker. Questo caso è reale e viene trattato nella seconda metà di questa guida. Se state ancora imparando a usare le definizioni dei servizi e i volumi, iniziate dalle basi di Docker Compose su un VPS e poi tornate qui.

Impostare la policy di riavvio in compose.yaml

La policy si specifica una volta per ogni servizio. Non esiste un'impostazione globale. Se si dimentica un servizio, questo rimane arrestato dopo il riavvio, mentre il resto dello stack si avvia.

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:

Applicarla, quindi leggere la policy dal container in esecuzione:

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

Il comando restituisce unless-stopped. Se restituisce no, il file è stato modificato, ma il container non è mai stato ricreato.

Questo è l'errore più comune. La policy di riavvio è memorizzata nel container, non nel file YAML. La modifica di compose.yaml non cambia un container già esistente. Anche docker compose restart non risolve il problema, perché arresta e avvia lo stesso oggetto container senza modificarne la configurazione. Solo docker compose up -d confronta il file con i container in esecuzione, rileva la modifica della policy e li ricrea.

Per un container che non si vuole ricreare subito, modificare la policy direttamente:

docker update --restart unless-stopped my-container

Modificare comunque anche il file YAML. docker update modifica il container in esecuzione, mentre il successivo docker compose up -d leggerà il file e ripristinerà il valore precedente.

Che cosa fa realmente ogni valore di riavvio

Docker definisce quattro valori. La differenza tra questi valori si manifesta solo quando la macchina viene riavviata o il daemon viene riavviato.

  • no è il valore predefinito. Il container non viene mai riavviato automaticamente, in nessuna circostanza.
  • always riavvia il container ogni volta che si arresta. Se lo hai arrestato manualmente, viene comunque riavviato al successivo avvio del daemon Docker. Questo comportamento è spesso sorprendente: un container che avevi arrestato deliberatamente la settimana scorsa è nuovamente in esecuzione dopo un riavvio.
  • unless-stopped si comporta come always, con l'eccezione che un container arrestato manualmente rimane arrestato anche dopo il riavvio del daemon. Questo è il valore da usare per un servizio che arresti occasionalmente per eseguire interventi di manutenzione.
  • on-failure riavvia il container solo quando termina con un codice di uscita diverso da zero. Puoi limitare il numero di tentativi, come in restart: on-failure:3.

Per uno stack che deve essere semplicemente in esecuzione quando il server è attivo, unless-stopped è il valore predefinito corretto. Scegli always solo quando vuoi impedire che un container rimanga arrestato.

Perché il riavvio: on-failure non sopravvive a un riavvio

Molti scelgono on-failure perché sembra una scelta prudente, poi scoprono che ogni container è arrestato dopo il primo riavvio. Il motivo è nella definizione. on-failure reagisce a una sola situazione: il processo del container termina con un codice di errore.

Un riavvio non è un errore. Quando l'host si arresta, systemd arresta docker.service e il daemon arresta deliberatamente ogni container. Il container non ha avuto un errore, quindi la policy non ha nulla a cui reagire. Al riavvio, il daemon controlla i container da riprendere e un container on-failure arrestato correttamente non rientra tra questi. Rimane nello stato exited.

Puoi verificarlo direttamente. Imposta restart: on-failure su un servizio, esegui docker compose up -d, riavvia il sistema, quindi esegui:

docker compose ps -a

Il servizio viene elencato con lo stato Exited e uno stato simile a Exited (0) 2 minutes ago. Non c'è alcun problema e non viene registrato alcun errore, rendendo difficile la diagnosi. La policy ha fatto esattamente ciò che indica.

on-failure è comunque utile. È adatto a un container che esegue un job e può terminare in modo anomalo, quando vuoi un numero limitato di tentativi e nessun ciclo di riavvio. È lo strumento sbagliato per mantenere attivo un servizio a esecuzione prolungata dopo i riavvii.

Le policy di riavvio funzionano solo se il servizio Docker viene avviato all'avvio del sistema

Le policy di riavvio vengono applicate dal demone Docker. Se il demone non viene avviato, non viene applicata alcuna policy. Verifica lo stato:

systemctl is-enabled docker
systemctl is-enabled containerd

Entrambi i comandi dovrebbero restituire enabled. I pacchetti del repository ufficiale Docker li abilitano durante l'installazione, quindi su un server appena installato questa verifica di solito ha esito positivo. Se uno dei due restituisce disabled, correggi la configurazione:

sudo systemctl enable --now docker containerd

È importante comprendere un dettaglio. Ubuntu include anche docker.socket, che avvia il demone su richiesta la prima volta che un processo comunica con l'API Docker. Si può vedere docker.socket abilitato, presumere che il demone sia configurato correttamente e disabilitare docker.service per risparmiare memoria. All'avvio del sistema, nessun processo chiama l'API, quindi il socket non viene mai utilizzato, il demone non viene avviato e nessun container viene avviato finché non si esegue il primo comando docker. L'attivazione tramite socket non sostituisce l'abilitazione di docker.service.

Quando un'unit systemd è la soluzione migliore

Le policy di riavvio non gestiscono l'ordine di avvio rispetto al resto del sistema. Il demone si avvia e porta i container allo stato attivo appena possibile. Se lo stack usa un bind mount per una directory presente su un volume separato, una condivisione NFS (network file system) o un disco crittografato, i container possono avviarsi prima che il percorso esista. Docker crea senza problemi una directory vuota nel punto di mount e avvia il container utilizzandola; il database viene quindi avviato senza dati.

Scrivi un'unit systemd quando si verifica una di queste condizioni. Lo stack richiede che prima sia pronto un mount, un'interfaccia VPN o un'altra unit. Vuoi che systemctl stop myapp e systemctl start myapp funzionino come per ogni altro servizio del sistema. Oppure vuoi che lo stack venga arrestato correttamente durante lo spegnimento, invece di essere terminato insieme al demone. Se le unit systemd sono una novità per te, scrivere un servizio e un timer systemd descrive più dettagliatamente il formato del file.

Scrivere l'unità systemd

Inserire lo stack in un percorso fisso, al di fuori di una directory home. /srv/myapp è una scelta appropriata, perché un'unità eseguita prima che qualcuno effettui l'accesso non deve leggere /home.

Creare /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

Abilitare e avviare l'unità:

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

Un'unità funzionante mostra Active: active (exited). La prima volta può sembrare errato. È corretto: Type=oneshot con RemainAfterExit=yes significa che l'unità ha eseguito il comando, il comando è terminato e systemd mantiene l'unità contrassegnata come attiva, in modo che ExecStop venga eseguito durante l'arresto.

Ogni riga ha una funzione precisa. Requires=docker.service indica che l'unità termina rapidamente invece di eseguire docker compose su un socket non disponibile. After= imposta l'ordine, perché Requires= da solo non lo fa. RequiresMountsFor= fa in modo che systemd includa l'unità di mount per quel percorso e attenda che sia pronta. Questo è il motivo principale per usare un'unità invece di una policy di riavvio. TimeoutStartSec=0 impedisce a systemd di terminare il job di avvio mentre è ancora in corso il pull di un'immagine di grandi dimensioni.

Una nota sulla combinazione dei due meccanismi. La documentazione di Docker sconsiglia di combinare le restart policy con un process manager dell'host. L'avvertenza riguarda un process manager che supervisiona direttamente il processo del container e lo riavvia mentre il daemon tenta di fare lo stesso. Un'unità Type=oneshot non supervisiona alcun processo. È quindi corretto mantenere restart: unless-stopped nel file compose insieme a questa unità, ed è la configurazione desiderata. systemd gestisce l'ordine durante l'avvio, mentre il daemon gestisce un container che termina inaspettatamente durante la notte.

Verifica con un riavvio reale

Non esiste un sostituto per il test reale. systemctl restart docker non verifica l'ordine dei mount e docker compose down seguito da docker compose up -d non verifica nulla relativo all'avvio.

sudo reboot

Attendi, riconnettiti e verifica in questo ordine:

uptime
systemctl is-active docker
docker compose ps

uptime conferma che stai esaminando una macchina che è stata realmente riavviata. docker compose ps, eseguito dalla directory dello stack, dovrebbe elencare ogni servizio come running, con un uptime vicino a quello della macchina. Il servizio che mostra Exited è quello da esaminare.

Se un servizio non è stato avviato, il log del demone copre la finestra di avvio:

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

Per uno stack gestito da una unit, journalctl -u myapp.service -b --no-pager mostra l'output docker compose esatto dell'avvio, incluso un pull dell'immagine non riuscito o un file .env mancante.

Elementi che interrompono silenziosamente l'avvio automatico

I container creati con docker compose run non ricevono mai la policy di riavvio definita nel file. Compose li tratta come container temporanei. Se un servizio sembra ignorare la propria policy, verifica se è stato avviato con run invece di up.

Un percorso relativo in un volume o in una voce env_file viene risolto rispetto alla directory del file Compose. Funziona dalla shell e funziona da un'unità che imposta WorkingDirectory. Non funziona da un'unità che non lo imposta, perché la directory di lavoro è quindi /.

Docker rootless è un caso distinto. Il daemon viene eseguito come servizio dell'utente e un servizio dell'utente si arresta quando termina l'ultima sessione di quell'utente. Abilitalo per l'utente e consenti che continui a essere eseguito anche senza utenti connessi:

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

Senza enable-linger, il daemon rootless si arresta quando esegui il logout e con esso si arrestano anche i container. Questo produce esattamente l'effetto di una policy di riavvio non funzionante.

Un'ultima considerazione. Gli aggiornamenti automatici di sicurezza possono riavviare un server a un'ora prestabilita. Questo è utile solo se lo stack si avvia nuovamente da solo. Configurare questa funzione su una macchina nuova rientra nelle attività iniziali, insieme a primi dieci minuti su un nuovo VPS.

FAQ

Qual è la differenza tra restart: always e restart: unless-stopped?

Entrambe riavviano il container quando si arresta autonomamente. La differenza si presenta dopo l'arresto manuale del container. Con always, il container viene avviato di nuovo al successivo avvio del demone Docker, quindi un riavvio annulla l'arresto manuale. Con unless-stopped, il demone ricorda che il container è stato arrestato intenzionalmente e lo lascia fermo. Usa unless-stopped, a meno che tu non voglia specificamente che un container rimanga arrestato.

Ho aggiunto restart: unless-stopped, ma il container non viene ancora avviato dopo un riavvio. Perché?

La policy è memorizzata nel container, non nel file, e la modifica del file YAML non aggiorna un container già esistente. Esegui docker compose up -d per fare in modo che Compose lo ricrei, quindi verifica con docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app). Se restituisce no, il container è stato creato prima della modifica. L'altra causa comune è che docker.service non sia abilitato; puoi verificarlo con systemctl is-enabled docker.

Mi serve un'unità systemd se uso già le policy di riavvio?

Di solito no. Una policy di riavvio è sufficiente per uno stack che dipende solo dalla rete, come avviene per la maggior parte degli stack. Aggiungi un'unità quando i container dipendono da una risorsa che non è pronta all'avvio del demone Docker, ad esempio un disco esterno, un volume crittografato, una condivisione NFS o un'interfaccia VPN. L'unità consente di definire l'ordine tramite After= e RequiresMountsFor=, cosa che una policy di riavvio non può esprimere.

Come posso arrestare definitivamente uno stack senza che venga riavviato al successivo riavvio del sistema?

Con unless-stopped, docker compose stop è sufficiente, perché un container arrestato manualmente non viene ripreso quando il demone viene riavviato. Con always, l'arresto non è sufficiente e il container viene riavviato dopo un reboot. Esegui docker compose down, che rimuove i container, oppure modifica prima la policy con docker update --restart no my-container. Se un'unità systemd gestisce lo stack, esegui anche sudo systemctl disable myapp.service, altrimenti l'unità lo riavvierà.