SSD Nodes Learn Hosting plans →
Guide Matt ConnorDi Matt Connor · Aggiornato 2026-08-27

Docker Compose: avvio automatico al riavvio

Scopri come riavviare i servizi Docker Compose dopo un reboot con restart policy, perché on-failure non basta e quando serve un'unita systemd.

La risposta breve

I servizi Docker Compose vengono avviati al boot quando sono soddisfatte contemporaneamente due condizioni. Il demone Docker deve essere abilitato come servizio di sistema e ogni servizio definito nel file deve avere una policy di riavvio pari a unless-stopped o always. Aggiungete restart: unless-stopped a ogni servizio ed eseguite una volta docker compose up -d: dopo un riavvio, i container si avvieranno automaticamente. Nel caso comune non è necessario altro.

Serve un'unità systemd solo quando l'ordine di avvio è importante: ad esempio, per uno stack che dipende da un disco montato, da un'interfaccia VPN o da una condivisione di rete che non è ancora pronta quando si avvia il demone Docker. Questo caso è reale e viene trattato nella seconda parte di questa guida. Se state ancora acquisendo familiarità con le definizioni dei servizi e i volumi, iniziate dalle nozioni di base su Docker Compose su un VPS e poi tornate qui.

Impostare la policy di riavvio in compose.yaml

La policy è una riga per ogni servizio. Non esiste un'impostazione globale: se si dimentica un servizio, questo resterà arrestato dopo il riavvio, mentre il resto dello stack verrà avviato.

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:

Applicare la modifica e 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. Modificare compose.yaml non cambia nulla in un container già esistente. Anche docker compose restart non è sufficiente, perché arresta e riavvia lo stesso oggetto container senza modificarne la configurazione. Solo docker compose up -d confronta il file con i container in esecuzione, rileva la modifica alla 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.

Cosa comporta realmente ciascun valore di riavvio

Docker definisce quattro valori. La differenza tra loro 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 si arresta manualmente, viene comunque riavviato al successivo avvio del daemon Docker. Questo comportamento è spesso sorprendente: un container arrestato intenzionalmente la settimana scorsa è nuovamente in esecuzione dopo un riavvio.
  • unless-stopped si comporta come always, con la differenza che un container arrestato manualmente rimane arrestato anche dopo il riavvio del daemon. È il valore da usare per un servizio che viene arrestato occasionalmente per la manutenzione.
  • on-failure riavvia il container solo quando termina con un codice di uscita diverso da zero. È possibile 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. Scegliere always solo quando si vuole che un container non rimanga arrestato.

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

Molti scelgono on-failure perché sembra un'impostazione prudente, poi scoprono che tutti i container sono fermi dopo il primo riavvio. Il motivo è nella definizione. on-failure reagisce a una sola condizione: il processo del container termina con un codice di errore.

Un riavvio non è un errore. Quando l'host si arresta, systemd ferma docker.service e il daemon arresta deliberatamente ogni container. Il container non è terminato in modo anomalo, quindi la policy non ha nulla a cui reagire. Al riavvio, il daemon verifica quali container deve riavviare. Un container on-failure arrestato correttamente non rientra tra questi. Rimane nello stato exited.

È possibile verificarlo direttamente. Impostare restart: on-failure su un servizio, eseguire docker compose up -d, riavviare il sistema, quindi eseguire:

docker compose ps -a

Il servizio viene elencato con lo stato Exited e uno status simile a Exited (0) 2 minutes ago. Non c'è alcun guasto e nei log non viene registrato alcun errore. Questo rende il problema difficile da diagnosticare. La policy ha funzionato esattamente come previsto.

on-failure resta utile. È adatto a un container che esegue un job e può terminare in modo anomalo, quando si desidera un numero limitato di tentativi senza creare un ciclo di riavvio. Non è lo strumento corretto per mantenere attivo un servizio a esecuzione continua dopo i riavvii.

Le policy di riavvio funzionano solo se il servizio Docker viene avviato al boot

Le policy di riavvio sono applicate dal demone Docker. Se il demone non viene avviato, non c'è alcun componente che le applichi. Verificate lo stato:

systemctl is-enabled docker
systemctl is-enabled containerd

Entrambi i comandi devono restituire enabled. I pacchetti del repository ufficiale Docker li abilitano durante l'installazione, quindi su un server appena installato questo controllo ha normalmente esito positivo. Se uno dei due restituisce disabled, correggete la configurazione:

sudo systemctl enable --now docker containerd

Esiste un dettaglio importante da comprendere. Ubuntu include anche docker.socket, che avvia il demone su richiesta quando qualcosa comunica per la prima volta con l'API Docker. Si vede docker.socket abilitato, si presume che il demone sia quindi gestito e si disabilita docker.service per risparmiare memoria. Al boot 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 è preferibile un'unità systemd

Le policy di riavvio non gestiscono l'ordine di avvio rispetto al resto del sistema. Il demone si avvia e porta online i container appena possibile. Se lo stack monta una directory da un volume separato, da una condivisione NFS (network file system) o da un disco cifrato, i container possono avviarsi prima che il percorso sia disponibile. Docker crea senza problemi una directory vuota nel punto di montaggio e avvia il container utilizzandola. Il database si avvia quindi senza dati.

Scrivete 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à. Volete che systemctl stop myapp e systemctl start myapp funzionino come per ogni altro servizio del server. Oppure volete arrestare lo stack correttamente durante lo spegnimento, invece di lasciarlo terminare insieme al demone. Se le unità systemd sono una novità, creare un servizio e un timer systemd descrive più dettagliatamente il formato del file.

Scrittura dell'unità systemd

Collocare lo stack in un percorso fisso al di fuori di una directory home. /srv/myapp è una scelta adatta, perché un'unità che viene eseguita prima che chiunque 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à in condizioni normali mostra Active: active (exited). La prima volta può sembrare errato. È corretto: Type=oneshot con RemainAfterExit=yes indica che l'unità ha eseguito il comando, che il comando è terminato e che systemd mantiene l'unità nello stato attivo affinché ExecStop venga eseguito durante l'arresto.

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

Una nota sull'uso combinato 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 la stessa cosa. Un'unità Type=oneshot non supervisiona alcun processo, quindi lasciare restart: unless-stopped nel file compose insieme a questa unità è corretto ed è la configurazione desiderata. systemd gestisce l'ordine durante il boot, mentre il daemon gestisce un container che si arresta alle tre del mattino.

L'unità ha un aspetto diverso quando ciò che deve rimanere attivo è un normale processo a esecuzione prolungata anziché uno stack, perché in quel caso non c'è un daemon sottostante e il Restart= di systemd deve occuparsi della supervisione; eseguire dsh senza interfaccia grafica tramite systemd è un esempio completo di questo modello, con tanto di utente dedicato e journal.

Verifica con un riavvio reale

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

sudo reboot

Attendi, riconnettiti e controlla in questo ordine:

uptime
systemctl is-active docker
docker compose ps

uptime conferma che stai verificando 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 daemon 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 completo dell’avvio, incluso il pull fallito di un’immagine o un file .env mancante. Il riavvio pianificato è quello che devi monitorare, quindi lascia che la unit ti segnali quelli che non puoi osservare direttamente: una riga OnFailure= indirizzata a un server ntfy self-hosted trasforma uno stack che non è tornato operativo in una notifica push, invece di qualcosa che scopriresti dopo diversi giorni.

Elementi che interrompono l'avvio automatico senza messaggi evidenti

I container creati con docker compose run non ricevono mai la policy di riavvio definita nel file. Compose li considera 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 anche da un'unità che imposta WorkingDirectory. Fallisce da un'unità che non lo imposta, perché in quel caso la directory di lavoro è /.

Docker rootless è un caso distinto. Il daemon viene eseguito come servizio utente e un servizio utente si arresta quando termina l'ultima sessione di quell'utente. Abilitalo per l'utente e consenti che continui a funzionare 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 i container. Il comportamento appare identico a quello di una policy di riavvio non funzionante.

Un'ultima considerazione. Gli aggiornamenti di sicurezza automatici possono riavviare un server a un'ora prestabilita. Questo è utile solo se lo stack si avvia nuovamente in autonomia. La configurazione su una macchina nuova rientra nelle attività della prima ora descritte in i primi dieci minuti su un nuovo VPS.

FAQ

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

Entrambi riavviano il container quando si arresta autonomamente. La differenza si manifesta dopo l'arresto manuale del container. Con always, il container viene avviato nuovamente al successivo avvio del daemon Docker, quindi un riavvio annulla l'arresto manuale. Con unless-stopped, il daemon conserva l'informazione che il container è stato arrestato intenzionalmente e lo lascia fermo. Usare unless-stopped, a meno che non si desideri specificamente che un container resti fermo.

Ho aggiunto restart: unless-stopped, ma il container non si avvia dopo un riavvio. Perché?

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

Devo creare un'unità systemd se uso già le restart policy?

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

Come posso arrestare definitivamente uno stack senza che torni attivo al riavvio successivo?

Con unless-stopped è sufficiente docker compose stop, perché un container arrestato manualmente non viene riavviato quando il daemon riparte. Con always, l'arresto non è sufficiente e il container torna attivo dopo un riavvio. Eseguire docker compose down, che rimuove i container, oppure modificare prima la policy con docker update --restart no my-container. Se un'unità systemd gestisce lo stack, eseguire anche sudo systemctl disable myapp.service, altrimenti l'unità lo avvierà nuovamente.