Supabase self-hosted su VPS con Docker
Configura lo stack Docker ufficiale di Supabase su un VPS: sostituisci i secret dimostrativi, comprendi i 14 servizi, verifica la RAM e aggiorna senza perdere il database.
Cosa stai configurando
Self-hosting Supabase significa eseguire sul proprio server lo stack Docker Compose ufficiale: Postgres, un'API REST, un servizio di autenticazione, l'archiviazione dei file, i websocket realtime e il dashboard Studio. Si clona un repository, si modifica un unico file .env e si avviano circa quattordici container che insieme funzionano come un progetto Supabase sotto il proprio controllo.
L'installazione è breve. I problemi si concentrano nel file .env. Il file contiene secret dimostrativi pubblicati nel repository e uno stack avviato con questi valori predefiniti è accessibile a chiunque li individui. Questa guida illustra quali secret sostituire, a cosa serve ogni servizio, quanta memoria richiede realmente lo stack e come aggiornarlo senza eliminare il database.
Se Docker Compose è una novità, leggere prima Nozioni di base su Docker Compose su un VPS. Tutto ciò che segue presuppone che docker compose version restituisca già una versione.
Cosa contiene realmente lo stack
Supabase non è un singolo programma. Il file Compose avvia un insieme di servizi separati sulla stessa rete. Capire il ruolo di ciascuno trasforma un lungo elenco di nomi di container in un ambiente che puoi sottoporre a troubleshooting.
dbè PostgreSQL con le estensioni Supabase caricate. Tutti gli altri servizi comunicano con questo database. Se questo container non è integro, anche tutti gli altri servizi smettono di funzionare.kongè il gateway API. È in ascolto sulla porta 8000 e instrada/rest/v1/,/auth/v1/e/storage/v1/al backend corretto. È l'unico container che dovresti esporre.restè PostgREST. Legge lo schema Postgres e lo espone come API REST. In questo modo, una nuova tabella diventa un nuovo endpoint senza scrivere codice.authè GoTrue. Emette i JSON Web Token (JWT) che identificano gli utenti.storageeimgproxygestiscono il caricamento dei file e il ridimensionamento delle immagini.realtimetrasmette le modifiche al database tramite WebSocket.studioemetasono la dashboard e l'API di amministrazione che la supporta.analytics(Logflare) evectorraccolgono i log, mentresupavisorè il connection pooler di Postgres.
Per questo i valori delle risorse riportati di seguito sono quelli indicati. Non stai eseguendo solo un database. Stai eseguendo un database e una dozzina di servizi di supporto.
Dimensionamento: prevedere 8 GB di RAM
Dopo una nuova installazione, lo stack utilizza a riposo circa 2.5-3 GB di memoria residente, secondo i dati di luglio 2026, prima di aggiungere i propri dati o il proprio traffico. Il servizio di analisi e il processo Node.js di Studio sono i due maggiori consumatori singoli. Un server con 2 GB avvia i container, quindi il kernel ne termina uno tramite l'out-of-memory killer, in genere analytics o db. Il sintomo è un container bloccato in riavvio con codice di uscita 137.
Assegnare 8 GB di RAM e 4 vCPU a qualsiasi installazione da cui dipendono servizi importanti. 4 GB sono sufficienti per un'istanza di sviluppo personale, se si accetta che una query pesante eseguita contemporaneamente a una sessione di Studio sarà lenta. Anche il disco è importante, perché Postgres, il volume di storage e i dati dei log si trovano nella directory del progetto. Iniziare con 40 GB e monitorare l'utilizzo. Contare i servizi prima di scegliere un piano è una buona pratica per qualsiasi software self-hosted, perché PhotoPrism e Immich hanno requisiti minimi di RAM effettivi molto superiori a quelli indicati nelle rispettive pagine di avvio rapido.
Installazione: clonare il repository ufficiale
La procedura supportata copia la directory docker dal repository principale in una directory di progetto dedicata. Questa separazione è importante perché impedisce a un successivo git pull di sovrascrivere il tuo .env.
git clone --depth 1 https://github.com/supabase/supabase
mkdir supabase-project
cp -rf supabase/docker/* supabase-project
cp supabase/docker/.env.example supabase-project/.env
cd supabase-project
docker compose pulldocker compose pull scarica diversi gigabyte di immagini. Al termine, ogni servizio dovrebbe essere contrassegnato come Pulled. Un errore manifest unknown in questa fase indica che il tag dell’immagine bloccato è stato rimosso upstream. Per risolvere il problema, scarica una copia più recente del repository invece di modificare manualmente i tag.
I secret che devi modificare prima del primo avvio
Esegui questa procedura prima di avviare lo stack, non dopo. Al primo avvio, alcuni di questi valori vengono scritti nei dati persistenti. Modificarli in seguito richiede quindi il reset del database.
Il repository include un generatore che produce correttamente tutti i valori, comprese le due API key che devono essere firmate con il nuovo secret JWT.
sh utils/generate-keys.sh --update-envLo script scrive nuovi valori per JWT_SECRET, ANON_KEY, SERVICE_ROLE_KEY, SECRET_KEY_BASE, REALTIME_DB_ENC_KEY, VAULT_ENC_KEY, PG_META_CRYPTO_KEY e per i token Logflare in .env. Richiede openssl, presente in qualsiasi normale immagine Ubuntu.
Due valori non vengono impostati dallo script. Devi modificarli manualmente in .env:
POSTGRES_PASSWORD. Usa solo lettere e cifre. La punteggiatura interrompe le stringhe di connessione che diversi servizi costruiscono concatenando più stringhe. L'errore appare come un problema di autenticazione, non di analisi sintattica. Questo porta a cercare la causa nel posto sbagliato.DASHBOARD_USERNAMEeDASHBOARD_PASSWORD. Sono le credenziali di autenticazione di base per Studio. La password predefinita fornita è letteralmentethis_password_is_insecure_and_should_be_updated.
È importante capire perché ANON_KEY e SERVICE_ROLE_KEY non possono essere inventati. Sono entrambi JWT firmati con JWT_SECRET. Il gateway verifica questa firma a ogni richiesta. Una chiave che non corrisponde al tuo secret viene quindi rifiutata con {"message":"Invalid authentication credentials"}. Questo è il problema più comune nelle installazioni self-hosted: l'operatore modifica JWT_SECRET ma mantiene le chiavi dimostrative. Genera sempre tutti e tre i valori insieme.
Tratta SERVICE_ROLE_KEY come una password di root. Bypassa completamente la row level security. Deve essere usato nel codice lato server e in nessun altro contesto.
Imposta SITE_URL e API_EXTERNAL_URL sull'indirizzo che gli utenti utilizzeranno effettivamente, ad esempio https://supabase.example.com. Auth costruisce i link di conferma dell'indirizzo email e i callback OAuth a partire da questi valori. Se li lasci impostati su http://localhost:8000, tutti gli utenti verranno reindirizzati alla propria macchina.
Controlla quindi i valori configurati:
sh run.sh secretsAvvialo e verifica che sia operativo
sh run.sh start
docker compose psrun.sh start esegue il wrapping di docker compose up -d --wait, quindi non restituisce il prompt finché i controlli di integrità non hanno esito positivo. Ogni servizio dovrebbe mostrare running (healthy) oppure running. Il primo avvio richiede da due a quattro minuti, perché Postgres esegue gli script di inizializzazione prima che qualsiasi altro componente possa connettersi.
Se un container viene riavviato, leggi i relativi log usando il nome del servizio:
docker compose logs db
docker compose logs authStudio è quindi disponibile sulla porta 8000 e chiederà il nome utente e la password della dashboard configurati.
Non esporre la porta 8000 a Internet
Kong sulla porta 8000 usa HTTP in chiaro. Ogni API key e ogni password utente attraversano la rete senza cifratura. Le credenziali di Studio usano inoltre l'autenticazione di base, che applica una codifica base64 e non la cifratura.
Configura un reverse proxy davanti a Kong e termina lì TLS (transport layer security). Associa quindi Kong all'indirizzo di loopback, in modo che nessun altro possa raggiungerlo. In docker-compose.yml, la mappatura della porta kong diventa 127.0.0.1:8000:8000 e il proxy inoltra le richieste a quella porta. Traefik davanti a più applicazioni Compose descrive la configurazione dei certificati.
Blocca anche le altre porte nel firewall. Docker pubblica le porte scrivendo regole iptables proprie, che una configurazione ingenua di ufw non intercetta. Questo problema è spiegato in perché i container Docker ignorano le regole ufw.
Esegui il backup del database, non della directory
I dati di Postgres si trovano in un bind mount in ./volumes/db/data. Copiare quella directory mentre il container è in esecuzione produce una copia incoerente, perché Postgres memorizza temporaneamente le scritture nei buffer e i file su disco sono coerenti solo al termine di un checkpoint. Il ripristino di questa copia di solito funziona, ma può anche perdere silenziosamente le transazioni più recenti. È il peggior tipo di errore per un backup.
Esegui invece un dump. pg_dumpall viene eseguito all'interno del container e produce uno snapshot coerente:
docker exec -t supabase-db pg_dumpall -U postgres > supabase-$(date +%F).sqlVerifica che il file non sia vuoto prima di considerarlo affidabile. Trasferisci quindi questi dump fuori dal server secondo una pianificazione. È questo lo scopo di backup cifrati fuori sede con restic. Esegui contemporaneamente il backup di .env. La perdita di JWT_SECRET rende non validi tutti i token emessi e impedisce di leggere ogni secret cifrato archiviato.
I file caricati si trovano in ./volumes/storage. Sono file normali, quindi è sufficiente una copia semplice.
Aggiornare senza perdere i dati
Supabase fissa le versioni delle immagini in docker-compose.yml, quindi nulla cambia finché non lo decidi tu. Il versionamento fisso merita di essere adottato in qualsiasi stack assemblato manualmente. Per questo un relay RustDesk self-hosted fissa le versioni delle sue due immagini server invece di seguire un tag variabile: un aggiornamento deve essere un’operazione pianificata, da eseguire quando hai tempo. Prima crea sempre un dump.
docker compose pull
sh run.sh recreaterecreate arresta lo stack e lo riavvia usando le nuove immagini. I dati restano disponibili perché si trovano nei bind mount sull’host, non all’interno dei container. Prima di eseguire un aggiornamento a una versione principale, leggi CHANGELOG.md nel repository: gli aggiornamenti principali di Postgres non sono automatici e richiedono un dump e un ripristino.
Per applicare le modifiche apportate direttamente al file Compose, clona nuovamente il repository upstream e copia la relativa directory docker nel progetto, facendo attenzione a non sovrascrivere .env.
Il reset completo, che elimina tutto compreso il database, usa uno script separato e richiede una conferma:
sh reset.shFAQ
Perché le chiamate API restituiscono "Invalid authentication credentials"?
ANON_KEY o SERVICE_ROLE_KEY non è stato firmato con il JWT_SECRET attualmente configurato in .env. Il gateway verifica la firma di ogni richiesta e rifiuta le richieste con una firma non corrispondente. Rigenera tutti e tre i valori insieme usando sh utils/generate-keys.sh --update-env, quindi esegui sh run.sh recreate affinché i servizi leggano i nuovi valori.
Posso eseguire Supabase self-hosted su un VPS da 2 GB?
Non in modo affidabile. A luglio 2026 lo stack utilizza quasi 3 GB in condizioni di inattività, perché esegue circa quattordici servizi. Di conseguenza, su un server da 2 GB l'out of memory killer termina alcuni container e in docker compose ps viene visualizzato il codice di uscita 137. Per la produzione usa 8 GB e considera 4 GB il minimo per lo sviluppo individuale.
Supabase self-hosted include le edge functions?
Sì. Il file Compose include il runtime per le funzioni basato su Deno e serve tutto ciò che inserisci in ./volumes/functions. Non include la rete globale di distribuzione della piattaforma hosted, quindi le funzioni vengono eseguite sul tuo unico server, in un'unica posizione.
Come posso connettermi direttamente al database Postgres?
Usa docker exec -it supabase-db psql -U postgres per aprire una shell interattiva direttamente sul server. Per un client esterno, connettiti a Supavisor sulla porta 5432 usando l'utente postgres.<POOLER_TENANT_ID> e il tuo POSTGRES_PASSWORD. Non esporre questa porta a Internet. Raggiungila tramite una VPN o un tunnel SSH.
Perché le email di conferma dell'autenticazione contenevano un link a localhost?
SITE_URL e API_EXTERNAL_URL in .env erano rimasti ai valori predefiniti. Il servizio di autenticazione costruisce ogni link di conferma e reimpostazione della password a partire da questi due valori, quindi invia l'indirizzo configurato. Imposta entrambi sull'URL pubblico effettivo e ricrea lo stack.