TuBrief
구독 채널
비디오
커뮤니티

Технические шаги по отказу от Firebase и переходу на собственный хостинг за 5 долларов в месяц

TuBrief 편집팀
2026년 7월 14일
0
Computing/Software

원본 영상을 바탕으로 AI의 도움을 받아 작성했습니다. 원본 영상이 기준입니다.

Русский한국어EnglishEspañol中文العربيةहिन्दीDeutschFrançaisPortuguêsBahasa Indonesia日本語

관련 영상

Эта бесплатная альтернатива Firebase работает всего в одном файле7:49

Эта бесплатная альтернатива Firebase работает всего в одном файле

Better Stack

커뮤니티의 다른 글

사내 시스템에 llm api 붙일 때 마주하는 현실적인 한계와 대응법

2026년 9월 13일

레거시 백엔드에 GPT-6 Astra 붙일 때 예산 승인과 보안 통과를 먼저 끝내는 법이 있습니다

2026년 9월 13일

에이전트끼리 대화하다 6천만 원 청구서가 나오는 이유

2026년 9월 13일

사내 RAG 벡터 검색에 Okta 권한 필터를 직접 거는 방법

2026년 9월 13일

브라우저 에이전트에게 내 구글 계정을 통째로 넘기면 안 되는 이유

2026년 9월 12일

Apple Won the AI Race

2026년 9월 12일

댓글 (0)

Log in to leave a comment

아직 작성된 글이 없습니다

© 2026 . All rights reserved.

TuBrief
구독 채널
비디오
커뮤니티
로그인

Технические шаги по отказу от Firebase и переходу на собственный хостинг за 5 долларов в месяц

Firebase поначалу кажется привлекательным. Ведь не нужно думать об инфраструктуре, можно просто реализовывать функции. Однако, стоит сервису хоть немного вырасти, цифры в счете начинают расти. Но при мысли о переходе на собственный хостинг становится страшно. Что если данные пропадут? Что если сервис будет останавливаться при каждом обновлении?

Это вполне обоснованные опасения. Но выход есть. Я подготовил пошаговое руководство, как безопасно перенести данные из Firestore в PocketBase — универсальный бэкенд на базе SQLite, а также как настроить среду с бесшовным развертыванием (zero-downtime deployment) с помощью GitHub Actions и Nginx.


Конвейер переноса данных из Firestore в PocketBase

Главная проблема при переносе неструктурированных данных из Firestore в схему PocketBase, основанную на реляционной модели, — это длина идентификаторов. Firestore использует 20-символьные ID документов, тогда как PocketBase по умолчанию ограничивает их 15 буквенно-цифровыми символами. Если игнорировать это ограничение, вы получите ошибку validation_length_invalid.

Чтобы избежать этой проблемы, нужно сократить длину, сохранив при этом уникальность. Самый надежный способ — хешировать ID из Firestore с помощью SHA256 и обрезать его до первых 15 символов. О коллизиях можно не беспокоиться. Даже в крупных наборах данных вероятность совпадения 15-символьного хеша пренебрежимо мала.

Структура скрипта на Node.js для миграции выглядит следующим образом. Используются пакеты firebase-admin и axios для получения данных порциями по 100 записей.

`javascript
// migration.js
const admin = require('firebase-admin');
const axios = require('axios');
const crypto = require('crypto');

admin.initializeApp({
credential: admin.credential.applicationDefault()
});

const db = admin.firestore();
const PB_URL = 'http://127.0.0.1:8090/api/collections/posts/records';

async function migrate() {
let lastDoc = null;
let hasMore = true;

while (hasMore) {
let query = db.collection('posts').orderBy('name').limit(100);
if (lastDoc) {
query = query.startAfter(lastDoc);
}

const snapshot = await query.get();
if (snapshot.empty) {
  hasMore = false;
  break;
}

for (const doc of snapshot.docs) {
  const data = doc.data();
  // Преобразование 20-символьного ID Firestore в 15-символьный буквенно-цифровой
  const newId = crypto.createHash('sha256').update(doc.id).digest('hex').substring(0, 15);
  
  try {
    await axios.post(PB_URL, {
      id: newId,
      firestore_id: doc.id, // запись оригинального ID для проверки на всякий случай
      title: data.title,
      content: data.content
    });
  } catch (err) {
    console.error(`Ошибка переноса: ${doc.id}`, err.response?.data);
  }
}
lastDoc = snapshot.docs[snapshot.docs.length - 1];

}
}

migrate();

`

Чтобы выполнить это без остановки сервиса, нужно организовать переходный период. Сначала с помощью этого скрипта заранее перенесите более 90% существующих данных в фоновом режиме. Затем разверните код с двойной записью (Dual-Write), который при выполнении операций записи в клиентском коде отправляет данные одновременно в Firebase и PocketBase. После того как вы убедились, что данные в обеих системах совпадают, измените эндпоинты для чтения на PocketBase и удалите код Firebase. Для пользователей сервер не будет недоступен ни на секунду.


Настройка круглосуточно работающего сервера PocketBase

PocketBase — это легкий бэкенд, запускаемый в виде одного бинарного файла. Однако, если просто запустить его на Linux-сервере, при непредвиденном завершении процесса из-за нехватки памяти (OOM) сервис не восстановится автоматически.

Необходимо настроить systemd, чтобы процесс автоматически перезапускался при падении. Создайте файл /etc/systemd/system/pocketbase.service и добавьте в него следующее содержимое:

`ini
[Unit]
Description=PocketBase Service
After=network.target

[Service]
Type=simple
User=pocketbase
Group=pocketbase
LimitNOFILE=65535
ExecStart=/opt/pocketbase/pocketbase serve --http="127.0.0.1:8090"
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target

`

Здесь важна настройка LimitNOFILE=65535. Она предотвращает отключение пользователей при пиковых нагрузках, когда количество одновременных подключений достигает лимита файловых дескрипторов Linux, что важно при использовании функции подписки в реальном времени (WebSockets).

Перед PocketBase установите Nginx для обработки SSL-сертификатов и проксирования. В настройках /etc/nginx/sites-available/pocketbase необходимо добавить параметры, чтобы избежать задержек в потоковой передаче в реальном времени.

`nginx
upstream pocketbase {
server 127.0.0.1:8090;
}

server {
server_name api.yourdomain.com;

location / {
    proxy_pass http://pocketbase;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;

    # Поддержка WebSockets и SSE (реальное время)
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_buffering off;
}

}

`

Теперь достаточно команды certbot --nginx, чтобы установить HTTPS-сертификат, и базовая инфраструктура готова.

По моему опыту, даже самый дешевый VPS с 512 МБ оперативной памяти легко обрабатывает более 1000 легких запросов в секунду при небольшой настройке SQLite. Чтобы максимизировать производительность SQLite при работе с PocketBase, нужно включить режим WAL (Write-Ahead Log). PocketBase по умолчанию включает WAL, но если вы работаете с SQLite напрямую, проверьте настройки PRAGMA journal_mode=WAL; и PRAGMA synchronous=NORMAL;. Это практически устраняет узкие места, когда запросы ждут записи на диск.


Резервное копирование в реальном времени с Cloudflare R2 и Litestream

Самая распространенная ошибка инди-разработчиков — копирование файла базы данных SQLite (data.db) напрямую с работающего сервера. Этот метод несет высокий риск повреждения данных во время резервного копирования.

Мы используем Litestream. Это инструмент, который перехватывает WAL-фреймы SQLite в реальном времени и отправляет только изменения в облачное хранилище. В качестве хранилища для бэкапов рекомендуется Cloudflare R2. Поскольку плата за исходящий трафик отсутствует, стоимость бэкапов будет практически нулевой, сколько бы вы их ни делали.

Создайте файл конфигурации /etc/litestream.yml следующим образом:

`yaml
dbs:

  • path: /opt/pocketbase/pb_data/data.db
    replicas:
    • type: s3
      bucket: your-r2-bucket-name
      endpoint: https://.r2.cloudflarestorage.com
      access-key-id:
      secret-access-key:

`

При такой настройке, даже если оригинальный файл базы данных будет удален, вы сможете восстановить его до состояния, актуального на последнюю секунду, одной командой:

`bash
litestream restore -if-replica-exists /opt/pocketbase/pb_data/data.db

`

Если во время разработки вы случайно удалили данные и хотите восстановить их на определенный момент времени, это также возможно:

`bash

1. Остановка PocketBase

sudo systemctl stop pocketbase.service

2. Создание файла восстановления на определенный момент времени

litestream restore -timestamp "2026-07-14T15:00:00Z" -o /tmp/recovered.db /opt/pocketbase/pb_data/data.db

3. Проверка целостности данных и замена файла

sqlite3 /tmp/recovered.db "PRAGMA integrity_check;"
mv /tmp/recovered.db /opt/pocketbase/pb_data/data.db
chown -R pocketbase:pocketbase /opt/pocketbase/pb_data/

4. Возобновление работы сервиса

sudo systemctl start pocketbase.service

`

Cloudflare R2 предоставляет 10 ГБ бесплатного места ежемесячно. С учетом того, что также предоставляются бесплатные лимиты на 1 миллион операций записи и 10 миллионов операций чтения, для сервиса одного автора расходы на бэкапы будут стремиться к нулю.


Бесшовное развертывание с помощью GitHub Actions и Nginx

Развертывание через Docker — это удобно, но на маленьких VPS с 512 МБ или 1 ГБ RAM жалко памяти, которую потребляет сам демон Docker. Давайте реализуем бесшовное развертывание без Docker, переключая порты службы Linux systemd (Blue-Green).

Мы воспользуемся особенностью SQLite, позволяющей нескольким процессам читать и писать в один файл базы данных. Мы создадим шаблон службы systemd, который позволит запускать бинарные файлы PocketBase на портах 9011 (Blue) и 9012 (Green) отдельно.

Сначала пример рабочего процесса (workflow) для отправки скомпилированного легкого бинарного файла на сервер через GitHub Actions:

`yaml

.github/workflows/deploy.yml

name: Deploy
on:
push:
branches: [ main ]

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Build
run: |
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o pocketbase main.go
- name: Transfer Binary to VPS
uses: appleboy/scp-action@master
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_KEY }}
source: "pocketbase"
target: "/srv/pocketbase/next_release"

`

Когда бинарный файл попадает на сервер, запускается скрипт развертывания. Он проверяет, какой порт активен сейчас (9011 или 9012), и запускает новую версию бинарного файла на свободном порту.

`bash
#!/bin/bash

deploy_swap.sh

CURRENT_PORT=$(curl -s http://127.0.0.1:8090/api/health | jq -r '.port' 2>/dev/null || echo "9011")

if [ "$CURRENT_PORT" = "9011" ]; then
TARGET_PORT="9012"
else
TARGET_PORT="9011"
fi

Запуск новой версии бинарного файла на целевом порту в фоновом режиме

sudo systemctl start pocketbase@$TARGET_PORT.service

Ожидание 3 секунды для проверки работоспособности

sleep 3
HEALTH_CHECK=(curl−shttp://127.0.0.1:(curl -s http://127.0.0.1:(curl−shttp://127.0.0.1:TARGET_PORT/api/health | jq -r '.status')

if [ "HEALTH_CHECK" = "OK" ]; then # Обновление настроек upstream Nginx на новый порт и перезагрузка echo "upstream pocketbase { server 127.0.0.1:TARGET_PORT; }" | sudo tee /etc/nginx/conf.d/upstream.conf
sudo systemctl reload nginx

# Остановка предыдущей версии процесса
sudo systemctl stop pocketbase@$CURRENT_PORT.service
echo "Развертывание завершено. Переключение на порт $TARGET_PORT успешно."

else
echo "Ошибка развертывания. Новая версия не прошла проверку работоспособности."
sudo systemctl stop pocketbase@$TARGET_PORT.service
exit 1
fi

`

Используя этот метод, вы получаете полноценное бесшовное развертывание без дорогостоящих инструментов оркестрации контейнеров. Даже если пользователь отправит запрос во время развертывания, Nginx в момент перезагрузки (измеряемый миллисекундами) автоматически придержит запрос и безопасно перенаправит его на новый порт.

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