Cos'è Neon: Postgres serverless
Cos'è Neon, il Postgres serverless con branching del database e sospensione automatica: come funziona il pooling, i limiti reali e quando conviene davvero.
Apri una pull request che cambia lo schema del database e parte la solita domanda: la provo dove? Su quello di sviluppo, che condividi con due colleghi e dove i dati sono un disastro? Su una copia locale che non somiglia alla produzione? Neon risponde in modo diverso, e nel farlo cambia un paio di abitudini. In questo articolo trovi come funziona, le due cose che lo distinguono davvero, e il problema tecnico che ti farà perdere un pomeriggio se non lo conosci prima.
Cos'è
Neon è un PostgreSQL gestito costruito separando il calcolo dallo storage: i dati stanno in un archivio a parte e i processi che li elaborano si accendono, si spengono e si duplicano in modo indipendente.
Sotto c'è Postgres vero, non un'imitazione compatibile: ti colleghi con psql, usi il tuo ORM, le tue migrazioni funzionano. Se non hai chiaro cosa distingua un database relazionale dagli altri, parti da cos'è un database.
La separazione tra calcolo e storage
Sembra un dettaglio da addetti ai lavori, ed è la premessa di tutto il resto.
In un Postgres tradizionale, il processo che esegue le query e il disco che contiene i dati stanno sulla stessa macchina. Vuoi una copia per fare test? Copi tutti i dati, e se il database è grande la copia dura ore.
Neon spezza quel legame: lo storage è un servizio a sé che conserva la storia delle modifiche, il calcolo è un processo separato che si collega per leggere e scrivere. Da qui discendono le due funzioni che contano.
Il branching del database
Questa è la funzione principale, e cambia il modo di lavorare più di quanto sembri leggendola.
Puoi creare un ramo del database esattamente come crei un ramo del codice: in un istante, senza copiare i dati.
Il ramo nuovo parte come riferimento allo stato attuale dello storage, non come duplicato: occupa spazio solo per ciò che modifichi da lì in poi. Un database da cinquanta gigabyte si ramifica in un paio di secondi.
# un ramo dalla produzione per provare una migrazione
neonctl branches create --name migrazione-utenti
# la stringa di connessione del nuovo ramo
neonctl connection-string migrazione-utenti
# finito il lavoro, il ramo si butta via
neonctl branches delete migrazione-utenti
Cosa cambia in pratica
Ogni pull request ha il suo database. Colleghi la creazione del ramo alla tua CI/CD e ogni PR ottiene un database proprio, con dati realistici. Chi ha visto una migrazione passare in sviluppo su cento righe e impiegare quaranta minuti in produzione su dieci milioni, sa quanto valga.
Combinato con le anteprime per ramo di piattaforme come Vercel, ogni pull request diventa un'applicazione completa e isolata, frontend e database inclusi.
Ognuno ha il suo ambiente. Basta il database di sviluppo condiviso dove qualcuno cancella una tabella mentre stai lavorando.
I test partono da uno stato pulito. Un ramo per la suite di test, distrutto alla fine, nessun residuo tra un'esecuzione e l'altra.
Puoi ramificare da un momento nel passato. Hai eseguito una DELETE senza WHERE? Rami da prima dell'incidente e recuperi quello che serve, senza ripristinare un backup sopra la produzione. Non sostituisce però i backup: la storia conservata ha una finestra limitata.
La sospensione automatica
La seconda conseguenza della separazione: se nessuno interroga il database, il processo di calcolo si spegne. I dati restano dove sono. Alla connessione successiva riparte.
Il vantaggio è economico e ovvio: paghi il calcolo quando lo usi. Venti rami di prova che nessuno tocca non consumano nulla oltre allo spazio, mentre nel modello classico sarebbero venti istanze accese.
Il rovescio va detto senza girarci intorno: la prima richiesta dopo un periodo di inattività aspetta la riaccensione. È un ritardo dell'ordine di qualche centinaio di millisecondi fino a qualche secondo — indicativo, dipende dalla configurazione — che il visitatore sfortunato si prende tutto intero.
In sviluppo non te ne accorgi, e con traffico costante il database non resta fermo abbastanza. Il caso problematico è il traffico sporadico: poche visite al giorno, dove ogni visitatore rischia di pagare la riaccensione. I piani prevedono di tenere il calcolo sempre acceso, ma costa di più: a quel punto valuta se il modello ti serve davvero.
Il pooling delle connessioni: il problema numero uno
Se leggi solo una sezione di questo articolo, leggi questa. È il problema pratico che colpisce quasi tutti, e il messaggio d'errore non aiuta a capire.
Perché succede
Postgres gestisce ogni connessione con un processo dedicato, e ognuna costa memoria. Per questo esiste un tetto massimo, e non è un numero grande: poche centinaia nella configurazione tipica.
Nelle applicazioni tradizionali non è un problema: il server resta acceso, apre un pool all'avvio e lo riusa per sempre.
Con le funzioni serverless il modello salta. Ogni invocazione è un ambiente isolato che non condivide niente con le altre: se ognuna apre la sua connessione, cento richieste contemporanee sono cento connessioni. E siccome quelle funzioni si moltiplicano sotto carico, il numero cresce nel momento peggiore.
Il risultato è questo, e arriva sempre quando il traffico sale:
FATAL: sorry, too many clients already
Sembra un problema del database ed è un problema dell'architettura. Aumentare la potenza dell'istanza non lo risolve, perché il tetto non è di CPU: è di connessioni.
La soluzione
Ci si mette in mezzo un pooler: un intermediario che mantiene un piccolo insieme di connessioni reali verso Postgres e le presta a quelle in arrivo. Mille client parlano con il pooler, che dietro tiene aperte venti connessioni vere e le fa ruotare.
Neon lo espone come un endpoint separato. La differenza sta in un pezzo di hostname:
# connessione diretta — per migrazioni e strumenti amministrativi
DATABASE_URL="postgresql://utente:password@ep-nome-123.eu-central-1.aws.neon.tech/db"
# connessione con pooler — per l'applicazione e le funzioni serverless
DATABASE_URL="postgresql://utente:password@ep-nome-123-pooler.eu-central-1.aws.neon.tech/db"
La regola operativa: l'applicazione usa sempre l'endpoint con il pooler, le migrazioni e gli strumenti che richiedono una sessione persistente usano quello diretto. Tenere due variabili distinte è la prassi sensata — sul come gestirle c'è cosa sono le variabili d'ambiente.
Un dettaglio che genera confusione: il pooler lavora in modalità transazione, e lì alcune cose non funzionano — istruzioni preparate, advisory lock, LISTEN/NOTIFY, transazioni aperte a lungo. Se il tuo ORM si lamenta di istruzioni preparate, è quasi sempre questo, e si risolve con un parametro.
C'è anche una terza via: il driver che parla al database via HTTP invece che con il protocollo nativo. Per una singola query in una funzione serverless è la strada più diretta, perché elimina la connessione persistente.
import { neon } from "@neondatabase/serverless";
const sql = neon(process.env.DATABASE_URL);
const utenti = await sql`SELECT id, email FROM utenti WHERE attivo = true`;
Neon e le alternative
| Neon | Supabase | Postgres su VPS | |
|---|---|---|---|
| Cos'è | Solo il database | Backend completo | Il database, tutto tuo |
| Branching | Sì, istantaneo | Limitato | No, copie manuali |
| Sospensione a riposo | Sì | Sul piano gratuito | No |
| Auth e storage inclusi | No | Sì | No |
| Pooling | Endpoint dedicato | Incluso | Da installare |
| Latenza a freddo | Presente | Presente sul gratuito | Assente |
| Prevedibilità della spesa | A consumo | A fasce | Canone fisso |
| Manutenzione | Nessuna | Nessuna | Tutta tua |
Il confronto con Supabase è il più frequente, e la distinzione è netta: Supabase è un backend, Neon è un database. Se ti serve anche autenticazione, storage e API generate, guarda Supabase. Se hai già il backend e vuoi solo un Postgres fatto bene, Neon fa una cosa sola e la fa meglio.
Sul confronto con un Postgres installato su una macchina tua vale quello che vale sempre: a parità di risorse costa meno gestirselo. Quello che paghi in più sono i backup automatici verificati, gli aggiornamenti di versione, il monitoraggio e il fatto che alle tre di notte il problema non è tuo. Se sai amministrare Postgres e il carico è stabile, il VPS è la scelta giusta: il punto di partenza è configurare un VPS da zero.
Il modello di prezzo
Neon fattura sostanzialmente due cose: il tempo di calcolo acceso e lo spazio occupato dai dati. Non do numeri perché cambiano, ma le conseguenze del modello sono stabili.
Il calcolo si paga a tempo, quindi la sospensione automatica è ciò che rende sostenibile avere molti rami. Lo spazio si paga per quanto occupi davvero: i rami costano solo per la differenza rispetto all'origine, quindi dieci rami di prova costano quasi nulla finché non ci scrivi sopra molto.
Le due voci che sfuggono: i rami dimenticati — ne crei uno per PR, la PR viene chiusa e il ramo resta lì per mesi — e un calcolo tenuto sempre acceso su un progetto che non lo giustifica.
Gli errori che fanno perdere ore
Usare l'endpoint diretto dall'applicazione. Funziona in sviluppo, si rompe in produzione al primo picco. È l'errore numero uno.
Aprire un client nuovo a ogni chiamata. Anche con il pooler, creare e distruggere il client a ogni invocazione spreca tempo: va istanziato fuori dal corpo della funzione.
Non dichiarare un timeout sulle connessioni. Un ambiente serverless congelato a metà lascia connessioni appese che nessuno chiude.
Dimenticare i rami aperti. Automatizza la cancellazione alla chiusura della pull request: a mano non lo farà nessuno.
Confondere il branching con i backup. La storia conservata ha una durata limitata: un backup esportato altrove serve comunque.
Mettere il database dall'altra parte del mondo rispetto all'applicazione. Ogni query paga il viaggio di andata e ritorno, e una pagina che ne fa venti diventa lenta senza motivo.
Quando conviene e quando no
Conviene se: lavori in team e le migrazioni vi fanno paura — il branching da solo giustifica la scelta; usi funzioni serverless; hai molti ambienti di prova; hai carico variabile e non vuoi pagare capacità ferma.
Non conviene se: hai traffico sporadico e la latenza del primo accesso è inaccettabile; hai carico costante e pesante, dove un'istanza a canone fisso costa meno; ti serve un backend completo; oppure hai già un Postgres che funziona bene e nessuno dei problemi che Neon risolve — in quel caso un Postgres normale va benissimo, e cambiare per moda è il modo più rapido di introdurre problemi che non avevi.
Se stai ancora scegliendo le fondamenta, il ragionamento a monte è in quale database scegliere.
In sintesi
Neon è Postgres vero, con calcolo e storage separati. Da quella separazione derivano tutte le sue caratteristiche.
Il branching è la funzione che cambia davvero il lavoro: un database per ogni ramo o pull request, creato in secondi, senza copiare i dati.
La sospensione automatica taglia i costi ma introduce un ritardo alla riaccensione. Irrilevante in sviluppo e con traffico costante, fastidiosa con traffico sporadico.
Il pooling non è opzionale con le funzioni serverless: ogni invocazione apre una connessione, e senza pooler saturi il database. Usa l'endpoint con il pooler per l'applicazione, quello diretto per le migrazioni.
Costa più di un Postgres su una macchina tua, e ha senso finché il tempo di amministrazione che ti risparmia vale più della differenza.
Se ti serve anche dove far girare il backend, leggi cos'è Railway e cos'è Render.