SSD Nodes Learn 🎉 VPS da $4.99/mese
Guide Matt ConnorDi Matt Connor · Aggiornato 2026-08-05

Docker su un VPS: cosa cambia davvero

Su un VPS Docker usa lo stesso motore, ma con meno margine: la RAM finisce, le porte pubblicate bypassano UFW, i container non ripartono e il disco si riempie.

Cosa cambia quando esegui Docker su un VPS

Docker su un VPS usa lo stesso motore e le stesse immagini di Docker sul laptop, quindi tutti i comandi che già conosci continuano a funzionare. Cambia lo spazio operativo disponibile. Un laptop ha memoria libera, un firewall che in genere nessuno scansiona e un disco abbastanza grande da non doverlo controllare. Un server a noleggio ha un limite di memoria fisso, un indirizzo IP pubblico che viene sottoposto a scansione entro pochi minuti dall'avvio e un filesystem root che Docker riempirà senza chiedere conferma.

Su una macchina con risorse limitate, quattro differenze causano la maggior parte dei problemi:

  • La memoria è limitata e il kernel risolve l'esaurimento terminando un processo.
  • Una porta pubblicata attraversa direttamente UFW (Uncomplicated Firewall), perché Docker scrive proprie regole del firewall.
  • I container non vengono riavviati dopo un reboot, a meno che non sia stato configurato in anticipo.
  • Immagini, container, volumi e cache delle build continuano a crescere finché il disco non è pieno.

Ogni sezione seguente descrive il problema, la stringa che vedrai effettivamente e la 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 pensi. Un container è un processo all'interno di un cgroup (control group), non una macchina virtuale. Non ha quindi un kernel guest né un'allocazione fissa. Il consumo corrisponde a ciò che il processo al suo interno utilizza. Per questo uno stack completo può rientrare in 2 GB, mentre lo stesso stack basato su macchine virtuali non ci riuscirebbe.

I valori riportati di seguito sono valori tipici a riposo per immagini standard su Ubuntu 24.04 con la configurazione predefinita, letti da docker stats pochi minuti dopo l'avvio. Sono un punto di partenza per la pianificazione, non un benchmark del tuo carico di lavoro. Esegui docker stats --no-stream sul tuo sistema prima di considerare attendibile qualsiasi valore, compresi questi.

ChartTypical idle container memory, stock images, megabytes
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 eseguendo attività. budget_mb indica quanta memoria riservare in fase di pianificazione, perché l'utilizzo reale non è quello a riposo. PostgreSQL utilizza circa 45 MB a riposo e richiede 512 MB quando connessioni, ordinamenti e cache sono attivi. Pianifica usando la colonna del budget. Per il debugging, usa la colonna del consumo a riposo.

Osserva la distribuzione di queste 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.

Un'avvertenza su docker stats: il valore della memoria include la page cache caricata dalle letture dei file eseguite dal container. Per questo il valore aumenta per un certo periodo dopo l'avvio e poi si stabilizza. Monitoralo per un'ora prima di concludere che ci sia una perdita di memoria.

Dimensionamento di una VPS: cosa entra in 2 GB, 4 GB e 8 GB

Sottrai innanzitutto la quota riservata all'host. Il kernel, systemd, journald, sshd e il daemon 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 build di un'immagine o l'esecuzione di un dump del database.

ChartRAM budget by plan size, megabytes
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 copre il sistema operativo, il daemon Docker e il margine necessario per mantenere il server reattivo sotto carico. Quello che resta è container_mb, ed è l'unica quantità che puoi utilizzare. 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 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à occupata. Restano due applicazioni piccole, con circa 256 MB ciascuna. È un server reale e utile. Non c'è spazio anche per Nextcloud e un cluster di ricerca.

Un piano da 4 GB lascia 3072 MB. In questa configurazione entrano contemporaneamente un database, un reverse proxy, tre applicazioni e un container di monitoraggio. È la dimensione minima che vale la pena usare per qualsiasi servizio importante, perché la memoria libera assorbe gli effetti di un deploy problematico.

Un piano da 8 GB lascia 6656 MB dei suoi 8192 MB. A questo punto, il limite passa solitamente dalla memoria alla CPU o alla velocità di elaborazione del disco. Se i calcoli indicano che il tuo stack non entra, acquista direttamente il piano più grande invece di cercare di aggirare il problema con l'ottimizzazione: quanto costa realmente una VPS spiega quanto valgono al mese i gigabyte aggiuntivi.

Due regole mantengono corretto il calcolo. Imposta un limite di memoria per ogni servizio, in modo che un processo fuori controllo non esaurisca la memoria dell'intero server. Inoltre, non utilizzare tutta la memoria disponibile, perché docker compose build e pg_dump richiedono entrambi memoria proprio nel momento peggiore. Limiti di memoria in Docker Compose descrive la sintassi e le 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’OOM killer lo ha terminato.

CONTAINER ID   IMAGE         STATUS
9f2c1a4d7b3e   postgres:16   Exited (137) 4 minutes ago

Conferma la causa invece di procedere per ipotesi:

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 scelto:

Memory cgroup out of memory: Killed process 2417 (postgres) total-vm:1284416kB

Questo è il caso migliore, perché il problema resta confinato a un solo container. Il caso peggiore si verifica quando un container non ha alcun limite. Senza un limite, il suo tetto è l’intera macchina. Una perdita di memoria in un servizio esaurisce le risorse dell’host e il kernel sceglie quindi una vittima in base alle dimensioni del processo sull’intero sistema. La riga di log perde il prefisso Memory cgroup e diventa Out of memory: Killed process 2417 (postgres). Spesso il processo scelto è il database, mentre il container che ha causato la perdita continua a funzionare. Per questo è più importante impostare un limite per ogni servizio che definire il valore esatto di un singolo limite.

La swap modifica la tempistica, non il calcolo. La maggior parte delle immagini VPS viene distribuita senza swap. Verifica con swapon --show: quando la swap non esiste, il comando non stampa nulla. Un file di swap offre al kernel uno spazio in cui spostare le pagine inattive e consente di guadagnare 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 -h

free -h dovrebbe ora mostrare un totale diverso da zero nella riga Swap. La swap non aggiunge RAM. Un sistema sottoposto a pressione costante sulla memoria diventa abbastanza lento da impedirti di collegarti via SSH per risolvere il problema. Considera quindi la swap un buffer di emergenza e correggi il dimensionamento.

Perché UFW non blocca una porta Docker pubblicata?

Perché il traffico non passa mai dalla catena controllata 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 5432

UFW 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 altro computer, nc -vz your.server.ip 5432 continua a connettersi. Il database è esposto a Internet, anche se il firewall indica il contrario.

La soluzione è pubblicare meno porte. I container dello stesso progetto Compose condividono una rete e possono raggiungersi usando il nome del servizio. Di conseguenza, un database utilizzato solo dall'applicazione che lo affianca non richiede alcuna voce ports:. Se vuoi consentire 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, mentre psql -h 127.0.0.1 -p 5432 sull'host continua a funzionare. In uno stack ridotto configurato correttamente, solo il reverse proxy pubblica porte, sulla 80 e sulla 443. Perché le porte Docker pubblicate ignorano UFW illustra la catena DOCKER-USER nei casi in cui devi pubblicare una porta e filtrarla comunque; Nozioni di base sul firewall UFW descrive le regole applicate all'host.

Perché i miei container scompaiono dopo un riavvio?

Perché nessuno ha indicato che devono essere riavviati. Un container viene creato con la policy di riavvio no se non ne imposti una. Di conseguenza, dopo un riavvio rimane arrestato e il daemon non interviene. I riavvii non sono rari su un VPS: gli aggiornamenti del kernel eseguiti da unattended upgrades, la manutenzione del provider e la sequenza OOM descritta sopra possono causare un riavvio.

Devono essere vere due condizioni. Il daemon deve avviarsi al boot:

systemctl is-enabled docker

Su un'installazione Ubuntu standard, il comando restituisce enabled. Poi ogni servizio deve avere una policy:

services:
  app:
    image: ghcr.io/example/app:1.4
    restart: unless-stopped

unless-stopped riavvia il container dopo un riavvio e rispetta un container arrestato intenzionalmente. always riavvia invece anche i container arrestati deliberatamente ogni volta che il daemon viene riavviato. Durante il debugging, questo comportamento può essere imprevisto. 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 RestartPolicy

Poi riavvia intenzionalmente il server ed esegui docker compose ps nella directory del progetto. Se lo stack supera un riavvio pianificato, supererà anche un riavvio imprevisto. Se lo stack richiede un ordine di avvio garantito o un job eseguito una sola volta al boot, è preferibile usare un'unità systemd: avvio di Docker Compose al boot contiene il file dell'unità. Per verificare che un container riavviato stia realmente servendo le richieste, aggiungi healthcheck di Compose.

Perché il disco della mia VPS è pieno?

Docker conserva tutto finché non gli si dice di eliminare i dati. 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, dimensioni normali per questi piani, lo spazio si esaurisce nel giro di mesi invece che di 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 resta operativo, quindi il problema è più difficile da notare 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 per ciascuna categoria. 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 df

docker 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 docker system prune --volumes, che elimina tutti i volumi a cui 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. Leggi bind mount e volumi denominati prima di usare quel 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 un limite dimensionale, quindi un container che genera molti messaggi può scrivere 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 in esecuzione con docker compose up -d --force-recreate e verifica:

docker inspect my-app | grep -A5 LogConfig
sudo du -sh /var/lib/docker/containers

L'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 df e df -h / il primo giorno del mese. Bastano due comandi e trenta secondi per vedere l’andamento molto prima che si trasformi in un’interruzione del servizio.
  • Imposta un limite di memoria per ogni servizio, anche per quelli che sei certo consumino poca memoria. Il limite trasforma un’interruzione che coinvolge l’intero host nel riavvio di un solo container.
  • Monitora l’host da un altro sistema, così ricevi una segnalazione quando la memoria o il disco sono sotto pressione, 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 è temporaneo, mentre il volume non lo è. 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 successivo docker compose pull è quella rilasciata quella mattina.

Un piccolo VPS con Docker rimane operativo per anni se quattro valori restano sotto controllo: 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 occupano insieme circa 100 MB; il resto dipende dai container. Riserva prima la quota dell'host: 768 MB su un sistema 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. Su un sistema da 1 GB, circa metà della memoria è già utilizzata dal sistema operativo e dal daemon Docker. Rimane spazio per una piccola applicazione e un reverse proxy, ma non per un database sotto carico reale. La creazione di immagini su un sistema di queste dimensioni può fallire o terminare un altro processo. Crea quindi le immagini altrove e scarica l'immagine completata.

UFW protegge un container Docker?

Non per le porte pubblicate. Docker scrive le proprie regole DNAT e di forwarding. Un pacchetto diretto a una porta pubblicata del container viene inoltrato al container invece di essere consegnato all'host. Le regole INPUT gestite da UFW non vedono quindi il pacchetto. ufw deny 5432 può essere attivo mentre quella porta risponde da Internet. Pubblica la porta sull'interfaccia loopback con 127.0.0.1:5432:5432, non pubblicare i servizi interni oppure applica i filtri nella catena DOCKER-USER.

I miei container si riavviano dopo il riavvio del VPS?

Solo se sono stati creati con una policy di riavvio. Imposta restart: unless-stopped su ogni servizio, esegui docker compose up -d per ricreare i container con questa policy e verifica che systemctl is-enabled docker restituisca enabled. Quindi riavvia intenzionalmente il sistema e controlla docker compose ps. Una policy di riavvio che non hai mai testato non è una policy di riavvio affidabile.

Con quale frequenza devo eliminare le immagini Docker non utilizzate?

Una volta al mese è sufficiente per la maggior parte dei server piccoli. Puoi farlo anche quando docker system df segnala spazio recuperabile di cui avresti bisogno. docker image prune -a e docker builder prune sono sicuri mentre i servizi sono in esecuzione, perché ignorano le immagini e la cache in uso. Evita docker system prune --volumes se non sai con precisione quali volumi non sono referenziati. Il comando elimina infatti i dati di qualsiasi stack che, in quel momento, è arrestato.