Installare e configurare PostgreSQL su Debian e Ubuntu
Installazione dal pacchetto della distribuzione o dal repository PGDG, primo accesso tramite l'utente postgres, creazione di database e ruolo, pg_hba.conf spiegata e un backup che regge davvero.
PostgreSQL si installa su Debian e Ubuntu in due minuti. Le due ore restanti se ne vanno di solito nel capire perché nessuno riesce ad accedere. Questo articolo porta l'installazione fino in fondo: scelta del pacchetto, primo accesso, database e utente, le regole di accesso in pg_hba.conf e un backup di cui sai con certezza che si lascia ripristinare.
Tutti i comandi vengono eseguiti come root. Chi lavora con un utente normale antepone sudo. Il numero di versione 17 nei percorsi lo sostituisci con la versione davvero installata sul tuo sistema.
Quale versione di PostgreSQL porta ogni distribuzione
La differenza più importante tra i quattro sistemi più diffusi è la versione principale che arriva dal repository della distribuzione. È legata in modo fisso al rilascio e non cambia più per tutta la vita della distribuzione.
| Distribuzione | PostgreSQL dal repository della distribuzione |
| Debian 13 (trixie) | 17 |
| Debian 12 (bookworm) | 15 |
| Ubuntu 24.04 LTS (noble) | 16 |
| Ubuntu 22.04 LTS (jammy) | 14 |
Questa dispersione ha conseguenze pratiche. Un dump prodotto su Debian 13 non si importa senza problemi in Ubuntu 22.04. E chi lavora su Ubuntu 22.04 dovrebbe sapere che PostgreSQL 14, secondo le regole di versionamento del PostgreSQL Global Development Group, esce dal supporto della community il 12 novembre 2026. Ubuntu continua a fornire aggiornamenti di sicurezza per la 22.04 nell'ambito del ciclo LTS, ma non arrivano più fix da upstream. Per i nuovi progetti su 22.04 questo è un ottimo motivo per passare subito al repository PGDG.
Che cosa è disponibile sul tuo sistema te lo dice uno sguardo al database dei pacchetti, prima ancora di installare qualsiasi cosa:
apt update
apt-cache policy postgresql
La riga Candidato, in inglese Candidate, mostra un numero di versione come 17+283. La cifra prima del segno più è la versione principale di PostgreSQL, il resto è il numero di versione del metapacchetto Debian.
Installazione dal pacchetto della distribuzione
Per la maggior parte dei casi d'uso il pacchetto della distribuzione è la scelta giusta. È integrato negli aggiornamenti di sicurezza della distribuzione, funziona con le librerie del sistema e non crea problemi durante un aggiornamento di release.
apt install -y postgresql postgresql-contrib
postgresql-contrib porta con sé le estensioni incluse, tra cui pgcrypto, uuid-ossp e pg_stat_statements. Senza quel pacchetto molte applicazioni si fermano più avanti su un ERROR: could not open extension control file, e la ricerca dell'errore dura più dell'installazione.
Durante l'installazione Debian e Ubuntu creano automaticamente un primo cluster chiamato main e lo avviano. Cluster qui significa un'istanza in esecuzione con una propria directory dati, una propria porta e una propria configurazione. Se l'operazione è riuscita non lo dice il codice di uscita di apt, lo dice questo:
pg_lsclusters
Nella colonna Status l'output deve mostrare un online:
Ver Cluster Port Status Owner Data directory Log file
17 main 5432 online postgres /var/lib/postgresql/17/main /var/log/postgresql/postgresql-17-main.log
Se lì compare down, avvii il servizio a posteriori. service postgresql start funziona su tutti e quattro i sistemi, anche nei container senza systemd. Su un server normale va bene allo stesso modo systemctl start postgresql.
service postgresql start
pg_isready
pg_isready risponde con /var/run/postgresql:5432 - accepting connections e restituisce il codice di uscita 0. È la prima prova concreta che il server è raggiungibile, ed è riutilizzabile negli script di monitoraggio.
Quando conviene il repository PGDG e come si integra in modo pulito
Il repository ufficiale su apt.postgresql.org fornisce in parallelo tutte le versioni principali supportate per trixie, bookworm, noble e jammy. È la scelta giusta se ti serve una versione principale precisa perché l'applicazione la richiede, se hai bisogno di estensioni che Debian non pacchettizza, oppure se il livello della tua distribuzione punta a una versione che sta per uscire dal supporto.
La chiave va in un file dedicato, non più nel portachiavi apt-key ormai deprecato. Debian 13 e Ubuntu 24.04 preferiscono inoltre il formato deb822 con estensione .sources, che però funziona anche su Debian 12 e Ubuntu 22.04:
apt install -y curl ca-certificates
install -d /usr/share/postgresql-common/pgdg
curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc
cat > /etc/apt/sources.list.d/pgdg.sources <<EOF
Types: deb
URIs: https://apt.postgresql.org/pub/repos/apt
Suites: $(. /etc/os-release && echo $VERSION_CODENAME)-pgdg
Components: main
Signed-By: /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc
EOF
La riga con $(. /etc/os-release ...) inserisce automaticamente trixie-pgdg, bookworm-pgdg, noble-pgdg oppure jammy-pgdg. Poi:
apt update
apt-cache policy postgresql-18
Per lo stesso scopo il pacchetto postgresql-common porta con sé uno script già pronto, /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh. È un'alternativa alla chiave e al file pgdg.sources visti sopra, non un complemento. Se percorri entrambe le strade, lo script crea in più una /etc/apt/sources.list.d/pgdg.list, e da quel momento apt avvisa a ogni esecuzione: W: Target Packages (main/binary-amd64/Packages) is configured multiple times in /etc/apt/sources.list.d/pgdg.list:1 and /etc/apt/sources.list.d/pgdg.sources:1. Chi preferisce lo script ai passaggi manuali di sopra installa prima anche gnupg, quindi apt install -y curl ca-certificates gnupg. La versione più vecchia dello script su Ubuntu 22.04 importa infatti la chiave ancora tramite apt-key e senza gnupg si interrompe con E: gnupg, gnupg2 and gnupg1 do not seem to be installed, but one of them is required for this operation e codice di uscita 255. Su Debian 13, Debian 12 e Ubuntu 24.04 la versione più recente salva la chiave direttamente come .asc e va a buon fine senza pacchetti aggiuntivi.
La trappola: due cluster, due porte
Se adesso installi una nuova versione principale su un sistema dove il pacchetto della distribuzione è già presente, nasce un secondo cluster. Quello non riceve la porta 5432, ma la prima libera successiva, quindi la 5433. Le applicazioni continuano a collegarsi alla vecchia versione, senza che nessun messaggio di errore lo segnali. È la causa più frequente della frase "ma io PostgreSQL 18 l'ho installato, e SELECT version() mi mostra 15".
apt install -y postgresql-18
pg_lsclusters
Adesso lì compaiono due righe con porte diverse. Se vuoi trasferire i dati nella nuova versione, lo strumento giusto è pg_upgradecluster, non un dump fatto a mano. Richiede che entrambi i pacchetti server siano installati e lascia il vecchio cluster fermo al suo posto, invece di cancellarlo:
pg_upgradecluster 15 main
Verifica poi con pg_lsclusters quale cluster si trova sulla porta 5432 e prova l'applicazione, prima di rimuovere definitivamente il vecchio cluster con pg_dropcluster --stop 15 main. Questo comando cancella la directory dati senza chiedere conferma.
Il primo accesso, e perché root non può usare psql
Il classico inciampo subito dopo l'installazione:
psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: FATAL: role "root" does not exist
Non è un errore, è il comportamento previsto. Debian e Ubuntu configurano le connessioni locali via socket con il metodo peer. PostgreSQL chiede al kernel con quale utente di sistema è stata aperta la connessione e pretende che esista un ruolo di database con lo stesso nome. Esiste soltanto il ruolo postgres, quindi devi prima passare a quell'utente di sistema.
su - postgres
Poi avvii psql senza argomenti. Per uscire dalla shell serve exit. Per singoli comandi lanciati da uno script è più pratico cambiare utente a ogni comando:
runuser -u postgres -- psql -c "SELECT version();"
Chi ha installato sudo scrive invece sudo -u postgres psql -c "SELECT version();". Le due varianti sono equivalenti. Con sudo compare spesso l'avviso could not change directory to "/root": Permission denied. È senza conseguenze, perché l'utente postgres non può entrare nella directory di lavoro di root, ma il comando viene comunque eseguito.
Queste tre query ti dicono con che cosa hai a che fare, e sono il primo passo di ogni ricerca di errori:
runuser -u postgres -- psql -c "SHOW server_version;"
runuser -u postgres -- psql -c "SHOW config_file;"
runuser -u postgres -- psql -c "SHOW hba_file;"
L'ultimo comando è particolarmente utile. Indica il percorso che il server in esecuzione legge davvero, di norma /etc/postgresql/17/main/pg_hba.conf. Se modifichi un file e non cambia nulla, quasi sempre hai davanti la configurazione di un altro cluster.
Dentro psql tornano utili i meta comandi: \l elenca i database, \du i ruoli, \dt le tabelle del database corrente, \conninfo mostra con quale utente e verso quale database sei connesso, e \q chiude la sessione.
Creare database e utente
Per ogni applicazione vanno creati un ruolo dedicato e un database dedicato. L'ordine conta, perché il database deve appartenere direttamente al ruolo.
runuser -u postgres -- psql -c "CREATE ROLE appuser LOGIN PASSWORD 'LaTuaPasswordSicura';"
runuser -u postgres -- psql -c "CREATE DATABASE appdb OWNER appuser;"
Da PostgreSQL 14 le password vengono salvate per impostazione predefinita con scram-sha-256, quindi vale su tutti e quattro i sistemi trattati qui. La password non finisce in chiaro nel database, ma resta nella history della tua shell. Chi vuole evitarlo usa in psql il comando \password appuser, che la chiede in modo interattivo.
La trappola da PostgreSQL 15: permission denied for schema public
Un comportamento che molte guide più vecchie ancora non conoscono: da PostgreSQL 15 non ogni utente può più creare oggetti nello schema public. Sono interessati Debian 12, Debian 13 e Ubuntu 24.04. Solo Ubuntu 22.04 con PostgreSQL 14 si comporta ancora secondo il vecchio modello. L'errore si presenta così:
ERROR: permission denied for schema public
LINE 1: CREATE TABLE clienti (id serial primary key);
La strada pulita è quella mostrata sopra: il database appartiene al ruolo. Dalla versione 15 lo schema public appartiene al ruolo pg_database_owner, e il rispettivo proprietario del database ne è membro implicito. Chi invece ha creato il database senza OWNER recupera il permesso a posteriori. Attenzione: questa istruzione va eseguita nel database interessato, non in postgres:
runuser -u postgres -- psql -d appdb -c "GRANT ALL ON SCHEMA public TO appuser;"
La prova che i permessi sono a posto
Un CREATE ROLE senza errori non significa ancora che l'applicazione riesca ad accedere. La prova è un accesso reale via TCP seguito da una scrittura. PGPASSWORD qui serve solo per il test, per l'esercizio quotidiano vedi più avanti:
PGPASSWORD='LaTuaPasswordSicura' psql -h 127.0.0.1 -U appuser -d appdb -c "SELECT current_user, current_database();"
PGPASSWORD='LaTuaPasswordSicura' psql -h 127.0.0.1 -U appuser -d appdb -c "CREATE TABLE prova (id int);"
Se entrambi i comandi vanno a buon fine, la combinazione di ruolo, password, database e permessi sullo schema è completa. La tabella prova resta volutamente al suo posto, perché il test del backup più avanti ha bisogno di almeno un oggetto nel database, altrimenti verifica il vuoto. Per controllo elenchi gli oggetti:
runuser -u postgres -- psql -c "\du"
runuser -u postgres -- psql -c "\l"
Una parola sulla codifica dei caratteri: se al momento della creazione del cluster il locale di sistema non era impostato su UTF-8, il database modello può essere SQL_ASCII. Un CREATE DATABASE ... ENCODING 'UTF8' fallisce allora con ERROR: new encoding (UTF8) is incompatible with the encoding of the template database (SQL_ASCII). La via d'uscita è TEMPLATE template0 in fase di creazione, la soluzione pulita è un sistema con locale UTF-8.
Capire pg_hba.conf, la fonte di errori più frequente
Il file pg_hba.conf (host-based authentication) decide prima di ogni verifica della password se una connessione viene ammessa. Viene letto dall'alto verso il basso, e vince la prima riga che corrisponde. Se non corrisponde nessuna, la connessione viene rifiutata. Una regola generosa più in basso non ti serve a niente se una più severa più in alto interviene per prima. È di gran lunga l'errore di configurazione più comune.
Lo stato di fornitura su Debian e Ubuntu è questo:
# TYPE DATABASE USER ADDRESS METHOD
local all postgres peer
local all all peer
host all all 127.0.0.1/32 scram-sha-256
host all all ::1/128 scram-sha-256
Così si presenta sui quattro sistemi trattati qui. Su versioni più vecchie, per esempio Debian 11 con PostgreSQL 13, le due righe host riportano ancora md5 invece di scram-sha-256. L'accesso via TCP funziona in entrambi i casi, ma su md5 non conviene più fare affidamento.
Le colonne significano questo: local indica il socket Unix, host il TCP con o senza TLS, hostssl solo le connessioni TLS. Seguono database, ruolo, intervallo di rete in notazione CIDR e il metodo. Importanti sono quattro metodi. peer verifica l'utente di sistema e funziona solo attraverso il socket. scram-sha-256 è il metodo moderno per le password ed è la scelta giusta per tutto ciò che passa da TCP. md5 è deprecato e non dovrebbe più comparire in configurazioni nuove. trust fa entrare chiunque senza verifica e su un server raggiungibile non ha alcun motivo di esistere.
I messaggi di errore alla lettera
Chi riesce a distinguerli si risparmia parecchie ipotesi:
FATAL: Peer authentication failed for user "appuser"
Sei collegato attraverso il socket, ma il tuo utente di sistema ha un nome diverso dal ruolo. O cambi utente, oppure ti colleghi con -h 127.0.0.1, così interviene la riga host.
FATAL: no pg_hba.conf entry for host "198.51.100.4", user "appuser", database "appdb", no encryption
Il server è raggiungibile, ma nessuna regola corrisponde a questa combinazione di indirizzo di origine, ruolo e database. Manca una riga, oppure la rete indicata nella riga esistente non copre l'indirizzo.
FATAL: password authentication failed for user "appuser"
La regola interviene, ma la password non è corretta. Succede spesso perché il ruolo è stato creato senza LOGIN oppure perché la password proviene ancora da un'installazione precedente.
psql: error: connection to server at "203.0.113.10", port 5432 failed: Connection refused
Qui pg_hba.conf non è mai entrata in gioco. O il server non è in esecuzione, oppure non è in ascolto su quell'indirizzo, oppure un firewall blocca. Tra poco ne parliamo meglio.
Verificare le modifiche prima di ricaricare
PostgreSQL offre una vista di sistema che mostra le regole già interpretate, con numero di riga ed errori di sintassi. Risponde alla domanda su quale regola il server veda davvero, invece di quella che credi di aver scritto:
runuser -u postgres -- psql -c "SELECT line_number, type, database, user_name, address, auth_method FROM pg_hba_file_rules;"
Le modifiche a pg_hba.conf non richiedono un riavvio, basta una ricarica, che non interrompe le connessioni esistenti:
runuser -u postgres -- psql -c "SELECT pg_reload_conf();"
In alternativa service postgresql reload. Un riavvio vero serve solo se hai cambiato parametri come listen_addresses, port o shared_buffers.
Abilitare l'accesso dall'esterno
Di fabbrica PostgreSQL è in ascolto solo su localhost. È una buona impostazione predefinita, e dovresti abbandonarla solo se è davvero necessario. Devono coincidere due cose: il server deve essere in ascolto su quell'indirizzo, e pg_hba.conf deve permettere la sorgente. Se manca la prima ottieni Connection refused, se manca la seconda ottieni no pg_hba.conf entry.
La configurazione si trova in /etc/postgresql/17/main/postgresql.conf. Debian fornisce per questo lo strumento pg_conftool, che modifica il file in modo più affidabile di una ricerca nell'editor:
pg_conftool 17 main show listen_addresses
pg_conftool 17 main set listen_addresses '10.0.0.5,127.0.0.1'
Indica indirizzi concreti invece di *. Su un server con indirizzo pubblico e indirizzo interno leghi così il servizio soltanto alla rete interna. Poi aggiungi una regola in pg_hba.conf, formulata il più stretta possibile:
host appdb appuser 10.0.0.0/24 scram-sha-256
Dopo un riavvio con service postgresql restart verifichi prima di tutto su che cosa il processo sia davvero in ascolto. ss -lntp | grep 5432 mostra gli indirizzi associati. Se lì compare solo 127.0.0.1:5432, la modifica non ha avuto effetto, di solito perché era coinvolto un secondo cluster oppure perché un file sotto conf.d sovrascrive il valore.
Anche il firewall ha bisogno di una regola, e con indicazione della sorgente. Una porta 5432 aperta a tutti su internet viene scansionata nel giro di poche ore:
ufw allow from 10.0.0.0/24 to any port 5432 proto tcp
Un consiglio onesto: nella maggior parte dei casi la soluzione migliore è non aprire affatto la porta. Un tunnel SSH con ssh -L 5432:127.0.0.1:5432 utente@server basta ampiamente per gli accessi di manutenzione. Per connessioni permanenti tra più server la scelta più pulita è una rete WireGuard, perché così il database resta in ascolto solo su un indirizzo privato. Debian e Ubuntu attivano peraltro TLS di default con un certificato autofirmato, quindi sslmode=require funziona subito. Una protezione reale contro un attaccante sulla linea la offre però solo sslmode=verify-full con un certificato di cui il client si fida.
Backup con pg_dump, e la prova che valga qualcosa
Per singoli database il formato custom è la scelta migliore. È compresso, si lascia ripristinare in modo selettivo e può essere importato in parallelo:
runuser -u postgres -- pg_dump -Fc -d appdb -f /var/lib/postgresql/appdb.dump
Un punto spesso trascurato: pg_dump non salva né i ruoli né le password. Quelli risiedono a livello di cluster e vanno salvati a parte, altrimenti dopo un ripristino mancano proprio gli utenti che servono all'applicazione:
runuser -u postgres -- pg_dumpall --globals-only -f /var/lib/postgresql/globals.sql
Quando le versioni non combaciano
pg_dump: error: server version: 17.5; pg_dump version: 15.10
pg_dump: error: aborting because of server version mismatch
La regola è questa: pg_dump può essere più recente del server, mai più vecchio. Su Debian e Ubuntu si risolve facilmente, perché /usr/bin/pg_dump è solo un wrapper che sceglie la versione giusta del programma. Installa il pacchetto postgresql-client-18 e la versione più recente sarà disponibile. E con l'estensione Debian --cluster costringi il wrapper su un cluster preciso, qui sulla versione 17, cluster main:
pg_dump --version
runuser -u postgres -- pg_dump --cluster 17/main -Fc -d appdb -f /var/lib/postgresql/appdb.dump
Tenere le password fuori dagli script
Per i backup automatici la password va in un file .pgpass nel formato host:porta:database:utente:password. PostgreSQL ignora il file senza dire niente se i permessi sono troppo ampi. Altrettanto importante è in quale home directory si trova, perché viene letto sempre il file dell'utente sotto il quale il comando gira davvero. Un ~/.pgpass creato come root resta senza effetto finché il backup passa, come in questo articolo, da runuser -u postgres. In quel caso conta la home di postgres:
touch /var/lib/postgresql/.pgpass
chown postgres:postgres /var/lib/postgresql/.pgpass
chmod 0600 /var/lib/postgresql/.pgpass
Anche un file vuoto resta senza effetto. Inserisci una riga per ogni connessione, per esempio 127.0.0.1:5432:appdb:appuser:LaTuaPasswordSicura. Se invece il tuo job di backup gira direttamente come root senza cambio utente, lo stesso file va in /root/.pgpass.
Verificare il backup
Un file di backup mai ripristinato è soltanto una supposizione. Il test dura un minuto. Prima si guarda l'indice, poi si importa in un database usa e getta e si contano le tabelle:
runuser -u postgres -- pg_restore -l /var/lib/postgresql/appdb.dump | head -n 20
runuser -u postgres -- createdb appdb_restore_test
runuser -u postgres -- pg_restore -d appdb_restore_test /var/lib/postgresql/appdb.dump
runuser -u postgres -- psql -d appdb_restore_test -c "\dt"
runuser -u postgres -- dropdb appdb_restore_test
Se \dt mostra le stesse tabelle dell'originale, quindi qui almeno la tabella prova, il backup è utilizzabile. Se invece il comando risponde Did not find any relations., il database salvato era vuoto e il test non dimostra niente. In appdb poi rimuovi la tabella di prova con runuser -u postgres -- psql -d appdb -c "DROP TABLE prova;". Per l'esercizio quotidiano basta una voce in /etc/cron.d che salvi entrambi i file con la data nel nome e faccia pulizia di quelli più vecchi. L'importante è che i file lascino poi il server. Un backup sullo stesso disco aiuta contro un DROP TABLE involontario, non contro un guasto hardware.
Quando il cluster non parte
Se il servizio non parte, lo stato del servizio di solito ti dice solo che qualcosa è fallito. La causa vera sta nel log del cluster:
tail -n 30 /var/log/postgresql/postgresql-*-main.log
Una riga puoi tranquillamente ignorarla. Il messaggio già noto dalla prima sezione, FATAL: role "root" does not exist, arriva di solito da pg_isready: lo strumento costruisce il suo tentativo di connessione con l'utente di sistema con cui sei collegato, quindi come root, e il server registra il ruolo sconosciuto. Il valore di ritorno resta comunque 0, l'output dice accepting connections e il cluster sta benissimo.
Sui sistemi con systemd journalctl -u postgresql@17-main --no-pager -n 50 fornisce le stesse righe. Attenzione alla unit legata alla versione: postgresql.service è solo un involucro che avvia tutti i cluster, e segnala successo anche quando un singolo cluster è fallito. Per questo pg_lsclusters è il controllo più affidabile.
Tre messaggi coprono la maggior parte dei casi. could not bind IPv4 address "0.0.0.0": Address already in use significa che un altro cluster occupa la porta, vedi la sezione sui due cluster. Un messaggio su No space left on device durante la scrittura del postmaster.pid vuol dire semplicemente disco pieno, cosa che confermi con df -h. E gli errori sui permessi non validi della directory dati compaiono dopo esecuzioni avventate di chmod o chown: /var/lib/postgresql/17/main deve appartenere all'utente postgres e avere il modo 0700.
Per chiudere, un'indicazione sull'esercizio quotidiano: nella configurazione di base PostgreSQL è conservativo e non sfrutta affatto la RAM di un server. Prima di mettere mano a shared_buffers e work_mem, attiva pg_stat_statements dal pacchetto contrib e guarda quali query costano davvero tempo. Nella pratica il collo di bottiglia è quasi sempre un indice mancante, non i parametri di memoria.
Domande frequenti
Quale versione di PostgreSQL ottengo sulla mia distribuzione?
Perché all'avvio di psql ricevo il messaggio "role root does not exist"?
Che cosa significa "no pg_hba.conf entry for host" e come lo risolvo?
Perché il mio utente non riesce a creare tabelle, pur potendo usare il database?
Devo riavviare PostgreSQL dopo una modifica a pg_hba.conf?
pg_dump basta come backup completo?
2026 KernelHost GmbH. Tutti i diritti riservati. Questa guida è protetta dal diritto d'autore. La ripubblicazione su altri siti web, anche parziale o in forma modificata, non è consentita senza il nostro consenso scritto. Le citazioni con indicazione della fonte e un link sono le benvenute.

