SSD Nodes Learn Hosting plans →
Gidsen Matt ConnorDoor Matt Connor · Bijgewerkt 2026-10-20

Supabase zelf hosten op een VPS met Docker

Leer hoe u de officiële Supabase Docker-stack op uw eigen server draait. Wij leggen uit welke elf services u start, welke geheimen u moet wijzigen en het RAM-gebruik.

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 elf containers die samen fungeren als een Supabase-project dat u zelf beheert.

De installatie is kort. Het onderdeel dat vaak fout gaat, is het .env-bestand. Dit wordt geleverd met demo-geheimen die in de repository zijn gepubliceerd, en een stack die met deze standaardwaarden 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.

Waar de stack daadwerkelijk uit bestaat

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.
  • api-gw is de API-gateway. Sinds de release van augustus 2026 draait deze op Envoy; daarvoor was het een service genaamd kong, en Kong is nog steeds beschikbaar als opt-in override. 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 aan het internet 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 schalen van afbeeldingen.
  • realtime streamt database-wijzigingen via websockets.
  • studio en meta zijn het dashboard en de admin-API die daarachter draait.
  • functions is de op Deno gebaseerde edge runtime, en supavisor is de Postgres connection pooler.

Logs en analytics (Logflare plus vector) zijn sinds juni 2026 uit het standaard Compose-bestand verwijderd. Het zijn nu opt-in override-bestanden die u inschakelt met sh run.sh config add logs, via dezelfde gelaagdheid van Compose-bestanden als beschreven in één stack draaien vanuit meerdere Compose-bestanden.

Deze lijst verklaart waarom de onderstaande resource-getallen zijn zoals ze zijn. U draait niet alleen een database. U draait een database plus tien ondersteunende services.

Dimensionering: plan voor 8 GB RAM

De stack verbruikte in juli 2026 bij een verse installatie ongeveer 2,5 tot 3 GB aan resident geheugen in ruststand, nog voordat u eigen data of verkeer toevoegde. In die meting waren de analytics-service en het Studio Node.js-proces de twee grootste individuele verbruikers. Analytics maakt geen deel meer uit van de standaard stack, dus dit kost u alleen geheugen als u de log-override inschakelt. Een server met 2 GB zal de containers starten, waarna er één verloren gaat aan de kernel out of memory killer. Het symptoom hiervan is een container die blijft herstarten met exitcode 137. De eigen self-hosting documentatie van Supabase, gecontroleerd in oktober 2026, noemt 4 GB RAM, 2 CPU-cores en 40 GB SSD als minimum, en 8 GB, 4 cores en 80 GB als aanbevolen specificaties.

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 ook van belang, omdat Postgres, het opslagvolume en, indien ingeschakeld, de loggegevens allemaal onder de projectmap vallen. Begin met 40 GB en monitor het verbruik. Het tellen van services voordat u een abonnement kiest, is een goede gewoonte voor alles wat u zelf host, aangezien PhotoPrism en Immich ook aanzienlijke minimale RAM-vereisten hebben: de quick start-pagina van Immich, gecontroleerd in oktober 2026, vraagt om ten minste 6 GB RAM en 2 CPU-cores. Als dit een grotere server is dan uw project momenteel vereist, vergelijk het dan met wat het gratis cloud-abonnement van Supabase daadwerkelijk bevat, aangezien een kleine applicatie die onder de 500 MB blijft en nooit een week inactief is, daar kosteloos kan draaien.

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; het achteraf wijzigen ervan betekent dat de database moet worden gereset.

De repository bevat een generator die elke waarde correct aanmaakt, inclusief de twee API-sleutels 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, de Logflare-tokens, de S3-protocolsleutels en MINIO_ROOT_PASSWORD naar .env. Sinds oktober 2026 schrijft het ook willekeurige waarden voor POSTGRES_PASSWORD en DASHBOARD_PASSWORD. Het vereist openssl, wat aanwezig is op elke standaard Ubuntu-image. Omdat het nu ook het databasewachtwoord herschrijft, voert u het alleen uit met --update-env vóór de eerste start; voer het nooit uit tegen een stack die al data bevat.

Twee waarden zijn het waard om handmatig te controleren in .env:

  • POSTGRES_PASSWORD. Het script vult dit met willekeurige hexadecimale tekens, wat veilig is. Als u dit vervangt door uw eigen waarde, gebruik dan alleen letters en cijfers. Leestekens verbreken hier de verbindingsreeksen die verschillende services opbouwen door strings samen te voegen. De foutmelding lijkt op een authenticatiefout in plaats van een parseerfout, waardoor gebruikers op de verkeerde plek gaan zoeken.
  • DASHBOARD_USERNAME en DASHBOARD_PASSWORD. Dit zijn de basis-authenticatiegegevens voor Studio. Het script stelt een willekeurig wachtwoord in, maar laat de gebruikersnaam op de standaardwaarde supabase staan. Het meegeleverde standaardwachtwoord is letterlijk this_password_is_insecure_and_should_be_updated; als u het script heeft overgeslagen, wijzig dit dan nu.

Begrijp waarom ANON_KEY en SERVICE_ROLE_KEY niet zelf verzonnen kunnen worden. Beide zijn JWT's ondertekend met JWT_SECRET. De gateway accepteert alleen de exacte sleutels in .env, en de services erachter verifiëren de handtekening tegen JWT_SECRET. Een sleutel die niet overeenkomt, wordt daarom geweigerd met HTTP 401. De standaard Envoy-gateway antwoordt met 401 Unauthorized, en dat geldt ook voor de huidige Kong-override, die Kong 3.9 draait en {"message":"Unauthorized"} retourneert. Installaties van vóór maart 2026 draaiden Kong 2.8, dat antwoordt met {"message":"Invalid authentication credentials"}. Dit is de meest voorkomende fout bij self-hosting: de beheerder heeft JWT_SECRET gewijzigd, maar de demo-sleutels behouden. Genereer ze altijd alle drie tegelijk.

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

Stel SITE_URL in op het adres van uw app, en API_EXTERNAL_URL op het publieke adres van de API met /auth/v1 aan het einde, bijvoorbeeld https://supabase.example.com/auth/v1. Stel SUPABASE_PUBLIC_URL in op hetzelfde publieke adres zonder het pad. Auth bouwt zijn e-mailbevestigings- en OAuth-callback-links op basis van deze waarden; als u ze op hun localhost-standaardwaarden laat staan, stuurt u al uw gebruikers naar hun eigen machine.

Controleer vervolgens 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

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

Plaats een reverse proxy voor de gateway, handel hier de TLS (transport layer security) af en bind de gateway aan het loopback-adres zodat deze vanaf andere locaties onbereikbaar is. In docker-compose.yml wordt de poortregel onder de api-gw-service gewijzigd naar 127.0.0.1:${API_GW_HTTP_PORT:-${KONG_HTTP_PORT:-8000}}:8000/tcp, waarna de proxy het verkeer hiernaartoe doorstuurt. Bij een installatie van vóór de overstap naar Envoy in augustus 2026 bevindt dezelfde poortregel zich onder een service genaamd kong. Traefik voor meerdere Compose-applicaties behandelt de configuratie van certificaten. Diezelfde proxy fungeert als centraal punt voor alle andere services op de server, van deze stack tot aan projecten zoals een Jellyfin-bibliotheek ingericht als videotheek uit de jaren 90. Elke service vereist hierbij een hostnaam in plaats van een extra open poort. Als een dashboard uitsluitend voor eigen gebruik is, kunt u de proxy overslaan en via een SSH-tunnel verbinding maken met de loopback-poort. Dit is dezelfde methode die self-hosted open-kritt gebruikt om de scaninterface volledig af te schermen van het openbare internet.

Sluit de overige poorten af in 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 directory

Postgres-data bevindt zich in een bind mount op ./volumes/db/data. Het kopiëren van die directory terwijl de container draait, levert een inconsistente kopie op, omdat Postgres schrijfacties buffert en de bestanden op schijf alleen consistent zijn bij een checkpoint. Herstel werkt meestal wel, maar soms gaan de laatste transacties stilletjes verloren; dit is het slechtst denkbare scenario voor een back-up.

Maak in plaats daarvan een dump. pg_dumpall draait binnen de container en produceert 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 het vertrouwt. Verplaats deze dumps vervolgens volgens een schema vanaf de server, waarvoor versleutelde offsite back-ups met restic bedoeld is. Een geplande dump die stilletjes faalt, is niet te onderscheiden van helemaal geen back-up; laat de cron- of systemd-taak daarom een melding naar uw telefoon sturen wanneer deze met een exitcode ongelijk aan nul eindigt. 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; dit zijn gewone bestanden, dus een eenvoudige kopie volstaat.

Updaten zonder gegevensverlies

Supabase zet image-versies vast in docker-compose.yml, waardoor er niets verandert totdat u dat zelf besluit. Het vastzetten van versies is aan te raden voor elke stack die u handmatig opbouwt. Daarom zet een zelfgehoste RustDesk relay zijn twee server-images vast in plaats van een variabele tag te volgen: een upgrade moet iets zijn waar u voor kiest op een moment dat u daar tijd voor heeft. Maak elke keer eerst een dump.

docker compose pull
sh run.sh recreate

recreate stopt de stack en start deze opnieuw met de nieuwe images. Uw gegevens blijven behouden omdat deze in de bind mounts op de host staan en niet in de containers zelf. Lees CHANGELOG.md in de repository door voor een grote versie-overstap, aangezien een grote Postgres-upgrade een aparte stap is die recreate niet uitvoert. Postgres 17 is de standaard sinds juni 2026. Een bestaande installatie op Postgres 15 migreert hiernaartoe met sudo bash utils/upgrade-pg17.sh, dat pg_upgrade gebruikt om de gegevens in-place te migreren en bij elke grote stap om bevestiging vraagt. Hiervoor moeten alle containers draaien en is er vrije schijfruimte nodig van ten minste tweemaal de grootte van uw database plus 5 GB. Supabase raadt nog steeds aan om zelf een onafhankelijke back-up te maken voordat u begint.

Om wijzigingen in het Compose-bestand zelf door te voeren, voert u sh update.sh uit vanuit de projectmap, of sh update.sh --dry-run om eerst te zien wat er zou veranderen. Dit voegt de upstream-bestanden samen met uw eigen aanpassingen, overschrijft nooit bestaande waarden in .env (het voegt alleen nieuwe keys toe) en archiveert uw configuratie onder backups/ voordat deze wordt weggeschreven, al geldt dit niet voor de database. Een installatie zonder .supabase-version-bestand heeft --from en de release waarmee het begon nodig. Dit is met name van belang voor de release van augustus 2026, die de kong-service verving door api-gw en een docker-compose.yml-update vereist. De handmatige route werkt ook nog steeds: kloon de upstream-repository opnieuw en kopieer de docker-map over uw project, waarbij u erop let dat u .env niet overschrijft.

De volledige reset, die alles inclusief de database verwijdert en .env terugzet naar de standaardwaarden (de oude wordt bewaard als .env.old), is een apart script dat om bevestiging vraagt:

sh reset.sh

FAQ

Waarom retourneren mijn API-aanroepen "Invalid authentication credentials"?

Die specifieke melding is afkomstig van Kong 2.8, de gateway in de stack bij installaties van vóór maart 2026. De standaard Envoy-gateway en de huidige Kong-override, die op Kong 3.9 draait, rapporteren hetzelfde probleem als een eenvoudige 401 Unauthorized. Hoe dan ook, de sleutel die u heeft verzonden komt niet overeen met de ANON_KEY of SERVICE_ROLE_KEY in .env, of deze sleutels zijn niet ondertekend met de JWT_SECRET die momenteel in .env staat. Op een stack die nog nooit gegevens heeft bevat, genereert u alle drie opnieuw met sh utils/generate-keys.sh --update-env. Op een actieve stack voert u het script uit zonder de flag en antwoordt u nee, zodat het alleen nieuwe waarden afdrukt. Kopieer vervolgens JWT_SECRET, ANON_KEY en SERVICE_ROLE_KEY handmatig naar .env en laat POSTGRES_PASSWORD ongewijzigd. Voer daarna sh run.sh recreate uit zodat de services de nieuwe waarden inlezen.

Kan ik zelf-gehoste Supabase draaien op een 2 GB VPS?

Niet betrouwbaar. De officiële documentatie voor zelf-hosting van Supabase vermeldt 4 GB RAM als minimum per oktober 2026. De stack verbruikte in juli 2026 in ruststand bijna 3 GB, in een meting inclusief de logging-services, die nu optioneel zijn. Een machine met 2 GB verliest containers door de out of memory killer en u ziet exitcode 137 in docker compose ps. Gebruik 8 GB voor productie en beschouw 4 GB als de ondergrens voor individuele ontwikkeling.

Bevat zelf-gehoste Supabase edge functions?

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

Hoe maak ik rechtstreeks 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 bevestigingsmails 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 die twee waarden, waardoor het adres wordt verzonden dat is geconfigureerd. Stel beide in op uw werkelijke publieke URL en maak de stack opnieuw aan.

Was dit artikel nuttig?