SSD Nodes Learn Hosting plans →
Przewodniki Matt ConnorAutor: Matt Connor · Zaktualizowano 2026-10-23

Jak zainstalować Supabase na własnym VPS przez Docker

Instrukcja uruchomienia Supabase na serwerze VPS przy użyciu Docker Compose. Dowiedz się, które zmienne w pliku .env wymienić, jak zarządzać pamięcią RAM i aktualizować stos.

Co budujesz

Self-hosting Supabase oznacza uruchomienie oficjalnego stosu Docker Compose na własnym serwerze: Postgres, interfejs REST API, usługę uwierzytelniania, przechowywanie plików, realtime websockets oraz panel Studio. Klonujesz jedno repozytorium, edytujesz jeden plik .env i uruchamiasz jedenaście kontenerów, które razem działają jak projekt Supabase pod Twoją kontrolą.

Instalacja jest krótka. Elementem, który zawodzi, jest plik .env. Jest on dostarczany z przykładowymi sekretami, które są opublikowane 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 zastąpić, przeznaczenie każdej usługi, rzeczywiste zapotrzebowanie stosu na pamięć oraz sposób aktualizacji bez usuwania bazy danych.

Jeśli Docker Compose jest dla Ciebie nowością, przeczytaj najpierw podstawy Docker Compose na VPS. Wszystko poniżej zakłada, że docker compose version wyświetla już wersję.

Co faktycznie zawiera stos

Supabase nie jest pojedynczym programem. Plik Compose uruchamia zestaw oddzielnych usług w jednej sieci, a zrozumienie ich roli pozwala przekształcić listę nazw kontenerów w coś, co można debugować.

  • db to PostgreSQL z załadowanymi rozszerzeniami Supabase. Każda inna usługa komunikuje się z nim. Jeśli ten kontener jest w stanie nieprawidłowym, wszystko inne również przestaje działać.
  • api-gw to brama API. Od wydania z sierpnia 2026 roku działa na Envoy; wcześniej była to usługa o nazwie kong, a Kong jest nadal dostępny jako opcjonalne zastąpienie. 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 powinien być wystawiony na zewnątrz.
  • rest to PostgREST. Odczytuje schemat Postgres i udostępnia go jako REST API, dzięki czemu nowa tabela staje się nowym endpointem bez konieczności pisania kodu.
  • auth to GoTrue. Wydaje tokeny JSON web tokens (JWT), które identyfikują użytkowników.
  • storage oraz imgproxy obsługują przesyłanie plików i zmianę rozmiaru obrazów.
  • realtime przesyła zmiany w bazie danych przez websockets.
  • studio oraz meta to panel sterowania i obsługujące go API administracyjne.
  • functions to środowisko uruchomieniowe edge oparte na Deno, a supavisor to puler połączeń Postgres.

Logi i analityka (Logflare oraz vector) zostały usunięte z domyślnego pliku Compose w czerwcu 2026 roku. Obecnie stanowią opcjonalny plik nadpisujący, który włącza się za pomocą sh run.sh config add logs, zgodnie z zasadą warstwowania plików Compose opisaną w uruchamianie jednego stosu z kilku plików Compose.

Ta lista wyjaśnia, dlaczego poniższe wymagania zasobów są na takim poziomie. Nie uruchamiasz samej bazy danych. Uruchamiasz bazę danych oraz dziesięć usług pomocniczych.

Wymagania sprzętowe: zaplanuj 8 GB pamięci RAM

Stos technologiczny w świeżej instalacji na lipiec 2026 roku zużywał w stanie spoczynku około 2,5 do 3 GB pamięci rezydentnej, jeszcze przed dodaniem własnych danych lub wygenerowaniem ruchu. W tym pomiarze największymi konsumentami zasobów były usługa analityczna oraz proces Node.js środowiska Studio. Moduł analityczny nie jest już częścią domyślnego stosu, więc obciąża pamięć tylko w przypadku włączenia nadpisywania logó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. Objawia się to kontenerem w pętli restartów z kodem wyjścia 137. Dokumentacja self-hostingowa Supabase, zweryfikowana w październiku 2026 roku, określa minimum na 4 GB pamięci RAM, 2 rdzenie CPU oraz 40 GB przestrzeni SSD, natomiast zaleca 8 GB pamięci RAM, 4 rdzenie CPU oraz 80 GB przestrzeni dyskowej.

Dla każdego rozwiązania produkcyjnego należy zapewnić 8 GB pamięci RAM oraz 4 vCPU. 4 GB wystarczą do 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 przechowywania danych oraz logi (jeśli zostaną włączone) znajdują się w katalogu projektu. Należy rozpocząć od 40 GB i monitorować zużycie. Liczenie usług przed wyborem planu to dobra praktyka przy każdym rozwiązaniu self-hosted, ponieważ PhotoPrism i Immich również mają wysokie wymagania minimalne: strona szybkiego startu Immich, sprawdzona w październiku 2026 roku, wymaga co najmniej 6 GB pamięci RAM i 2 rdzeni CPU. Jeśli wymagania te przewyższają potrzeby projektu, warto porównać je z ofertą darmowego planu chmurowego Supabase, ponieważ niewielka aplikacja, która nie przekracza 500 MB i nie pozostaje nieaktywna przez tydzień, może być tam uruchomiona bez ponoszenia kosztów.

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 pull

Polecenie 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, które należy zmienić przed pierwszym uruchomieniem

Czynność tę należy wykonać 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 wiąże się z koniecznością 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-env

Skrypt 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, tokenów Logflare, kluczy protokołu S3 oraz MINIO_ROOT_PASSWORD w pliku .env. Od października 2026 roku zapisuje on również losowe wartości dla POSTGRES_PASSWORD oraz DASHBOARD_PASSWORD. Wymaga on openssl, który jest obecny w każdym standardowym obrazie Ubuntu. Ponieważ skrypt nadpisuje również hasło do bazy danych, należy go użyć z flagą --update-env wyłącznie przed pierwszym uruchomieniem; nigdy nie należy go uruchamiać na stosie, który zawiera już dane.

Dwie wartości warto sprawdzić ręcznie w .env:

  • POSTGRES_PASSWORD. Skrypt wypełnia to pole losowym ciągiem szesnastkowym, co jest bezpieczne. W przypadku zastąpienia go własną wartością, należy użyć wyłącznie liter i cyfr. Znaki interpunkcyjne w tym miejscu powodują błędy w ciągach połączeniowych, które kilka usług buduje poprzez łączenie ciągów znaków. Awaria ta wygląda jak błąd uwierzytelniania, a nie błąd parsowania, co kieruje uwagę administratora w niewłaściwe miejsce.
  • DASHBOARD_USERNAME oraz DASHBOARD_PASSWORD. Są to dane uwierzytelniające do podstawowego uwierzytelniania w Studio. Skrypt ustawia losowe hasło, ale pozostawia nazwę użytkownika na domyślnej wartości supabase. Domyślne hasło dostarczane w pakiecie to dosłownie this_password_is_insecure_and_should_be_updated, więc jeśli pominięto uruchomienie skryptu, należy je teraz zmienić.

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 (gateway) akceptuje tylko klucze znajdujące się dokładnie w .env, a usługi znajdujące się za nią weryfikują podpis względem JWT_SECRET, więc klucz, który nie pasuje, jest odrzucany z błędem HTTP 401. Domyślna brama Envoy odpowiada 401 Unauthorized, podobnie jak bieżące nadpisanie Kong, które uruchamia Kong 3.9 i zwraca {"message":"Unauthorized"}. Instalacje sprzed marca 2026 roku korzystały z Kong 2.8, który odpowiada {"message":"Invalid authentication credentials"}. Jest to najczęstsza przyczyna awarii przy samodzielnym hostowaniu: operator zmienił JWT_SECRET, ale zachował klucze demonstracyjne. Wszystkie trzy wartości należy zawsze generować łącznie.

SERVICE_ROLE_KEY należy traktować jak hasło root. Omija ono całkowicie zabezpieczenia na poziomie wiersza (row level security). Powinno znajdować się wyłącznie w kodzie po stronie serwera.

Ustaw SITE_URL na adres swojej aplikacji, a API_EXTERNAL_URL na publiczny adres API z /auth/v1 na końcu, na przykład https://supabase.example.com/auth/v1. Ustaw SUPABASE_PUBLIC_URL na ten sam publiczny adres bez ścieżki. Moduł Auth buduje linki do potwierdzenia adresu e-mail oraz wywołania zwrotnego OAuth na podstawie tych wartości, więc pozostawienie ich na domyślnych ustawieniach localhost spowoduje, że wszyscy użytkownicy będą kierowani na własne maszyny.

Następnie sprawdź bieżącą konfigurację:

sh run.sh secrets

Uruchomienie i weryfikacja poprawności działania

sh run.sh start
docker compose ps

run.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 auth

Studio 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 w Internecie

Brama na porcie 8000 komunikuje się za pomocą zwykłego protokołu HTTP. Każdy klucz API oraz hasło użytkownika przesyłane są w sieci tekstem jawnym, a poświadczenia Studio wykorzystują uwierzytelnianie podstawowe (Basic Authentication), które jest jedynie kodowaniem base64, a nie szyfrowaniem.

Umieść przed nią reverse proxy, dokonaj tam terminacji TLS (transport layer security) i powiąż bramę z adresem loopback, aby nikt inny nie mógł uzyskać do niej dostępu. W docker-compose.yml linia portu w sekcji usługi api-gw zmienia się na 127.0.0.1:${API_GW_HTTP_PORT:-${KONG_HTTP_PORT:-8000}}:8000/tcp, a proxy przekazuje ruch na ten adres. W instalacjach sprzed zmiany na Envoy z sierpnia 2026 roku, ta sama linia portu znajduje się w usłudze o nazwie kong. Traefik przed kilkoma aplikacjami Compose omawia kwestię certyfikatów. To samo proxy docelowo obsługuje wszystko inne na serwerze, od tego stosu po projekty takie jak biblioteka Jellyfin stylizowana na wypożyczalnię wideo z lat 90., a każdy z nich wymaga nazwy hosta zamiast kolejnego otwartego portu. Jeśli panel sterowania jest przeznaczony wyłącznie do Twojego użytku, pomiń proxy i połącz się z portem loopback przez tunel SSH; to samo podejście stosuje self-hosted open-kritt, aby całkowicie odizolować swój interfejs skanowania od publicznego Internetu.

Zamknij również pozostałe porty na firewallu, ponieważ Docker publikuje porty poprzez automatyczne dopisywanie własnych 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.

Twórz kopię zapasową bazy danych, a nie katalogu

Dane Postgres znajdują się w dowiązaniu (bind mount) w ./volumes/db/data. Kopiowanie tego katalogu podczas pracy kontenera tworzy niespójną kopię, ponieważ Postgres buforuje zapisy, a pliki na dysku są spójne tylko w punkcie kontrolnym (checkpoint). Przywrócenie takiej kopii zazwyczaj zadziała, ale czasami po cichu utraci ostatnie transakcje, co jest najgorszym możliwym trybem awarii kopii zapasowej.

Zamiast tego wykonaj zrzut. pg_dumpall uruchamia się wewnątrz kontenera i tworzy spójny zrzut:

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

Sprawdź, czy plik nie jest pusty, zanim zaczniesz mu ufać. Następnie przesyłaj te zrzuty poza serwer zgodnie z harmonogramem, do czego służy szyfrowana kopia zapasowa poza serwerem z użyciem restic. Zaplanowany zrzut, który kończy się niepowodzeniem bez powiadomienia, jest nieodróżnialny od braku kopii zapasowej, dlatego skonfiguruj zadanie cron lub systemd tak, aby wysyłało powiadomienie na telefon, gdy zakończy się kodem wyjścia innym niż zero. W tym samym czasie wykonaj kopię zapasową .env. Utrata JWT_SECRET oznacza, że każdy wydany token staje się nieważny, a każdy przechowywany zaszyfrowany sekret staje się nieczytelny.

Przesłane pliki znajdują się w ./volumes/storage i 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, dzięki czemu nic nie ulega zmianie, dopóki użytkownik nie podejmie takiej decyzji. Przypinanie wersji warto stosować w każdym ręcznie konfigurowanym stosie technologicznym. Z tego powodu samodzielnie hostowany przekaźnik RustDesk przypina dwa obrazy serwerowe zamiast korzystać z ruchomych tagów: aktualizacja powinna być świadomym wyborem dokonanym w czasie, gdy administrator dysponuje wolnymi zasobami. Przed każdą operacją należy wykonać zrzut danych.

docker compose pull
sh run.sh recreate

recreate zatrzymuje stos i uruchamia go ponownie na nowych obrazach. Dane pozostają bezpieczne, ponieważ znajdują się w dowiązaniach (bind mounts) na hoście, a nie wewnątrz kontenerów. Przed wykonaniem skoku między głównymi wersjami należy zapoznać się z CHANGELOG.md w repozytorium, ponieważ główna aktualizacja Postgres jest odrębnym krokiem, którego recreate nie wykonuje automatycznie. Postgres 17 jest wersją domyślną od czerwca 2026 roku, a istniejąca instalacja na Postgres 15 przechodzi na tę wersję za pomocą sudo bash utils/upgrade-pg17.sh. Narzędzie to wykorzystuje pg_upgrade do migracji danych w miejscu i wymaga potwierdzenia na każdym głównym etapie. Wymaga ono uruchomienia wszystkich kontenerów oraz wolnego miejsca na dysku o rozmiarze co najmniej dwukrotności bazy danych plus 5 GB. Supabase nadal zaleca wykonanie niezależnej kopii zapasowej przed rozpoczęciem procesu.

Aby uwzględnić zmiany w samym pliku Compose, należy uruchomić sh update.sh z poziomu katalogu projektu lub sh update.sh --dry-run, aby najpierw sprawdzić planowane zmiany. Narzędzie scala pliki nadrzędne z własnymi edycjami, nigdy nie nadpisuje istniejących wartości w .env (jedynie dopisuje nowe klucze) i archiwizuje konfigurację w backups/ przed zapisem, choć nie dotyczy to bazy danych. Instalacja bez pliku .supabase-version wymaga --from oraz wersji, od której rozpoczęto pracę. Jest to szczególnie istotne w przypadku wydania z sierpnia 2026 roku, które zastąpiło usługę kong usługą api-gw i wymaga aktualizacji docker-compose.yml. Metoda ręczna również pozostaje skuteczna: należy ponownie sklonować repozytorium nadrzędne i skopiować jego katalog docker nad katalog projektu, uważając, aby nie nadpisać .env.

Pełny reset, który niszczy wszystko, włącznie z bazą danych, i przywraca .env do ustawień domyślnych (stara konfiguracja jest zachowywana jako .env.old), jest osobnym skryptem wymagającym potwierdzenia:

sh reset.sh

FAQ

Dlaczego wywołania API zwracają komunikat "Invalid authentication credentials"?

Ten konkretny komunikat pochodzi z Kong 2.8, bramy używanej w stosie w instalacjach sprzed marca 2026. Domyślna brama Envoy oraz obecna wersja Kong 3.9 zgłaszają ten sam problem jako standardowy kod 401 Unauthorized. W każdym przypadku przesłany klucz nie pasuje do ANON_KEY lub SERVICE_ROLE_KEY w .env, albo klucze te nie zostały podpisane za pomocą JWT_SECRET znajdującego się obecnie w .env. W przypadku stosu, który nigdy nie zawierał danych, należy wygenerować wszystkie trzy elementy jednocześnie za pomocą sh utils/generate-keys.sh --update-env. W działającym stosie należy uruchomić skrypt bez flagi i odpowiedzieć przecząco, aby wyświetlić tylko nowe wartości, a następnie ręcznie skopiować JWT_SECRET, ANON_KEY oraz SERVICE_ROLE_KEY do .env, pozostawiając POSTGRES_PASSWORD bez zmian. Następnie należy wykonać sh run.sh recreate, aby usługi wczytały nowe wartości.

Czy można uruchomić samodzielnie hostowany Supabase na VPS z 2 GB RAM?

Nie w sposób niezawodny. Dokumentacja Supabase dotycząca samodzielnego hostowania określa 4 GB RAM jako minimum (stan na październik 2026), a w pomiarach z lipca 2026 stos w stanie spoczynku zajmował blisko 3 GB (wliczając usługi logowania, które obecnie są opcjonalne). Serwer z 2 GB RAM traci kontenery z powodu mechanizmu out of memory killer, co skutkuje kodem wyjścia 137 w docker compose ps. Do środowiska produkcyjnego należy użyć 8 GB RAM, a 4 GB traktować jako absolutne minimum dla prac rozwojowych.

Czy samodzielnie hostowany Supabase zawiera edge functions?

Tak. Plik Compose zawiera środowisko uruchomieniowe oparte na Deno, które obsługuje wszystkie pliki umieszczone w ./volumes/functions. Nie obejmuje ono 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?

Należy użyć docker exec -it supabase-db psql -U postgres, aby uzyskać interaktywną powłokę na samym 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 swojego POSTGRES_PASSWORD. Nie należy otwierać tego portu na świat. Dostęp powinien odbywać się przez VPN lub tunel SSH.

Dlaczego linki w e-mailach potwierdzających autoryzację prowadzą do localhost?

Wartości SITE_URL oraz API_EXTERNAL_URL w .env pozostały na ustawieniach domyślnych. 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. Należy ustawić oba parametry na właściwy publiczny adres URL i odtworzyć stos.

Czy ten artykuł był przydatny?