Sevro

Self-Hosting

Deploy Sevro on your own infrastructure with Docker or Kubernetes.

Quick start with Docker Compose

The repository includes a docker-compose.yml that runs Postgres and Sevro:

git clone https://github.com/jackcaldwell/sevro.git
cd sevro
docker compose up -d

This starts:

  • Postgres 17 on the internal network (port 5432, no host mapping by default)
  • Sevro server on port 8080

Configuration

Sevro is configured via environment variables:

VariableDefaultDescription
DB_URLpostgres://postgres:postgres@localhost:5432/postgresPostgres connection string
SERVER_ADDR:8080Server listen address
SEVRO_ENCRYPTION_KEY(dev key)AES-256-GCM key for encrypting connection credentials. Set this in production!
SEVRO_UI_DEV_URL(unset)URL of the admin UI dev server for proxying (development only)
SEVRO_SUPABASE_URL(unset)Supabase URL for JWT auth (optional)
SEVRO_SUPABASE_JWT_SECRET(unset)Supabase JWT secret (optional)

Encryption key

The SEVRO_ENCRYPTION_KEY is used to encrypt connection credentials at rest. If not set, Sevro generates a deterministic dev key — this is insecure for production.

Generate a secure key:

openssl rand -base64 32

Set it as an environment variable:

export SEVRO_ENCRYPTION_KEY="your-base64-key-here"

Initial setup

After starting Sevro for the first time:

# 1. Apply migrations
docker compose exec sevro /app/sevro migrate

# 2. Create an admin API key
docker compose exec sevro /app/sevro bootstrap --name admin --subject admin

Save the raw token — it's shown only once.

Building from source

# Build the server and CLI
go build -o bin/sevroserver ./cmd/fxserver
go build -o bin/sevro ./cmd/sevro

# Run migrations
./bin/sevro migrate --db "postgres://postgres:postgres@localhost:5432/postgres"

# Start the server
./bin/sevroserver

Docker image

Build the Docker image:

docker build -t sevro:latest .

Run it:

docker run -d \
  --name sevro \
  -p 8080:8080 \
  -e DB_URL="postgres://user:pass@db-host:5432/sevro" \
  -e SEVRO_ENCRYPTION_KEY="your-key" \
  sevro:latest

The Docker image is Alpine-based and includes both sevroserver and sevro (the CLI). The entrypoint automatically applies migrations before starting the server.

Kubernetes

Sevro is a single binary with a Postgres dependency — it deploys cleanly on Kubernetes.

Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sevro
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sevro
  template:
    metadata:
      labels:
        app: sevro
    spec:
      containers:
        - name: sevro
          image: sevro:latest
          ports:
            - containerPort: 8080
          env:
            - name: DB_URL
              valueFrom:
                secretKeyRef:
                  name: sevro-secrets
                  key: db-url
            - name: SEVRO_ENCRYPTION_KEY
              valueFrom:
                secretKeyRef:
                  name: sevro-secrets
                  key: encryption-key
            - name: SERVER_ADDR
              value: ":8080"
          livenessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
  name: sevro
spec:
  selector:
    app: sevro
  ports:
    - port: 8080
      targetPort: 8080

Health check

Sevro exposes a /healthz endpoint that checks database connectivity:

curl http://localhost:8080/healthz
{
  "status": "ok",
  "version": "0.0.1",
  "database": "connected"
}

Admin UI

The admin UI is a static SPA built with React + TanStack Router. To serve it alongside Sevro:

  1. Build the UI:

    cd admin && npm run build
  2. Serve the admin/dist/ directory via a reverse proxy (nginx, Caddy) in front of Sevro, or use the SEVRO_UI_DEV_URL env var during development to proxy to the Vite dev server.

Production checklist

  • Set SEVRO_ENCRYPTION_KEY to a securely generated value
  • Use a managed Postgres instance with automated backups
  • Put Sevro behind a TLS-terminating reverse proxy
  • Create API keys with least-privilege roles (editor/viewer) for applications
  • Set up monitoring on the /healthz endpoint
  • Enable Postgres connection pooling (PgBouncer) if needed

On this page