Docker su VPS: cosa cambia davvero
Docker su un VPS usa lo stesso engine, ma RAM, porte UFW, riavvio dei container e spazio disco diventano vincoli da configurare e controllare.
Cosa cambia quando esegui Docker su un VPS
Docker su un VPS utilizza lo stesso engine e le stesse immagini di Docker sul laptop, quindi ogni comando che già conosci continua a funzionare. Cambia lo spazio disponibile intorno a Docker. Un laptop dispone di memoria inutilizzata, di un firewall che nessuno analizza e di un disco abbastanza capiente da non doverlo controllare. Un server a noleggio ha un limite di memoria fisso, un indirizzo IP pubblico che viene analizzato entro pochi minuti dall'avvio e un filesystem root che Docker riempirà senza chiedere conferma.
Su una macchina di piccole dimensioni, la maggior parte dei problemi dipende da quattro differenze:
- La memoria è limitata e il kernel risolve l'insufficienza terminando un processo.
- Una porta pubblicata passa direttamente attraverso UFW (uncomplicated firewall), perché Docker scrive regole firewall proprie.
- I container non vengono riavviati dopo un reboot, a meno che non sia stato configurato in anticipo.
- Immagini, container, volumi e build cache continuano a crescere finché il disco è pieno.
Ogni sezione seguente identifica il problema, riporta la stringa visualizzata effettivamente e rimanda alla guida che lo risolve in dettaglio. Se non hai ancora scritto un file Compose, leggi prima Nozioni di base su Docker Compose su un VPS e poi torna qui. Questa pagina presuppone che tu sappia già avviare uno stack.
Quanta RAM utilizza un container Docker?
Meno di quanto si aspetti la maggior parte degli utenti. Un container è un processo inserito in un cgroup (control group), non una macchina virtuale. Non dispone quindi di un kernel guest né di un'allocazione fissa. Il consumo corrisponde alle risorse effettivamente utilizzate dal processo al suo interno. Per questo uno stack completo può entrare in 2 GB, mentre lo stesso stack basato su macchine virtuali non ci entrerebbe.
I valori riportati di seguito sono consumi tipici a riposo per immagini standard su Ubuntu 24.04 con la configurazione predefinita, rilevati da docker stats alcuni minuti dopo l'avvio. Servono come riferimento iniziale per la pianificazione, non come benchmark del carico di lavoro. Eseguire docker stats --no-stream sul proprio sistema prima di considerare affidabile qualsiasi valore, compresi questi.
The data behind this chart
[
{
"label": "nginx",
"idle_mb": 8,
"budget_mb": 64
},
{
"label": "Redis 7",
"idle_mb": 12,
"budget_mb": 128
},
{
"label": "Traefik v3",
"idle_mb": 40,
"budget_mb": 128
},
{
"label": "PostgreSQL 16",
"idle_mb": 45,
"budget_mb": 512
},
{
"label": "Uptime Kuma",
"idle_mb": 95,
"budget_mb": 256
},
{
"label": "MariaDB 11",
"idle_mb": 190,
"budget_mb": 512
},
{
"label": "Nextcloud (Apache)",
"idle_mb": 210,
"budget_mb": 768
}
]Le due colonne hanno funzioni diverse. idle_mb indica il consumo del container quando non sta svolgendo attività. budget_mb indica quanta memoria riservare in fase di pianificazione, perché l'utilizzo reale non corrisponde allo stato di inattività. PostgreSQL utilizza circa 45 MB a riposo e richiede 512 MB quando connessioni, ordinamenti e cache sono attivi. Pianificare usando la colonna del budget. Usare la colonna del consumo a riposo per il debug.
Osservare la distribuzione delle 7 righe. nginx utilizza 8 MB a riposo, mentre Nextcloud utilizza 210 MB. Il proxy davanti alle applicazioni ha un consumo quasi trascurabile. Sono il database e l'applicazione PHP a determinare la dimensione necessaria del server.
Una precisazione su docker stats: il valore della memoria include la page cache caricata dalle letture dei file eseguite dal container. Per questo aumenta per un certo periodo dopo l'avvio e poi si stabilizza. Monitorarlo per un'ora prima di concludere che si tratti di una perdita di memoria.
Dimensionamento di una VPS: cosa entra in 2 GB, 4 GB e 8 GB
Sottrai prima la quota riservata all'host. Il kernel, systemd, journald, sshd e il demone Docker usano la stessa RAM dei container e dockerd con containerd ne occupa circa 100 MB. Serve inoltre memoria libera per la page cache e per il picco di utilizzo che si verifica durante la build di un'immagine o l'esecuzione di un dump del database.
The data behind this chart
[
{
"plan": "2 GB VPS",
"total_mb": 2048,
"host_reserve_mb": 768,
"container_mb": 1280
},
{
"plan": "4 GB VPS",
"total_mb": 4096,
"host_reserve_mb": 1024,
"container_mb": 3072
},
{
"plan": "8 GB VPS",
"total_mb": 8192,
"host_reserve_mb": 1536,
"container_mb": 6656
}
]host_reserve_mb comprende il sistema operativo, il demone Docker e il margine che mantiene il server reattivo sotto carico. La memoria rimanente è container_mb, ed è l'unica quantità che puoi allocare. La riserva aumenta con il piano, da 768 MB sul server più piccolo a 1536 MB su quello più grande, perché un server più grande esegue più container, scrive più log e richiede una page cache maggiore.
Un piano da 2 GB lascia 1280 MB ai container. Se ne assegni 512 MB a PostgreSQL e 128 MB a Traefik, metà della memoria è già utilizzata. Restano due applicazioni di piccole dimensioni, da circa 256 MB ciascuna. È un server reale e utile. Non è sufficiente per aggiungere Nextcloud e un cluster di ricerca.
Un piano da 4 GB lascia 3072 MB, una quantità sufficiente per eseguire contemporaneamente un database, un reverse proxy, tre applicazioni e un container di monitoraggio. È il taglio minimo adatto a servizi importanti, perché la memoria libera assorbe gli effetti di un deploy errato.
Un piano da 8 GB lascia 6656 MB dei suoi 8192 MB, e il limite di solito passa dalla memoria alla CPU o alla velocità di I/O del disco. Alcuni container dimensionano l'uso della memoria in base alla configurazione, non al carico: un server per modelli locale riserva la cache KV in proporzione alla finestra di contesto, quindi aumentare num_ctx di Ollama può aggiungere gigabyte al budget prima ancora che arrivi una richiesta. Se i calcoli mostrano che lo stack non entra, acquista direttamente il piano più grande invece di aggirare il problema con la configurazione: quanto costa davvero una VPS spiega quanto valgono al mese i gigabyte aggiuntivi.
Due regole mantengono corretto il calcolo. Imposta un limite di memoria per ogni servizio, così un processo fuori controllo non esaurisce la memoria dell'intero server. Inoltre, non allocare tutta la memoria disponibile, perché docker compose build e pg_dump richiedono entrambi memoria nel momento di massimo utilizzo. Limiti di memoria in Docker Compose descrive la sintassi e le principali insidie.
Perché il mio container termina con il codice 137?
Perché il kernel lo ha terminato. 137 è 128 più 9 e il segnale 9 è SIGKILL. Il container ha richiesto più memoria di quella consentita e l’out of memory (OOM) killer lo ha terminato.
CONTAINER ID IMAGE STATUS
9f2c1a4d7b3e postgres:16 Exited (137) 4 minutes agoConferma la causa invece di fare supposizioni:
docker inspect my-db | grep -i oomkilled
sudo journalctl -k | grep -i "out of memory""OOMKilled": true indica che il container ha raggiunto il proprio limite cgroup e il log del kernel identifica il processo selezionato:
Memory cgroup out of memory: Killed process 2417 (postgres) total-vm:1284416kBQuesto è il caso migliore, perché il problema resta confinato a un solo container. Il caso peggiore è un container senza alcun limite. Senza un limite, il tetto coincide con l’intera macchina: una perdita di memoria in un servizio sottrae risorse all’host e il kernel seleziona quindi una vittima in base alle dimensioni dell’intero sistema. La riga del log perde il prefisso Memory cgroup e diventa Out of memory: Killed process 2417 (postgres). Il processo selezionato è spesso il database, mentre il container che ha la perdita continua a funzionare. Per questo è più importante impostare un limite per ogni servizio che scegliere il valore esatto di un singolo limite.
Lo swap modifica la tempistica, non l’aritmetica. La maggior parte delle immagini VPS viene distribuita senza swap. Verifica con swapon --show, che non stampa nulla quando lo swap non è presente. Un file di swap offre al kernel uno spazio in cui spostare le pagine inattive e ti concede alcuni minuti per rilevare il problema.
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
free -hfree -h dovrebbe ora mostrare un totale diverso da zero nella riga Swap. Lo swap non aggiunge RAM. Un sistema sottoposto a pressione costante sulla memoria rallenta al punto che potresti non riuscire più a collegarti via SSH per risolvere il problema. Considera quindi lo swap un buffer di emergenza e correggi il dimensionamento.
Perché UFW non blocca la porta pubblicata da Docker?
Perché il traffico non passa mai dalla catena protetta da UFW. Quando pubblichi una porta con -p 5432:5432 o con una voce ports: di Compose, il daemon scrive una regola DNAT (destination network address translation) nella tabella nat e una regola accept nella propria catena DOCKER. Un pacchetto destinato a un container viene inoltrato al container invece di essere consegnato all'host. Di conseguenza, viene gestito nel percorso FORWARD e non passa mai dalle regole INPUT scritte da UFW.
Puoi osservare questo comportamento sul server:
sudo ufw status | grep 5432
sudo iptables -t nat -L DOCKER -n | grep 5432UFW può mostrare 5432 DENY IN Anywhere mentre la tabella nat contiene una regola DNAT tcp ... to:172.18.0.2:5432 per la stessa porta. Da un'altra macchina, nc -vz your.server.ip 5432 continua a connettersi. Il database è esposto su Internet, mentre il firewall indica che non lo è.
La soluzione consiste nel pubblicare meno porte. I container dello stesso progetto Compose condividono una rete e possono raggiungersi tramite il nome del servizio. Un database che serve soltanto l'applicazione affiancata non ha quindi bisogno di alcuna voce ports:. Quando serve l'accesso locale, associa la pubblicazione all'interfaccia loopback:
services:
db:
image: postgres:16
ports:
- "127.0.0.1:5432:5432"Dopo docker compose up -d, nc -vz your.server.ip 5432 dall'esterno non riesce a connettersi, mentre psql -h 127.0.0.1 -p 5432 sull'host continua a funzionare. In uno stack ridotto configurato correttamente, soltanto il reverse proxy pubblica porte: 80 e 443. Perché le porte pubblicate da Docker bypassano UFW descrive la catena DOCKER-USER nei casi in cui devi pubblicare una porta e applicare comunque un filtro. Nozioni di base sul firewall UFW descrive invece le regole dell'host sottostanti.
Perché i miei container scompaiono dopo un riavvio?
Perché nulla ha detto loro di riavviarsi. Se non ne imposti una, un container viene creato con la policy di riavvio no; dopo un riavvio resta quindi arrestato e il daemon non interviene. I riavvii non sono rari su un VPS: gli aggiornamenti del kernel eseguiti dagli aggiornamenti automatici, la manutenzione del provider e la sequenza OOM descritta sopra possono causarne uno.
Devono essere vere due condizioni. Il daemon deve avviarsi durante il boot:
systemctl is-enabled dockerIn un'installazione Ubuntu standard, questo comando restituisce enabled. Poi ogni servizio deve avere una policy:
services:
app:
image: ghcr.io/example/app:1.4
restart: unless-stoppedunless-stopped riavvia il container dopo un reboot e rispetta un container arrestato intenzionalmente. always riavvia invece anche i container arrestati deliberatamente ogni volta che il daemon viene riavviato, una conseguenza imprevista durante il debugging. Modificare il file non basta, perché la policy di riavvio viene impostata quando il container viene creato. Esegui docker compose up -d per ricrearlo, quindi controlla il valore effettivo:
docker inspect my-app | grep -A3 RestartPolicyPoi riavvia intenzionalmente il server ed esegui docker compose ps nella directory del progetto. Uno stack che sopravvive a un riavvio pianificato sopravvive anche a un riavvio imprevisto. Se lo stack richiede un ordine di avvio garantito o un job eseguito una sola volta durante il boot, è preferibile usare un'unità systemd: avviare Docker Compose durante il boot contiene il file dell'unità. Per verificare che un container riavviato stia realmente fornendo il servizio, aggiungi i controlli di integrità di Compose.
Perché il disco della mia VPS è pieno?
Docker conserva tutto finché non gli si dice di non farlo. Ogni tag di immagine scaricato, ogni container arrestato, ogni volume anonimo lasciato da una ricreazione e ogni livello della cache di build resta sul disco. Su un filesystem root da 40 GB o 80 GB, valori normali per questi piani, questo può causare un'interruzione del servizio dopo pochi mesi anziché dopo anni.
Un disco pieno non si manifesta come un arresto improvviso. Nella stessa ora si ricevono no space left on device da un container, da apt, da journald e da docker pull. PostgreSQL smette di accettare scritture. Il server è ancora operativo, quindi il problema è più difficile da rilevare rispetto a un ciclo continuo di riavvii.
Controllare prima di eliminare:
docker system df
df -h /docker system df suddivide lo spazio totale tra immagini, container, volumi locali e cache di build, mostrando una colonna RECLAIMABLE per ciascuna voce. Su un server che crea le proprie immagini, la cache di build è solitamente la voce più grande.
docker image prune -a
docker builder prune
docker system dfdocker image prune -a rimuove tutte le immagini non utilizzate da alcun container. docker builder prune svuota la cache di build. Entrambi i comandi sono sicuri mentre i servizi sono in esecuzione, perché gli elementi utilizzati vengono ignorati. Non è invece sicuro usare docker system prune --volumes, che elimina tutti i volumi ai quali nessun container fa riferimento in quel momento. Uno stack arrestato per il fine settimana si trova esattamente in questa situazione e il relativo volume del database viene eliminato. Leggere bind mount e volumi denominati prima di usare quel flag e creare prima un backup.
I log dei container crescono più lentamente, ma continuano a occupare spazio. Il driver predefinito json-file non ha limiti dimensionali, quindi un container che genera molti messaggi può scrivere gigabyte in /var/lib/docker/containers. Impostare un limite per ogni container in /etc/docker/daemon.json:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}Applicare la modifica con sudo systemctl restart docker. Il comando riavvia i container, quindi scegliere il momento opportuno. Il limite si applica ai container creati dopo la modifica. Ricreare quelli già in esecuzione con docker compose up -d --force-recreate e verificare:
docker inspect my-app | grep -A5 LogConfig
sudo du -sh /var/lib/docker/containersL'output di inspect dovrebbe mostrare max-size impostato. Se il valore è vuoto, il container è stato creato prima della modifica e continua a scrivere senza limiti.
Abitudini per mantenere in salute un piccolo host Docker
Niente di tutto questo richiede una dashboard o uno strumento da imparare.
- Esegui
docker system dfedf -h /il primo giorno di ogni mese. Bastano due comandi e trenta secondi per vedere l'andamento molto prima che si verifichi un'interruzione del servizio. - Imposta un limite di memoria per ogni servizio, compresi quelli che sei certo siano di dimensioni ridotte. Il limite trasforma un'interruzione che coinvolge l'intero host nel riavvio di un solo container.
- Monitora l'host da un altro sistema, così puoi rilevare la pressione su memoria o disco prima che intervenga il kernel. Uptime Kuma viene eseguito in un container e, quando è inattivo, utilizza circa 95 MB.
- Esegui il backup dei volumi, non dei container. Il container è sacrificabile, il volume no. backup restic su un VPS include una pianificazione e un test di ripristino.
- Blocca i tag delle immagini nel file compose e aggiornali nel giorno che hai scelto. Con
latest, la versione ottenuta dal prossimodocker compose pullè quella rilasciata quella mattina.
Un piccolo VPS che esegue Docker rimane in salute per anni quando quattro valori restano entro i limiti: il budget di memoria, l'elenco delle porte pubblicate, la policy di riavvio di ogni servizio e lo spazio libero su disco. Per il resto, è lo stesso Docker che esegui già a casa.
FAQ
Quanta RAM serve per eseguire Docker su un VPS?
Docker in sé richiede poche risorse. Il daemon e containerd insieme occupano circa 100 MB; il resto del fabbisogno dipende dai container. Riserva prima la quota dell'host: 768 MB su una macchina da 2048 MB per il sistema operativo, il daemon e il margine operativo. Restano 1280 MB per i container. Un database da 512 MB, un reverse proxy da 128 MB e due applicazioni leggere rientrano in questo limite. Misura il tuo stack con docker stats --no-stream invece di affidarti a valori pubblicati.
Posso eseguire Docker su un VPS da 1 GB?
Sì, per uno o due container leggeri. Prima di iniziare, aggiungi un file di swap. In linea generale, circa metà di una macchina da 1 GB viene consumata dal sistema operativo e dal Docker daemon in esecuzione. Rimane spazio per una piccola applicazione e un reverse proxy, ma non per un database sotto carico reale. La creazione delle immagini su una macchina di queste dimensioni può fallire o terminare un altro processo. Esegui quindi la build altrove e scarica l'immagine già pronta.
UFW protegge un container Docker?
Non per le porte pubblicate. Docker scrive regole DNAT e di inoltro proprie. Un pacchetto destinato alla porta pubblicata di un container viene inoltrato al container invece di essere consegnato all'host. Di conseguenza, non passa dalle regole INPUT gestite da UFW. ufw deny 5432 può essere attivo anche quando quella porta risponde da Internet. Pubblica sulla loopback con 127.0.0.1:5432:5432, lascia non pubblicati i servizi interni oppure filtra nella catena DOCKER-USER.
I miei container verranno riavviati dopo il riavvio del VPS?
Solo se sono stati creati con una restart policy. Imposta restart: unless-stopped su ogni servizio ed esegui docker compose up -d, in modo che i container vengano ricreati con questa policy. Verifica quindi che systemctl is-enabled docker stampi enabled. Riavvia intenzionalmente il sistema e controlla docker compose ps. Una restart policy che non hai mai verificato non è una restart policy affidabile.
Con quale frequenza devo eliminare le immagini Docker?
Per la maggior parte dei server piccoli è sufficiente una volta al mese, oppure ogni volta che docker system df segnala spazio recuperabile che ti serve. docker image prune -a e docker builder prune sono entrambi sicuri mentre i servizi sono in esecuzione, perché ignorano le immagini e la cache in uso. Evita docker system prune --volumes se non sai esattamente quali volumi non sono referenziati, perché elimina i dati di qualsiasi stack che in quel momento risulta arrestato.