Last updated 2026-06-29

Deploy with Docker

This page gets workspaces running in a container — first as a local Compose stack with Postgres, then as a standalone image in production. The service ships as a single image, ghcr.io/elloloop/workspace, built from cmd/workspace.

docker compose up

The repository ships a docker-compose.yml that brings up Postgres and the workspaces service together. From a clone:

docker compose
docker compose up -d
curl http://localhost:8080/healthz # -> 200 OK
docker compose logs -f workspaces
docker compose down -v # stop and wipe the postgres volume

Migrations are a deliberate operator step: GATEWAY_POSTGRES_AUTO_MIGRATE defaults to false, so apply the schema explicitly with workspace migrate (an init container or a one-shot job) before serving, or set it to true for a small dev database to migrate on first connect. See Configuration for the full knob reference.

A Compose snippet

A minimal stack: Postgres plus the workspaces service, wired with a durable store, a service-auth token, and a default project.

docker-compose.yml
services:
db:
image: postgres:16
environment:
POSTGRES_USER: workspaces
POSTGRES_PASSWORD: password
POSTGRES_DB: workspaces
healthcheck:
test: ["CMD-SHELL", "pg_isready -U workspaces"]
interval: 5s
retries: 10
workspaces:
image: ghcr.io/elloloop/workspace
depends_on:
db:
condition: service_healthy
ports:
- "8080:8080" # Connect-RPC (HTTP/JSON, gRPC, gRPC-Web)
- "9090:9090" # Prometheus metrics + health probes
environment:
GATEWAY_POSTGRES_DSN: postgres://workspace:password@db:5432/workspace?sslmode=disable
GATEWAY_SERVICE_AUTH_TOKENS: prod-token-a,prod-token-b
GATEWAY_DEFAULT_PROJECT_ID: my-product

See Configuration for every GATEWAY_* variable. The three that matter most for a deployment are above:

  • GATEWAY_POSTGRES_DSN — the durable store. Empty would select the in-memory driver, which is not suitable for production or multiple replicas.
  • GATEWAY_SERVICE_AUTH_TOKENS — the accepted service credentials. Callers present one as Authorization: Bearer <token>. Leaving it empty disables auth and logs a warning.
  • GATEWAY_DEFAULT_PROJECT_ID — the project shard for requests that omit project_id.

Running migrations

Auto-migration is convenient but couples schema changes to process startup. To run migrations as an explicit, gated step — for example a Compose one-shot service or a Kubernetes init job — set GATEWAY_POSTGRES_AUTO_MIGRATE=false and invoke the binary's migrate subcommand. It applies pending Postgres migrations and exits:

workspace migrate
docker run --rm \
-e GATEWAY_POSTGRES_DSN='postgres://workspace:password@db:5432/workspace?sslmode=disable' \
ghcr.io/elloloop/workspace workspace migrate

Ports

PortPurpose
8080Connect-RPC over HTTP/JSON, gRPC, and gRPC-Web (GATEWAY_CONNECT_PORT).
9090Prometheus /metrics and the /healthz / /readyz probes (GATEWAY_METRICS_PORT).

Behind a mesh or mTLS

Workspaces is an internal service — never expose port 8080 to the public internet. Run it on a private network behind your API gateway or product backend. If a service mesh or mTLS already authenticates every caller at the transport layer, you may leave GATEWAY_SERVICE_AUTH_TOKENS empty to skip the redundant bearer check; the service logs a service_auth_disabled warning so the choice is never silent. With no mesh, set the tokens. See Security & Service Auth.

Where to next