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.
I dati alla base di questo grafico
[
{
"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 che si verifica durante la compilazione di un'immagine o l'esecuzione di un dump del database.
I dati alla base di questo grafico
[
{
"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 copre il sistema operativo, il demone Docker e il margine necessario per mantenere il server reattivo sotto carico. La memoria rimanente è container_mb, ed è l'unico valore che puoi utilizzare. La riserva aumenta insieme al 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 più page cache.
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, con 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. In questo spazio entrano contemporaneamente un database, un reverse proxy, tre applicazioni e un container di monitoraggio. È la dimensione minima che vale la pena utilizzare per servizi importanti, perché la memoria libera assorbe gli effetti di un deploy problematico.
Un piano da 8 GB lascia 6656 MB dei suoi 8192 MB e, di norma, il limite si sposta dalla memoria alla CPU o al throughput del disco. SSD Nodes non offre piani più piccoli: il suo piano minimo ha 8 GB di RAM (pagina dei prezzi verificata il 2026-10-11). Su quell'host si applica quindi questa riga, mentre le righe da 2 GB e 4 GB descrivono server più piccoli di altri provider. Alcuni container determinano la propria dimensione in base alla configurazione, non al carico: un server di modelli locale riserva la KV cache in proporzione alla finestra di contesto, quindi aumentare num_ctx di Ollama può aggiungere gigabyte al budget prima che arrivi una singola richiesta. Se i calcoli indicano che lo stack non entra, acquista il piano più grande invece di cercare compromessi nella configurazione: quanto costa davvero una VPS mostra il valore mensile dei gigabyte aggiuntivi.
Due regole mantengono corretti i calcoli. Imposta un limite di memoria per ogni servizio, così un processo fuori controllo non può esaurire tutta la memoria del server. Lascia inoltre inutilizzata la parte superiore del budget, perché docker compose build e pg_dump richiedono entrambi memoria nel momento peggiore. Limiti di memoria in Docker Compose contiene la sintassi e le relative 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 del mio VPS è pieno?
Perché Docker conserva tutto finché non gli si indica di eliminare i dati non più necessari. 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. Un disco più grande rimanda soltanto il problema. Anche l'SSD da 160 GB del piano più economico di SSD Nodes (pagina dei prezzi verificata il 2026-10-11) si riempie allo stesso modo su un server che scarica e crea immagini ogni settimana; con un filesystem root più piccolo, l'interruzione del servizio arriva prima.
Un disco pieno non si manifesta come un crash. Nella stessa ora ricevi no space left on device da un container, da apt, da journald e da docker pull. PostgreSQL smette di accettare scritture. Il server resta attivo, quindi il problema è più difficile da individuare rispetto a un ciclo continuo di riavvii.
Controlla 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 accanto a ciascuna voce. Su un server che crea autonomamente le proprie immagini, la cache di build è di solito 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 eseguire docker system prune --volumes. Questo comando rimuove i container arrestati e, successivamente, ogni volume anonimo non utilizzato da alcun container rimanente. Uno stack arrestato per il fine settimana ha esattamente questa configurazione. Se il database salvava i dati in un volume anonimo, come accade quando un'immagine dichiara un volume e il file compose non monta nulla in quel percorso, i dati vengono eliminati insieme al volume. I volumi denominati sopravvivono a questo comando, ma docker volume prune --all rimuove anche i volumi denominati non utilizzati. Leggi mount bind e volumi denominati prima di usare uno dei due flag e crea prima un backup.
I log dei container crescono più lentamente, ma possono occupare molto spazio. Il driver json-file predefinito non ha limiti di dimensione, quindi un container che genera molti messaggi scrive gigabyte in /var/lib/docker/containers. Imposta un limite per ogni container in /etc/docker/daemon.json:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}Applica la modifica con sudo systemctl restart docker. Il comando riavvia i container, quindi scegli il momento opportuno. Il limite si applica ai container creati dopo la modifica. Ricrea quindi quelli attualmente in esecuzione con docker compose up -d --force-recreate e verifica:
docker inspect my-app | grep -A5 LogConfig
sudo du -sh /var/lib/docker/containersL'output di inspect dovrebbe mostrare max-size valorizzato. Se il campo è 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 richiede poche risorse. Il demone e containerd occupano insieme circa 100 MB; il resto del fabbisogno dipende dai container. Riserva prima la quota dell'host: 768 MB su un sistema da 2048 MB per il sistema operativo, il demone e un margine operativo. Restano 1280 MB per i container. Un database da 512 MB, un reverse proxy da 128 MB e due applicazioni di piccole dimensioni rientrano in questa disponibilità. Misura il tuo stack con docker stats --no-stream invece di fare affidamento su 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. Una volta avviati il sistema operativo e il demone Docker, è occupata all'incirca metà di un sistema da 1 GB. Resta spazio per una piccola applicazione e un reverse proxy, ma non per un database sottoposto a carico reale. La compilazione delle immagini su un sistema di queste dimensioni può fallire o terminare un altro processo. Esegui quindi la compilazione altrove e scarica l'immagine già pronta.
UFW protegge un container Docker?
Non per le porte pubblicate. Docker scrive le proprie regole DNAT e di inoltro. Di conseguenza, un pacchetto destinato alla porta pubblicata di un container viene inoltrato al container invece di essere consegnato all'host. Le regole INPUT gestite da UFW non lo intercettano. ufw deny 5432 può essere attivo mentre quella porta risponde da Internet. Pubblica la porta sull'interfaccia di loopback con 127.0.0.1:5432:5432, non pubblicare i servizi interni oppure applica il filtro nella catena DOCKER-USER.
I container si riavviano dopo il riavvio di un VPS?
Solo se sono stati creati con una politica di riavvio. Imposta restart: unless-stopped su ogni servizio, esegui docker compose up -d per ricreare i container con questa impostazione e verifica che systemctl is-enabled docker visualizzi enabled. Poi riavvia intenzionalmente il sistema e controlla docker compose ps. Una politica di riavvio che non hai mai verificato non è una politica di riavvio affidabile.
Con quale frequenza devo eliminare le immagini Docker non utilizzate?
Per la maggior parte dei server di piccole dimensioni è sufficiente una volta al mese, oppure ogni volta che docker system df segnala spazio recuperabile di cui hai bisogno. docker image prune -a e docker builder prune sono sicuri anche 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: elimina i volumi anonimi degli stack eventualmente arrestati. Anche docker volume prune --all elimina i volumi con nome non utilizzati.