Skip to content

Deployment

This guide covers three deployment paths: Linux VM with systemd, Docker Compose, and Kubernetes.

  • PostgreSQL 14+
  • S3-compatible object storage (RustFS (bundled), MinIO, AWS S3, or other S3-compatible storage)
  • edgeplane-tower binary (see Installation)
Terminal window
cp target/release/edgeplane-tower /usr/local/bin/edgeplane-tower

Create /etc/edgeplane/env:

Terminal window
# Auth
AUTH_MODE=dual
OIDC_REQUIRED=false
OIDC_ISSUER_URL=https://<your-idp-host>/application/o/<provider-slug>/
OIDC_AUDIENCE=<oidc-client-id>
EP_ADMIN_EMAILS=<comma-separated-admin-emails>
# Database
DATABASE_URL=postgresql://edgeplane:password@localhost/edgeplane
DB_POOL_SIZE=20
DB_MAX_OVERFLOW=10
DB_POOL_PRE_PING=true
DB_POOL_RECYCLE_SECONDS=3600
EP_DB_RUNTIME_MIGRATIONS=false
# S3-compatible object storage (optional, for artifact/doc content)
EP_OBJECT_STORAGE_ENDPOINT=http://<s3-host>:<port>
EP_OBJECT_STORAGE_REGION=us-east-1
EP_OBJECT_STORAGE_BUCKET=edgeplane
EP_OBJECT_STORAGE_SECURE=false
EP_OBJECT_STORAGE_ACCESS_KEY=<access-key>
EP_OBJECT_STORAGE_ACCESS_SECRET=<secret>
# Request limits (optional)
EP_REQUEST_TIMEOUT_SECONDS=30
EP_RATE_LIMIT_DEFAULT_CAPACITY=240
EP_RATE_LIMIT_SEARCH_CAPACITY=60
EP_RATE_LIMIT_WRITE_CAPACITY=120
EP_RATE_LIMIT_APPROVAL_CAPACITY=30

Create /etc/systemd/system/edgeplane.service:

[Unit]
Description=EdgePlane Control Plane
After=network.target postgresql.service
[Service]
Type=simple
ExecStart=/usr/local/bin/edgeplane-tower --serve --bind 0.0.0.0:8008
Restart=on-failure
EnvironmentFile=/etc/edgeplane/env
[Install]
WantedBy=multi-user.target

Enable and start:

Terminal window
sudo systemctl daemon-reload
sudo systemctl enable --now edgeplane
Terminal window
curl http://localhost:8008/health
curl http://localhost:8008/raft/status

The repo ships a production-oriented Compose stack and a quickstart variant.

Quickstart (local dev — Postgres + RustFS, no external infrastructure required):

Terminal window
docker compose -f docker-compose.quickstart.yml up

Full stack (Postgres + S3-compatible storage):

Provide secrets via environment before startup:

Terminal window
export POSTGRES_PASSWORD=<password>
export EP_OBJECT_STORAGE_ACCESS_KEY=<key>
export EP_OBJECT_STORAGE_ACCESS_SECRET=<secret>
docker compose up

Health endpoints:

  • /health — process alive (no auth required)
  • /readyz — DB ready, object storage reachable when configured

When running on Kubernetes, source all secrets via platform secret objects — do not commit credentials to Git.

# Recommended pattern: envFrom + secretRef
spec:
containers:
- name: edgeplane-tower
image: ghcr.io/ryanmerlin/edgeplane:<version>
envFrom:
- secretRef:
name: edgeplane-env
ports:
- containerPort: 8008

Store all auth settings (OIDC secrets, static token, DB credentials, S3 credentials) as Kubernetes Secrets and mount via envFrom.secretRef or env.valueFrom.secretKeyRef.

See Helm chart in the repo for a complete Kubernetes deployment.

AUTH_MODEBehavior
tokenStatic bearer token only
oidcOIDC JWT only
dualAccept both token and OIDC

OIDC_REQUIRED=true in dual mode enforces OIDC for non-/mcp paths. If AUTH_MODE is unset, the server defaults to OIDC when OIDC vars are present.

edgeplane-tower runs migrations automatically on startup. To run manually:

Terminal window
cd crates/edgeplane-tower && sqlx migrate run

Confirm migration state:

Terminal window
sqlx migrate info

After deployment:

  • GET /health returns 200 without auth
  • GET /readyz returns 200 (DB ready, S3 reachable if configured)
  • edgeplane health --json returns connected from operator workstation
  • Bearer token callers are not admins unless their subject/email is in EP_ADMIN_SUBJECTS or EP_ADMIN_EMAILS
  • Create + delete mission paths work with expected authorization