Когда разработчик говорит «у нас всё на Firebase», он обычно называет одним словом несколько разных вещей. Где-то там живёт вход пользователей. Где-то лежат документы. Где-то хранятся загруженные картинки. Ещё есть функции, которые срабатывают сами, и клиентская библиотека, которая тихо делает массу работы: держит сессию, обновляет токен, кэширует данные, переподписывается на изменения после обрыва сети. Пока всё работает, это выглядит как один сервис. При переезде он распадается на части, и у каждой части своя цена замены.
Поэтому оценивать переезд на собственный сервер стоит не с вопроса «какой VPS взять», а с вопроса «что именно мы используем». Аренда сервера занимает несколько минут, а вот переписывание приложения может занять недели. Ниже разберём, как составить честную карту зависимостей, какие части заменяются обычным API, а какие меняют поведение приложения, и с чего начать, чтобы не застрять посередине.
Карта зависимостей: ищем не сервисы, а вызовы
Первый шаг не требует ни сервера, ни новых технологий. Нужно пройтись по коду и консоли проекта и выписать, что приложение реально делает с Firebase. Список включённых в консоли продуктов обманчив: что-то включали на пробу и забыли, а что-то, наоборот, используется неявно через клиентский SDK.
Удобно искать по импортам и инициализации. В клиентском коде видно, какие модули подключены: аутентификация, Firestore, хранилище, обмен сообщениями. В серверной части видны функции и обращения через административный SDK. Для каждого найденного места полезно записать четыре вещи: что вызывается, откуда (браузер, мобильный клиент, функция), как часто и что произойдёт, если вызов перестанет работать.
Получится таблица примерно такого вида. Это условный пример небольшого приложения для записи клиентов, а не описание конкретного проекта.
| Что используется | Где вызывается | Что ломается при отключении | Тип замены |
|---|---|---|---|
| Вход по email и паролю | браузер | никто не войдёт | свой слой авторизации, перенос пользователей |
| Чтение и запись документов | браузер, функции | пропадают все данные | новая база и API |
| Подписка на изменения списка | браузер | список перестанет обновляться сам | смена поведения: опрос или push-канал |
| Загрузка аватаров | браузер | не загрузятся картинки | файловое хранилище и раздача |
| Функция при создании записи | серверная часть | не уйдёт письмо клиенту | фоновая задача или очередь |
Уже по такой таблице видно главное: строки неравноценны. Одни заменяются «подставьте другой адрес», другие требуют решить, как приложение вообще должно себя вести.
Что заменяется обычным API, а что меняет поведение
Полезно разделить зависимости на две группы. В первой клиент просто просит сервер что-то сделать и получает ответ. Создать запись, получить список, загрузить файл, отправить письмо. Это привычная модель запрос-ответ, и её можно воспроизвести на любом бэкенде: вы пишете обработчики, клиент вызывает их по HTTPS. Трудоёмко, но принципиально понятно.
Во второй группе приложение полагалось на поведение, которое давал SDK. Типичные примеры:
- интерфейс обновляется сам, когда данные изменились на другом устройстве;
- приложение продолжает работать без сети, а изменения синхронизируются позже;
- права доступа проверяются правилами рядом с данными, а не в вашем коде;
- сессия пользователя и токены обновляются незаметно.
Всё это можно воссоздать, но не «переключением адреса». Каждое такое свойство придётся либо реализовать вручную, либо сознательно отказаться от него. И здесь честный вопрос к себе звучит так: пользователь заметит отсутствие этого свойства или нет? Если список заказов обновляется раз в минуту вместо мгновенного, а работают с ним раз в день, это может оказаться приемлемой потерей. Если приложение — общий редактируемый документ, где важна живая синхронизация, цена совсем другая.
Отсюда вывод, который стоит принять сразу: универсальной прямой замены всех SDK и realtime-возможностей не существует. Есть готовые платформы с похожим набором функций, и сравнивать их стоит построчно с вашей картой, а не по общему названию. Например, о схожей по идее самохостируемой платформе можно прочитать в материале Appwrite на собственном VPS. Но даже при таком выборе вопрос остаётся тот же: какие из ваших конкретных вызовов поддерживаются, а какие придётся переписать.
Авторизация: пользователи переезжают отдельно от данных
Самая частая ошибка в планировании — считать, что вход пользователей переносится вместе с базой. На деле это независимая задача, причём с острыми углами.
Начать можно с выгрузки. Инструмент командной строки Firebase умеет выгружать пользователей в файл формата JSON или CSV, это описано в документации по импорту и экспорту аутентификации. В выгрузке есть идентификаторы, адреса почты и данные для проверки паролей. Тут и начинаются нюансы.
Пароли не хранятся в открытом виде, хранятся их хеши. По документации Firebase использует модифицированный вариант SCRYPT, а параметры хеширования чувствительны и должны храниться защищённо. Практический смысл такой: чтобы пользователь мог войти в новой системе со старым паролем, новая система должна уметь проверять именно этот вариант хеша с этими параметрами. Если ваш новый стек такого обработчика не имеет, прямой перенос паролей не получится. Параметры при этом нельзя вставлять в тикеты, чаты и репозиторий: их место в защищённом хранилище секретов, а в статьях и заметках для команды достаточно ссылки на то, где они лежат.
Если совместимой проверки нет, остаётся запасной путь, и он тоже стоит денег. Пользователям нужно предложить сбросить пароль при первом входе, либо поддерживать старый способ входа параллельно на время миграции. Первое просто технически, но вызывает поток писем и часть пользователей, которые не вернутся. Второе удобно для людей, но дольше живёт в коде. Выбор здесь не технический, а продуктовый, и его лучше обсудить до начала работ.
Есть ещё тонкость, о которой забывают: вход через внешних провайдеров, например через аккаунт крупного сервиса. Для таких пользователей пароля нет, зато есть привязка к конкретному идентификатору провайдера и настройки приложения на стороне этого провайдера. Новому серверу придётся заново зарегистрироваться как приложение и сохранить соответствие между старыми и новыми учётными записями. Проверьте, какие способы входа реально включены, и посчитайте долю каждого: от этого зависит объём работы.
Правила доступа — это логика, а не конфигурация
В проекте на Firebase права часто описаны правилами безопасности рядом с данными. Они читаются как короткие условия: «читать можно только свои документы», «писать может владелец». При переезде это превращается в код вашего API, и автоматического преобразования ждать не стоит: правила нужно прочитать и заново выразить в виде проверок на сервере или в политиках новой базы.
Практичный приём — выписать правила простым языком, по одному предложению на каждое, и пометить, какие из них выражают бизнес-смысл, а какие появились случайно. Часто выясняется, что часть правил давно не соответствует реальному поведению приложения. Переезд — удобный повод это обнаружить, но и риск: если пропустить правило, на новом сервере у пользователя появится доступ к чужим данным. Поэтому для каждого правила стоит написать хотя бы один тест: «пользователь А не видит данные пользователя Б».
База данных: документы не равны таблицам
Firestore хранит данные как документы в коллекциях, часто с вложенными структурами и ссылками. Привычная реляционная база устроена иначе. Поэтому вопрос не в том, как выгрузить, а в том, как перестроить модель.
Выгрузка сама по себе возможна. По документации Firestore, управляемый экспорт и импорт используют Cloud Storage и доступны через gcloud и API. Нужно учесть два практических момента: для этого требуются соответствующие права и включённый биллинг, а экспорт тарифицируется по чтениям документов. Для небольшой базы это, скорее всего, мелочь, но заранее прикиньте число документов, чтобы не удивиться счёту.
Важно понимать, чем экспорт не является. Это формат, предназначенный для возврата данных в Firestore, а не готовый набор строк для другой СУБД. Совместимость такого файла с PostgreSQL или другой базой никто не обещает, поэтому между выгрузкой и новой базой почти наверняка понадобится ваш скрипт преобразования. Его задача — пройти по документам, разложить вложенные поля по таблицам или оставить их как JSON-колонку, восстановить связи и проверить, что ничего не потерялось.
Здесь есть две стратегии, и выбирать между ними стоит по карте зависимостей.
- Перенести структуру как есть. Документы остаются документами, например в базе документного типа. Скрипт простой, форма данных похожа, код меняется меньше. Но вы сохраняете привычки модели, включая дублирование данных, которое Firestore поощряет ради быстрых чтений.
- Переложить в реляционную модель. Дольше и требует проектирования схемы, зато связи, ограничения и выборки с соединениями становятся обычными средствами базы. Для приложений с отчётами и сложными фильтрами это часто окупается.
Обычно вторая стратегия выигрывает, когда данные по природе табличные: клиенты, заказы, записи. Первая разумнее, если структура действительно документная и запросы простые. И ещё один довод в пользу осторожности: запросы Firestore имеют свои ограничения и особенности, к которым приложение могло подстроиться. Новая база позволит больше, но запросы придётся написать заново.
Realtime: решаем, нужна ли подписка на самом деле
Подписка на изменения — самое заметное свойство, которое придётся пересмотреть. На собственном сервере есть несколько способов получить похожий эффект: периодический опрос, длинные соединения, потоки событий от сервера, веб-сокеты. Они различаются сложностью и нагрузкой. Опрос раз в несколько секунд реализуется за вечер и для многих экранов достаточен. Веб-сокеты дают мгновенность, но добавляют состояние на сервере, переподключения и вопросы масштабирования.
Практичный подход: перечислите экраны, где подписка есть, и для каждого задайте вопрос «что случится, если данные обновятся с задержкой в десять секунд?». Для списка задач — ничего. Для чата или совместного редактирования — многое. Скорее всего, настоящих realtime-экранов окажется меньше, чем кажется, и их можно вынести в отдельный этап.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации Россия, США, Франция и Великобритания. Оплата картой РФ и по СБП.
Файлы: хранение, раздача и адреса
Файлы выглядят самой простой частью: взял каталог, скопировал. Но у них три слоя, и каждый может преподнести сюрприз.
Первый слой — сами байты. Их нужно выгрузить и положить в новое хранилище: каталог на диске, либо объектное хранилище, совместимое с обычными клиентами. Для небольшого приложения каталога на NVMe-диске часто достаточно, а вот объём стоит посчитать заранее и сравнить с диском выбранного тарифа.
Второй слой — адреса. В базе нередко лежат ссылки на файлы с адресом старого хранилища и временными токенами доступа. После переезда такие ссылки перестанут работать, и их нужно либо заменить в данных, либо сделать слой, который преобразует старые адреса в новые. Это скучная, но обязательная часть скрипта миграции.
Третий слой — права и загрузка. Раньше клиент мог загружать файл напрямую в хранилище, а правила проверяли, кому это можно. Теперь нужно решить, как это будет устроено: загрузка через ваш API, либо выдача временной ссылки на загрузку. Выбор влияет на нагрузку: если все загрузки идут через приложение, оно будет читать и писать файлы целиком. Дополнительно придётся подумать об ограничении размера и типов файлов, а также о раздаче: отдавать файлы напрямую веб-сервером эффективнее, чем через код приложения.
Фоновые функции и уведомления
Облачные функции создают иллюзию, что код «просто срабатывает» при событии: создали документ, и ушло письмо. На своём сервере за этим стоит конкретный механизм, и его нужно выбрать.
Для небольшого приложения подходят три типовых решения:
- Обработчик внутри приложения. После сохранения записи код сам вызывает отправку. Проще всего, но если отправка упадёт, придётся продумать повтор.
- Очередь задач. Запись создаёт задачу, отдельный процесс забирает её и выполняет, повторяя при сбое. Надёжнее, но это ещё один компонент.
- Расписание. Для задач вида «каждую ночь пересчитать» достаточно планировщика на сервере.
Здесь легко споткнуться на гарантиях. Если функция в Firebase запускалась по событию базы, вы могли не задумываться, что будет при повторном запуске. В собственной реализации придётся решить, как не отправить одно письмо дважды. Транзакция вашей базы не охватывает внешний вызов к почтовому сервису, поэтому надёжной схемой будет хранить в базе состояние задачи и отмечать выполнение отдельным шагом, допуская, что при сбое в неудачный момент повтор всё же возможен. Для писем это обычно терпимо, для платежей — нет, и там нужен отдельный продуманный механизм.
Отдельно стоят push-уведомления на мобильные устройства. Это не просто функция, а канал, который связан с вашим приложением и сервисами платформ-владельцев систем. Если приложение их использует, не стоит предполагать, что собственный сервер заменит этот канал одним действием. Отметьте их в плане как самостоятельную часть и решите, переносится ли она сейчас вообще. Для веб-приложения без мобильных клиентов вопрос часто снимается: достаточно писем и уведомлений внутри интерфейса.
Первый перенос: один законченный сценарий
Соблазн переехать целиком, одним релизом, понятен, но рискован: слишком много движущихся частей. Надёжнее выбрать один небольшой пользовательский сценарий, который можно пройти от начала до конца на новом стеке, и сделать его первым.
Хороший кандидат обладает тремя свойствами. Он затрагивает немного сервисов, например вход и одну коллекцию, но не требует realtime. Он понятен пользователю, и по нему легко заметить поломку. И он обратим: если что-то пошло не так, можно вернуться к старому поведению без потери данных.
Для нашего условного приложения записи клиентов это мог бы быть просмотр личного списка записей. Он включает вход, чтение документов одного пользователя и проверку прав. Он не требует файлов, функций и живых подписок, но проходит через самые важные слои: авторизацию, модель данных и правила доступа. Если здесь всё сходится, остальное — расширение уже проверенной основы.
Пока идёт работа, есть смысл держать старую систему основной, а новую запускать параллельно. Данные для проверки можно периодически выгружать и сверять: число записей, выборочные документы, права разных пользователей. Расхождения в такой сверке — самый дешёвый способ найти ошибки модели. Только когда сценарий стабильно проходит проверки, имеет смысл решать, как переключать пользователей.
Где здесь VPS и сколько он значит в общей цене
Собственный сервер нужен для того, чтобы разместить на нём новые компоненты: приложение или API, базу, файлы, очередь, обратный прокси. Всё это умещается на одной машине, если проект небольшой, и в этом сильная сторона такого подхода: вы получаете root-доступ и сами решаете, что ставить. VPS MAATRIX работают на NVMe и процессорах AMD EPYC, доступны в нескольких локациях, а оплатить можно картой РФ или через СБП; сервер создаётся автоматически вскоре после оплаты. Полный список тарифов и актуальное наличие образов смотрите на странице аренды VPS.
Ресурсы тарифов в линейке растут от 1 vCPU и 2 ГБ памяти (Ba) до 16 vCPU и 32 ГБ (Djed). Это ориентир для первого выбора, а не обещание ёмкости: сколько пользователей выдержит ваше приложение, покажет только замер на ваших сценариях. Разумный порядок такой: взять скромный тариф для первого сценария и параллельного запуска, измерить потребление памяти и диска под реальной нагрузкой, а затем решать, нужен ли шаг вверх. Памяти базе и приложению обычно требуется больше, чем кажется на бумаге, а диск придётся делить между данными, файлами и логами.
Теперь о главном: стоимость сервера — самая маленькая часть общей картины. Чтобы оценить переезд честно, сложите три статьи.
- Разработка. Новый API, перенос правил, миграционные скрипты, переписывание клиентских вызовов, тесты. Это почти всегда крупнейшая статья.
- Переход. Выгрузка и сверка данных, параллельная работа двух систем, обработка пользователей с паролями и внешними входами, возможные просьбы сбросить пароль.
- Поддержка. То, что раньше делала платформа и что теперь ваше: обновления системы и зависимостей, наблюдение за диском и памятью, резервные копии, реакция на сбои, дежурство. Резервное копирование автоматически не гарантируется ни в одном тарифе, его нужно настроить и проверить самому.
Для поддержки полезно оценить не только деньги, но и часы в месяц, и главное — кто будет этим заниматься. Если в команде нет человека, готового отвечать за сервер, облачная платформа брала на себя часть работы, которую вы теперь покупаете собственным временем. Это не аргумент против переезда, но он должен быть в расчёте. Прямое сравнение «месяц в облаке против месяца на VPS» без этих строк почти всегда обманчиво.
Что переносить сейчас, а что отложить
Если свести разбор к решению, получится примерно такая раскладка для небольшого веб-приложения.
Разумно переносить в первую очередь: модель данных и API для основных сценариев, потому что это ядро, от которого зависит всё остальное; вход пользователей с продуманной стратегией для паролей, потому что она влияет на опыт людей и требует времени на проверку; файлы, если их немного и они раздаются просто, поскольку это механическая работа с понятным результатом.
Разумно оставить на отдельный этап: экраны с живыми подписками, пока не ясно, нужна ли там мгновенность; офлайн-режим и синхронизацию, если приложение ими пользуется; push-уведомления для мобильных клиентов; сложные цепочки функций, где важны гарантии выполнения. Эти части можно временно оставить на прежней платформе, если приложение допускает такое соседство, и переносить, когда основа уже стоит и измерена.
Такой порядок не выглядит эффектно, зато каждая его ступень заканчивается работающим результатом. Вы не рискуете оказаться посередине, когда старое уже отключено, а новое ещё не умеет главного. А карта зависимостей, с которой всё началось, превращается в рабочий план: строки отмечаются по мере переноса, и в любой момент видно, сколько осталось и что из оставшегося действительно стоит затрат.