Skip to content

Deployment on Yandex Cloud

The cloud version of Notifly runs entirely on Yandex Cloud serverless services: there are no always-on VMs, billing is only for actual traffic and storage.

This document describes how the deployed stack is organized and how to reproduce it in your own folder.

┌──────────────────────────────────────────────────────────────────────────┐
│ Клиент │
│ (браузер · Android · desktop · MCP-агент · cron · веб-сайт · SMTP) │
└──────┬──────────────┬──────────────┬───────────────┬────────────────────┘
│ HTTPS REST │ WSS │ POST /script │ SMTP
▼ ▼ ▼ ▼
┌──────────────────────────────────────────────────────────────────────────┐
│ Yandex Cloud API Gateway (notifly-ws-gateway) │
│ ─ /api/* → notifly-api (REST) │
│ ─ /ws → notifly-ws-handler (WebSocket) │
│ ─ /script/* → notifly-api (публичные web-script триггеры) │
└──────┬─────────────────────┬──────────────────────────┬─────────────────┘
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────────┐ ┌────────────────────────────┐
│ notifly-api │ │ notifly-ws- │ │ notifly-email │
│ (golang121) │ │ handler │ │ (Mail Trigger → Email Inbox)│
└─────┬────────┘ │ (golang123) │ └──────────┬─────────────────┘
│ └──────┬───────────┘ │
│ │ │
▼ ▼ ▼
┌──────────────────────────────────────────────────────────────────────────┐
│ YDB Serverless (notifly-db) │
│ users · applications · clients · messages · webhooks · email_inboxes │
│ web_scripts · heartbeats · monitors · mcp_tokens · channel_shares │
└──────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────┴──────────────────────────┐
│ Object Storage notifly-ws-connections │
│ connections/<id>.json — состояние WebSocket-сессий │
└──────────────────────────────────────────────────────┘
Timer triggers (cron `* * * * ? *`):
• notifly-heartbeat — проверяет просроченные heartbeat-ы
• notifly-monitor — выполняет активные проверки HTTP/TCP/TLS/...
ComponentResourceType
notifly-apiCloud FunctionREST + Web Script triggers
notifly-ws-handlerCloud FunctionWebSocket handler
notifly-emailCloud Functionreceiving emails via Mail Trigger
notifly-heartbeatCloud Functiontimer check of heartbeats
notifly-monitorCloud Functiontimer check of active monitors
notifly-ws-gatewayAPI Gatewaysingle entry point
notifly-dbYDB Serverlessall business logic
notifly-ws-connectionsObject Storagestate of WebSocket connections

When migrating from the self-hosted version the following were deliberately excluded:

  • Go plugins (.so) — the YC Functions runtime does not support them. See Introduction to plugins for alternatives.
  • OIDC — authorization is only local (BasicAuth + Bearer tokens).
  • Embed UI — the frontend is deployed separately to the app.notifly.ru bucket.
  • Image upload — a stub (uploading images to Object Storage — on the roadmap).
  • Fractional sort keys for messages — simplified to a string field.

You will need:

  • a Yandex Cloud account with a folder;
  • the yc CLI (installation);
  • a service account with roles serverless.functions.admin, ydb.admin, storage.editor, api-gateway.admin;
  • static access keys for the service account (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY) — for S3 from Cloud Function;
  • an IAM token (short-lived, 12 hours) — for the deployment itself.

All secrets are kept in .env at the repository root and are loaded via set -a; source .env; set +a before running the deploy scripts.

Окно терминала
yc ydb database create notifly-db --serverless
# .env: YDB_DSN=grpcs://ydb.serverless.yandexcloud.net:2135/?database=...
cd server_ya
go run ./cmd/initdb # creates schema + admin/admin

The initdb script is idempotent: rerunning it won’t break existing tables or recreate the admin.

Окно терминала
yc storage bucket create --name notifly-ws-connections --max-size 1073741824
Окно терминала
cd server_ya
bash deploy/deploy.sh

The script:

  1. Builds a zip for notifly-api and uploads it as a Cloud Function;
  2. Builds a zip for notifly-ws-handler and uploads it as a Cloud Function;
  3. Applies a unified OpenAPI specification to the API Gateway (REST /{proxy+} + WebSocket /ws).

If you redeploy ws-handler/deploy.sh separately — it will overwrite the API Gateway with a WS-only spec; to restore REST, rerun server_ya/deploy/deploy.sh.

4. Deploy timer functions (heartbeat, monitor)

Section titled “4. Deploy timer functions (heartbeat, monitor)”
Окно терминала
cd server_ya
bash deploy/deploy_heartbeat.sh
bash deploy/deploy_monitor.sh

Each script creates a Cloud Function and attaches a timer-trigger with the schedule * * * * ? * (once per minute). Inside the function a SELECT WHERE next_check_at <= now() is executed using the corresponding YDB index.

Окно терминала
cd server_ya
bash deploy/deploy_email.sh

After deployment, in the console bind the notifly-email function to the desired mailbox (<local>+<alias>@<domain>). User mailboxes are managed via Email Inbox — aliases are stored in YDB, and Mail Trigger routes emails based on the subaddress part.

Окно терминала
cd admin_ya
npm ci
npm run build
bash deploy.sh # synchronizes dist/ to the app.notifly.ru bucket

See also admin deployment instructions — this step is largely automated.

After deployment run:

Окно терминала
bash server_ya/deploy/smoke_test.sh
python3 server_ya/deploy/ws_test.py

Check:

  • GET /health{"health":"green","database":"green"}
  • POST /auth/local/login (Basic admin/admin)
  • POST /application + POST /message ×N
  • GET /message, GET /application/{id}/message
  • wss://.../ws?token=<C...> — handshake 101, ping → pong, status with connection_id and source_ip.
ActionWhere to check
Function logsyc logging read --group-id <log-group> --format json
Function metricsYandex Cloud Console → Cloud Functions → metrics
YDB table stateYDB UI or yc ydb yql ...
Active WS connectionsaws s3 ls s3://notifly-ws-connections/connections/

The stack is optimized for the FREE-tier:

  • YDB Serverless — Request Units, no always-on storage; indexes on next_check_at provide O(1) checks in the heartbeat/monitor timers.
  • Cloud Functions — billed by millisec-execution. Functions wake up only on activity (POST /message, ping, cron).
  • Object Storage — a few bytes per WebSocket connection (connections/<id>.json ~200 bytes).
  • API Gateway — billed per request.

With zero activity Notifly consumes nothing except the fee for reserved space in YDB (~10 ₽/month on the free-tier).