Как я выбирал стек для SaaS-мониторинга сайтов
- четверг, 9 апреля 2026 г. в 00:00:09
Я написал сервис мониторинга сайтов. Проверяю доступность из 10 точек мира, шлю алерты в Telegram, Slack, на почту. Есть бесплатный план, платные тарифы, status-страницы, SSL-мониторинг, серверный агент — всё как у больших.

Тут не будет универсальных советов «как выбрать стек для стартапа». Просто мой опыт: что взял, почему, и что бы поменял.
Выбирал между Django, Flask и FastAPI. Django отпал сразу — мне не нужен его ORM (я хотел async), не нужен встроенный шаблонизатор (фронт на Vue), не нужен admin panel (написал свой). Django — это как купить грузовик, чтобы ездить за хлебом.
Flask — ок, но голый. Роутинг есть, а структуры нет. Каждый Flask-проект, который я видел после полугода разработки, превращается в кашу.
FastAPI сел идеально:
Async из коробки. У меня checker делает сотни HTTP-запросов параллельно — без async это смерть.
WebSocket поддержка. Dashboard обновляется в реалтайме через Redis pub/sub.
Dependency Injection через Depends() — вместо глобальных переменных.
Автодокументация на /api/docs. Swagger генерится сам из тайпхинтов. Я ничего не писал.
Вот как выглядит типичный эндпоинт:
@router.get("/sites") async def list_sites( db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_user), ): sites = await db.execute( select(Site).where(Site.user_id == current_user.id) ) return sites.scalars().all()
Чистый async, типизированный, без магии. Pydantic валидирует всё, что приходит и уходит. Если кто-то пришлёт {"url": 123} вместо строки — FastAPI вернёт 422 без единой строчки моего кода.
Чекер проверяет сайты каждые 1-10 минут. Каждая проверка — строка в таблице: timestamp, статус-код, время ответа, регион, IP. За месяц на 20 мониторах это ~860 000 строк. За год — 10 миллионов.
Обычный PostgreSQL справится. Но запросы «покажи среднее время ответа за последние 30 дней с группировкой по часам» будут тормозить.
TimescaleDB — расширение для PostgreSQL. Ставишь, делаешь SELECT create_hypertable('checks', 'time') — и таблица автоматически партиционируется по времени. Запросы по временным диапазонам летают. При этом это всё тот же PostgreSQL — тот же pg_dump, тот же Alembic, те же миграции.
Почему не MongoDB: мне нужны JOIN'ы. Сайт → проверки → инциденты → пост-мортемы → пользователь → подписка. Это реляционные данные. Запихивать их в документы — мучение.
Почему не ClickHouse: оверкилл. У меня не петабайты логов, а миллионы строк. TimescaleDB хватает с запасом, и он не требует отдельной инфраструктуры.
Docker-образ — timescale/timescaledb:latest-pg15. Пул — 20 соединений с overflow до 30:
engine = create_async_engine( DATABASE_URL, pool_size=20, max_overflow=10, pool_pre_ping=True, )
React я знаю. Vue тоже знаю. Выбрал Vue.
Причина тупая: мне быстрее на нём писать. Composition API во Vue 3 — это почти тот же React hooks, но без boilerplate. Не нужен useEffect с массивами зависимостей. watch() делает то, что написано — следит за реактивной переменной.
Стейт-менеджмент — Pinia. Это как Vuex, только без мутаций и без commit('SET_SOMETHING', payload). Просто стор с реактивными свойствами. Пишешь store.user = data — и всё.
Билд — Vite. Холодный старт dev-сервера за 300мс. После webpack это как пересесть с трамвая на мотоцикл.
Полный package.json:
{ "dependencies": { "vue": "^3.4.21", "vue-router": "^4.3.0", "pinia": "^2.1.7", "vue-i18n": "^9.14.5", "apexcharts": "^3.54.0", "@simplewebauthn/browser": "^13.3.0" }, "devDependencies": { "vite": "^5.2.0", "@vitejs/plugin-vue": "^5.0.4" } }
6 зависимостей в production. Шесть. Никаких axios (fetch хватает), никаких lodash (не нужен), никаких UI-фреймворков (свой CSS, ~20KB). Dashboard грузится за секунду.
У меня 12 фоновых задач, которые крутятся по расписанию:
Проверка сайтов — каждую минуту
SSL-сертификаты — раз в сутки
Safe Browsing (Google) — каждые 6 часов
DNS-записи — каждый час
Heartbeat-мониторы — каждую минуту
Отправка отчётов — ежедневно
Скриншоты упавших сайтов — по событию
...ещё пять штук
Celery + Redis — скучный выбор. И это хорошо. Драматик (Dramatiq) модный, но экосистема меньше. RQ простой, но нет Beat (планировщика). Celery тяжёлый, с кучей настроек — зато работает. Пять лет на проде у тысяч компаний.
celery = Celery( "valpero", broker=REDIS_URL, include=[ "app.tasks.checker", "app.tasks.notifier", "app.tasks.billing", "app.tasks.screenshot_task", # ...ещё 8 модулей ], )
Redis заодно работает как pub/sub для реалтайм-обновлений дашборда. Один Redis — два применения.
Серверный агент собирает метрики: CPU, RAM, диск, сеть, процессы, Docker-контейнеры. Раз в 30 секунд шлёт JSON на бэкенд.
Написал на Go. Не на Python. Вот почему:
Размер. Go-бинарник — 5.6 MB. Один файл, без зависимостей. Скачал, chmod +x, запустил. Python-агент — это Python runtime (~30 MB) + pip + venv + зависимости + psutil. На дешёвом VPS с 1 GB RAM это больно.
Кросс-компиляция. GOOS=linux GOARCH=arm64 go build — и готов бинарник для ARM. Без Docker, без CI, без танцев. Поддерживаю amd64 и arm64 одной командой.
gopsutil. Библиотека для сбора системных метрик. Один import — и есть CPU, RAM, диск, температура, процессы. На Python есть psutil, но тогда нужен Python на целевой машине.
Агент ставится одной командой:
curl -sSL https://valpero.com/agent/install.sh | sudo bash -s -- --token YOUR_TOKEN
Скачивает бинарник, создаёт systemd-сервис, всё. Без Python, без npm, без ничего. То как и где генерится токен не расскажу, может пока что.

Нужны были нативные приложения для Windows и macOS. Не Electron — он жрёт 200 MB RAM ради обёртки над веб-страницей.
Windows — pywebview. Открывает Valpero-дашборд в нативном WebView2 (Chromium). По сути обёртка, но весит мало и запускается быстро. Билд через PyInstaller в .exe.
macOS — rumps. Иконка в меню-баре, показывает статус мониторов. Зелёная — всё ок, красная — что-то упало. Клик — попап с деталями. Без браузера, чисто нативный виджет.
Почему два разных подхода? На маке menubar-виджет полезнее, чем окно. На винде нет menubar, зато есть трей — но полноценное окно удобнее.

Основной сервер — Hetzner, Хельсинки, ~€4/мес. FastAPI, PostgreSQL, Redis, Celery — всё в Docker Compose.
Probe-ноды для мониторинга из разных регионов — Ferrum от WorkTitans по €1.20/мес за штуку. 1 vCore, 1 GB RAM, 15 GB NVMe. Есть в Исландии, Италии, Японии, Бразилии, Новой Зеландии, Казахстане. Нода — один Python-скрипт, ~200 строк. Подключается к центру по WebSocket, получает задания, отправляет результаты.
Итого: €4 (core) + 6 × €1.20 (пробы) = €11.20/мес за 10 точек мониторинга на четырёх континентах. Ещё 4 ноды у меня на существующих серверах.
Год назад я бы ответил иначе на пару вопросов.
Celery → может Dramatiq или Taskiq. Celery работает, но конфигурация — это ад. Документация огромная и запутанная. Celery Beat хранит расписание в файле (по умолчанию). Если воркер умирает криво то расписание может сломаться. Для нового проекта я бы посмотрел на Taskiq он async-native и проще.
Один репозиторий. Бэкенд, фронт, агент, десктоп-приложения, лендинг — всё в одном репо. Для соло-разработчика это ок. Но git log превращается в кашу. Уже вынес агент в отдельный репозиторий, как и виджеты.
CSS без фреймворка. Написал ~20KB CSS руками. Для дашборда это нормально, но для лендинга с 6 языковыми версиями — боль. Каждый визуальный баг надо фиксить в 6 файлах. Tailwind бы упростил жизнь, вопрос — не поздно ли еще?
Бэкенд и базу менять бы не стал. FastAPI + PostgreSQL + TimescaleDB — попадание в десятку.
Слой | Технология |
Бэкенд | FastAPI + Uvicorn |
ORM | SQLAlchemy (async) |
База | PostgreSQL + TimescaleDB |
Кэш/брокер | Redis |
Очередь | Celery + Beat |
Фронтенд | Vue 3 + Pinia + Vue Router |
Билд | Vite |
i18n | vue-i18n (6 языков) |
Агент | Go + gopsutil |
Скриншоты | Playwright (Chromium) |
Десктоп Win | pywebview |
Десктоп Mac | rumps |
Платежи | Stripe |
Алерты | Email, Telegram, Slack, Discord, SMS, WhatsApp, Webhook |
Деплой | Docker Compose + Nginx |
Хостинг | Hetzner + WorkTitans Ferrum |
Общая стоимость инфраструктуры: €11.20/мес. Python 3.11. Один разработчик. Много времени.
Если делаете SaaS и выбираете стек — не парьтесь слишком долго. Берите то, на чём быстро пишете. Стек не определит успех. Продукт — определит. Мой мониторит сайты на valpero.com.
Как то так. Хостинг на Хетзере поэтому возможно понадобится VPN или bypasser DPI,ну и большей статей позже.