PostgreSQL su un VPS: dimensionare memoria e disco

Un database PostgreSQL veloce è un database i cui dati consultati spesso stanno in memoria. Tutto il dimensionamento parte da qui: il volume del database, la parte che lavora davvero, il numero di connessioni, poi il disco e il suo margine. Questa guida dà il metodo, le impostazioni di partenza per 4, 8 e 16 GB di memoria, e le query che misurano un database esistente.

Pubblicato il 29 ottobre 2026 Di Lettura: 10 minuti

Tutte le guide

In breve

  1. Misurate il volume del database e dei suoi indici, poi la parte consultata spesso: è questa che deve stare in memoria.
  2. Punto di partenza: shared_buffers al 25% della memoria, effective_cache_size al 75%, random_page_cost = 1.1 su SSD NVMe.
  3. Ogni connessione è un processo: oltre qualche decina di connessioni attive, mettete PgBouncer davanti al database.
  4. Disco: dati e indici a un anno, più almeno il 30% di margine. Un disco pieno ferma PostgreSQL.
  5. Il tasso di lettura dalla cache deve restare sopra il 99%: al di sotto, il database è a corto di memoria.
  6. Fate un backup ogni giorno con pg_dump, fuori dal server, e ripristinatelo regolarmente per verificarlo.

Partire dal volume utile

La memoria di un server di database serve prima di tutto da cache: una pagina letta in memoria si serve molto più in fretta di una pagina letta su disco, anche NVMe. La domanda non è quindi solo «quanto pesa il database?», ma «quale parte viene letta spesso?». Un database da 50 GB di cui si consultano solo gli ultimi dodici mesi lavora forse su 8 GB.

Su un database esistente, due query danno il volume totale e le tabelle più grandi, indici compresi:

SQL (psql)
SELECT pg_size_pretty(pg_database_size(current_database()));

SELECT relname AS nome,
       pg_size_pretty(pg_total_relation_size(relid)) AS dimensione_con_indici
FROM pg_catalog.pg_statio_user_tables
ORDER BY pg_total_relation_size(relid) DESC
LIMIT 10;

Per un database ancora da creare, stimate tabella per tabella: numero di righe attese a un anno × dimensione media di una riga, poi aggiungete gli indici, che pesano comunemente dal 30 al 100% dei dati. Esempio: 2 milioni di righe al mese, da 500 byte ciascuna, fanno 12 GB di dati a un anno, e da 18 a 24 GB con gli indici.

La memoria: le impostazioni di partenza

PostgreSQL tiene le pagine lette su due livelli: la propria cache (shared_buffers) e la cache dei file del sistema. La sua documentazione raccomanda di partire dal 25% della memoria per shared_buffers su un server dedicato, e osserva che oltre il 40% il guadagno diventa raro: il resto va a vantaggio della cache del sistema e degli ordinamenti.

Parametro4 GB8 GB16 GBRuolo
shared_buffers1 GB2 GB4 GBLa cache propria di PostgreSQL
effective_cache_size3 GB6 GB12 GBLa cache totale stimata, che orienta la scelta dei piani di esecuzione
work_mem8 MB16 MB32 MBLa memoria di ogni ordinamento o hash, per connessione: da moltiplicare per le query simultanee
maintenance_work_mem256 MB512 MB1 GBLa creazione degli indici e il VACUUM
random_page_cost1.11.11.1Il costo stimato di una lettura casuale: 4 per impostazione predefinita, ben al di sopra del costo reale su SSD NVMe

Questi valori presuppongono un database da solo sul server. Se l'applicazione gira accanto, togliete prima la sua memoria (processi, cache, compilazione) e applicate le percentuali al resto: un server da 8 GB che fa girare un'applicazione da 3 GB si regola come un database da 5 GB.

postgresql.conf, per un server da 8 GB dedicato al database
shared_buffers = 2GB
effective_cache_size = 6GB
work_mem = 16MB
maintenance_work_mem = 512MB
random_page_cost = 1.1

shared_buffers ha effetto solo al riavvio di PostgreSQL; gli altri parametri si ricaricano senza interruzione, con SELECT pg_reload_conf();.

Per sapere se la memoria basta, leggete il tasso di lettura dalla cache. Su un'applicazione gestionale o un sito, supera il 99%:

SQL (psql)
SELECT round(100.0 * blks_hit / nullif(blks_hit + blks_read, 0), 2) AS tasso_cache
FROM pg_stat_database
WHERE datname = current_database();

Le connessioni: un processo ciascuna

PostgreSQL apre un processo per connessione, e ne accetta 100 per impostazione predefinita (max_connections). Ogni processo occupa qualche megabyte a riposo, di più quando ordina o fa l'hash dei dati (work_mem, a volte più volte nella stessa query). Alzare max_connections a 500 per stare dietro a un'applicazione che apre molte connessioni significa esporre il server alla mancanza di memoria al primo picco.

La risposta giusta è un pooler di connessioni: PgBouncer, in modalità transaction, fa condividere qualche decina di connessioni reali a centinaia di connessioni dell'applicazione. Anche i framework hanno il proprio pool (SQLAlchemy, Prisma, HikariCP): regolatene la dimensione sul numero di vCPU del server del database piuttosto che sul numero di utenti.

Il disco: volume, margine e log delle transazioni

Il disco di un server PostgreSQL contiene più delle sole tabelle:

La regola: il volume a un anno, più almeno il 30% di margine. È una necessità: quando il disco che ospita il log delle transazioni si riempie, PostgreSQL si ferma per proteggere i suoi dati. Tenete d'occhio l'occupazione, e ampliate prima dell'80%.

Sul fronte delle prestazioni, ogni commit di una transazione attende la scrittura del log su disco: la latenza dello storage decide la velocità di scrittura. È qui che un SSD NVMe fa la differenza, molto più che sulle letture, già servite dalla memoria.

Il processore: una query, un core

Una query PostgreSQL viene eseguita su un solo core, tranne le letture di grandi dimensioni che il motore parallelizza. Il numero di vCPU decide quindi il numero di query pesanti elaborate nello stesso momento: 2 vCPU bastano a un'applicazione gestionale al lancio, da 4 a 8 vanno bene per i database molto sollecitati e i report pesanti.

Prima di aggiungere vCPU, cercate le query che li consumano. L'estensione pg_stat_statements le classifica per tempo totale:

postgresql.conf, poi riavvio
shared_preload_libraries = 'pg_stat_statements'
SQL (psql)
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

SELECT calls, round(total_exec_time) AS ms_totale,
       round(mean_exec_time::numeric, 1) AS ms_medio, query
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;

Stesso server dell'applicazione, o server separato?

Al lancio, il database sullo stesso server dell'applicazione è la scelta giusta: nessuna latenza di rete tra i due, una connessione tramite socket Unix, un solo server da salvare e da monitorare. Si separano quando i due si contendono le risorse: una compilazione o un picco di traffico che svuota la cache del database, un database che ha bisogno di tutta la memoria per i suoi dati, oppure requisiti di backup diversi.

Sicurezza e backup

Terminale: esportazione compressa, poi ripristino in un database di test
pg_dump -Fc -d mio_db -f mio_db-$(date +%F).dump
createdb mio_db_test
pg_restore -d mio_db_test mio_db-2026-10-29.dump

Un database PostgreSQL veloce, sul server giusto

Il VPS Pro gira su processori Intel Xeon, memoria ECC che corregge gli errori al volo e SSD NVMe in mirroring, con ogni blocco di dati verificato tramite checksum e un backup cifrato ogni notte, fuori dalla macchina. L'Assistenza integrale è compresa: quando il vostro database rallenta, analizziamo le query lente e gli indici, poi vi indichiamo le impostazioni che restituiscono velocità alla vostra applicazione.

Scoprire il VPS Pro

Domande frequenti

Quanta RAM serve per PostgreSQL?

Abbastanza perché i dati consultati spesso stiano in memoria. Per un'applicazione gestionale al lancio bastano 4 GB, database e applicazione compresi. Poi misurate il tasso di lettura dalla cache: finché resta sopra il 99%, la memoria basta.

Quale valore per shared_buffers?

Il 25% della memoria su un server dedicato al database, come raccomanda la documentazione di PostgreSQL: 1 GB su 4 GB, 2 GB su 8 GB, 4 GB su 16 GB. Oltre il 40% il guadagno diventa raro, perché la cache del sistema fa il resto.

Quante connessioni PostgreSQL può accettare un VPS?

PostgreSQL ne accetta 100 per impostazione predefinita, e ogni connessione è un processo che consuma memoria. Invece di alzare questo tetto, mettete un pooler come PgBouncer davanti al database: qualche decina di connessioni reali serve allora centinaia di connessioni dell'applicazione.

Bisogna separare il database e l'applicazione?

Al lancio conviene tenerli insieme: sullo stesso server, il database risponde senza latenza di rete e l'insieme si salva in un colpo solo. Si separano quando l'uno ostacola l'altra, per esempio quando una compilazione o un picco di traffico svuota la cache del database.

Contattaci