Firebase – штука удобная. Пока ты не начнешь платить $1000 в месяц за 5000 пользователей. Или пока не поймешь, что твои данные навсегда привязаны к экосистеме Google. Vendor lock-in – единственная болезнь, которая лечится только долларом. Но есть и другой путь – собрать свой BaaS из open-source кирпичиков.
В 2026 году экосистема созрела: Supabase v1.5 (последняя версия на июнь), PocketBase, Appwrite – каждый закрывает свою нишу. Но для максимально близкой копии Firebase (база данных, realtime, авторизация, storage, edge functions) лучше всего связка Supabase + собственный Docker Compose workflow. Под капотом – PostgreSQL, GoTrue, Kong, Realtime, Storage API. Всё как у взрослых, только бесплатно.
Мы не будем использовать облачный Supabase – мы поднимем self-hosted версию на своей машине. Полный контроль, никаких лимитов. Только ты и твой сервер.
Почему не Firebase?
Давай честно: Firebase – это приманка. Бесплатный Spark план – идеальный вход, но как только проект начинает расти, цены взлетают. Realtime Database стоит денег, Storage стоит денег, Cloud Functions – отдельная история. И самое ужасное – миграция на другую платформу превращается в харакири. Попробуй вытащить данные из Firestore и перенести в PostgreSQL без даунтайма.
Open-source альтернативы сняли все эти проблемы. Ты можешь запустить BaaS на своём VPS за $5 (аренда сервера) и получить ровно то же самое, но с возможностью кастомизации. Хочешь заменить email-аутентификацию на Telegram OAuth? Пожалуйста. Хочешь хранить файлы на S3? Конфиг в одну строку.
Кстати, в последнее время инструменты ИИ здорово упрощают настройку такой инфраструктуры. Например, нейросети уже генерируют конфиги и Dockerfile, а Cursor AI может написать сам SDK. Но мы не будем полагаться на магию – сделаем всё руками. От этого будет больше пользы.
Что собираем?
Наш самодельный Firebase будет уметь:
- База данных – реляционная (PostgreSQL) с автоматической генерацией REST и GraphQL API через PostgREST.
- Realtime – WebSocket подписки на изменения таблиц (usign Supabase Realtime).
- Авторизация – email/пароль, Google, GitHub, Apple, Telegram (через GoTrue).
- Storage – загрузка и раздача файлов (S3-совместимый или локальный).
- Edge Functions – Deno-рантайм для серверного кода (аналог Cloud Functions).
Всё это – через единый Docker Compose файл. Развернуть можно на любой VPS, где есть Docker. Если у тебя нет сервера – рекомендую DigitalOcean (дешевый droplet за $4) или Hetzner.
Засада: как НЕ надо делать
Перед тем как писать код, покажу типичную ошибку новичков. Многие скачивают официальный супабейсовский docker-compose.yml и запускают как есть. Через 10 минут работа падает – порты конфликтуют, секретные ключи установлены по умолчанию, а Realtime не работает, потому что не настроен JWT.
Вот как НЕ надо:
# Не делай так! Плохая практика
git clone https://github.com/supabase/supabase
cd supabase/docker
docker compose up -d # никакой конфигурации, никаких переменных окружения
Это рабочий способ для тестов, но не для продакшена. Мы пойдем правильным путем – создадим файл .env с кастомными паролями, выключим ненужные сервисы и настроим SSL.
Шаг 1: Подготовка окружения
Нам понадобятся:
- Docker & Docker Compose v2.29+ (можно использовать Podman, я лично не советую – у Supabase есть нюансы с сетевыми мостами).
- Node.js v22 (для клиентского SDK и edge functions).
- Linux или Mac (на Windows тоже работает, но через WSL2, и это боль).
1 Клонируем репозиторий Supabase self-hosted
mkdir my-baas && cd my-baas
# Скачиваем актуальный compose-файл (на 2026 год последняя стабильная – v1.5.0)
curl -o docker-compose.yml https://raw.githubusercontent.com/supabase/supabase/master/docker/docker-compose.yml
curl -o .env.example https://raw.githubusercontent.com/supabase/supabase/master/docker/.env.example
cp .env.example .env
2 Настраиваем .env
Открой .env и измени эти строки. Без них продакшн – дыра:
# Обязательно меняем все пароли и ключи
POSTGRES_PASSWORD=your_strong_password_here
JWT_SECRET=$(openssl rand -hex 64) # генерируем случайный
SITE_URL=http://localhost:3000 # потом поменяем на домен
# Отключаем ненужные сервисы, если не планируем их использовать
# Например, если тебе не нужен Logflare (логирование) – ставь false
ENABLE_LOGFLARE=false
# Для Storage лучше указать S3-бакет, но пока оставим локальный
FILE_SIZE_LIMIT=52428800 # 50 MB
Зачем это? Supabase запускает контейнеры с GoTrue (аутентификация), Realtime, Kong (API gateway), PostgREST, Storage. Все сервисы используют общий JWT-секрет, так что если он будет известен – злоумышленник сможет сгенерировать токен от имени любого пользователя. Не повторяй мой факап с default-ключом в песочнице.
3 Запускаем Docker Compose
docker compose up -d
# Проверяем, что все сервисы поднялись
docker compose ps
# Должны быть примерно такие:
# supabase-studio Up 0.0.0.0:3000->3000/tcp
# supabase-kong Up 0.0.0.0:8000->8000/tcp
# supabase-auth Up 0.0.0.0:9999->9999/tcp (GoTrue)
# supabase-realtime Up 0.0.0.0:4000->4000/tcp
Если всё зелёное – открывай в браузере http://localhost:8000 (Kong API). Там будет заглушка. Админка Supabase Studio – http://localhost:3000. Войди с логином supabase и паролем из .env (STUDIO_PASSWORD).
Шаг 2: Создаем базу и таблицы
В админке Studio перейди в SQL Editor и создай простую таблицу для сообщений (как в firebase realtime database, только реляционная):
CREATE TABLE public.messages (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMPTZ DEFAULT NOW(),
content TEXT NOT NULL,
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE
);
-- Включаем Realtime для этой таблицы
ALTER PUBLICATION supabase_realtime ADD TABLE public.messages;
Обрати внимание на ALTER PUBLICATION – это и есть магия realtime. Без этой строки WebSocket подписки не будут работать. Firebase реалтайм база привязана к типу данных, а здесь ты контролируешь, какие таблицы транслировать.
Шаг 3: Авторизация – свой Auth сервер
GoTrue уже запущен. Чтобы зарегистрировать пользователя через email, используем клиентскую библиотеку. Но сначала надо настроить провайдеров.
3.1 Включаем OAuth провайдеров
В админке Studio -> Authentication -> Providers. Включи Google, GitHub, или любой другой. Для получения Client ID и Client Secret придется зарегистрировать OAuth приложение в соответствущем сервисе. Но если лень – можно работать только с email/пароль.
3.2 Пишем клиентский код (по сути – наш SDK)
Создай директорию client/ и поставь пакет Supabase JS:
mkdir client && cd client
npm init -y
npm install @supabase/supabase-js
Теперь создай файл supabase-client.js с конфигурацией. В 2026 году @supabase/supabase-js уже версия 2.45, API стабилен.
import { createClient } from '@supabase/supabase-js'
const supabaseUrl = 'http://localhost:8000' // для разработки
const supabaseAnonKey = 'your-anon-key-from-supabase-studio-settings'
export const supabase = createClient(supabaseUrl, supabaseAnonKey)
// Регистрация пользователя
async function signUp(email, password) {
const { data, error } = await supabase.auth.signUp({
email,
password,
})
if (error) throw error
return data
}
// Вход
async function signIn(email, password) {
const { data, error } = await supabase.auth.signInWithPassword({
email,
password,
})
if (error) throw error
return data
}
Анонимный ключ (anon key) лежит в админке Studio -> Settings -> API. Этот же ключ будет передаваться клиентом, поэтому он публичный. Безопасность обеспечивается Row Level Security (RLS) – политиками доступа к таблицам.
Шаг 4: Realtime подписки – чувствуем магию
Самое вкусное – получать обновления таблицы в реальном времени. Вот как подписаться на изменения messages:
// Подписываемся на все изменения (INSERT, UPDATE, DELETE) в messages
const subscription = supabase
.channel('public:messages')
.on(
'postgres_changes',
{ event: '*', schema: 'public', table: 'messages' },
(payload) => {
console.log('Change received!', payload)
}
)
.subscribe()
// Чтобы отписаться позже:
// supabase.removeChannel(subscription)
Это работает потому, что Supabase Realtime слушает WAL (Write-Ahead Log) PostgreSQL и транслирует события через WebSocket. В Firebase под капотом та же логика, но там они скрывают реализацию, а здесь ты видишь каждый байт.
Если при подписке вылетает ошибка 42601: publication supabase_realtime does not exist – значит ты забыл выполнить ALTER PUBLICATION для таблицы. Возвращайся к Шагу 2.
Шаг 5: Storage – загружаем файлы
Firebase Storage – это просто бакет. Supabase Storage – тоже бакет, но можно прикрутить S3 (например, MinIO для самодостаточности). В self-hosted варианте по умолчанию используется локальная файловая система (volume docker). Загрузить файл:
// Создаем бакет (один раз) – лучше через админку
const { data: bucket, error } = await supabase.storage.createBucket('avatars', {
public: false, // только авторизованные пользователи
})
// Загрузка файла
const file = document.querySelector('input[type=file]').files[0]
const { data, error } = await supabase.storage
.from('avatars')
.upload(`public/${file.name}`, file, {
cacheControl: '3600',
upsert: false,
})
Чтобы скачать файл, нужно сгенерировать подписанный URL. Безопасность контролируется через RLS и политики бакета.
Шаг 6: Edge Functions – serverless на Deno
Supabase использует Deno для serverless функций. Они запускаются в отдельном контейнере. Создай функцию в папке supabase/functions/ (если используешь CLI) или через интерфейс Studio -> Edge Functions. Пример функции для отправки приветственного письма:
// supabase/functions/welcome/index.ts
import { serve } from 'https://deno.land/std@0.200.0/http/server.ts'
serve(async (req) => {
const { email } = await req.json()
// Отправляем письмо через какой-нибудь email API
console.log(`Sending welcome to ${email}`)
return new Response(JSON.stringify({ success: true }), {
headers: { 'Content-Type': 'application/json' },
})
})
Задеплоить можно через CLI: supabase functions deploy welcome. Теперь у тебя своя Cloud Functions, только без цен за вызовы.
Шаг 7: Деплой на VPS
На продакшене нельзя оставлять localhost. Выбери VPS с Ubuntu 24.04, установи Docker, скопируй папку с compose-файлами и .env. Подними через docker compose up -d.
Не забудь:
- Настроить HTTPS через reverse proxy (Caddy или Nginx + Let's Encrypt).
- Изменить
SITE_URLиAPI_EXTERNAL_URLна реальный домен. - Включить бэкапы базы данных (через pg_dump внутри контейнера).
Вся магия в Docker Compose – он поднимает Kong как API-шлюз, который маршрутизирует запросы к нужным сервисам. Если нужен автоматический SSL, добавь Caddy:
# docker-compose.override.yml
version: '3'
services:
caddy:
image: caddy:2
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
networks:
- supabase
volumes:
caddy_data:
И простой Caddyfile:
yourdomain.com {
reverse_proxy kong:8000
}
Где спотыкаются даже опытные
Собрал коллекцию граблей, на которые наступил сам и которые видел в issue Supabase (их баг-трекер на GitHub – кладезь боли).
- Порты. Kong по умолчанию слушает 8000 (API) и 8443 (gRPC). Если на сервере уже крутится что-то на 80/443 – будут конфликты. Либо останови другие сервисы, либо перенастрой Kong.
- JWT в Realtime. WebSocket требует токен доступа. Если клиент не авторизован – подписка упадет с 401. Всегда проверяй, что
supabase.auth.getSession()возвращает валидную сессию перед подпиской. - Row Level Security. Без RLS анонимный ключ сможет читать любые данные. Покажи уважение к безопасности: добавь политики для каждой таблицы. Пример:
CREATE POLICY "Users can read own messages" ON public.messages
FOR SELECT
USING (auth.uid() = user_id);
CREATE POLICY "Users can insert own messages" ON public.messages
FOR INSERT
WITH CHECK (auth.uid() = user_id);
- Обновления Supabase. У них релизный цикл – раз в месяц. Если обновляешь версию, читай
CHANGELOGи не забывай мигрировать БД (через supabase migration up). - Масштабирование. Один Docker Compose потянет несколько тысяч пользователей. Если больше – придется разворачивать кластер PostgreSQL, ставить балансировщик для Kong и разносить сервисы по отдельным хостам. Но это уже тема для отдельной статьи.
Неочевидный совет на десерт
Не пытайтесь сделать из Supabase универсальное решение для всех проектов. Для простых CRUD-приложений (блог, форма обратной связи) лучше использовать PocketBase – он поднимается одним бинарником, не требует Docker, встроенная админка и realtime. А Supabase берите, когда нужна полноценная SQL база, сложные запросы и гранулярные права доступа.
И ещё – обязательно автоматизируйте деплой через CI/CD. Например, с помощью гибридного workflow Cloud Architect + Local Builder или через Roo Code для QA-тестов инфраструктуры. Будущее за тем, что инфраструктура как код (IaC) пишется нейросетями, а ты только контролируешь результат.
Собранный аналог Firebase – это не просто экономия денег. Это свобода. Ты можешь в любой момент мигрировать, можешь форкнуть любой компонент, можешь вшить кастомную логику без согласования с вендором. И когда проект вырастет до миллионов пользователей – твой стек останется с тобой, а не сгорит на счете за облачные сервисы.