Deployment
Two supported deployment shapes:
Two supported deployment shapes:
- Docker Compose — single host, fine up to ~hundreds of clients.
- Kubernetes — horizontally scaled (planned; manifests not yet shipped).
This page documents the compose path. Kubernetes lands in a future phase once the hosting target decision is finalised.
Prerequisites
- Docker Engine 24+ and Docker Compose v2.
- A registered Telegram bot (
BotFather→ token). - (Optional) Telegram
api_id/api_hashfrommy.telegram.orgif you intend to run the userbot pool. - ~5 GB of disk for Postgres + Redis + MinIO data volumes.
Environment
Create .env in the repo root (the .env.sample lists every variable):
# Bot
BOT_TOKEN=123456:ABC-DEF…
OWNER_TG_USER_ID=12345678
# Telegram API (userbot pool — optional in dev)
TELEGRAM_API_ID=…
TELEGRAM_API_HASH=…
# Crypto
KMS_PROVIDER=env
AES_MASTER_KEY=<64 hex chars>
# Postgres / Redis come from the compose stack; only override for remote DBs.
For production, set KMS_PROVIDER=aws_kms (or gcp_kms) and supply
KMS_REGION / KMS_KEY_ID. The dev default keeps the master key in env so
you can iterate locally; never use that in production.
Bring-up
# 1. Start the data + observability stack
docker compose up -d postgres redis minio meilisearch \
prometheus loki grafana
# 2. Apply migrations (runs as the superuser).
make migrate
# 3. Seed the parameters registry.
make seed-params
# 4. Bring up application processes (one at a time, or all via profile).
docker compose --profile workers up -d
The bot, API and userbot processes run outside of compose by default during local development:
make run-api # FastAPI on :8000
make run-bot # aiogram long-poll bot
make run-userbot # one or more pyrotgfork pool members
In production each of these runs as its own container/pod with the
SERVICE= env var dispatched by the Dockerfile entrypoint.
Verifying
After make migrate && make run-api, check:
| Endpoint | Expected | Notes |
|---|---|---|
GET /healthz | {"status": "ok"} | Bypasses auth + rate limit |
GET /metrics | Prometheus text | Same |
GET /v1/me | 401 without X-Telegram-Init-Data | Sanity check the auth chain |
Grafana lives on http://localhost:3000 with admin / helios_dev (change
this immediately on any non-local deploy). The Helios overview dashboard is
provisioned automatically from infra/grafana/provisioning/dashboards/.
Backups
pg_dumphourly to S3 — wire via cron +WAL-Gin production.- Redis is intentionally treated as ephemeral; the data tier is Postgres.
- The AES master key MUST be backed up outside the database. If it's lost, every encrypted session blob is unrecoverable.
Upgrade
git pull
docker compose pull
make migrate # alembic upgrade head
docker compose up -d # rolling restart of running services
Migrations are append-only — Phase 33 removed the automatic downgrade rehearsal from CI because the TimescaleDB hypertable's downgrade isn't clean. Reversing a migration on a prod-sized database is a manual, rehearsed operation; see the runbook for the drill.