Deployment¶
Self-hosting¶
The backend ships as a single container image run in different modes, backed by
one Postgres database. The distribution compose file
docker-compose.dist.yml
brings up Postgres, server, scheduler, and worker together.
docker compose -f docker-compose.dist.yml up -d
docker compose -f docker-compose.dist.yml run --rm server -mode=provision -name=me
Configuration¶
Backend environment variables are all prefixed BOUNDFLOW_:
| Variable | Applies to | Purpose |
|---|---|---|
DATABASE_URL |
all | Postgres connection string. |
GRPC_PORT |
server | Client-facing gRPC port (default 50051). |
WORKER_GRPC_PORT |
worker | Worker-facing gRPC port (default 50052). |
NUM_PARTITIONS |
scheduler | Scheduler partition count. |
JOB_TIMEOUT_SECS |
scheduler | Default job timeout. |
LOG_LEVEL / DEBUG |
all | Logging. |
SDK-side: BOUNDFLOW_API_KEY, BOUNDFLOW_SERVER_ADDRESS /
BOUNDFLOW_WORKER_ADDRESS (default to localhost), and ANTHROPIC_API_KEY for
real agents.
Secrets — the .env file¶
docker compose automatically reads a .env file next to the compose file, so
that's where deployment secrets go. Copy the template and set your values:
cp .env.example .env
# BOUNDFLOW_DB_PASSWORD is required — the stack won't start without it.
# Generate a strong one: echo "BOUNDFLOW_DB_PASSWORD=$(openssl rand -hex 16)" >> .env
.env is gitignored; never commit real secrets. BOUNDFLOW_DB_PASSWORD feeds
both the bundled Postgres container and the backend's connection string.
docker-compose.dist.yml ships no default for it — a deployment must set its own,
so it can't accidentally run on a known password. (The dev compose,
docker-compose.yml, keeps a local default for tests.)
Production database — bring your own¶
For anything beyond a local trial, don't rely on the bundled postgres container —
point the backend at a managed Postgres (RDS / Cloud SQL / Azure DB) over TLS.
Set BOUNDFLOW_DATABASE_URL in .env; it overrides the bundled URL entirely:
Then remove the bundled postgres service (and the depends_on: postgres entries)
from your compose file — or override them in a docker-compose.override.yml — and
run -mode=migrate once against your database to create the schema.
Warning
Don't publish the Postgres port. The bundled Postgres isn't published to the
host. If you expose it, put it behind your network's controls — and set
BOUNDFLOW_DB_PASSWORD to a strong secret (required; see above).
TLS¶
The Go server speaks plaintext gRPC; TLS is expected to be terminated at the edge — a reverse proxy or load balancer (Caddy, nginx, Envoy, or a cloud LB) in front of the server that presents the certificate and forwards plaintext to the backend. This is a standard gRPC deployment pattern.
The SDK selects TLS by URL scheme: an https:// endpoint uses a secure channel
(validated against system root CAs); anything else is insecure. So point the SDK
at your terminating proxy over https:// in production:
export BOUNDFLOW_SERVER_ADDRESS=https://boundflow.example.com:443
export BOUNDFLOW_WORKER_ADDRESS=https://boundflow.example.com:8443
Note
Custom CA / self-signed certs. The SDK currently validates against system root CAs only. End-to-end TLS to a private-CA or self-signed certificate (e.g. on localhost) is not yet configurable from the SDK — front the server with a publicly-trusted cert for now.
A minimal Caddy terminator, for reference: