Esegui Docker Compose come servizio Linux con systemd

Avvio di Docker Compose all'avvio, gestito da systemd.

Indice

Docker Compose su un server Linux dovrebbe avviarsi all’avvio, fermarsi in modo pulito allo spegnimento e sopravvivere ai riavvii senza intervento manuale.

Docker Compose non è Kubernetes, e questo va bene per i carichi di lavoro a cui questa guida si rivolge. Per molti sistemi reali, un progetto Compose su un singolo host Linux è la quantità giusta di infrastruttura: semplice, leggibile, facile da backuppare e sufficiente per strumenti interni, progetti laterali, servizi self-hosted, ambienti di staging, piccole applicazioni in produzione e infrastrutture per sviluppatori.

docker compose config ont the table with laptop

Il pezzo mancante è solitamente la gestione del servizio. Eseguire questo manualmente non è sufficiente:

docker compose up -d

Un singolo comando avvia lo stack, ma non documenta come lo stack dovrebbe avviarsi all’avvio, fermarsi durante lo spegnimento, ricaricarsi dopo le modifiche, scrivere i log, riprendersi dai guasti o essere aggiornato in modo sicuro. È qui che systemd aiuta.

Questa guida illustra come eseguire un progetto Docker Compose come servizio Linux con systemd — file unit, ordine di avvio, aggiornamenti, log e backup. La divisione delle responsabilità è deliberata: Docker esegue i container, Compose definisce lo stack e systemd avvia e ferma il progetto sull’host. Fa parte di Strumenti per Sviluppatori - Una guida ai flussi di lavoro di sviluppo.

Quando ha senso usare Docker Compose come servizio

Eseguire Compose sotto systemd ha senso quando si ha:

  • Un singolo server Linux
  • Una piccola applicazione self-hosted
  • Uno stack di reverse proxy
  • Uno stack di monitoraggio
  • Una piattaforma di sviluppo locale
  • Uno strumento interno
  • Un ambiente di staging
  • Un semplice servizio di produzione con limiti noti

Esempi:

  • Nginx Proxy Manager
  • Traefik
  • Gitea
  • Grafana e Prometheus
  • PostgreSQL più una piccola app web
  • Uptime Kuma
  • Servizi ausiliari per Home Assistant
  • Registro privato
  • API interna più worker più Redis

Compose è una buona scelta quando il modello operativo è ancora comprensibile da una persona che legge una directory.

Quando Docker Compose non è sufficiente

Usa qualcos’altro quando hai bisogno di:

  • Schedulazione multi-nodo
  • Riassegnazione automatica tra host
  • Scoperta dei servizi a livello di cluster
  • Autoscaling orizzontale
  • Distribuzioni rolling su molte macchine
  • Identità del carico di lavoro granulare
  • Policy di rete complesse
  • Operazioni di piattaforma multi-team su larga scala

A quel punto, Kubernetes, Nomad, Swarm o una piattaforma gestita potrebbero essere più adatti.

La mia regola pratica è evitare di usare Kubernetes solo per saltare l’apprendimento di systemd, e evitare di usare Compose quando il carico di lavoro ha chiaramente bisogno di orchestrazione tra più host.

L’architettura di base

Una configurazione pulita separa i file del progetto, l’unità systemd e i dati persistenti sull’host. Il progetto Compose risiede sotto /opt/myapp/ con compose.yaml, .env, data/, backups/ e script opzionali come scripts/update.sh. Il file unit systemd si trova a /etc/systemd/system/myapp.service.

flowchart TB subgraph host["Linux host"] systemd["systemd unit\n/etc/systemd/system/myapp.service"] compose["Docker Compose\n/opt/myapp/compose.yaml"] docker["Docker Engine"] fs["Persistent data\n/opt/myapp/data/"] end systemd -->|"ExecStart: docker compose up -d"| compose compose --> docker docker --> fs

Ogni livello ha un compito chiaro: Docker esegue i container, Compose definisce lo stack dell’applicazione, systemd avvia e ferma il progetto Compose all’avvio e allo spegnimento, il filesystem dell’host memorizza i dati persistenti, i backup restano espliciti e gli aggiornamenti passano attraverso passaggi scriptizzati e verificabili. Questo layout è deliberatamente noioso, perché un’infrastruttura noiosa è più facile da riparare quando qualcosa si rompe alle 2 del mattino.

Prepara la directory del progetto Compose

Crea una directory sotto /opt:

sudo mkdir -p /opt/myapp
sudo chown -R "$USER":"$USER" /opt/myapp
cd /opt/myapp

Crea un file Compose:

nano compose.yaml

Esempio:

services:
  web:
    image: nginx:stable
    restart: unless-stopped
    ports:
      - "8080:80"
    volumes:
      - ./html:/usr/share/nginx/html:ro
    healthcheck:
      test: ["CMD-SHELL", "nginx -t || exit 1"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s

volumes: {}

Crea la directory dei contenuti:

mkdir -p html
echo "Hello from Docker Compose" > html/index.html

Testa manualmente prima:

docker compose up -d
docker compose ps
docker compose logs --tail=50

Poi fermalo prima di affidare il ciclo di vita a systemd:

docker compose down

Non creare un servizio systemd finché il progetto Compose non funziona manualmente. Mentre testi, tieni a portata di mano il Docker Compose Cheatsheet per ps, logs, pull e struttura del progetto.

Usa il comando moderno docker compose

Docker Engine e il plugin Compose devono essere installati prima di scrivere un file unit. Su Ubuntu, Installare Docker su Ubuntu illustra APT, Snap, modalità rootless e la sicurezza post-installazione in modo da finire con un comando docker compose funzionante.

Usa questo:

docker compose version

Non questo:

docker-compose version

Il vecchio binario docker-compose esiste ancora su molte macchine, ma Docker moderno usa Compose come plugin della CLI Docker.

Nei file di servizio e negli script, preferisci:

/usr/bin/docker compose

Puoi trovare il percorso di Docker con:

command -v docker

Di solito è:

/usr/bin/docker

Crea un servizio systemd per Docker Compose

Se i file unit sono nuovi per te, Eseguire qualsiasi eseguibile come servizio in Linux spiega Type, ExecStart, systemctl e il flusso di lavoro generale di systemd. Questa sezione applica questi modelli specificamente a uno stack Compose.

Crea il file di servizio:

sudo nano /etc/systemd/system/myapp.service

Usa questa unità:

[Unit]
Description=MyApp Docker Compose stack
Requires=docker.service
After=docker.service network-online.target
Wants=network-online.target

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

[Install]
WantedBy=multi-user.target

Ricarica systemd:

sudo systemctl daemon-reload

Avvia il servizio:

sudo systemctl start myapp.service

Abilitalo all’avvio:

sudo systemctl enable myapp.service

Controlla lo stato:

systemctl status myapp.service

Controlla i container:

cd /opt/myapp
docker compose ps

Perché Type=oneshot e RemainAfterExit=yes?

Questa è la parte che molte guide sbagliano in modo sottile.

docker compose up -d avvia i container in modalità staccata ed esce, quindi non c’è un processo Compose in primo piano in esecuzione continua per systemd da supervisionare. L’unità systemd non dovrebbe fingere che docker compose up -d sia un daemon in esecuzione continua.

Usa:

Type=oneshot
RemainAfterExit=yes

Questo dice a systemd:

  • Esegui il comando di avvio.
  • Considera l’unità attiva dopo che il comando è uscito con successo.
  • Esegui ExecStop quando il servizio viene fermato.

Questo corrisponde al comportamento effettivo di Compose staccato, ecco perché Type=oneshot con RemainAfterExit=yes è il predefinito corretto per la maggior parte degli stack.

Perché non Type=simple?

Con Type=simple, systemd si aspetta che il processo ExecStart continui a essere in esecuzione, ma docker compose up -d esce dopo aver avviato i container. Questo può far credere a systemd che il servizio sia terminato, chiamando poi la logica di stop o segnando l’unità come inattiva a seconda della configurazione.

Se vuoi Type=simple, di solito dovresti eseguire Compose in primo piano:

ExecStart=/usr/bin/docker compose up

Questo può funzionare, ma di solito non lo preferisco per gli stack Compose sui server. Container staccati più ExecStop esplicito sono più facili da gestire.

Un’unità più adatta alla produzione

Per un server reale, preferisco un’unità leggermente più rigorosa:

[Unit]
Description=MyApp Docker Compose stack
Documentation=https://example.com/docs/myapp
Requires=docker.service
After=docker.service network-online.target
Wants=network-online.target

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/myapp
EnvironmentFile=-/opt/myapp/.env.systemd
ExecStartPre=/usr/bin/docker compose config --quiet
ExecStart=/usr/bin/docker compose up -d --remove-orphans
ExecReload=/usr/bin/docker compose up -d --remove-orphans
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=0
TimeoutStopSec=120

[Install]
WantedBy=multi-user.target

Dettagli importanti:

  • WorkingDirectory punta al progetto Compose.
  • ExecStartPre valida la configurazione Compose.
  • ExecReload ricrea i servizi modificati.
  • ExecStop ferma e rimuove i container del progetto Compose e la rete predefinita.
  • EnvironmentFile=-... significa che il file è opzionale.

Crea il file di ambiente systemd opzionale:

nano /opt/myapp/.env.systemd

Esempio:

COMPOSE_PROJECT_NAME=myapp

Poi ricarica systemd:

sudo systemctl daemon-reload
sudo systemctl restart myapp.service

Compose .env vs systemd EnvironmentFile

Compose e systemd hanno ciascuno il proprio meccanismo di ambiente, e mescolarli causa errori confusi di “variabile non impostata” all’avvio.

Compose legge automaticamente un file .env nella directory del progetto per la sostituzione delle variabili nel file Compose.

Esempio .env:

APP_TAG=1.2.3
WEB_PORT=8080

Esempio compose.yaml:

services:
  web:
    image: nginx:${APP_TAG}
    ports:
      - "${WEB_PORT}:80"

Un EnvironmentFile systemd imposta le variabili d’ambiente per il comando docker compose stesso.

Esempio:

EnvironmentFile=-/opt/myapp/.env.systemd

Per molti progetti, hai bisogno solo del .env di Compose.

Usa un file di ambiente systemd quando vuoi definire cose come:

COMPOSE_PROJECT_NAME=myapp
COMPOSE_FILE=compose.yaml
DOCKER_HOST=unix:///var/run/docker.sock

Non usare né l’uno né l’altro come un vault di segreti occasionale. Se i segreti sono importanti, usa i segreti Docker, un gestore di segreti esterno, file crittografati o almeno permessi rigorosi.

Imposta perm restrittivi:

chmod 600 /opt/myapp/.env
chmod 600 /opt/myapp/.env.systemd

Politiche di riavvio: Docker vs systemd

Ci sono due livelli di riavvio — la politica di riavvio del container in Compose e la politica di riavvio del servizio systemd — e non dovrebbero essere mescolati ciecamente.

Per container in esecuzione continua, imposta le politiche di riavvio in Compose:

services:
  web:
    image: nginx:stable
    restart: unless-stopped

Valori comuni di riavvio:

Policy Significato
no Non riavviare automaticamente
always Riavvia dopo l’uscita e il riavvio del daemon
on-failure Riavvia solo dopo il fallimento
unless-stopped Riavvia a meno che non sia fermato manualmente

Per la maggior parte dei servizi persistenti, preferisco:

restart: unless-stopped

È prevedibile e rispetta le fermate manuali intenzionali.

L’unità systemd stessa di solito non dovrebbe riavviarsi ripetutamente, perché docker compose up -d non è il carico di lavoro in esecuzione. Sono i container.

Quindi evita questo a meno che tu non abbia un motivo specifico:

Restart=always

Nella maggior parte delle unità Compose-as-service, lascia che Docker gestisca i riavvii dei container.

Controlli di salute

Le politiche di riavvio riavviano i container quando i processi escono. Non risolvono magicamente ogni applicazione non sana.

Aggiungi controlli di salute dove sono utili:

services:
  app:
    image: example/app:latest
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "curl -fsS http://localhost:8080/health || exit 1"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 20s

Controlla la salute:

docker compose ps

Ispeziona un container:

docker inspect container-name

I controlli di salute sono particolarmente utili per:

  • App web
  • Reverse proxy
  • Database
  • Code
  • API interne
  • Worker con un endpoint di salute

Sono meno utili quando controllano solo che un processo esista, perché un processo che è vivo ma bloccato appare comunque sano. Un controllo di salute cattivo è solo un’altra bugia in YAML.

Ordine di avvio e depends_on

Compose può definire dipendenze:

services:
  app:
    image: example/app:latest
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

Questo può aiutare l’ordine di avvio, ma non fidartene troppo. Le applicazioni dovrebbero comunque gestire i retry — i database si riavviano, le reti fluttuano, DNS impiega tempo, e un’app resiliente riprova le connessioni invece di assumere un ordine di avvio perfetto.

Log: journalctl e docker compose logs

Due viste dei log coprono la maggior parte del debugging: systemd cattura il ciclo di vita dell’unità stessa, mentre Compose cattura l’output dell’applicazione dai container in esecuzione.

Log del servizio systemd:

journalctl -u myapp.service -n 100 --no-pager

Segui i log systemd:

journalctl -u myapp.service -f

Log del servizio Compose:

cd /opt/myapp
docker compose logs --tail=100
docker compose logs -f
docker compose logs -f web

Per la maggior parte del debugging delle app, docker compose logs è più utile; per il debugging del ciclo di vita — fallimenti di avvio, crash dell’unità, errori di autorizzazione — journalctl è più utile. Se systemctl start myapp fallisce, controlla prima journalctl. Se lo stack si avvia ma l’app è rotta, controlla docker compose logs.

Rotazione dei log

I log di Docker possono crescere all’infinito se non li configuri.

Per server piccoli, configura la rotazione dei log di Docker in /etc/docker/daemon.json:

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "5"
  }
}

Riavvia Docker:

sudo systemctl restart docker

Poi riavvia lo stack Compose:

sudo systemctl restart myapp.service

Questo si applica ai container appena creati. Ricrea i container se necessario:

cd /opt/myapp
docker compose up -d --force-recreate

La rotazione dei log non è glamour, ma è uno dei modi più semplici per prevenire un’interruzione per disco pieno su un server piccolo.

Aggiornamento di un servizio Compose

Un semplice flusso di aggiornamento manuale:

cd /opt/myapp
docker compose pull
docker compose up -d --remove-orphans
docker image prune -f

Se gestito da systemd, puoi usare:

sudo systemctl reload myapp.service

Se la tua unità ha:

ExecReload=/usr/bin/docker compose up -d --remove-orphans

Ma nota: ExecReload non tira le immagini a meno che tu non includa quel passaggio.

Per aggiornamenti espliciti, crea uno script.

mkdir -p /opt/myapp/scripts
nano /opt/myapp/scripts/update.sh

Script:

#!/usr/bin/env bash
set -euo pipefail

cd /opt/myapp

docker compose config --quiet
docker compose pull
docker compose up -d --remove-orphans
docker image prune -f
docker compose ps

Rendilo eseguibile:

chmod +x /opt/myapp/scripts/update.sh

Eseguilo:

/opt/myapp/scripts/update.sh

Poi l’unità del servizio può rimanere focalizzata sul ciclo di vita, mentre lo script di aggiornamento gestisce il deployment.

Script di aggiornamento più sicuro con hook di backup

Per servizi con stato, aggiorna solo dopo il backup.

#!/usr/bin/env bash
set -euo pipefail

APP_DIR="/opt/myapp"
BACKUP_DIR="/opt/myapp/backups"

cd "$APP_DIR"

mkdir -p "$BACKUP_DIR"

echo "Validating compose file"
docker compose config --quiet

echo "Running backup hook"
if [ -x "$APP_DIR/scripts/backup.sh" ]; then
  "$APP_DIR/scripts/backup.sh"
else
  echo "No backup hook found"
fi

echo "Pulling images"
docker compose pull

echo "Recreating services"
docker compose up -d --remove-orphans

echo "Pruning unused images"
docker image prune -f

echo "Current status"
docker compose ps

Questo è ancora semplice, ma ora codifica un’abitudine operativa: backup prima del cambiamento.

Fermare il servizio

Ferma lo stack:

sudo systemctl stop myapp.service

Questo esegue:

docker compose down

Di default, docker compose down rimuove:

  • Container per i servizi nel file Compose
  • Reti definite dal file Compose
  • La rete predefinita

Non rimuove i volumi nominati a meno che tu non glielo chieda.

Non usare casualmente:

docker compose down -v

Questo rimuove i volumi nominati dichiarati nel file Compose e i volumi anonimi attaccati ai container. Per database e app con stato, questo può significare eliminare dati reali.

Usa down -v solo quando intendi “distruggi questo ambiente”.

Riavviare il servizio

Riavvia l’unità systemd:

sudo systemctl restart myapp.service

Questo esegue il comando di stop e poi il comando di avvio.

Per riavviare solo i container senza ricrearli:

cd /opt/myapp
docker compose restart

Importante distinzione:

  • docker compose restart riavvia i container esistenti.
  • docker compose up -d applica le modifiche di configurazione o immagine ricreando i container quando necessario.

Se hai modificato compose.yaml, usa:

docker compose up -d

Non solo:

docker compose restart

Gestione dei container orfani

Se rinomini o rimuovi un servizio in compose.yaml, i vecchi container possono rimanere come orfani.

Usa:

docker compose up -d --remove-orphans

È per questo che gli esempi di servizio systemd in questa guida usano:

ExecStart=/usr/bin/docker compose up -d --remove-orphans

Mantiene lo stack più vicino al file Compose corrente.

Backup

I backup dipendono dal carico di lavoro, ma i principi sono stabili.

Per bind mounts:

/opt/myapp/data/

Fai il backup di quella directory.

Per volumi nominati:

docker volume ls

Ispeziona un volume:

docker volume inspect volume-name

Per i database, le copie del filesystem non sono sempre sufficienti. Usa backup consapevoli dell’applicazione:

Esempio PostgreSQL:

docker compose exec -T db pg_dump -U postgres appdb > backups/appdb.sql

Esempio MariaDB:

docker compose exec -T db mariadb-dump -u root -p appdb > backups/appdb.sql

Esempio Redis:

docker compose exec redis redis-cli BGSAVE

Uno stack Compose senza un piano di backup non è un servizio — è un esperimento temporaneo che casualmente ha uptime.

Baseline di sicurezza

Per un piccolo servizio Compose su Linux, inizia con questa baseline:

  • Mantieni il progetto Compose sotto /opt/appname.
  • Usa tag delle immagini espliciti, non solo latest, quando la stabilità è importante.
  • Usa bind mounts o volumi nominati deliberatamente.
  • Non esporre porte che non ti servono.
  • Metti i servizi pubblici dietro un reverse proxy.
  • Usa HTTPS al bordo.
  • Mantieni i segreti fuori da Git.
  • Restringi i permessi di .env.
  • Evita container privilegiati a meno che non siano truly richiesti.
  • Evita di montare il socket Docker nei container.
  • Mantieni Docker e le immagini aggiornate.
  • Testa il comportamento del firewall da un’altra macchina.

Un modello pericoloso:

volumes:
  - /var/run/docker.sock:/var/run/docker.sock

Questo dà al container il controllo su Docker. Nella pratica, questo può diventare il controllo a livello di host. Usalo solo quando comprendi il rischio.

Limiti delle risorse

Sui server piccoli, un container cattivo può consumare l’host.

Compose supporta impostazioni relative alle risorse, ma il comportamento può dipendere dalla versione di Docker Engine e Compose. Per una protezione semplice, inizia con limiti a livello di applicazione e limiti di logging Docker.

Per alcuni carichi di lavoro, puoi aggiungere limiti di memoria:

services:
  app:
    image: example/app:stable
    restart: unless-stopped
    mem_limit: 512m

Configura anche i conteggi dei worker a livello di app, i limiti della coda e le dimensioni della cache. I limiti dei container sono utili, ma non sono un sostituto per la comprensione dell’applicazione.

Esempio: Un servizio Compose realistico

Directory:

/opt/whoami/
  compose.yaml
  .env

File Compose:

services:
  whoami:
    image: traefik/whoami:v1.10
    restart: unless-stopped
    ports:
      - "${WHOAMI_PORT}:80"
    healthcheck:
      test: ["CMD-SHELL", "wget -qO- http://localhost || exit 1"]
      interval: 30s
      timeout: 5s
      retries: 3

File .env:

WHOAMI_PORT=8080
COMPOSE_PROJECT_NAME=whoami

Unità systemd:

[Unit]
Description=Whoami Docker Compose stack
Requires=docker.service
After=docker.service network-online.target
Wants=network-online.target

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/whoami
ExecStartPre=/usr/bin/docker compose config --quiet
ExecStart=/usr/bin/docker compose up -d --remove-orphans
ExecReload=/usr/bin/docker compose up -d --remove-orphans
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=0
TimeoutStopSec=120

[Install]
WantedBy=multi-user.target

Installalo:

sudo systemctl daemon-reload
sudo systemctl enable --now whoami.service

Testa:

curl http://localhost:8080

Controlla lo stato:

systemctl status whoami.service
cd /opt/whoami
docker compose ps

Troubleshooting

Il servizio si avvia ma i container non sono in esecuzione

Controlla systemd:

journalctl -u myapp.service -n 100 --no-pager

Valida Compose:

cd /opt/myapp
docker compose config

Controlla Docker:

systemctl status docker
docker info

WorkingDirectory è sbagliato

Se systemd non trova il tuo file Compose, conferma:

WorkingDirectory=/opt/myapp

Poi controlla:

ls -la /opt/myapp
ls -la /opt/myapp/compose.yaml

Il servizio esegue da WorkingDirectory, non dalla directory corrente del tuo shell.

Docker Permission Denied

Se l’unità si esegue come root, può normalmente accedere a Docker.

Se imposti User=someuser, quell’utente deve poter accedere a Docker. Di solito significa appartenenza al gruppo docker, o una configurazione Docker rootless.

Controlla:

groups someuser

Aggiungi l’utente se appropriato:

sudo usermod -aG docker someuser

Fai attenzione. Il gruppo Docker è effettivamente privilegiato.

Comando Compose non trovato

Trova Docker:

command -v docker

Usa il percorso completo nell’unità:

ExecStart=/usr/bin/docker compose up -d --remove-orphans

Se il plugin Compose manca:

docker compose version

Installalo usando la tua fonte del pacchetto Docker.

Variabili d’ambiente mancanti

Controlla la configurazione Compose come systemd la vedrebbe:

cd /opt/myapp
docker compose config

Se systemd ha bisogno di variabili d’ambiente extra, usa:

EnvironmentFile=-/opt/myapp/.env.systemd

Se Compose ha bisogno di variabili per la sostituzione, usa:

/opt/myapp/.env

Questi sono correlati, ma non identici.

I container non si avviano dopo il riavvio

Controlla se il servizio systemd è abilitato:

systemctl is-enabled myapp.service

Abilitalo:

sudo systemctl enable myapp.service

Controlla Docker:

systemctl is-enabled docker
systemctl status docker

Controlla i log di avvio:

journalctl -u myapp.service -b --no-pager

L’app si avvia prima che il database sia pronto

Aggiungi un controllo di salute al database e depends_on con service_healthy.

Correggi anche l’applicazione. Dovrebbe riprovare le connessioni al database. L’ordine di avvio dell’infrastruttura è utile, ma la logica di retry dell’applicazione è migliore.

Disco pieno di log Docker

Controlla l’uso del disco di Docker:

docker system df

Controlla i log dei container grandi:

sudo du -h /var/lib/docker/containers | sort -h | tail

Configura la rotazione dei log di Docker in /etc/docker/daemon.json.

Poi ricrea i container.

Errori comuni

Errore 1: Eseguire docker compose up in rc.local

Eseguire docker compose up da rc.local o da uno script di login funziona finché non smette — usa un’unità systemd appropriata invece.

Errore 2: Usare Restart=always in systemd e restart: always in Compose

Di solito hai bisogno solo delle politiche di riavvio del container in Compose. Evita che due supervisori si combattano.

Errore 3: Dimenticare –remove-orphans

Le rinominazioni e le rimozioni dei servizi possono lasciare vecchi container dietro. Usa:

docker compose up -d --remove-orphans

Errore 4: Usare docker compose restart dopo modifiche alla configurazione

restart riavvia i container. Non applica tutte le modifiche alla configurazione.

Usa:

docker compose up -d

Errore 5: Eseguire down -v senza pensare

Questo può eliminare volumi. Per servizi con stato, questo può significare eliminare dati.

Errore 6: Nessun backup prima di Pull

Le nuove immagini possono rompere. I database possono migrare. I tag possono spostarsi. Fai prima il backup.

Errore 7: Pubblicare ogni porta

Pubblica solo ciò che l’host ha bisogno di esporre. Il traffico servizio-a-servizio interno può rimanere sulla rete Compose.

Modello finale consigliato

Per la maggior parte dei servizi Linux su singolo host, usa questo modello:

File Compose:

services:
  app:
    image: example/app:stable
    restart: unless-stopped
    ports:
      - "8080:8080"
    env_file:
      - .env

Unità systemd:

[Unit]
Description=MyApp Docker Compose stack
Requires=docker.service
After=docker.service network-online.target
Wants=network-online.target

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/myapp
ExecStartPre=/usr/bin/docker compose config --quiet
ExecStart=/usr/bin/docker compose up -d --remove-orphans
ExecReload=/usr/bin/docker compose up -d --remove-orphans
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=0
TimeoutStopSec=120

[Install]
WantedBy=multi-user.target

Abilitalo:

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

Gestiscilo:

sudo systemctl status myapp.service
sudo systemctl restart myapp.service
journalctl -u myapp.service -f
cd /opt/myapp && docker compose logs -f

Questo modello non è fancy, e questo è il punto. Docker Compose è eccellente per sistemi piccoli e comprensibili, systemd è eccellente nell’avviare e fermare i servizi dell’host, e insieme ti danno un modello di deployment su server singolo affidabile senza fingere che ogni progetto abbia bisogno di un cluster. Per comandi a livello di container fuori da Compose — immagini, volumi, reti e pulizia — vedi il Docker Cheatsheet.

Iscriviti

Ricevi nuovi articoli su sistemi, infrastruttura e ingegneria AI.