Open the app
Operations

Containerization & CI/CD

Docker Compose behind Traefik, and the live GitHub Actions pipeline that ships the frontend + api to the VPS through ghcr on every push to main.

Every component ships as a container, orchestrated by Docker Compose behind the existing Traefik. The frontend and api now redeploy automatically on every push to main — built on GitHub runners, shipped through the GitHub Container Registry, pulled by the VPS.

Live today
Continuous deployment is wired and verified for the frontend + api. A green CI on main triggers a build, a push to ghcr.io, and a zero-touch redeploy on the box. The Convex schema deploy stays a deliberate manual step (see below). See VPS foundation (live) for the full running stack.

Continuous deployment (live)

Two workflows split the work. ci.yml validates every push and PR; deploy.yml reacts to a successful CI run on main (via workflow_run) so PR builds never deploy and a deploy is never cancelled mid-flight.

  1. Push to mainA conventional commit lands on the default branch.
  2. CI gateLint, type-check, tests, build, gitleaks + audit. No green, no deploy.
  3. Build & pushFrontend + api images build on GitHub runners → ghcr.io, tagged :latest and :<sha>.
  4. Ship to the VPSdeploy.sh is copied over SSH; the box pulls the immutable :<sha> image.
  5. Swap & verifyRetag to the running :latest, compose up -d, Traefik health-checks the new container.

Build off the box, not on it

The VPS is small (the frontend is capped near half a core). Building a whole pnpm workspace there competed with the running stack, so the build moved to GitHub runners. Images are pushed to ghcr.io/clawlas/clawlas-frontend and …/clawlas-api, tagged both :latest and the commit :<sha> — the SHA tag is immutable, which is what makes rollback deterministic.

The VPS only pulls

No git clone and no Node toolchain live on the box. deploy.sh pulls the SHA-pinned image and retags it to the local tag the compose file already references, so nothing on the VPS has to change for a deploy:

deploy/scripts/deploy.sh (essence)
docker pull  ghcr.io/clawlas/clawlas-frontend:<sha>
docker tag   ghcr.io/clawlas/clawlas-frontend:<sha>  clawlas-frontend:latest
# …same for the api image, then:
docker compose up -d frontend api

Gated on green CI, authenticated without a stored token

The deploy job authenticates to ghcr transiently with the workflow's built-in GITHUB_TOKEN — there is no Personal Access Token to mint or rotate on the VPS. Secret scanning runs the gitleaks binary directly, not the action (which now needs a paid licence for organisation repos).

NameKindPurpose
VPS_SSH_HOST / _USER / _KEYSecretSSH onto the VPS with a dedicated deploy key
VITE_CONVEX_URL / _SITE_URL / VITE_SITE_URLVariablePublic client URLs baked into the frontend bundle at build time
GITHUB_TOKENBuilt-inPushes the images and authorises the VPS pull — nothing to manage
Not automated: the Convex schema
The box has no Node toolchain and the target deployment is a deliberate choice, so schema + functions are not auto-deployed. When the backend changes, run deploy/scripts/deploy-backend.sh from a machine with Node.

Rollback is one command

Because every release is a distinct :<sha> image, rolling back is re-running deploy.sh against an earlier commit (or re-running the Deploy workflow on it):

rollback on the VPS
FRONTEND_IMAGE=ghcr.io/clawlas/clawlas-frontend:<previous-sha> \
API_IMAGE=ghcr.io/clawlas/clawlas-api:<previous-sha> \
  bash /opt/clawlas/deploy/scripts/deploy.sh

Services

  • frontend
  • convex
  • fastapi
  • redis
  • agent-worker
  • quant-worker
  • memory-worker
  • backtest-worker
  • execution-engine
  • risk-engine
  • scheduler
  • security-scanner
  • sellability-tester
  • feature-engine
  • position-monitor
  • prometheus
  • grafana
  • loki
  • portainer
  • backup

Each has a multi-stage Dockerfile, pinned base images and no secrets baked in. The runtime image is profile-agnostic — config selects what a container runs. Today the frontend, api, Convex, Redis and the monitoring stack are live; the trading workers below are the planned target.

Early-token collectors (kept separate)

One failing source must never stop discovery, so each collector is its own container:

  • collector-cex-listings
  • collector-cex-market-data
  • collector-solana-launches
  • collector-evm-contracts
  • collector-dex-pools
  • collector-launchpads
  • collector-liquidity
  • collector-wallet-activity
  • collector-social

Redeploying the trading runtime is safety-critical

App containers are stateless and swap freely. The trading runtime is not: while real capital is in play, a redeploy follows a stricter sequence.

  1. 1
    Graceful shutdown
    On SIGTERM the runtime stops new entries, flushes in-flight work, checkpoints SQLite (WAL), then exits.
  2. 2
    Exchange-native stops
    In production, stops live as exchange-side orders so a restart never leaves a position unprotected.
  3. 3
    Reconciliation on boot
    On startup the runtime reconciles against the real exchange account before acting — no phantom or duplicate positions.
Production needs a human
Auto-deploy covers the app surface. Promoting the trading runtime with live credentials still requires manual approval.

Environments

EnvironmentTrading
LocalCompose, synthetic/historical data, isolated Convex, no real keys
VPS stagingProduction-like, shadow & paper only, no real trading permissions
VPS productionReal credentials, restricted execution network, manual deploy, separate volumes/secrets