SSD Nodes Learn Hosting plans →
Посібники Matt ConnorВід Matt Connor · Оновлено 2026-08-27

Як розгорнути Supabase на VPS через Docker

Запустіть офіційний стек Supabase Docker на власному сервері: замініть демонстраційні секрети, розберіться з 14 сервісами, RAM, резервними копіями та оновленнями.

Що ви налаштовуєте

Self-hosting Supabase означає запуск офіційного стека Docker Compose на власному сервері: Postgres, REST API перед ним, сервіс автентифікації, файлове сховище, realtime WebSockets і панель Studio. Ви клонуєте один репозиторій, редагуєте один файл .env і запускаєте приблизно чотирнадцять контейнерів, які разом працюють як керований вами проєкт Supabase.

Установлення нескладне. Проблеми виникають через файл .env. Він містить демонстраційні секрети, опубліковані в репозиторії, тому стек, запущений із цими значеннями за замовчуванням, доступний кожному, хто їх знайде. У цьому посібнику описано, які секрети потрібно замінити, для чого призначений кожен сервіс, скільки пам’яті насправді потребує стек і як оновлювати його без видалення бази даних.

Якщо Docker Compose для вас новий, спочатку прочитайте Основи Docker Compose на VPS. Усе нижче передбачає, що команда docker compose version уже виводить версію.

Що насправді входить до stack

Supabase — це не одна програма. Файл Compose запускає набір окремих сервісів в одній мережі. Якщо розуміти призначення кожного сервісу, довгий список назв контейнерів буде простіше діагностувати.

  • db — це PostgreSQL із завантаженими розширеннями Supabase. Усі інші сервіси підключаються до нього. Якщо цей контейнер працює некоректно, решта сервісів також не працюватиме.
  • kong — це API-шлюз. Він прослуховує порт 8000 і маршрутизує /rest/v1/, /auth/v1/ та /storage/v1/ до відповідних бекендів. Це єдиний контейнер, який слід відкривати для зовнішнього доступу.
  • rest — це PostgREST. Він читає схему Postgres і надає її через REST API. Тому нова таблиця автоматично стає новою кінцевою точкою без написання коду.
  • auth — це GoTrue. Він видає JSON Web Tokens (JWT), які ідентифікують користувачів.
  • storage і imgproxy обробляють завантаження файлів і змінюють розмір зображень.
  • realtime передає зміни бази даних через WebSocket.
  • studio і meta — це dashboard і адміністративний API, який він використовує.
  • analytics (Logflare) і vector збирають журнали, а supavisor — це пулер підключень Postgres.

Саме тому наведені нижче значення ресурсів мають такий вигляд. Ви запускаєте не лише базу даних. Ви запускаєте базу даних і ще близько десятка допоміжних сервісів.

Розмір ресурсів: плануйте 8 GB RAM

У стані простою стек використовує приблизно 2.5–3 GB резидентної пам’яті на щойно встановленій системі, станом на July 2026, ще до появи власних даних або мережевого трафіку. Сервіс аналітики та процес Studio Node.js є двома найбільшими окремими споживачами ресурсів. Сервер із 2 GB RAM запустить контейнери, після чого kernel out-of-memory killer зазвичай завершить один із них — analytics або db. Ознака проблеми — контейнер постійно перезапускається з кодом завершення 137.

Для сервісів, від яких ви залежите, надайте серверу 8 GB RAM і 4 vCPU. 4 GB достатньо для автономного середовища розробки, якщо ви погоджуєтеся з тим, що одночасний важкий запит і сеанс Studio працюватимуть повільно. Дисковий простір також має значення, оскільки Postgres, том сховища та дані журналів зберігаються в каталозі проєкту. Почніть із 40 GB і стежте за використанням. Підраховувати кількість сервісів перед вибором тарифного плану — корисна звичка для будь-яких self-hosted систем, оскільки PhotoPrism та Immich мають фактичні мінімальні вимоги до RAM, значно вищі за ті, що випливають з їхніх сторінок швидкого запуску.

Встановлення: клонуйте офіційний репозиторій

Підтримуваний спосіб передбачає копіювання каталогу docker з основного репозиторію до власного каталогу проєкту. Такий поділ важливий, оскільки під час подальшого виконання git pull ваш .env не буде перезаписано.

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 завантажує кілька гігабайтів образів. Виконання має завершитися зі статусом Pulled для кожного сервісу. Помилка manifest unknown означає, що закріплений тег образу видалили у вихідному репозиторії. Щоб виправити це, завантажте новішу копію репозиторію, а не редагуйте теги вручну.

Секрети, які потрібно змінити перед першим запуском

Зробіть це до запуску стека, а не після нього. Деякі з цих значень записуються в дані під час першого запуску, тому їхня подальша зміна потребує скидання бази даних.

У репозиторії є генератор, який правильно створює всі значення, зокрема два API-ключі, які потрібно підписати новим JWT-секретом.

sh utils/generate-keys.sh --update-env

Цей скрипт записує нові значення для JWT_SECRET, ANON_KEY, SERVICE_ROLE_KEY, SECRET_KEY_BASE, REALTIME_DB_ENC_KEY, VAULT_ENC_KEY, PG_META_CRYPTO_KEY і токени Logflare у .env. Для його роботи потрібен openssl, який є в будь-якому стандартному образі Ubuntu.

Два значення він не встановлює, тому їх потрібно змінити вручну в .env:

  • POSTGRES_PASSWORD. Використовуйте лише літери й цифри. Розділові знаки тут пошкодять рядки підключення, які кілька сервісів формують об’єднанням рядків. Помилка виглядатиме як помилка автентифікації, а не як помилка розбору, тому пошук причини піде в неправильному напрямку.
  • DASHBOARD_USERNAME і DASHBOARD_PASSWORD. Це облікові дані базової автентифікації для Studio. Пароль за замовчуванням буквально має значення this_password_is_insecure_and_should_be_updated.

Важливо розуміти, чому ANON_KEY і SERVICE_ROLE_KEY не можна вигадувати. Обидва значення є JWT, підписаними за допомогою JWT_SECRET. Gateway перевіряє цей підпис у кожному запиті, тому ключ, який не відповідає вашому секрету, відхиляється з помилкою {"message":"Invalid authentication credentials"}. Це найпоширеніша проблема під час self-hosting: адміністратор змінив JWT_SECRET, але залишив демонстраційні ключі. Завжди генеруйте всі три значення разом.

Ставтеся до SERVICE_ROLE_KEY як до пароля root. Він повністю обходить row level security. Його можна використовувати лише в коді на стороні сервера.

Установіть SITE_URL і API_EXTERNAL_URL як адресу, за якою користувачі фактично матимуть доступ до сервісу, наприклад https://supabase.example.com. Auth формує посилання для підтвердження електронної пошти та callback-посилання OAuth на основі цих значень. Якщо залишити http://localhost:8000, усі користувачі перенаправлятимуться на власні комп’ютери.

Після цього перевірте налаштовані значення:

sh run.sh secrets

Запустіть його та переконайтеся, що він працює належним чином

sh run.sh start
docker compose ps

run.sh start обгортає docker compose up -d --wait, тому команда не повертає керування, доки не пройдуть перевірки стану. Для кожного сервісу має відображатися running (healthy) або running. Перший запуск триває від двох до чотирьох хвилин, оскільки Postgres спочатку виконує скрипти ініціалізації, і лише після цього до нього можуть підключатися інші компоненти.

Якщо контейнер перезапускається, перегляньте його журнали за назвою сервісу:

docker compose logs db
docker compose logs auth

Studio буде доступний на порту 8000 і запросить ім’я користувача та пароль для dashboard, які ви вказали.

Не відкривайте порт 8000 у публічний Інтернет

Kong на порту 8000 працює через звичайний HTTP. Кожен API key і кожен пароль користувача передається мережею у відкритому вигляді. Облікові дані Studio використовують basic authentication, тобто кодування base64, а не шифрування.

Розмістіть перед Kong reverse proxy, завершуйте TLS (transport layer security) на ньому та прив’яжіть Kong до loopback-адреси, щоб жоден інший компонент не мав до нього доступу. У docker-compose.yml зіставлення портів kong стає 127.0.0.1:8000:8000, а проксі пересилає запити на цей порт. У розділі Traefik перед кількома застосунками Compose описано налаштування сертифікатів.

Також закрийте решту портів у firewall, оскільки Docker публікує порти, додаючи власні правила iptables, яких типова конфігурація ufw не бачить. Цю проблему описано в розділі чому контейнери Docker ігнорують правила ufw.

Резервуйте базу даних, а не каталог

Дані Postgres зберігаються у bind mount за адресою ./volumes/db/data. Копіювання цього каталогу під час роботи контейнера створює неповну копію, оскільки Postgres буферизує записи, а файли на диску є узгодженими лише під час checkpoint. Відновлення зазвичай спрацює, але останні транзакції іноді непомітно втрачаються. Для резервної копії це найгірший можливий сценарій.

Замість цього створіть dump. Команда pg_dumpall виконується всередині контейнера та створює узгоджений snapshot:

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

Переконайтеся, що файл не порожній, перш ніж вважати резервну копію надійною. Потім регулярно передавайте ці dump-файли на інший сервер. Саме для цього призначені зашифровані віддалені резервні копії за допомогою restic. Одночасно створюйте резервну копію .env. Втрата JWT_SECRET зробить недійсними всі видані токени, а всі збережені зашифровані секрети стане неможливо прочитати.

Завантажені файли зберігаються в ./volumes/storage. Це звичайні файли, тому для них достатньо простого копіювання.

Оновлення без втрати даних

Supabase фіксує версії образів у docker-compose.yml, тому нічого не оновлюється, доки ви самі не виконаєте оновлення. Фіксацію версій варто застосовувати в кожному стеку, який ви збираєте вручну. Саме тому self-hosted relay RustDesk фіксує версії двох серверних образів, а не використовує тег, який змінюється: оновлення має виконуватися тоді, коли ви самі його запланували й маєте на це час. Щоразу спочатку створюйте дамп.

docker compose pull
sh run.sh recreate

recreate зупиняє стек і запускає його знову на нових образах. Дані зберігаються, оскільки вони розміщені в bind mount на хості, а не всередині контейнерів. Перед переходом на нову основну версію прочитайте CHANGELOG.md у репозиторії, оскільки основні оновлення Postgres не виконуються автоматично й потребують створення дампа та відновлення.

Щоб застосувати зміни до самого Compose-файлу, повторно клонуйте upstream-репозиторій і скопіюйте його каталог docker у свій проєкт. Не перезаписуйте .env.

Повне скидання, яке видаляє все, включно з базою даних, виконується окремим скриптом і потребує підтвердження:

sh reset.sh

FAQ

Чому мої API-виклики повертають "Invalid authentication credentials"?

Ваш ANON_KEY або SERVICE_ROLE_KEY не було підписано за допомогою JWT_SECRET, який наразі зберігається в .env. Шлюз перевіряє підпис кожного запиту й відхиляє запит, якщо підпис не збігається. Повторно згенеруйте всі три значення за допомогою sh utils/generate-keys.sh --update-env, а потім виконайте sh run.sh recreate, щоб сервіси зчитали нові значення.

Чи можна запускати self-hosted Supabase на VPS із 2 GB пам’яті?

Ненадійно. Станом на July 2026 стек у режимі простою використовує близько 3 GB, оскільки запускає приблизно fourteen сервісів. Тому на сервері з 2 GB OOM killer завершує роботу контейнерів, і в docker compose ps з’являється код завершення 137. Для production використовуйте 8 GB, а 4 GB вважайте мінімальним обсягом для самостійної розробки.

Чи включає self-hosted Supabase edge functions?

Так. Compose-файл містить середовище виконання функцій на основі Deno і обробляє все, що ви розміщуєте в ./volumes/functions. До нього не входить глобальна мережа розгортання hosted-платформи, тому ваші функції працюють на одному сервері та в одному розташуванні.

Як безпосередньо підключитися до бази даних Postgres?

Використовуйте docker exec -it supabase-db psql -U postgres для інтерактивної оболонки безпосередньо на сервері. Для зовнішнього клієнта підключайтеся через Supavisor на порту 5432, використовуючи користувача postgres.<POOLER_TENANT_ID> і ваш POSTGRES_PASSWORD. Не відкривайте цей порт для інтернету. Підключайтеся через VPN або SSH-тунель.

Чому посилання в моїх електронних листах із підтвердженням автентифікації ведуть на localhost?

SITE_URL і API_EXTERNAL_URL у .env залишилися зі стандартними значеннями. Сервіс автентифікації формує кожне посилання для підтвердження та скидання пароля на основі цих двох значень, тому надсилає адресу, яку йому вказано. Установіть для обох параметрів фактичну публічну URL-адресу та повторно створіть стек.