InquirerNorthern Samar leads Eastern Visayas in economic growth at 4.6%The Jerusalem PostIn Pittsburgh, campus antisemitism report’s authors neglected to speak with Jewish studentsPunchEPL: Neto double gives Chelsea 2-0 win at EvertonESPN DeportesChelsea consiguió tres puntos ante un Everton que no dejó de lucharCNN TürkTürkiye’de elektrikli araç şarj tüketimi eylülde yüzde 14,6 azaldıESPNDropping your QB? Six fantasy replacements to pick up ASAP for Week 6Bollywood HungamaCountry Delight names Rishab Shetty as its new brand ambassadorRTP DesportoOuro renovado. Iúri Leitão volta a sagrar-se campeão mundial de ciclismo de pistaZDF heuteAktuelle Pressemitteilungen des ZDFColliderRussell Crowe’s Best-Reviewed Movie in 27 Years Is Officially Coming HomeSCMP ChinaHow Taiwan’s new AI satellite system aims to speed up early warning against PLA movesMexico News DailySpider-Man 2099 has a Día de Muertos secret in his origin story
The Daily Newsstand · Free, Always
Saturday, October 17, 2026

Self‑hosted PaaS в 20 МБ памяти: что мы выкинули из классической архитектуры и почему

Translate

Мы делаем Pier — open‑source панель для деплоя приложений на свои серверы, по смыслу близкую к Coolify, Dokku или CapRover. Подключаешь Git‑репозиторий, получаешь контейнер за Traefik с сертификатом Let's Encrypt, бэкапы базы в S3 и алерты.

Функционально таких проектов много, поэтому в этой статье речь пойдёт не о том, что Pier умеет, а о том, как он устроен: мы с самого начала ставили жёсткое ограничение по ресурсам и почти каждое архитектурное решение выросло из него. За семь месяцев и ~550 коммитов набралось достаточно решений и граблей, о которых хочется рассказать.

Ограничение, с которого всё началось

Coolify после установки поднимает Laravel‑приложение, PostgreSQL, Redis, Horizon, Soketi и Traefik. В простое это 750 МБ — 1,2 ГБ RAM. Для VPS с 1 ГБ памяти это неприемлемо: панель съедает сервер раньше, чем на нём появится первое приложение.

Отсюда требование: панель в простое должна занимать десятки мегабайт, а не сотни. В итоге получилось 20–40 МБ RSS для процесса Pier плюс Traefik. Ниже о том, за счёт чего.

Стек

Слой

Выбор

Почему

Язык

Rust

один статический бинарник, без рантайма, предсказуемая память

HTTP

Axum + Tokio

async, WebSocket для логов и метрик

Docker

Bollard

Docker Engine API напрямую, без вызова CLI

Хранилище

SQLite (rusqlite)

без отдельного контейнера с СУБД

UI

MiniJinja + HTMX + Alpine.js

серверный рендеринг, ~30 КБ JS

Стили

Tailwind v3, собирается заранее

итоговый CSS вшит в бинарник

Прокси

Traefik

маршрутизация и ACME без нашего кода

Почему SQLite, а не Postgres

Панель хранит конфигурацию: проекты, сервисы, домены, секреты, историю деплоев, метрики алертов. Это небольшой объём данных с редкими записями от единственного процесса. Отдельный Postgres здесь дал бы +100–200 МБ памяти, ещё один контейнер, который нужно бэкапить и обновлять, и сетевую зависимость при старте панели.

Миграции у нас — append‑only массив SQL‑строк в коде; номер последней применённой хранится в БД. Никаких внешних инструментов миграций: бинарник при старте сам доводит схему до актуальной. Правило одно: старые миграции никогда не редактируются, только добавляются новые.

Почему HTMX, а не React

Панель управления — это в основном формы, таблицы и логи. SPA здесь означал бы отдельную сборку фронтенда, дублирование валидации и сотни килобайт JS. С HTMX сервер отдаёт готовые HTML‑фрагменты, а Alpine.js закрывает локальную интерактивность (модалки, вкладки, тумблеры).

Шаблоны MiniJinja, CSS, шрифты и переводы вшиты в бинарник через rust-embed. Следствие, о котором мы узнали не сразу: даже в debug‑сборке правка шаблона требует пересборки, потому что файлы с диска не читаются. Зато деплой самой панели — это замена одного файла.

Локализация без runtime‑оверхеда

Интерфейс переводится через rust-i18n, а текущая локаль запроса хранится в task‑local переменной Tokio. В шаблонах доступны функции t() и te(), в ошибках API — те же ключи. Каталог сейчас около 2000 ключей; добавить язык — значит положить ещё один YAML‑файл.

Аутентификация: почему мы отказались от JWT

Изначально сессии были на JWT. Потом мы перешли на opaque‑сессии в SQLite и не жалеем:

  • JWT нельзя отозвать без чёрного списка, а чёрный список — это и есть серверное хранилище сессий, только неудобное.

  • Для панели с одним инстансом stateless‑аутентификация ничего не даёт: проверка сессии — это поиск по индексу в локальной SQLite.

  • С серверными сессиями просто сделать то, что нужно админке: выход со всех устройств при смене пароля, ротацию ID сессии после логина, абсолютный TTL, привязку к User‑Agent, сборку мусора просроченных сессий, мгновенный оффбординг удалённого пользователя.

Сверху — TOTP 2FA, роли с проверкой прав на уровне ресурса и API‑токены для CI.

Несколько серверов

На дополнительные серверы ставится pier-agent — отдельный лёгкий бинарник, который выполняет Docker‑операции и отдаёт метрики. Центральная панель общается с агентами по HTTP, агент шлёт heartbeat.

Самая интересная часть — кросс‑серверные кластеры баз данных: реплика MongoDB или Redis, разнесённая по разным машинам. Здесь пришлось решать связность контейнеров между хостами без оверлейной сети Docker Swarm. Мы публикуем порты узлов кластера в выделенном диапазоне и открываем их в файрволе только для внутренних подсетей.

Самообновление без SSH

Панель работает не от root. Но обновление затрагивает systemd‑юниты, вспомогательные сервисы и сам бинарник — это root‑операции. Решение:

  1. Панель пишет файл‑запрос в специальную директорию.

  2. systemd .path‑юнит видит файл и запускает root‑сервис pier-updater.

  3. Апдейтер скачивает последний официальный релиз, проверяет sha256 и запускает тот же установщик, что и curl | bash.

  4. Статус и лог пишутся в файлы, которые панель показывает пользователю.

Принципиальный момент: панель может только попросить обновиться. Какую версию и откуда ставить, решает root‑сторона. Скомпрометированная панель не может подсунуть апдейтеру свой бинарник.

Сборка без Dockerfile

Для репозиториев без Dockerfile мы интегрировали Railpack (преемник Nixpacks от Railway) поверх локального BuildKit. Это единственная по‑настоящему тяжёлая часть системы: компиляция пользовательского кода требует 4+ ГБ RAM и десятки гигабайт под кэш. Поэтому:

  • на хостах с RAM < 4 ГБ UI показывает жёсткое предупреждение;

  • параллельные сборки ограничены (по умолчанию одна);

  • кэш BuildKit ежедневно подрезается до ~10 ГБ / 7 дней;

  • установку Railpack можно отключить флагом, тогда остаются деплой из Dockerfile, образа и Compose.

Грабли

Хостеры фильтруют порты. На одном из VPS входящие соединения на 5432 молча отбрасывались на уровне сети хостера. ufw тут ни при чём — к тому же он не управляет портами, которые публикует Docker. Теперь для публичных портов БД мы советуем нестандартные значения.

Telegram‑алерты из России. api.telegram.org на российских VPS часто недоступен. Уведомления в Telegram требуют прокси или туннеля через зарубежный сервер, поэтому у нас есть и другие каналы: email, Discord, Slack.

Traefik, оставленный выключенным. Одно действие в панели могло остановить контейнер прокси и не поднять его обратно — все сайты на сервере ложились. Теперь панель гарантирует, что Traefik не может остаться в остановленном состоянии.

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

Bitnami и MongoDB на новых ядрах. Обновление образов сломало кластерные шаблоны, а MongoDB не стартовала на свежих ядрах Linux. Поддержка каталога из 40 шаблонов оказалась отдельной постоянной работой.

Итоги

Coolify

Pier

RAM в простое

750 МБ — 1,2 ГБ

20–40 МБ (+ Traefik)

Диск

~1 ГБ

15–30 МБ

Процессов/контейнеров панели

6+

1 + Traefik

Минимальный VPS

2 ГБ / 2 vCPU

512 МБ / 1 vCPU

Лицензия AGPL-3.0. Установка: curl -fsSL https://pier.team/install | sudo bash.

Код: github.com/joveptesg/pier
Документация

Если у вас есть опыт с Coolify, Dokku или CapRover, расскажите в комментариях, чего вам в них не хватало. Нам это поможет решить, что делать дальше.

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.