SSD Nodes Learn Hosting plans →
Gidsen Matt ConnorDoor Matt Connor · Bijgewerkt 2026-08-27

Supabase zelf hosten op een VPS met Docker

Leer hoe u de officiële Supabase Docker-stack op uw eigen server installeert. Ontdek welke geheimen u moet wijzigen, het benodigde RAM-geheugen en hoe u veilig updates uitvoert.

Wat u bouwt

Het zelf hosten van Supabase betekent dat u de officiële Docker Compose-stack op uw eigen server draait: Postgres, een REST API daarvoor, een authenticatieservice, bestandsopslag, realtime websockets en het Studio-dashboard. U kloont één repository, bewerkt één .env-bestand en start ongeveer veertien containers die samen fungeren als een Supabase-project dat u beheert.

De installatie is kort. Het onderdeel dat vaak fout gaat, is het .env-bestand. Dit wordt geleverd met demo-geheimen die openbaar in de repository staan, en een stack die met deze standaardinstellingen wordt gestart, is toegankelijk voor iedereen die deze vindt. Deze handleiding behandelt de geheimen die u moet vervangen, waar elke service voor dient, hoeveel geheugen de stack daadwerkelijk nodig heeft en hoe u deze bijwerkt zonder uw database te verwijderen.

Als Compose nieuw voor u is, lees dan eerst Docker Compose-basisprincipes op een VPS. Alles hieronder gaat ervan uit dat docker compose version al een versie weergeeft.

Wat de stack daadwerkelijk bevat

Supabase is niet één enkel programma. Het Compose-bestand start een set afzonderlijke services op één netwerk. Door te weten welke service wat doet, verandert een muur van containernamen in iets dat u kunt debuggen.

  • db is PostgreSQL met de geladen Supabase-extensies. Elke andere service communiceert hiermee. Als deze container niet gezond is, faalt al het andere ook.
  • kong is de API gateway. Deze luistert op poort 8000 en routeert /rest/v1/, /auth/v1/ en /storage/v1/ naar de juiste backend. Dit is de enige container die u ooit zou moeten blootstellen.
  • rest is PostgREST. Deze leest uw Postgres-schema en serveert dit als een REST API, waardoor een nieuwe tabel zonder code een nieuw eindpunt wordt.
  • auth is GoTrue. Deze geeft de JSON web tokens (JWT) uit die uw gebruikers identificeren.
  • storage en imgproxy verwerken bestandsuploads en het wijzigen van de grootte van afbeeldingen.
  • realtime streamt database-wijzigingen via websockets.
  • studio en meta zijn het dashboard en de admin API die daarachter draait.
  • analytics (Logflare) en vector verzamelen logs, en supavisor is de Postgres connection pooler.

Deze lijst verklaart waarom de onderstaande resourcegetallen zijn zoals ze zijn. U draait niet alleen een database. U draait een database plus een dozijn ondersteunende services.

Dimensionering: plan voor 8 GB RAM

De stack verbruikt bij een nieuwe installatie, peildatum juli 2026, ongeveer 2,5 tot 3 GB aan resident geheugen in ruststand, nog voordat u eigen data toevoegt of verkeer verwerkt. De analytics-service en het Studio Node.js-proces zijn de twee grootste individuele verbruikers. Een server met 2 GB zal de containers wel starten, maar verliest er vervolgens een aan de kernel out of memory killer, meestal analytics of db. Het symptoom hiervan is een container die blijft herstarten met exitcode 137.

Reserveer 8 GB RAM en 4 vCPU voor elke omgeving waar u op vertrouwt. 4 GB volstaat voor een individuele ontwikkelomgeving, mits u accepteert dat een zware query in combinatie met een Studio-sessie traag zal verlopen. Schijfruimte is eveneens van belang, aangezien Postgres, het opslagvolume en de loggegevens zich allemaal in de projectmap bevinden. Begin met 40 GB en monitor het verbruik. Het tellen van het aantal services voordat u een abonnement kiest, is een goede gewoonte voor alles wat u zelf host, aangezien PhotoPrism en Immich een minimale RAM-behoefte hebben die aanzienlijk hoger ligt dan hun quick start-pagina's suggereren.

Installatie: kloon de officiële repository

Het ondersteunde pad kopieert de docker-directory uit de hoofdrepository naar een eigen projectdirectory. Deze scheiding is van belang, omdat een latere git pull uw .env niet kan overschrijven.

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 pull

docker compose pull downloadt enkele gigabytes aan images. Het proces moet eindigen met elke service gemarkeerd als Pulled. Een manifest unknown-foutmelding hier betekent dat de vastgezette image-tag upstream is verwijderd. De oplossing is om een nieuwere kopie van de repository op te halen in plaats van de tags handmatig aan te passen.

De geheimen die u moet wijzigen vóór de eerste start

Voer dit uit voordat u de stack start, niet daarna. Verschillende van deze waarden worden bij de eerste opstart naar de data geschreven; wijziging achteraf betekent dat de database moet worden gereset.

De repository bevat een generator die elke waarde correct aanmaakt, inclusief de twee API-keys die moeten worden ondertekend met uw nieuwe JWT-geheim.

sh utils/generate-keys.sh --update-env

Dit script schrijft nieuwe waarden voor JWT_SECRET, ANON_KEY, SERVICE_ROLE_KEY, SECRET_KEY_BASE, REALTIME_DB_ENC_KEY, VAULT_ENC_KEY, PG_META_CRYPTO_KEY en de Logflare-tokens naar .env. Het vereist openssl, wat aanwezig is op elke standaard Ubuntu-image.

Twee waarden worden niet ingesteld en moet u handmatig bewerken in .env:

  • POSTGRES_PASSWORD. Gebruik uitsluitend letters en cijfers. Leestekens hierin verbreken de connection strings die verschillende services opbouwen door strings samen te voegen. De foutmelding lijkt op een authenticatiefout in plaats van een parsingfout, waardoor men op de verkeerde plek naar de oorzaak zoekt.
  • DASHBOARD_USERNAME en DASHBOARD_PASSWORD. Dit zijn de basis-authenticatiegegevens voor Studio. Het standaardwachtwoord is letterlijk this_password_is_insecure_and_should_be_updated.

Begrijp waarom ANON_KEY en SERVICE_ROLE_KEY niet zelf kunnen worden verzonnen. Beide zijn JWT's ondertekend met JWT_SECRET. De gateway verifieert die handtekening bij elk verzoek; een key die niet overeenkomt met uw geheim wordt daarom afgewezen met {"message":"Invalid authentication credentials"}. Dit is de meest voorkomende fout bij self-hosting: de beheerder wijzigt JWT_SECRET, maar behoudt de demo-keys. Genereer ze altijd alle drie tegelijk.

Behandel SERVICE_ROLE_KEY als een root-wachtwoord. Het omzeilt row level security volledig. Het hoort uitsluitend thuis in server-side code.

Stel SITE_URL en API_EXTERNAL_URL in op het adres dat uw gebruikers daadwerkelijk zullen bereiken, bijvoorbeeld https://supabase.example.com. Auth bouwt de e-mailbevestigings- en OAuth-callbacklinks op basis van deze waarden. Als u ze op http://localhost:8000 laat staan, worden al uw gebruikers naar hun eigen machine gestuurd.

Controleer daarna wat u heeft:

sh run.sh secrets

Start de service en controleer of deze correct functioneert

sh run.sh start
docker compose ps

run.sh start omhult docker compose up -d --wait, waardoor het commando pas terugkeert zodra de health checks zijn geslaagd. Elke service dient de status running (healthy) of running te tonen. De eerste keer opstarten duurt twee tot vier minuten, omdat Postgres zijn initialisatiescripts uitvoert voordat andere services verbinding kunnen maken.

Als een container continu opnieuw opstart, lees dan de logs per servicenaam:

docker compose logs db
docker compose logs auth

Studio is vervolgens beschikbaar op poort 8000 en zal vragen om de gebruikersnaam en het wachtwoord voor het dashboard die u heeft ingesteld.

Stel poort 8000 niet bloot aan het openbare internet

Kong op poort 8000 communiceert via onversleuteld HTTP. Elke API-sleutel en elk gebruikerswachtwoord wordt in leesbare tekst over het netwerk verstuurd. De inloggegevens voor de Studio maken gebruik van basic authentication, wat neerkomt op base64-codering in plaats van encryptie.

Plaats een reverse proxy voor de service, handel hier de TLS-termination (transport layer security) af en bind Kong aan het loopback-adres zodat de service vanaf andere locaties onbereikbaar is. In docker-compose.yml wordt de poortmapping kong gewijzigd naar 127.0.0.1:8000:8000, waarna de proxy het verkeer hiernaartoe doorstuurt. Traefik voor meerdere Compose-applicaties behandelt de configuratie van certificaten.

Sluit de overige poorten eveneens af via de firewall. Docker publiceert poorten namelijk door eigen iptables-regels te schrijven die door een standaard ufw-configuratie niet worden opgemerkt. Dit risico wordt toegelicht in waarom Docker-containers uw ufw-regels negeren.

Maak een back-up van de database, niet van de map

Postgres-data bevindt zich in een bind mount op ./volumes/db/data. Het kopiëren van die map terwijl de container draait, resulteert in een inconsistente kopie, omdat Postgres schrijfacties buffert en de bestanden op schijf alleen consistent zijn op het moment van een checkpoint. Het terugzetten van zo'n kopie werkt meestal wel, maar kan soms stilletjes de laatste transacties verliezen; dit is het slechtst denkbare scenario voor een back-up.

Gebruik in plaats daarvan een dump. pg_dumpall draait binnen de container en genereert een consistente snapshot:

docker exec -t supabase-db pg_dumpall -U postgres > supabase-$(date +%F).sql

Controleer of het bestand niet leeg is voordat u erop vertrouwt. Verplaats deze dumps vervolgens volgens een vast schema van de server af; dit is waar versleutelde offsite back-ups met restic voor bedoeld is. Maak tegelijkertijd een back-up van uw .env. Het verlies van JWT_SECRET betekent dat elk uitgegeven token ongeldig wordt en elk opgeslagen versleuteld geheim onleesbaar is.

Geüploade bestanden staan in ./volumes/storage. Aangezien dit gewone bestanden zijn, volstaat een eenvoudige kopie.

Updaten zonder dataverlies

Supabase legt imageversies vast in docker-compose.yml, zodat er niets verandert totdat u die versie zelf wijzigt. Het vastleggen van versies is ook aan te raden voor elke stack die u handmatig samenstelt. Daarom legt een zelfgehoste RustDesk-relay de versies van de twee serverimages vast en volgt deze geen veranderende tag. Een upgrade moet u uitvoeren op een moment waarop u daar tijd voor hebt. Maak altijd eerst een dump.

docker compose pull
sh run.sh recreate

recreate stopt de stack en start deze opnieuw met de nieuwe images. Uw data blijft behouden omdat deze in de bind mounts op de host staat en niet in de containers zelf. Lees CHANGELOG.md in de repository voordat u een grote versie-upgrade uitvoert, aangezien grote Postgres-upgrades niet automatisch verlopen en een dump en restore vereisen.

Om wijzigingen in het Compose-bestand zelf door te voeren, kloont u de upstream-repository opnieuw en kopieert u de map docker over uw project. Let er hierbij op dat u .env niet overschrijft.

De volledige reset, die alles inclusief de database verwijdert, is een apart script en vraagt om bevestiging:

sh reset.sh

FAQ

Waarom geven mijn API-aanroepen de melding "Invalid authentication credentials"?

Uw ANON_KEY of SERVICE_ROLE_KEY was niet ondertekend met de JWT_SECRET die momenteel in .env staat. De gateway verifieert de handtekening bij elk verzoek en wijst een mismatch af. Genereer alle drie opnieuw met sh utils/generate-keys.sh --update-env en voer vervolgens sh run.sh recreate uit zodat de services de nieuwe waarden inlezen.

Kan ik een zelfgehoste Supabase draaien op een 2 GB VPS?

Niet betrouwbaar. De stack verbruikt in rust bijna 3 GB (stand juli 2026) omdat deze ongeveer veertien services uitvoert. Een 2 GB-server verliest containers door de out of memory killer, wat resulteert in exitcode 137 in docker compose ps. Gebruik 8 GB voor productie en beschouw 4 GB als het absolute minimum voor individuele ontwikkeling.

Bevat de zelfgehoste Supabase edge functions?

Ja. Het Compose-bestand bevat de op Deno gebaseerde runtime voor functies en voert alles uit wat u in ./volumes/functions plaatst. Het bevat niet het wereldwijde distributienetwerk van het gehoste platform, dus uw functies draaien op uw eigen server, op één locatie.

Hoe maak ik direct verbinding met de Postgres-database?

Gebruik docker exec -it supabase-db psql -U postgres voor een interactieve shell op de server zelf. Voor een externe client maakt u verbinding via Supavisor op poort 5432 met de gebruiker postgres.<POOLER_TENANT_ID> en uw POSTGRES_PASSWORD. Stel deze poort niet open naar het internet. Benader deze via een VPN of een SSH-tunnel.

Waarom verwezen mijn verificatie-e-mails voor authenticatie naar localhost?

SITE_URL en API_EXTERNAL_URL in .env stonden nog op hun standaardwaarden. De auth-service bouwt elke bevestigings- en wachtwoordherstellink op basis van deze twee waarden, waardoor het adres wordt verzonden dat is geconfigureerd. Stel beide in op uw werkelijke publieke URL en maak de stack opnieuw aan.