Skip to content

Deploy

GitHub Actions → VPS (auto-deploy)

After CI succeeds on main, .github/workflows/deploy.yml SSHs to the VPS and deploys into /home/smdg/agentbi.

Required repository secrets: VPS_HOST, VPS_USER, VPS_SSH_KEY.

Production stack (docker-compose.prod.yml):

  • No public host ports for API/UI/DB/Redis
  • Frontend joins Traefik network agentops-edge
  • HTTPS via file route in $TRAEFIK_DIR/agentbi.yml when AGENTBI_DOMAIN is set
  • Images tagged agentbi-api:<git-sha> / agentbi-frontend:<git-sha> (IMAGE_TAG)

Manual run: Actions → Deploy to VPS → Run workflow.

First-time VPS setup

```bash cd /home/smdg/agentbi # after first clone by Actions, or git clone manually cp .env.production.example .env

Set strong POSTGRES_PASSWORD, REDIS_PASSWORD, SECRET_KEY, ADMIN_PASSWORD

Set AGENTBI_DOMAIN and CORS_ORIGINS=https://$AGENTBI_DOMAIN

DNS A-record → VPS (see https://github.com/valera7623/AgentBI/blob/main/deploy/DNS.md)

chmod +x deploy.sh scripts/.sh scripts/vps/.sh export COMPOSE_FILE=docker-compose.prod.yml ./deploy.sh ```

deploy.sh phases:

  1. Pre-checks — clean git (or SKIP_GIT_CLEAN=1 on Actions), requirements-lock.txt
  2. Build API before migrate (so Alembic sees new revisions)
  3. Pre-migrate Postgres backup (gzip, keep last 7) — abort if backup fails
  4. alembic upgrade head + verify schema head
  5. Start stack with SHA-tagged images, write VERSION, health + /api/v1/version

Useful flags: ./deploy.sh --help, --skip-backup, --run-tests, --allow-dirty.

Isolated demo stand (AgentOps pattern)

Demo data must never land in the production Postgres. AgentBI follows AgentOps:

Surface Stack
https://agentbi.online apitimescaledb (prod)
https://demo.agentbi.online api-demopostgres-demo (isolated volume)

```bash

On the VPS .env — merge from .env.demo (keep SEED_DEMO_DATA=false for prod)

AGENTBI_DEMO_DOMAIN=demo.agentbi.online CORS_ORIGINS=https://agentbi.online,https://demo.agentbi.online

DNS A-record demo.agentbi.online → VPS

COMPOSE_FILE=docker-compose.prod.yml ./deploy.sh COMPOSE_FILE=docker-compose.prod.yml ./scripts/run_demo_data.sh ```

docker-compose.prod.yml hard-disables DEMO_USER_ENABLED / DEMO_SEED_ON_START on the prod api service. The overlay docker-compose.demo.yml enables them only on api-demo.

Verify:

```bash curl -sf https://$AGENTBI_DOMAIN/api/v1/health/ready curl -sf https://$AGENTBI_DOMAIN/api/v1/version

or without DNS yet:

docker exec agentbi-api curl -sf http://localhost:8000/api/v1/health/ready ```

Backups & restore

Pre-migrate backups run automatically from deploy.sh. Manual / cron:

```bash ./scripts/backup_postgres.sh --help

cron (daily 02:15):

15 2 * * * cd /home/smdg/agentbi && ./scripts/backup_postgres.sh >>/var/log/agentbi-backup.log 2>&1

```

Files: ./backups/backup_YYYYMMDD_HHMMSS[_SHA].dump.gz (pg_dump -Fc + gzip). Rotation keeps the last 7. Copy off-host (e.g. /home/smdg/backups-offsite).

```bash ./scripts/restore_postgres.sh --file backups/backup_YYYYMMDD_HHMMSS.dump.gz

type "restore" to confirm

```

Rollback

bash ./scripts/rollback.sh --sha "$(cat VERSION.prev)" # previous image tags ./scripts/rollback.sh --sha <sha> --restore backups/….gz # image + DB

Requires local images agentbi-api:<sha> / agentbi-frontend:<sha> from a prior deploy.

VPS ops (shared host)

Same VM also runs AgentOps / AegisAI. Host helpers live in scripts/vps/:

```bash

Install docker prune cron (user smdg, docker group — no root)

./scripts/vps/install_vps_ops.sh

Cron (example):

15 2 * * * agentbi backup

15 3 * * * /home/smdg/bin/docker-prune-safe

Safe prune: stopped containers, dangling images, old build cache — never volumes

/home/smdg/bin/docker-prune-safe

One-time 2G swapfile (swappiness=10) — needs sudo:

sudo /home/smdg/bin/setup-swapfile swapon --show && free -h ```

Tenant domains

Two different fields on tenants:

Field Purpose UI
allowed_email_domains Google SSO email → tenant mapping (unique across tenants) Users → Organizations
custom_domain Vanity hostname for future routing (unique, optional) Settings → Domain

custom_domain is DB-only — DNS / Traefik / Let's Encrypt are manual.

Stripe return URLs

bash STRIPE_SUCCESS_URL=https://$AGENTBI_DOMAIN/src/pages/settings.html?billing=success STRIPE_CANCEL_URL=https://$AGENTBI_DOMAIN/src/pages/settings.html?billing=cancel

Defaults (if unset) use SSO_FRONTEND_URL / SSO_PUBLIC_BASE_URL + the same paths.

Paid plans (pro / business) unlock features only while subscription_status is active or trialing; otherwise entitlements fall back to free. enterprise is assigned by super_admin and is not gated on Stripe status.

Sentry & alerts

  1. Set SENTRY_DSN (+ optional SENTRY_SEND_DEFAULT_PII, SENTRY_TRACES_SAMPLE_RATE).
  2. Events tagged release=agentbi@<GIT_SHA>, environment=<APP_ENV>.
  3. Unhandled exceptions and HTTP 5xx are captured.

Recommended Sentry alerts: error spike, new issue → Slack/email, regression after new release. Optional uptime check on GET /api/v1/health/ready.

Dependencies & CI

  • Production/CI install from requirements-lock.txt (uv pip compile requirements.txt -o requirements-lock.txt).
  • ./scripts/check_lockfile.sh must pass in CI.
  • Coverage gate: ≥ 55% (--cov-fail-under=55); HTML artifact uploaded from CI. Stretch goal: 70%.

Local development

bash cp .env.example .env docker compose up --build -d curl http://localhost:8000/api/v1/health/ready curl http://localhost:8000/api/v1/version

TimescaleDB

Default images use PostgreSQL 15. Hypertables enable only when the Timescale extension is present.

yaml image: timescale/timescaledb:latest-pg15

Otherwise AgentBI runs on plain Postgres (supported for production SaaS).