Supabase Docker self-hosting AWS

Supabase self-hosted z Docker Compose bez CLI: konfiguracja własnego rejestru

Uruchomienie produkcyjnego stacku Supabase na czystym Docker Compose bez CLI. Pełna konfiguracja z własnymi rejestrami, zmiennymi środowiskowymi i siecią między serwisami.

·
Supabase CLI sprawdza się w lokalnym developmencie, ale nie nadaje się do produkcyjnego self-hostingu. Ten runbook pokazuje jak uruchomić pełny stack Supabase na czystym Docker Compose, pobierać obrazy z dowolnego rejestru i skonfigurować serwisy bez zależności od CLI.

Ten runbook rozszerza poradnik ECR 403 zmiana rejestru. Tamten naprawia lokalne środowisko deweloperskie (development). Ten pokrywa produkcyjne wdrożenia self-hosted, gdzie CLI nie powinien brać udziału w procesie.

Objawy

Chcesz uruchomić Supabase self-hosted, ale:

  • supabase start nie nadaje się do produkcji (to polecenie wyłącznie deweloperskie).
  • Twój pipeline CI/CD nie może zależeć od zainstalowanego Supabase CLI.
  • Potrzebujesz obrazów z prywatnego rejestru, nie z publicznego ECR.
  • Potrzebujesz pełnej kontroli nad zmiennymi środowiskowymi, siecią i wersjami serwisów.

Przyczyna

Supabase CLI ukrywa w sobie konfigurację Docker Compose, którą generuje w czasie wykonania. To oznacza:

  • Nie możesz przypiąć dokładnych wersji w swoim IaC lub GitOps.
  • CLI narzuca własną konfigurację sieci i wolumenów.
  • Aktualizacje następują gdy CLI się zaktualizuje, nie gdy Ty zdecydujesz.
  • Środowiska korporacyjne wymagające audytu pochodzenia obrazów nie mogą sprawdzić konfiguracji generowanej przez CLI.

Rozwiązaniem jest utrzymywanie własnego docker-compose.yml, który odwołuje się bezpośrednio do obrazów Supabase z pełnym przypięciem wersji i kontrolą rejestru.

Rozwiązanie

Krok 1: Utwórz bazowy plik Docker Compose

Zacznij od oficjalnej referencji self-hostingu Supabase, a następnie dostosuj do swojego rejestru:

# docker-compose.yml
version: "3.8"

services:
  postgres:
    image: ${IMAGE_REGISTRY:-docker.io}/supabase/postgres:15.6.1.143
    restart: unless-stopped
    ports:
      - "5432:5432"
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: postgres
    volumes:
      - postgres-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

  auth:
    image: ${IMAGE_REGISTRY:-docker.io}/supabase/gotrue:v2.158.1
    restart: unless-stopped
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      GOTRUE_DB_DATABASE_URL: postgres://supabase_auth_admin:${POSTGRES_PASSWORD}@postgres:5432/postgres
      GOTRUE_SITE_URL: ${SITE_URL}
      GOTRUE_URI_ALLOW_LIST: ${ADDITIONAL_REDIRECT_URLS:-}
      GOTRUE_JWT_SECRET: ${JWT_SECRET}
      GOTRUE_JWT_EXP: 3600
      GOTRUE_EXTERNAL_EMAIL_ENABLED: "true"
      GOTRUE_MAILER_AUTOCONFIRM: "false"
      API_EXTERNAL_URL: ${API_EXTERNAL_URL}

  rest:
    image: ${IMAGE_REGISTRY:-docker.io}/supabase/postgrest:v12.2.3
    restart: unless-stopped
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      PGRST_DB_URI: postgres://authenticator:${POSTGRES_PASSWORD}@postgres:5432/postgres
      PGRST_DB_SCHEMAS: public,storage,graphql_public
      PGRST_DB_ANON_ROLE: anon
      PGRST_JWT_SECRET: ${JWT_SECRET}

  realtime:
    image: ${IMAGE_REGISTRY:-docker.io}/supabase/realtime:v2.30.34
    restart: unless-stopped
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      DB_HOST: postgres
      DB_PORT: "5432"
      DB_USER: supabase_admin
      DB_PASSWORD: ${POSTGRES_PASSWORD}
      DB_NAME: postgres
      SECRET_KEY_BASE: ${SECRET_KEY_BASE}
      API_JWT_SECRET: ${JWT_SECRET}

  storage:
    image: ${IMAGE_REGISTRY:-docker.io}/supabase/storage-api:v1.0.6
    restart: unless-stopped
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      DATABASE_URL: postgres://supabase_storage_admin:${POSTGRES_PASSWORD}@postgres:5432/postgres
      STORAGE_BACKEND: file
      FILE_STORAGE_BACKEND_PATH: /var/lib/storage
      ANON_KEY: ${ANON_KEY}
      SERVICE_KEY: ${SERVICE_ROLE_KEY}
      POSTGREST_URL: http://rest:3000
    volumes:
      - storage-data:/var/lib/storage

  kong:
    image: ${IMAGE_REGISTRY:-docker.io}/library/kong:3.5
    restart: unless-stopped
    ports:
      - "8000:8000"
      - "8443:8443"
    environment:
      KONG_DATABASE: "off"
      KONG_DECLARATIVE_CONFIG: /var/lib/kong/kong.yml
    volumes:
      - ./kong.yml:/var/lib/kong/kong.yml:ro

volumes:
  postgres-data:
  storage-data:

Krok 2: Utwórz plik .env dla sekretów

# .env
IMAGE_REGISTRY=docker.io
POSTGRES_PASSWORD=twoje-silne-haslo
JWT_SECRET=twoj-jwt-secret-min-32-znaki
ANON_KEY=twoj-anon-key
SERVICE_ROLE_KEY=twoj-service-role-key
SITE_URL=https://twoja-aplikacja.example.com
API_EXTERNAL_URL=https://api.example.com
SECRET_KEY_BASE=twoj-secret-key-base-min-64-znaki

Krok 3: Zmiana rejestru

Aby pobierać obrazy z prywatnego ECR:

IMAGE_REGISTRY=123456789.dkr.ecr.eu-west-1.amazonaws.com

Aby pobierać z GHCR:

IMAGE_REGISTRY=ghcr.io

Wszystkie serwisy pobierają prefix rejestru z jednej zmiennej. Nie trzeba edytować każdej linii z obrazem.

Krok 4: Uruchomienie stacku

docker compose up -d

# Obserwuj logi pod kątem błędów startu
docker compose logs -f --tail=50

Krok 5: Konfiguracja bramki API Kong

Utwórz deklaratywną konfigurację kong.yml, aby kierować żądania do odpowiednich serwisów:

# kong.yml
_format_version: "3.0"

services:
  - name: auth
    url: http://auth:9999
    routes:
      - name: auth-routes
        paths:
          - /auth/v1
        strip_path: true

  - name: rest
    url: http://rest:3000
    routes:
      - name: rest-routes
        paths:
          - /rest/v1
        strip_path: true

  - name: realtime
    url: http://realtime:4000
    routes:
      - name: realtime-routes
        paths:
          - /realtime/v1
        strip_path: true

  - name: storage
    url: http://storage:5000
    routes:
      - name: storage-routes
        paths:
          - /storage/v1
        strip_path: true

Weryfikacja

# 1. Potwierdź, że wszystkie kontenery działają
docker compose ps
# Oczekiwany wynik: wszystkie serwisy pokazują status "Up"

# 2. Zweryfikuj, że obrazy pochodzą z właściwego rejestru
docker compose images
# Oczekiwany wynik: kolumna IMAGE pokazuje prefix Twojego rejestru

# 3. Sprawdź czy PostgREST odpowiada
curl -s http://localhost:8000/rest/v1/ \
  -H "apikey: ${ANON_KEY}" | head -c 100
# Oczekiwany wynik: odpowiedź JSON (pusta tablica lub informacja o schemacie)

# 4. Sprawdź health Auth (GoTrue)
curl -s http://localhost:8000/auth/v1/health
# Oczekiwany wynik: {"status":"ok"}

# 5. Sprawdź łączność z bazą danych
docker compose exec postgres pg_isready -U postgres
# Oczekiwany wynik: accepting connections

Jeśli wszystkie pięć sprawdzeń przechodzi, Twój stack Supabase działa niezależnie od CLI. Kontrolujesz wersje, źródło rejestru i pełną konfigurację. Stąd możesz zintegrować z pipeline deployment, generując plik .env z menedżera sekretów (AWS Secrets Manager, Vault itp.).

Dla zespołów potrzebujących skanowania obrazów i kontroli łańcucha dostaw, zobacz nasz poradnik self-hosting Supabase na AWS bez ECR, który pokrywa budowanie i podpisywanie własnych obrazów.

 

Jerzy Kopaczewski

Potrzebujesz produkcyjnego Supabase na AWS?

Umów bezpłatną 30-minutową rozmowę. Budujemy infrastrukturę dla self-hosted Supabase z prywatnymi rejestrami, automatycznymi backupami i pełnym monitoringiem.