Binary / tarball deployment
Deploy the server components as standalone binaries or a portable tarball instead of Docker images. This is useful when you want to run the services directly on a VM, bare metal, or under systemd without a container runtime.
What’s available
| Component | Format | Size | Runtime requirement |
|---|---|---|---|
| ingest | Bun compiled binary | ~70 MB | None (Bun runtime is bundled) |
| github-app | Bun compiled binary | ~67 MB | None (Bun runtime is bundled) |
| web | Node.js tarball | ~16 MB | Node >= 24 |
| migrations-runner | Not available as binary | — | Docker or Bun + source (reads migration files from disk at runtime) |
| hook | Bun compiled binary | ~50-80 MB | None (already distributed this way — see hook-binary.md) |
Why migrations-runner is not a binary
The migrations runner shells out to bunx prisma migrate deploy and reads .sql files from packages/db/sql/migrations/ at runtime via readdirSync + readFileSync. Both require filesystem access to the source tree, so it can’t be compiled into a single binary. For binary-only deployments, run migrations using the Docker image or from source with Bun.
Building locally
Ingest
# Current platformbun run --cwd apps/ingest build:compile
# All platforms (cross-compile)bun run --cwd apps/ingest build:allOutput: apps/ingest/dist/ingest-server[-<target>]
GitHub App
# Current platformbun run --cwd apps/github-app build:compile
# All platformsbun run --cwd apps/github-app build:allOutput: apps/github-app/dist/github-app-server[-<target>]
Web tarball
bash scripts/package-web.sh distOutput: dist/web-standalone-<version>.tar.gz
Important: NODE_ENV=production at compile time
The build scripts set NODE_ENV=production at compile time. This is critical: Bun’s --compile bakes in NODE_ENV at build time, and pino’s pino-pretty transport (used in development) relies on worker threads that are incompatible with Bun’s compiled binary virtual filesystem. In production mode, pino writes plain JSON with no worker thread, so the binary runs clean.
Downloading from GitHub Releases
For every approved release, CI builds all artifacts and publishes them to the GitHub Release:
TAG=v1.0.0
# Download server binariesgh release download "${TAG}" --repo yorch/ai-agents-observability \ --pattern "ingest-server-linux-x64" \ --pattern "github-app-server-linux-x64" \ --pattern "web-standalone-*.tar.gz" \ --pattern "SHA256SUMS-binaries"Available targets: darwin-arm64, darwin-x64, linux-x64, linux-arm64.
Verify
sha256sum -c SHA256SUMS-binaries --ignore-missingDeploy: ingest
chmod +x ingest-server-linux-x64
# Set required env varsexport DATABASE_URL=postgresql://user:pass@host:5432/ai_agents_observabilityexport S3_ENDPOINT=http://minio:9000export S3_ACCESS_KEY_ID=minioadminexport S3_SECRET_ACCESS_KEY=minioadminexport S3_BUCKET=transcriptsexport S3_REGION=us-east-1export S3_FORCE_PATH_STYLE=trueexport INGEST_PORT=4000export GIT_SHA=v1.0.0 # or $(git rev-parse --short HEAD) if deployed from a clone
# Run./ingest-server-linux-x64Deploy: github-app
chmod +x github-app-server-linux-x64
export DATABASE_URL=postgresql://user:pass@host:5432/ai_agents_observabilityexport GITHUB_APP_ID=...export GITHUB_APP_PRIVATE_KEY=...export GITHUB_APP_WEBHOOK_SECRET=...export GITHUB_HOST=https://github.comexport GITHUB_APP_PORT=4001export GIT_SHA=v1.0.0
./github-app-server-linux-x64Deploy: web
# Extracttar -xzf web-standalone-v1.0.0.tar.gzcd web/
# Set required env varsexport DATABASE_URL=postgresql://user:pass@host:5432/ai_agents_observabilityexport JWT_ED25519_PRIVATE_KEY=...export JWT_ED25519_PUBLIC_KEY=...export GITHUB_OAUTH_CLIENT_ID=...export GITHUB_OAUTH_CLIENT_SECRET=...export GITHUB_HOST=https://github.comexport S3_ENDPOINT=http://minio:9000export S3_ACCESS_KEY_ID=minioadminexport S3_SECRET_ACCESS_KEY=minioadminexport S3_BUCKET=transcriptsexport S3_REGION=us-east-1export S3_FORCE_PATH_STYLE=trueexport PORT=3000
# Run (requires Node >= 24)./run.shRunning under systemd
Ingest
[Unit]Description=AI Agents Observability — IngestAfter=network.target postgresql.service
[Service]Type=simpleExecStart=/opt/ai-agents-observability/ingest-server-linux-x64Environment=DATABASE_URL=postgresql://user:pass@localhost:5432/ai_agents_observabilityEnvironment=S3_ENDPOINT=http://localhost:9000Environment=S3_ACCESS_KEY_ID=minioadminEnvironment=S3_SECRET_ACCESS_KEY=minioadminEnvironment=S3_BUCKET=transcriptsEnvironment=S3_REGION=us-east-1Environment=S3_FORCE_PATH_STYLE=trueEnvironment=INGEST_PORT=4000Environment=GIT_SHA=v1.0.0Restart=alwaysRestartSec=5
[Install]WantedBy=multi-user.targetGitHub App
[Unit]Description=AI Agents Observability — GitHub AppAfter=network.target postgresql.service
[Service]Type=simpleExecStart=/opt/ai-agents-observability/github-app-server-linux-x64Environment=DATABASE_URL=postgresql://user:pass@localhost:5432/ai_agents_observabilityEnvironment=GITHUB_APP_ID=...Environment=GITHUB_APP_PRIVATE_KEY=...Environment=GITHUB_APP_WEBHOOK_SECRET=...Environment=GITHUB_HOST=https://github.comEnvironment=GITHUB_APP_PORT=4001Environment=GIT_SHA=v1.0.0Restart=alwaysRestartSec=5
[Install]WantedBy=multi-user.targetWeb
[Unit]Description=AI Agents Observability — WebAfter=network.target postgresql.service
[Service]Type=simpleWorkingDirectory=/opt/ai-agents-observability/webExecStart=/opt/ai-agents-observability/web/run.shEnvironment=DATABASE_URL=postgresql://user:pass@localhost:5432/ai_agents_observabilityEnvironment=JWT_ED25519_PRIVATE_KEY=...Environment=JWT_ED25519_PUBLIC_KEY=...Environment=GITHUB_OAUTH_CLIENT_ID=...Environment=GITHUB_OAUTH_CLIENT_SECRET=...Environment=GITHUB_HOST=https://github.comEnvironment=S3_ENDPOINT=http://localhost:9000Environment=S3_ACCESS_KEY_ID=minioadminEnvironment=S3_SECRET_ACCESS_KEY=minioadminEnvironment=S3_BUCKET=transcriptsEnvironment=S3_REGION=us-east-1Environment=S3_FORCE_PATH_STYLE=trueEnvironment=PORT=3000Restart=alwaysRestartSec=5
[Install]WantedBy=multi-user.targetEnable and start
sudo systemctl daemon-reloadsudo systemctl enable --now ai-agents-ingest ai-agents-github-app ai-agents-websudo systemctl status ai-agents-ingestMigrations
Binary deployments still need migrations. Options:
-
Run the migrations Docker image (one-shot, then remove):
Terminal window docker run --rm --network host \-e DATABASE_URL=postgresql://user:pass@localhost:5432/ai_agents_observability \ghcr.io/yorch/ai-agents-observability/migrations-runner:v1.0.0 -
Run from source with Bun (if Bun is installed):
Terminal window git clone https://github.com/yorch/ai-agents-observability.gitcd ai-agents-observabilitygit checkout v1.0.0bun install --frozen-lockfileDATABASE_URL=postgresql://user:pass@localhost:5432/ai_agents_observability bun run db:deploy
Run migrations before starting the server binaries. The migration runner is idempotent (prisma migrate deploy + applySqlMigrations), so re-running is safe.
Updating
- Download the new release binaries/tarball.
- Verify checksums.
- Stop the systemd services.
- Run migrations (if the new version has schema changes).
- Replace the binaries/tarball.
- Start the services.
Tradeoffs
- No auto-update. Updates are a manual download + replace. This is by design for governance-controlled environments.
- Migrations still need Docker or Bun. The migration runner can’t be compiled as a binary because it reads migration files from disk at runtime.
- Web requires Node >= 24. The web tarball is a Next.js standalone bundle, not a compiled binary. Node must be installed on the target machine.
- Web tarball is built for linux-x64 in CI. The standalone bundle includes a native
keytar.nodebinding compiled for the CI runner’s platform. On macOS or ARM Linux, keytar fails to load — the web app falls back to file-based token storage (packages/auth/src/keychain.tshas a try/catch). This is transparent but undocumented in the tarball itself. - No health check built into systemd units. The units above use
Type=simplewithRestart=always. For active health checks, add aExecStartPostor use an external monitor.