Как развернуть Supabase на своем VPS через Docker
Узнайте, как запустить официальный стек Supabase на собственном сервере. Разберем замену секретов в файле .env, требования к RAM, настройку 14 сервисов и обновление базы данных.
Что вы создаете
Self-hosting Supabase означает запуск официального стека Docker Compose на вашем собственном сервере: Postgres, REST API поверх него, сервис аутентификации, хранилище файлов, realtime websockets и панель управления Studio. Вы клонируете один репозиторий, редактируете один файл .env и запускаете около четырнадцати контейнеров, которые вместе работают как проект Supabase под вашим контролем.
Установка выполняется быстро. Часть, в которой часто возникают ошибки, — это файл .env. Он поставляется с демонстрационными секретами, которые опубликованы в репозитории, и стек, запущенный с этими значениями по умолчанию, открыт для любого, кто его обнаружит. В этом руководстве рассматриваются секреты, которые необходимо заменить, назначение каждого сервиса, реальные требования стека к оперативной памяти и способы обновления без удаления базы данных.
Если Docker Compose для вас в новинку, сначала прочитайте Основы Docker Compose на VPS. Все, что описано ниже, предполагает, что команда docker compose version уже выводит версию программы.
Из чего на самом деле состоит стек
Supabase — это не одна программа. Файл Compose запускает набор отдельных сервисов в одной сети, и понимание того, какой сервис за что отвечает, превращает список имен контейнеров в понятную структуру для отладки.
db— это PostgreSQL с загруженными расширениями Supabase. Все остальные сервисы взаимодействуют с ним. Если этот контейнер неисправен, всё остальное также перестает работать.kong— это API gateway. Он прослушивает порт 8000 и направляет запросы/rest/v1/,/auth/v1/и/storage/v1/к нужному backend. Это единственный контейнер, который следует открывать во внешнюю сеть.rest— это PostgREST. Он считывает вашу схему Postgres и предоставляет её в виде REST API, поэтому новая таблица автоматически становится новой конечной точкой без написания кода.auth— это GoTrue. Он выдает JSON web tokens (JWT), которые идентифицируют ваших пользователей.storageиimgproxyотвечают за загрузку файлов и изменение размера изображений.realtimeтранслирует изменения базы данных через websockets.studioиmeta— это панель управления (dashboard) и административный API, работающий в её основе.analytics(Logflare) иvectorсобирают логи, аsupavisor— это пул соединений для Postgres.
Этот список объясняет, почему требования к ресурсам, приведенные ниже, именно такие. Вы запускаете не просто базу данных. Вы запускаете базу данных вместе с десятком вспомогательных сервисов.
Планирование ресурсов: выделите 8 ГБ ОЗУ
На июль 2026 года стек в режиме простоя потребляет примерно от 2.5 до 3 ГБ резидентной памяти сразу после установки, еще до загрузки ваших данных или начала обработки трафика. Сервис аналитики и процесс Studio Node.js являются двумя крупнейшими потребителями ресурсов. На сервере с 2 ГБ ОЗУ контейнеры запустятся, но затем один из них будет завершен механизмом kernel out of memory killer, обычно это analytics или db. Симптомом станет контейнер, который постоянно перезапускается с кодом выхода 137.
Для любого критически важного сервиса выделяйте 8 ГБ ОЗУ и 4 vCPU. 4 ГБ достаточно для одиночного экземпляра разработки, если вы готовы к тому, что выполнение тяжелого запроса и сессия в Studio одновременно будут работать медленно. Дисковое пространство также имеет значение, так как Postgres, том с данными и логи находятся в директории проекта. Начните с 40 ГБ и следите за расходом. Подсчет сервисов перед выбором тарифа — полезная привычка для любого self-hosted решения, поскольку PhotoPrism и Immich имеют реальные минимальные требования к ОЗУ, значительно превышающие те, что указаны в их руководствах по быстрому запуску.
Установка: клонирование официального репозитория
Поддерживаемый метод предполагает копирование директории 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. Шлюз проверяет эту подпись при каждом запросе, поэтому ключ, который не совпадает с вашим секретом, отклоняется с ошибкой {"message":"Invalid authentication credentials"}. Это самая распространенная ошибка при самостоятельном хостинге: оператор изменил JWT_SECRET, но оставил демонстрационные ключи. Всегда генерируйте все три значения вместе.
Относитесь к SERVICE_ROLE_KEY как к паролю root. Он полностью обходит безопасность на уровне строк (row level security). Его место — только в серверном коде, и нигде больше.
Установите SITE_URL и API_EXTERNAL_URL в соответствии с адресом, по которому пользователи будут фактически обращаться к сервису, например https://supabase.example.com. Сервис Auth формирует ссылки для подтверждения по электронной почте и OAuth-коллбэки на основе этих значений, поэтому, если оставить их как http://localhost:8000, каждый ваш пользователь будет перенаправлен на свою собственную машину.
Затем проверьте полученную конфигурацию:
sh run.sh secretsЗапуск и проверка работоспособности
sh run.sh start
docker compose psrun.sh start является оберткой для docker compose up -d --wait, поэтому команда не завершится, пока не будут пройдены проверки работоспособности. Каждый сервис должен находиться в состоянии running (healthy) или running. Первый запуск занимает от 2 до 4 минут, так как Postgres выполняет скрипты инициализации до того, как другие компоненты смогут подключиться.
Если контейнер постоянно перезапускается, изучите его логи, указав имя сервиса:
docker compose logs db
docker compose logs authПосле этого Studio будет доступна на порту 8000; система запросит имя пользователя и пароль для панели управления, которые вы задали ранее.
Не открывайте порт 8000 в публичную сеть
Kong на порту 8000 использует обычный HTTP. Все API-ключи и пароли пользователей передаются по сети в открытом виде, а учетные данные Studio используют базовую аутентификацию (basic authentication), которая представляет собой кодирование base64, а не шифрование.
Установите перед ним reverse proxy, настройте там TLS (transport layer security) и привяжите Kong к адресу loopback, чтобы никто другой не мог к нему обратиться. В docker-compose.yml проброс порта kong меняется на 127.0.0.1:8000:8000, и прокси перенаправляет запросы туда. В Traefik перед несколькими Compose-приложениями описана работа с сертификатами. Этот же прокси в итоге обслуживает всё остальное на сервере: от данного стека до таких проектов, как медиатека Jellyfin в стиле видеопроката 90-х, и для каждого из них требуется имя хоста, а не очередной открытый порт. Если панель управления нужна только вам, откажитесь от прокси и подключайтесь к порту loopback через SSH-туннель — это тот же подход, который self-hosted open-kritt использует для полной изоляции интерфейса сканирования от публичного интернета.
Закройте остальные порты на межсетевом экране, так как Docker публикует порты, создавая собственные правила iptables, которые стандартная конфигурация ufw не учитывает. Эта ловушка описана в статье почему Docker-контейнеры игнорируют правила ufw.
Создавайте резервную копию базы данных, а не директории
Данные Postgres хранятся в bind mount по пути ./volumes/db/data. Копирование этой директории во время работы контейнера приводит к созданию несогласованной копии, так как Postgres буферизирует операции записи, а файлы на диске становятся консистентными только в момент контрольной точки (checkpoint). Восстановление из такой копии обычно проходит успешно, но иногда приводит к незаметной потере последних транзакций, что является худшим сценарием отказа для резервной копии.
Вместо этого используйте дамп. Команда pg_dumpall выполняется внутри контейнера и создает консистентный снимок:
docker exec -t supabase-db pg_dumpall -U postgres > supabase-$(date +%F).sqlПеред тем как доверять файлу, убедитесь, что он не пуст. Затем настройте регулярную отправку этих дампов с сервера, для чего предназначены зашифрованные удаленные резервные копии с помощью restic. Регулярный дамп, который завершается с ошибкой без уведомления, ничем не отличается от отсутствия резервной копии, поэтому настройте cron или systemd-задание так, чтобы оно отправляло уведомление на ваш телефон при ненулевом коде выхода. Одновременно с этим создавайте резервную копию .env. Потеря JWT_SECRET означает, что все выданные токены станут недействительными, а все сохраненные зашифрованные секреты — нечитаемыми.
Загруженные файлы находятся в ./volumes/storage; поскольку это обычные файлы, их можно копировать стандартными средствами.
Обновление без потери данных
Supabase фиксирует версии образов в docker-compose.yml, поэтому ничего не обновляется, пока вы сами не выполните обновление. Такой подход стоит применять в любом стеке, который вы собираете вручную. Поэтому самостоятельно размещённый relay RustDesk фиксирует версии двух серверных образов, а не использует изменяемый тег. Обновление должно выполняться в выбранное вами утро, когда у вас есть на него время. Перед обновлением всегда создавайте дамп.
docker compose pull
sh run.sh recreaterecreate останавливает стек и запускает его снова на новых образах. Ваши данные сохраняются, так как они находятся в bind mounts на хосте, а не внутри контейнеров. Перед переходом на мажорную версию прочитайте CHANGELOG.md в репозитории, так как мажорные обновления Postgres не выполняются автоматически и требуют выполнения дампа и восстановления.
Чтобы применить изменения в самом файле Compose, снова клонируйте upstream-репозиторий и скопируйте его директорию docker поверх вашего проекта, стараясь не перезаписать .env.
Полный сброс, который уничтожает всё, включая базу данных, выполняется отдельным скриптом и требует подтверждения:
sh reset.shFAQ
Почему мои 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 ГБ ОЗУ?
Это не будет работать стабильно. По состоянию на июль 2026 года стек в режиме простоя потребляет около 3 ГБ памяти, так как запускает около четырнадцати сервисов. На машине с 2 ГБ ОЗУ контейнеры будут завершаться механизмом out of memory killer, и вы увидите код выхода 137 в docker compose ps. Используйте 8 ГБ для production-среды, а 4 ГБ считайте минимально допустимым объемом для индивидуальной разработки.
Включает ли self-hosted Supabase edge functions?
Да. Файл Compose содержит среду выполнения функций на базе Deno, которая обрабатывает всё, что вы разместите в ./volumes/functions. Она не включает глобальную сеть развертывания, доступную в облачной платформе, поэтому ваши функции будут выполняться только на вашем сервере в одной локации.
Как подключиться к базе данных 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 и пересоздайте стек.