Supabase selbst hosten: Docker auf einem VPS
Betreiben Sie den offiziellen Supabase-Docker-Stack auf Ihrem VPS. Ersetzen Sie Demo-Secrets, verstehen Sie alle elf Dienste und planen Sie RAM, Backups und Updates.
Was Sie aufbauen
Self-Hosting von Supabase bedeutet, den offiziellen Docker-Compose-Stack auf Ihrem eigenen Server zu betreiben: Postgres, eine REST-API davor, einen Authentifizierungsdienst, Dateispeicher, Realtime-WebSockets und das Studio-Dashboard. Sie klonen ein Repository, bearbeiten eine .env-Datei und starten elf Container, die zusammen wie ein Supabase-Projekt funktionieren, das Sie selbst kontrollieren.
Die Installation ist kurz. Probleme entstehen meist in der .env-Datei. Sie enthält Demo-Secrets, die im Repository veröffentlicht sind. Ein Stack, der mit diesen Standardwerten gestartet wird, ist für jeden zugänglich, der sie findet. Dieser Leitfaden behandelt die Secrets, die Sie ersetzen müssen, die Aufgaben der einzelnen Dienste, den tatsächlichen Speicherbedarf des Stacks und die Aktualisierung, ohne Ihre Datenbank zu löschen.
Wenn Compose für Sie neu ist, lesen Sie zuerst Docker-Compose-Grundlagen auf einem VPS. Im Folgenden wird vorausgesetzt, dass docker compose version bereits eine Version ausgibt.
Was der Stack tatsächlich enthält
Supabase ist nicht nur ein einzelnes Programm. Die Compose-Datei startet eine Gruppe separater Dienste in einem Netzwerk. Wenn Sie die Zuständigkeit der einzelnen Dienste kennen, lässt sich eine lange Liste von Containern gezielt untersuchen.
dbist PostgreSQL mit geladenen Supabase-Erweiterungen. Jeder andere Dienst kommuniziert damit. Wenn dieser Container nicht gesund ist, schlagen auch alle anderen Dienste fehl.api-gwist das API-Gateway. Seit dem Release von August 2026 verwendet es Envoy. Zuvor war es ein Dienst namenskong. Kong ist weiterhin als optionale Überschreibung verfügbar. Der Dienst lauscht auf Port 8000 und leitet/rest/v1/,/auth/v1/und/storage/v1/an das jeweils zuständige Backend weiter. Dies ist der einzige Container, den Sie jemals nach außen veröffentlichen sollten.restist PostgREST. Der Dienst liest Ihr Postgres-Schema und stellt es als REST-API bereit. Eine neue Tabelle wird dadurch ohne zusätzlichen Code zu einem neuen Endpunkt.authist GoTrue. Der Dienst stellt die JSON Web Tokens (JWT) aus, die Ihre Benutzer identifizieren.storageundimgproxyverarbeiten Datei-Uploads und die Größenänderung von Bildern.realtimeüberträgt Datenbankänderungen über WebSockets.studioundmetasind das Dashboard und die dahinterliegende Admin-API.functionsist die Edge-Laufzeit auf Basis von Deno.supavisorist der Pooler für PostgreSQL-Verbindungen.
Protokolle und Analytics (Logflare und vector) wurden im Juni 2026 aus der standardmäßigen Compose-Datei entfernt. Sie befinden sich jetzt in einer optionalen Override-Datei, die Sie mit sh run.sh config add logs aktivieren. Dabei wird dieselbe Schichtung von Compose-Dateien verwendet, die in einen Stack aus mehreren Compose-Dateien starten beschrieben ist.
Diese Liste erklärt, warum die folgenden Ressourcenwerte so ausfallen. Sie betreiben nicht nur eine Datenbank. Sie betreiben eine Datenbank plus zehn unterstützende Dienste.
Dimensionierung: Planen Sie 8 GB RAM ein
Der Stack belegte bei einer frischen Installation im Juli 2026 im Leerlauf etwa 2.5 bis 3 GB Resident Memory, noch ohne eigene Daten oder Netzwerkverkehr. In dieser Messung waren der Analytics-Dienst und der Studio-Node.js-Prozess die beiden größten Einzelverbraucher. Analytics gehört nicht mehr zum Standard-Stack. Speicher kostet dieser Dienst daher nur, wenn Sie die Logs-Überschreibung aktivieren. Ein Server mit 2 GB startet die Container und verliert anschließend einen davon an den OOM-Killer des Kernels. Das äußert sich durch einen Container, der mit Exit-Code 137 ständig neu gestartet wird. Die Self-Hosting-Dokumentation von Supabase nennt bei einer Prüfung im Oktober 2026 mindestens 4 GB RAM, 2 CPU-Kerne und 40 GB SSD. Als Empfehlung werden 8 GB, 4 Kerne und 80 GB genannt.
Geben Sie dem System für alles, worauf Sie angewiesen sind, 8 GB RAM und 4 vCPU. 4 GB reichen für eine allein genutzte Entwicklungsinstanz aus, wenn Sie akzeptieren, dass eine umfangreiche Abfrage und eine Studio-Sitzung gleichzeitig langsam werden. Auch der Speicherplatz ist wichtig, weil Postgres, das Storage-Volume und bei aktivierten Logs auch die Logdaten unter dem Projektverzeichnis liegen. Beginnen Sie mit 40 GB und überwachen Sie die Auslastung. Die Dienste zu zählen, bevor Sie einen Plan auswählen, ist für alles, was Sie selbst hosten, eine sinnvolle Gewohnheit, denn auch PhotoPrism und Immich haben konkrete RAM-Mindestanforderungen: Die Quickstart-Seite von Immich fordert laut Prüfung im Oktober 2026 mindestens 6 GB RAM und 2 CPU-Kerne. Wenn das für Ihr Projekt noch mehr Serverleistung ist als nötig, vergleichen Sie dies mit dem tatsächlichen Umfang des kostenlosen Cloud-Plans von Supabase, denn eine kleine Anwendung, die unter 500 MB bleibt und eine Woche lang nie im Leerlauf läuft, kann dort kostenlos betrieben werden.
Installieren: Offizielles Repository klonen
Der unterstützte Weg kopiert das Verzeichnis docker aus dem Haupt-Repository in ein eigenes Projektverzeichnis. Diese Trennung ist wichtig, weil ein späteres git pull dadurch Ihr .env nicht überschreiben kann.
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 lädt mehrere Gigabyte an Images herunter. Am Ende sollte jeder Dienst mit dem Status Pulled gekennzeichnet sein. Ein manifest unknown-Fehler bedeutet hier, dass der festgelegte Image-Tag im Upstream-Repository entfernt wurde. Die Lösung besteht darin, eine neuere Kopie des Repositorys abzurufen, statt die Tags manuell zu bearbeiten.
Die Geheimnisse, die Sie vor dem ersten Start ändern müssen
Führen Sie diesen Schritt vor dem Start des Stacks aus, nicht danach. Mehrere dieser Werte werden beim ersten Boot in Daten geschrieben. Wenn Sie sie später ändern, müssen Sie die Datenbank zurücksetzen.
Das Repository enthält einen Generator, der alle Werte korrekt erzeugt. Dazu gehören auch die beiden API-Schlüssel, die mit Ihrem neuen JWT-Secret signiert werden müssen.
sh utils/generate-keys.sh --update-envDas Script schreibt neue Werte für JWT_SECRET, ANON_KEY, SERVICE_ROLE_KEY, SECRET_KEY_BASE, REALTIME_DB_ENC_KEY, VAULT_ENC_KEY, PG_META_CRYPTO_KEY, die Logflare-Tokens, die S3-Protokollschlüssel und MINIO_ROOT_PASSWORD in .env. Seit Oktober 2026 schreibt es außerdem zufällige Werte für POSTGRES_PASSWORD und DASHBOARD_PASSWORD. Es benötigt openssl, das in jedem normalen Ubuntu-Image vorhanden ist. Da das Script jetzt auch das Datenbankpasswort neu schreibt, dürfen Sie es mit --update-env nur vor dem ersten Start ausführen, niemals gegen einen Stack, der bereits Daten enthält.
Zwei Werte in .env sollten Sie trotzdem manuell prüfen:
POSTGRES_PASSWORD. Das Script füllt den Wert mit zufälligen Hexadezimalzeichen, was sicher ist. Wenn Sie einen eigenen Wert verwenden, dürfen Sie nur Buchstaben und Ziffern verwenden. Satzzeichen führen hier zu fehlerhaften Verbindungszeichenfolgen, die mehrere Dienste durch das Zusammenfügen von Zeichenfolgen erzeugen. Der Fehler sieht dann wie ein Authentifizierungsfehler und nicht wie ein Parsing-Fehler aus. Dadurch suchen Sie möglicherweise an der falschen Stelle nach der Ursache.DASHBOARD_USERNAMEundDASHBOARD_PASSWORD. Dies sind die Zugangsdaten für die einfache Authentifizierung von Studio. Das Script setzt ein zufälliges Passwort, lässt den Benutzernamen aber auf dem Standardwertsupabase. Das mitgelieferte Standardpasswort lautet wörtlichthis_password_is_insecure_and_should_be_updated. Wenn Sie das Script übersprungen haben, ändern Sie es jetzt.
Beachten Sie, warum ANON_KEY und SERVICE_ROLE_KEY nicht frei erfunden werden können. Beide sind JWTs, die mit JWT_SECRET signiert werden. Das Gateway akzeptiert nur die exakten Schlüssel in .env. Die dahinterliegenden Dienste prüfen die Signatur anhand von JWT_SECRET. Ein nicht übereinstimmender Schlüssel wird daher mit HTTP 401 abgewiesen. Das Standard-Envoy-Gateway antwortet mit 401 Unauthorized. Das gilt auch für die aktuelle Kong-Überschreibung, die Kong 3.9 ausführt und {"message":"Unauthorized"} zurückgibt. Installationen aus der Zeit vor März 2026 verwendeten Kong 2.8, das mit {"message":"Invalid authentication credentials"} antwortet. Dies ist der häufigste Fehler beim Self-Hosting: Der Betreiber ändert JWT_SECRET, behält aber die Demo-Schlüssel bei. Erzeugen Sie immer alle drei gemeinsam.
Behandeln Sie SERVICE_ROLE_KEY wie ein root-Passwort. Der Wert umgeht Row-Level-Security vollständig. Er gehört in serverseitigen Code und nirgendwo sonst.
Setzen Sie SITE_URL auf die Adresse Ihrer Anwendung und API_EXTERNAL_URL auf die öffentliche Adresse der API mit /auth/v1 am Ende, zum Beispiel https://supabase.example.com/auth/v1. Setzen Sie SUPABASE_PUBLIC_URL auf dieselbe öffentliche Adresse ohne den Pfad. Auth erstellt daraus die Links für E-Mail-Bestätigungen und OAuth-Callbacks. Wenn Sie die localhost-Standardwerte beibehalten, werden alle Benutzer auf ihre jeweils eigene Maschine weitergeleitet.
Prüfen Sie anschließend Ihre Einstellungen:
sh run.sh secretsStarten und den fehlerfreien Betrieb bestätigen
sh run.sh start
docker compose psrun.sh start startet docker compose up -d --wait und kehrt erst zurück, wenn die Healthchecks erfolgreich sind. Jeder Dienst sollte running (healthy) oder running anzeigen. Der erste Start dauert zwei bis vier Minuten, weil Postgres seine Initialisierungsskripte ausführt, bevor eine andere Komponente eine Verbindung herstellen kann.
Wenn ein Container neu gestartet wird, lesen Sie seine Logs anhand des Servicenamens:
docker compose logs db
docker compose logs authStudio ist anschließend über Port 8000 erreichbar und fordert den von Ihnen festgelegten Benutzernamen und das Passwort für das Dashboard an.
Port 8000 nicht im öffentlichen Internet bereitstellen
Das Gateway auf Port 8000 verwendet unverschlüsseltes HTTP. Jeder API-Schlüssel und jedes Benutzerpasswort wird im Klartext über das Netzwerk übertragen. Auch die Zugangsdaten für Studio verwenden Basic Authentication, also Base64-Kodierung statt Verschlüsselung.
Schalten Sie einen Reverse Proxy davor, terminieren Sie dort TLS (Transport Layer Security), und binden Sie das Gateway an die Loopback-Adresse, damit kein anderer Prozess darauf zugreifen kann. In docker-compose.yml wird die Portzeile unter dem Dienst api-gw zu 127.0.0.1:${API_GW_HTTP_PORT:-${KONG_HTTP_PORT:-8000}}:8000/tcp, und der Proxy leitet Anfragen dorthin weiter. Bei einer Installation aus der Zeit vor der Umstellung auf Envoy im August 2026 befindet sich dieselbe Portzeile stattdessen unter einem Dienst namens kong. Traefik vor mehreren Compose-Anwendungen behandelt die Zertifikatsseite. Dieser Proxy übernimmt letztlich auch für alle anderen Dienste auf dem Server die externe Anbindung, von diesem Stack bis zu etwas so Ungewöhnlichem wie einer als Videothek der 90er neu aufgebauten Jellyfin-Bibliothek. Jeder dieser Dienste benötigt einen Hostnamen statt eines weiteren offenen Ports. Wenn ein Dashboard ausschließlich für Sie bestimmt ist, verzichten Sie auf den Proxy und greifen Sie stattdessen über einen SSH-Tunnel auf den Loopback-Port zu. Nach demselben Prinzip hält self-hosted open-kritt seine Scanoberfläche vollständig aus dem öffentlichen Internet heraus.
Sperren Sie außerdem alle übrigen Ports in der Firewall, da Docker Ports veröffentlicht, indem es eigene iptables-Regeln schreibt, die eine einfache ufw-Konfiguration nicht berücksichtigt. Diese Falle wird in war Docker-Container Ihre ufw-Regeln ignorieren erklärt.
Datenbank sichern, nicht das Verzeichnis
Die Postgres-Daten liegen in einem Bind-Mount unter ./volumes/db/data. Wenn Sie dieses Verzeichnis kopieren, während der Container läuft, erhalten Sie eine inkonsistente Kopie. Postgres puffert Schreibvorgänge, und die Dateien auf dem Datenträger sind erst nach einem Checkpoint konsistent. Eine Wiederherstellung funktioniert in der Regel. Dabei können jedoch unbemerkt die letzten Transaktionen verloren gehen. Das ist der schlimmstmögliche Fehlerfall bei einem Backup.
Erstellen Sie stattdessen einen Dump. pg_dumpall wird im Container ausgeführt und erzeugt einen konsistenten Snapshot:
docker exec -t supabase-db pg_dumpall -U postgres > supabase-$(date +%F).sqlPrüfen Sie, dass die Datei nicht leer ist, bevor Sie ihr vertrauen. Übertragen Sie diese Dumps anschließend regelmäßig vom Server auf ein anderes System. Dafür sind verschlüsselte Offsite-Backups mit restic vorgesehen. Ein geplanter Dump, der unbemerkt fehlschlägt, ist von einem vollständig fehlenden Backup nicht zu unterscheiden. Lassen Sie den Cron- oder systemd-Job deshalb eine Warnung an Ihr Telefon senden, wenn er mit einem Exit-Code ungleich null beendet wird. Sichern Sie gleichzeitig Ihr .env. Wenn JWT_SECRET verloren geht, wird jedes ausgestellte Token ungültig und jedes gespeicherte verschlüsselte Geheimnis unlesbar.
Hochgeladene Dateien liegen unter ./volumes/storage. Dabei handelt es sich um gewöhnliche Dateien, daher reicht eine einfache Kopie aus.
Aktualisieren ohne Datenverlust
Supabase legt die Image-Versionen in docker-compose.yml fest. Daher wird nichts geändert, bis Sie es selbst aktualisieren. Diese Versionsfixierung ist für jeden manuell zusammengestellten Stack sinnvoll. Deshalb fixiert beispielsweise ein selbst gehostetes RustDesk-Relay seine beiden Server-Images, statt einem sich ändernden Tag zu folgen. Ein Upgrade sollte nur zu einem Zeitpunkt erfolgen, den Sie bewusst dafür einplanen. Erstellen Sie vorher immer einen Dump.
docker compose pull
sh run.sh recreaterecreate stoppt den Stack und startet ihn mit den neuen Images erneut. Ihre Daten bleiben erhalten, weil sie in den Bind-Mounts auf dem Host liegen und nicht innerhalb der Container. Lesen Sie vor einem Sprung auf eine neue Hauptversion CHANGELOG.md im Repository. Ein Major-Upgrade von Postgres ist ein separater Schritt, den recreate nicht ausführt. Postgres 17 ist seit Juni 2026 die Standardversion. Eine bestehende Installation mit Postgres 15 wird mit sudo bash utils/upgrade-pg17.sh darauf aktualisiert. Dabei wird pg_upgrade verwendet, um die Daten direkt zu migrieren, und Sie werden bei jedem Hauptschritt zur Bestätigung aufgefordert. Dafür müssen alle Container laufen. Außerdem benötigen Sie freien Speicherplatz von mindestens dem Doppelten der Datenbankgröße plus 5 GB. Supabase empfiehlt weiterhin, vor dem Start ein unabhängiges eigenes Backup zu erstellen.
Um Änderungen an der Compose-Datei zu übernehmen, führen Sie sh update.sh im Projektverzeichnis aus. Alternativ können Sie zuerst mit sh update.sh --dry-run anzeigen, was geändert würde. Der Befehl führt die Dateien aus dem Upstream-Repository mit Ihren eigenen Anpassungen zusammen. Vorhandene Werte in .env werden nicht überschrieben. Es werden nur neue Schlüssel angehängt. Vor dem Schreiben wird Ihre Konfiguration unter backups/ archiviert, die Datenbank jedoch nicht. Für eine Installation ohne Datei .supabase-version benötigen Sie --from und die Release-Version, mit der die Installation gestartet wurde. Das ist insbesondere für die Release-Version vom August 2026 wichtig. Sie ersetzt den Dienst kong durch api-gw und erfordert eine Aktualisierung von docker-compose.yml. Der manuelle Weg funktioniert ebenfalls: Klonen Sie das Upstream-Repository erneut und kopieren Sie dessen Verzeichnis docker in Ihr Projekt. Achten Sie dabei darauf, .env nicht zu überschreiben.
Das vollständige Zurücksetzen ist ein separates Script. Es löscht alles, einschließlich der Datenbank, und setzt .env auf die Standardwerte zurück. Die alte Version bleibt als .env.old erhalten. Das Script fordert eine Bestätigung an:
sh reset.shFAQ
Warum geben meine API-Aufrufe „Invalid authentication credentials“ zurück?
Diese genaue Meldung stammt von Kong 2.8, dem Gateway in Stacks mit Installationen vor März 2026. Das standardmäßige Envoy-Gateway und die aktuelle Kong-Überschreibung mit Kong 3.9 melden dasselbe Problem als einfachen 401-Fehler Unauthorized. In beiden Fällen stimmt der gesendete Schlüssel nicht mit ANON_KEY oder SERVICE_ROLE_KEY in .env überein, oder die Schlüssel wurden nicht mit dem aktuell in .env enthaltenen JWT_SECRET signiert. Wenn der Stack noch nie Daten enthalten hat, generieren Sie alle drei gemeinsam mit sh utils/generate-keys.sh --update-env neu. Führen Sie das Skript bei einem laufenden Stack ohne das Flag aus und antworten Sie mit Nein. Dadurch werden nur neue Werte ausgegeben. Kopieren Sie JWT_SECRET, ANON_KEY und SERVICE_ROLE_KEY manuell nach .env und lassen Sie POSTGRES_PASSWORD unverändert. Führen Sie anschließend sh run.sh recreate aus, damit die Dienste die neuen Werte einlesen.
Kann ich selbst gehostetes Supabase auf einem VPS mit 2 GB ausführen?
Nicht zuverlässig. Die Dokumentation von Supabase zum Self-Hosting nennt seit Oktober 2026 mindestens 4 GB RAM. Im Leerlauf belegte der Stack in einer Messung von Juli 2026 fast 3 GB. Diese Messung umfasste auch die Logging-Dienste, die inzwischen optional aktiviert werden. Auf einem System mit 2 GB beendet der Out-of-Memory-Killer Container, und in docker compose ps sehen Sie den Exit-Code 137. Verwenden Sie für den Produktivbetrieb 8 GB. Für die alleinige Entwicklung sollten 4 GB die Untergrenze sein.
Enthält selbst gehostetes Supabase Edge Functions?
Ja. Die Compose-Datei enthält die Deno-basierte Functions-Laufzeit. Sie verarbeitet alles, was Sie unter ./volumes/functions ablegen. Das globale Deployment-Netzwerk der gehosteten Plattform ist nicht enthalten. Ihre Functions laufen daher auf Ihrem einen Server an einem einzigen Standort.
Wie verbinde ich mich direkt mit der Postgres-Datenbank?
Verwenden Sie docker exec -it supabase-db psql -U postgres für eine interaktive Shell direkt auf dem Server. Für einen externen Client stellen Sie über Supavisor auf Port 5432 eine Verbindung mit dem Benutzer postgres.<POOLER_TENANT_ID> und Ihrem POSTGRES_PASSWORD her. Öffnen Sie diesen Port nicht für das Internet. Greifen Sie über ein VPN oder einen SSH-Tunnel darauf zu.
Warum verlinken meine Auth-Bestätigungs-E-Mails auf localhost?
SITE_URL und API_EXTERNAL_URL in .env standen noch auf ihren Standardwerten. Der Auth-Dienst erstellt alle Links zur Bestätigung und zum Zurücksetzen des Passworts aus diesen beiden Werten. Er versendet daher genau die Adresse, die dort konfiguriert ist. Setzen Sie beide Werte auf Ihre echte öffentliche URL und erstellen Sie den Stack neu.