Jak samodzielnie hostować Supabase na VPS przez Docker
Dowiedz się, jak uruchomić stos Supabase na własnym serwerze. Sprawdź, które sekrety wymienić w pliku .env, jak zarządzać czternastoma kontenerami i zapewnić bezpieczeństwo danych.
Co budujesz
Self-hosting Supabase oznacza uruchomienie oficjalnego stosu Docker Compose na własnym serwerze: Postgres, interfejsu REST API, usługi uwierzytelniania, magazynu plików, websocketów czasu rzeczywistego oraz panelu Studio. Klonujesz jedno repozytorium, edytujesz jeden plik .env i uruchamiasz około czternaście kontenerów, które wspólnie działają jak projekt Supabase pod Twoją kontrolą.
Instalacja jest krótka. Elementem, który często sprawia problemy, jest plik .env. Jest on dostarczany z demonstracyjnymi sekretami, które są publicznie dostępne w repozytorium, a stos uruchomiony z takimi ustawieniami domyślnymi jest otwarty dla każdego, kto go znajdzie. Ten przewodnik omawia sekrety, które należy wymienić, przeznaczenie każdej usługi, rzeczywiste zapotrzebowanie stosu na pamięć oraz sposób aktualizacji bez usuwania bazy danych.
Jeśli Compose jest dla Ciebie nowością, najpierw przeczytaj Podstawy Docker Compose na VPS. Wszystkie poniższe instrukcje zakładają, że docker compose version wyświetla już wersję oprogramowania.
Co faktycznie zawiera stos
Supabase nie jest pojedynczym programem. Plik Compose uruchamia zestaw oddzielnych usług w jednej sieci, a zrozumienie ich roli zmienia ścianę nazw kontenerów w coś, co można debugować.
dbto PostgreSQL z załadowanymi rozszerzeniami Supabase. Każda inna usługa komunikuje się z nim. Jeśli ten kontener jest w stanie niezdrowym, wszystko inne również przestaje działać.kongto brama API. Nasłuchuje na porcie 8000 i kieruje ruch/rest/v1/,/auth/v1/oraz/storage/v1/do właściwego backendu. Jest to jedyny kontener, który należy wystawiać na zewnątrz.restto PostgREST. Odczytuje schemat Postgres i udostępnia go jako REST API, dzięki czemu nowa tabela staje się nowym punktem końcowym bez pisania kodu.authto GoTrue. Wydaje tokeny JSON web tokens (JWT), które identyfikują użytkowników.storageorazimgproxyobsługują przesyłanie plików i zmianę rozmiaru obrazów.realtimeprzesyła zmiany w bazie danych przez websockets.studioorazmetato pulpit nawigacyjny i obsługujące go API administracyjne.analytics(Logflare) orazvectorzbierają logi, asupavisorto puler połączeń Postgres.
Ta lista wyjaśnia, dlaczego poniższe wymagania zasobowe są tak wysokie. Nie uruchamiasz tylko bazy danych. Uruchamiasz bazę danych wraz z tuzinem usług pomocniczych.
Wymagania sprzętowe: zaplanuj 8 GB pamięci RAM
W stanie spoczynku, przy świeżej instalacji (stan na lipiec 2026), stos technologiczny zajmuje około 2,5 do 3 GB pamięci rezydentnej, jeszcze przed dodaniem własnych danych lub rozpoczęciem obsługi ruchu. Usługa analityczna oraz proces Node.js środowiska Studio to dwa największe pojedyncze źródła zużycia zasobów. Serwer z 2 GB pamięci RAM uruchomi kontenery, po czym jeden z nich zostanie zamknięty przez mechanizm OOM (Out of Memory) jądra systemu, zazwyczaj analytics lub db. Objawem jest kontener, który utknął w pętli restartów z kodem wyjścia 137.
Dla każdego środowiska produkcyjnego należy zapewnić 8 GB pamięci RAM oraz 4 vCPU. 4 GB wystarczą do pracy w pojedynczej instancji deweloperskiej, pod warunkiem akceptacji spadku wydajności przy jednoczesnym wykonywaniu złożonych zapytań i pracy w środowisku Studio. Istotna jest również przestrzeń dyskowa, ponieważ Postgres, wolumeny danych oraz logi znajdują się w katalogu projektu. Należy zacząć od 40 GB i monitorować zużycie. Liczenie usług przed wyborem planu to dobry nawyk przy samodzielnym hostowaniu, ponieważ PhotoPrism i Immich mają realne wymagania minimalne dotyczące pamięci RAM znacznie wyższe, niż sugerują ich strony szybkiego startu.
Instalacja: klonowanie oficjalnego repozytorium
Zalecana ścieżka polega na skopiowaniu katalogu docker z głównego repozytorium do własnego katalogu projektu. Ta separacja jest istotna, ponieważ zapobiega nadpisaniu pliku .env podczas późniejszego polecenia git pull.
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 pullPolecenie docker compose pull pobiera kilka gigabajtów obrazów. Proces powinien zakończyć się oznaczeniem każdej usługi jako Pulled. Błąd manifest unknown w tym miejscu oznacza, że przypięty tag obrazu został usunięty w źródle. Rozwiązaniem jest pobranie nowszej kopii repozytorium, a nie ręczna edycja tagów.
Sekrety wymagające zmiany przed pierwszym uruchomieniem
Należy wykonać te czynności przed uruchomieniem stosu, a nie po nim. Wiele z tych wartości jest zapisywanych w danych podczas pierwszego rozruchu, więc ich późniejsza zmiana wymaga zresetowania bazy danych.
Repozytorium zawiera generator, który tworzy każdą wartość poprawnie, w tym dwa klucze API, które muszą zostać podpisane nowym sekretem JWT.
sh utils/generate-keys.sh --update-envSkrypt ten zapisuje nowe wartości dla JWT_SECRET, ANON_KEY, SERVICE_ROLE_KEY, SECRET_KEY_BASE, REALTIME_DB_ENC_KEY, VAULT_ENC_KEY, PG_META_CRYPTO_KEY oraz tokenów Logflare w pliku .env. Wymaga on narzędzia openssl, które jest dostępne w każdym standardowym obrazie Ubuntu.
Dwie wartości, których skrypt nie ustawia i które należy edytować ręcznie w .env:
POSTGRES_PASSWORD. Należy używać wyłącznie liter i cyfr. Znaki interpunkcyjne w tym miejscu powodują błędy w ciągach połączeniowych, które są budowane przez łączenie ciągów znaków przez kilka usług. Awaria wygląda wtedy jak błąd uwierzytelniania, a nie błąd parsowania, co kieruje uwagę administratora w niewłaściwe miejsce.DASHBOARD_USERNAMEorazDASHBOARD_PASSWORD. Są to dane uwierzytelniające do podstawowego logowania w Studio. Domyślne hasło dostarczone w konfiguracji to dosłowniethis_password_is_insecure_and_should_be_updated.
Należy zrozumieć, dlaczego ANON_KEY oraz SERVICE_ROLE_KEY nie mogą zostać wymyślone samodzielnie. Oba są tokenami JWT podpisanymi za pomocą JWT_SECRET. Brama weryfikuje ten podpis przy każdym żądaniu, więc klucz niezgodny z sekretem jest odrzucany z błędem {"message":"Invalid authentication credentials"}. Jest to najczęstsza przyczyna awarii w instalacjach self-hosted: operator zmienił JWT_SECRET, ale zachował klucze demonstracyjne. Wszystkie trzy wartości należy generować zawsze razem.
SERVICE_ROLE_KEY należy traktować jak hasło użytkownika root. Omija ono całkowicie zabezpieczenia na poziomie wiersza (row level security). Powinno znajdować się wyłącznie w kodzie po stronie serwera.
Wartości SITE_URL oraz API_EXTERNAL_URL należy ustawić na adres, pod którym użytkownicy będą faktycznie uzyskiwać dostęp do usługi, na przykład https://supabase.example.com. Usługa Auth buduje linki do potwierdzenia adresu e-mail oraz callbacki OAuth na podstawie tych wartości, więc pozostawienie ich jako http://localhost:8000 spowoduje, że każdy użytkownik zostanie przekierowany na własną maszynę lokalną.
Następnie należy zweryfikować konfigurację:
sh run.sh secretsUruchomienie i weryfikacja poprawności działania
sh run.sh start
docker compose psrun.sh start opakowuje docker compose up -d --wait, dlatego polecenie nie kończy działania, dopóki testy poprawności nie zostaną zaliczone. Każda usługa powinna wykazywać stan running (healthy) lub running. Pierwsze uruchomienie trwa od 2 do 4 minut, ponieważ Postgres wykonuje skrypty inicjalizacyjne, zanim jakiekolwiek inne połączenie będzie możliwe.
Jeśli kontener restartuje się w pętli, należy odczytać jego dzienniki według nazwy usługi:
docker compose logs db
docker compose logs authStudio jest dostępne na porcie 8000 i wyświetli monit o podanie nazwy użytkownika oraz hasła do panelu, które zostały skonfigurowane wcześniej.
Nie wystawiaj portu 8000 na publiczny dostęp do Internetu
Kong na porcie 8000 korzysta z niezaszyfrowanego protokołu HTTP. Każdy klucz API oraz hasło użytkownika są przesyłane przez sieć otwartym tekstem, a dane uwierzytelniające Studio opierają się na mechanizmie Basic Authentication, który stanowi jedynie kodowanie base64, a nie szyfrowanie.
Umieść przed nim reverse proxy, wykonaj tam terminację TLS (transport layer security) i powiąż Kong z adresem loopback, aby uniemożliwić dostęp z zewnątrz. W docker-compose.yml mapowanie portu kong zmienia się na 127.0.0.1:8000:8000, a proxy przekazuje ruch do tego miejsca. Sekcja Traefik przed kilkoma aplikacjami Compose opisuje kwestie związane z certyfikatami.
Zamknij również pozostałe porty na firewallu, ponieważ Docker publikuje porty poprzez automatyczne dopisywanie reguł iptables, których standardowa konfiguracja ufw nie uwzględnia. Ta pułapka została wyjaśniona w artykule dlaczego kontenery Docker ignorują reguły ufw.
Tworzenie kopii zapasowej bazy danych, a nie katalogu
Dane Postgres znajdują się w punkcie montowania typu bind w ./volumes/db/data. Kopiowanie tego katalogu podczas pracy kontenera prowadzi do powstania niespójnej kopii, ponieważ Postgres buforuje operacje zapisu, a pliki na dysku są spójne tylko w punktach kontrolnych (checkpoint). Przywrócenie takiej kopii zazwyczaj kończy się powodzeniem, ale czasem prowadzi do cichej utraty ostatnich transakcji, co jest najgorszym możliwym scenariuszem awarii kopii zapasowej.
Zamiast tego należy wykonać zrzut bazy. Polecenie pg_dumpall uruchamia się wewnątrz kontenera i tworzy spójną migawkę:
docker exec -t supabase-db pg_dumpall -U postgres > supabase-$(date +%F).sqlPrzed uznaniem kopii za wiarygodną należy sprawdzić, czy plik nie jest pusty. Następnie należy regularnie przesyłać te zrzuty poza serwer, do czego służą szyfrowane kopie zapasowe poza lokalizacją z użyciem restic. W tym samym czasie należy wykonać kopię zapasową .env. Utrata JWT_SECRET oznacza, że każdy wydany token stanie się nieważny, a każdy przechowywany zaszyfrowany sekret będzie niemożliwy do odczytania.
Przesłane pliki znajdują się w ./volumes/storage; są to zwykłe pliki, więc ich proste kopiowanie jest wystarczające.
Aktualizacja bez utraty danych
Supabase przypina wersje obrazów w docker-compose.yml, więc nic się nie zmienia, dopóki nie zostanie wykonana aktualizacja. Warto stosować przypinanie wersji w każdym stosie składanym ręcznie. Dlatego samodzielny relay RustDesk przypina dwa obrazy serwerowe zamiast śledzić zmienny tag. Aktualizację należy przeprowadzać wyłącznie wtedy, gdy można poświęcić na nią czas. Za każdym razem najpierw wykonaj zrzut.
docker compose pull
sh run.sh recreaterecreate zatrzymuje stos i uruchamia go ponownie na nowych obrazach. Dane pozostają nienaruszone, ponieważ znajdują się w dowiązaniach (bind mounts) na hoście, a nie wewnątrz kontenerów. Przed wykonaniem aktualizacji do nowej wersji głównej należy zapoznać się z CHANGELOG.md w repozytorium, ponieważ główne aktualizacje Postgres nie są automatyczne i wymagają wykonania zrzutu oraz przywrócenia bazy danych.
Aby uwzględnić zmiany w samym pliku Compose, należy ponownie sklonować repozytorium nadrzędne i skopiować jego katalog docker do projektu, uważając, aby nie nadpisać .env.
Pełny reset, który usuwa wszystko, w tym bazę danych, jest realizowany za pomocą oddzielnego skryptu i wymaga potwierdzenia:
sh reset.shFAQ
Dlaczego wywołania API zwracają błąd "Invalid authentication credentials"?
ANON_KEY lub SERVICE_ROLE_KEY nie zostały podpisane przy użyciu JWT_SECRET znajdującego się obecnie w .env. Brama weryfikuje podpis każdego żądania i odrzuca te, które nie pasują. Wygeneruj ponownie wszystkie trzy elementy za pomocą sh utils/generate-keys.sh --update-env, a następnie uruchom sh run.sh recreate, aby usługi wczytały nowe wartości.
Czy można uruchomić samodzielnie hostowany Supabase na VPS z 2 GB pamięci RAM?
Nie w sposób stabilny. Według stanu na lipiec 2026 r. stos usług w stanie spoczynku zajmuje blisko 3 GB, ponieważ składa się z około czternastu komponentów. Na maszynie z 2 GB pamięci RAM kontenery są zamykane przez mechanizm out of memory killer, co skutkuje kodem wyjścia 137 w docker compose ps. W środowisku produkcyjnym należy zapewnić 8 GB pamięci RAM, a 4 GB traktować jako absolutne minimum dla prac deweloperskich.
Czy samodzielnie hostowany Supabase zawiera edge functions?
Tak. Plik Compose zawiera środowisko uruchomieniowe dla funkcji oparte na Deno i obsługuje wszystko, co zostanie umieszczone w ./volumes/functions. Rozwiązanie to nie obejmuje globalnej sieci wdrożeniowej znanej z platformy chmurowej, więc funkcje działają wyłącznie na jednym serwerze, w jednej lokalizacji.
Jak połączyć się bezpośrednio z bazą danych Postgres?
Użyj docker exec -it supabase-db psql -U postgres, aby uzyskać interaktywną powłokę bezpośrednio na serwerze. W przypadku klienta zewnętrznego należy połączyć się przez Supavisor na porcie 5432, używając użytkownika postgres.<POOLER_TENANT_ID> oraz POSTGRES_PASSWORD. Nie należy wystawiać tego portu na dostęp z Internetu. Dostęp powinien odbywać się przez VPN lub tunel SSH.
Dlaczego linki w e-mailach potwierdzających autoryzację prowadzą do localhost?
SITE_URL oraz API_EXTERNAL_URL w .env pozostały ustawione na wartości domyślne. Usługa autoryzacji tworzy każdy link potwierdzający i resetujący hasło na podstawie tych dwóch wartości, więc wysyła adres, który został jej wskazany. Ustaw obie zmienne na właściwy publiczny adres URL i zrestartuj stos.