Helios
Operations

First-deploy onboarding

From an empty Ubuntu VPS to a running Helios platform on your domain in ~75 minutes.

Replace your-domain.tld with your domain and 1.2.3.4 with your server's public IPv4 throughout.

Prerequisites

  • Ubuntu 24.04 LTS VPS, 4 vCPU / 8 GB RAM minimum.
  • Docker 27+, Docker Compose v2.
  • A domain with DNS control.
  • A Telegram bot token from @BotFather.
  • Your numeric Telegram user id from @userinfobot.

Step 1 — DNS

Add these records:

TypeNameValue
A@1.2.3.4
Aapp1.2.3.4
Aapi1.2.3.4
Adocs1.2.3.4
CAA@0 issue "letsencrypt.org"

Wait 5–10 minutes; verify with dig +short api.your-domain.tld @1.1.1.1.

Step 2 — clone + secrets

git clone <your-fork> /opt/helios && cd /opt/helios
cp .env.example .env
chmod 600 .env

Generate secrets — the values go straight into .env:

openssl rand -hex 32                          # AES_MASTER_KEY
openssl rand -base64 24 | tr -d /=+ | cut -c1-32  # postgres owner pwd
openssl rand -base64 24 | tr -d /=+ | cut -c1-32  # postgres app pwd
openssl rand -base64 24 | tr -d /=+ | cut -c1-32  # minio root pwd
openssl rand -hex 32                          # meilisearch master key

Fill in .env:

BOT_TOKEN=                       # from @BotFather
OWNER_TG_USER_ID=                # from @userinfobot
BOT_USERNAME=YourBotName          # without @
BRAND_NAME=YourBrand
PUBLIC_BASE_URL=https://api.your-domain.tld
PORTAL_URL=https://your-domain.tld
SUPPORT_EMAIL=[email protected]
 
POSTGRES_OWNER_PASSWORD=<from openssl>
POSTGRES_APP_PASSWORD=<from openssl>
POSTGRES_URL=postgresql+asyncpg://helios_app:<app-pwd>@postgres:5432/helios
POSTGRES_URL_SYNC=postgresql+psycopg2://helios:<owner-pwd>@postgres:5432/helios
 
AES_MASTER_KEY=<from openssl>
S3_SECRET_KEY=<minio pwd>
MINIO_ROOT_PASSWORD=<minio pwd>
MEILI_API_KEY=<meili key>
 
API_CORS_ALLOWED_ORIGINS=https://your-domain.tld,https://app.your-domain.tld

Step 3 — nginx + TLS

sudo cp infra/nginx/sites-available/helios.mom.conf \
   /etc/nginx/sites-available/your-domain.tld.conf
# edit server_name lines to match your domain
sudo ln -sf /etc/nginx/sites-available/your-domain.tld.conf \
   /etc/nginx/sites-enabled/
sudo mkdir -p /var/www/letsencrypt
sudo nginx -t && sudo systemctl reload nginx
 
sudo certbot certonly --webroot -w /var/www/letsencrypt \
  -d your-domain.tld -d app.your-domain.tld \
  -d api.your-domain.tld -d docs.your-domain.tld \
  --email [email protected] --agree-tos --non-interactive --no-eff-email

Edit the vhost to add the 443 server blocks (see /etc/nginx/sites-available/helios.mom.conf for the template), then reload nginx.

Step 4 — build the frontends

cd apps/miniapp && pnpm install --frozen-lockfile && \
  VITE_API_BASE=https://api.your-domain.tld pnpm build && cd ../..
sudo mkdir -p /var/www/your-domain.tld/{miniapp,docs}
sudo cp -r apps/miniapp/dist/. /var/www/your-domain.tld/miniapp/
sudo chown -R www-data:www-data /var/www/your-domain.tld

Step 5 — bootstrap

./bootstrap.sh

The script:

  1. Validates .env.
  2. Pulls docker images.
  3. Brings up postgres + redis + minio + meilisearch.
  4. Runs alembic upgrade head.
  5. Seeds the parameters registry.
  6. Runs preflight checks (stealth whitelist).
  7. Starts the application stack (api, bot, 11 workers).
  8. Polls /healthz until it returns 200.

Total time: ~5 minutes on a fresh server.

Step 6 — first /start

Open Telegram → message your bot → /start. The owner panel button is visible only to the OWNER_TG_USER_ID you configured.

Done

Verify:

curl https://api.your-domain.tld/healthz
# {"status":"ok"}
 
curl https://api.your-domain.tld/v1/public/config | jq
# { "bot_username": "...", "brand_name": "...", ... }

If anything went wrong, the runbook has the first three things to check for the common incidents.

On this page