Self-host with Docker Compose
The Fleans release pipeline publishes a Docker Compose bundle on every
v<SemVer> tag. This guide walks through the full lifecycle of a Compose-based
deployment: acquire the artifact, run the stack, tune .env, back up state,
and upgrade across releases. For non-release-asset topology details (raw
aspire publish, port matrix, reverse-proxy patterns), the canonical
reference is Deployment.
Prerequisites
Section titled “Prerequisites”- Docker Engine 24+ with the Compose v2 plugin (
docker compose version). - ~2 GB free disk for images + Postgres data volume.
- 2 GB RAM as a working baseline (Orleans + Redis + Postgres + Web + MCP).
- Linux, macOS, or Windows (WSL2). The bundle is shell-friendly; on Windows
use Git Bash or WSL2 for the unzip +
docker composeinvocations. - A running Docker daemon with internet pull access to
ghcr.io/nightbaker/*.
1. Download the release bundle
Section titled “1. Download the release bundle”The release workflow attaches docker-compose-v<VER>.zip to every GitHub
Release. Download it via the gh CLI:
gh release download v0.11.0 --repo nightBaker/fleans -p 'docker-compose-*.zip'Or, without the gh CLI, fetch the asset directly:
curl -LO https://github.com/nightBaker/fleans/releases/download/v0.11.0/docker-compose-v0.11.0.zipVerify the images the bundle pulls
Section titled “Verify the images the bundle pulls”The compose bundle itself is just declarative YAML — its bytes aren’t signed. The substantive supply-chain risk lives in the four container images the YAML references; those images ARE signed by the release pipeline using cosign keyless signing (Sigstore Fulcio CA). Verify each image before docker compose up:
for SVC in api web worker mcp; do cosign verify \ --certificate-identity-regexp "https://github.com/nightBaker/fleans/.github/workflows/release.yml@refs/tags/v.*" \ --certificate-oidc-issuer https://token.actions.githubusercontent.com \ ghcr.io/nightbaker/fleans-$SVC:0.11.0doneEach cosign verify exits 0 and prints a JSON object containing a Bundle block with tlogEntries proving the signature was logged to the public Rekor transparency log.
docker compose pull does NOT verify signatures by default; for enforcement, run an admission controller (Kyverno + Sigstore policy) or pre-pull via docker pull --policy=verify (Docker 25+ with content trust enabled).
2. Run the stack
Section titled “2. Run the stack”unzip docker-compose-v0.11.0.zip -d fleanscd fleansdocker compose up -ddocker compose psdocker compose ps should show all services as running. The default port map:
| Service | Container port | Host port | Purpose |
|---|---|---|---|
fleans-management | 8080 | 8080 | Blazor Server admin UI |
fleans-core | 8080 | 8081 | Orleans Core silo + REST API (/workflow/*) |
fleans-mcp | 8080 | (internal) | MCP server for AI-agent integration |
fleans-worker | (none) | (internal) | Worker silo (script/condition grains) |
orleans-redis | 6379 | (internal) | Orleans clustering + PubSub grain storage |
postgres | 5432 | (internal) | Workflow event store + read model |
compose-dashboard | 18888 | (random) | Aspire dashboard for OTel traces (dev tool) |
Open http://localhost:8080 — the admin UI lands on the dashboard. Programmatic clients hit the REST API at http://localhost:8081/workflow/….
3. .env reference
Section titled “3. .env reference”The bundle ships an .env file populated with sensible defaults — the release
pipeline post-processes Aspire’s compose output to bake in version-pinned image
refs, randomly generated secrets, and conventional ports so docker compose up -d works out of the box. Override values before bringing the stack up to
customise the install. Variables follow the .NET hierarchical naming rule — :
becomes __ (e.g. Authentication:Audience → Authentication__Audience); see
Configuration for the full convention.
| Variable | Default | Description |
|---|---|---|
FLEANS_CORE_IMAGE | ghcr.io/nightbaker/fleans-api:<release-tag> | Orleans Core silo image. Change to pin a different version or registry. |
FLEANS_MANAGEMENT_IMAGE | ghcr.io/nightbaker/fleans-web:<release-tag> | Blazor admin UI image. |
FLEANS_MCP_IMAGE | ghcr.io/nightbaker/fleans-mcp:<release-tag> | MCP server image. |
FLEANS_WORKER_IMAGE | ghcr.io/nightbaker/fleans-worker:<release-tag> | Worker silo image. |
FLEANS_CORE_PORT | 8080 | Container port the API listens on. The bundle maps host 8081 → container 8080. |
FLEANS_MANAGEMENT_PORT | 8080 | Container port the Web UI listens on. The bundle maps host 8080 → container 8080. |
FLEANS_MCP_PORT | 8080 | Container port the MCP server listens on (no host binding by default). |
ORLEANS_REDIS_PASSWORD | (random per release) | Override in production. Defaults to a random secret baked into .env at release time, identical across silos. |
POSTGRES_PASSWORD | (random per release) | Override in production. Same caveat as the redis password. |
CLUSTER_CLUSTER_ID / CLUSTER_SERVICE_ID | fleans / fleans | Orleans cluster identity. Change if running multiple Fleans clusters against shared Redis/Postgres. |
Authentication__Authority | (empty — auth disabled) | OIDC issuer URL, passed to core, management and MCP. Alone it turns on JWT enforcement for REST (audience fleans-api) and MCP (fleans-mcp); the admin UI also needs ClientId/ClientSecret. |
Authentication__ClientId | (empty) | Web admin UI OIDC client ID (Fleans.Web). |
Authentication__ClientSecret | (empty) | Web OIDC client secret. Use a Docker secret in production; do not commit. |
Fleans__Role | Core (api) / Worker (worker) | Internal role per service. Don’t change unless you understand the Core/Worker split. |
Inspect the effective env at any time with docker compose config | less.
4. Persistence
Section titled “4. Persistence”The bundle uses Postgres as the workflow event store + read-model, backed by a
single named volume that Aspire generates per release (the volume name embeds
an Aspire-internal hash, e.g. fleans.aspire-<hash>-postgres-data). Find the
exact name with:
docker volume ls --filter name=postgres-dataBack up the database with pg_dump:
docker compose exec -T postgres \ pg_dump -U postgres fleans > fleans-backup-$(date +%Y%m%d).sqlRestore with docker compose exec -T postgres psql -U postgres fleans < fleans-backup-YYYYMMDD.sql.
Redis (Orleans clustering + PubSub grain storage) is in-memory only — the bundle does not configure persistence on the Redis container. Orleans re-bootstraps cluster membership and PubSub state from Postgres + the Redis clustering provider on silo restart, so a Redis wipe is non-destructive. Never wipe Postgres without a backup.
docker compose down -v removes the Postgres volume. Use docker compose down
(without -v) to stop the stack while preserving state.
To tune Orleans consumer parallelism (pulling-agent count per cluster) for higher throughput, set Fleans__Streaming__Redis__TotalQueueCount (Redis), Fleans__Streaming__Kafka__QueueCount (Kafka), or an explicit Fleans__Streaming__AzureQueue__QueueNames__0..N list in the environment section of every silo service (fleans-core, fleans-worker) in your Compose file. Setting a value only in .env does not pass it to containers unless the Compose file references it. Keep the queue count identical across all silos. See Tuning throughput for the sizing heuristic, rehash caveat, and the one-partition-per-topic rule for Kafka.
5. External Postgres
Section titled “5. External Postgres”To use a managed Postgres (RDS, Cloud SQL, Aiven), comment out the postgres
service in docker-compose.yml and point the connection string at the
external host:
ConnectionStrings__fleans=Host=my-pg.example.com;Port=5432;Database=fleans;Username=fleans;Password=<secret>;Ssl Mode=RequireThe schema migrations run automatically on Fleans.Api startup (MigrateAsync()).
First boot needs the Postgres user to have CREATE TABLE privileges; subsequent
boots only need INSERT/UPDATE/SELECT/DELETE on the fleans database.
6. Upgrade path
Section titled “6. Upgrade path”Across patch / minor releases:
# Replace the bundle in-place with the new release.gh release download v0.2.0 --repo nightBaker/fleans -p 'docker-compose-*.zip'unzip -o docker-compose-v0.2.0.zip -d fleans
cd fleansdocker compose pulldocker compose up -dThe new Fleans.Api startup runs any pending EF migrations against the
existing Postgres volume. Redis state is rebuilt automatically on silo
re-activation.
For breaking version bumps (e.g. major release), read the GitHub Release notes — they call out any migration steps Compose itself does not perform (secret rotations, value renames, removed env vars).
7. Troubleshooting
Section titled “7. Troubleshooting”- Port 8080 already in use. Edit the host port in
docker-compose.yaml— e.g.8080:${FLEANS_MANAGEMENT_PORT}→9080:${FLEANS_MANAGEMENT_PORT}— or stop the conflicting process. The container-side port stays unchanged. fleans-corerestarts a few times before becoming stable. The Postgres container takes 20–40s to initialise on first run. The silos use Compose’srestart: alwayspolicy and retry connecting until Postgres is ready. As long asdocker compose pseventually shows them all asUpand stable, this is expected. Watch withdocker compose logs -f fleans-core postgres.- Migration errors like
relation "X" already exists. All silos (fleans-core,fleans-management,fleans-mcp,fleans-worker) callMigrateAsyncat startup; they serialize via a Postgres advisory lock (transaction-scoped, so it also holds through PgBouncer in transaction mode) so concurrent migrations are safe. If you ever see a “relation already exists” error, the lock acquisition was likely skipped (e.g. running an out-of-tree fork without the fix shipped inFleans.ServiceDefaults); upgrade to a release ≥ 0.1.0-beta. docker compose configshows blank values forAuthentication__*. The bundle ships those empty by default (auth-disabled mode). Fill them in.envto enable OIDC.- Linux SELinux/AppArmor permission denied on volumes. Add the
:Zsuffix to bind-mount paths indocker-compose.yaml(e.g../data:/var/lib/postgresql/data:Z) to relabel for SELinux, or run with--privilegedin dev.
If a specific error is not covered here, the Aspire-emitted compose YAML mirrors the production topology in Deployment — check that page for component-level explanations.
See also
Section titled “See also”- Deployment — non-Helm topology overview, reverse-proxy patterns, port matrix, bare-VM story.
- Configuration — full env-var matrix
(Tier 1 dev knobs vs Tier 2 runtime keys), the
:↔__naming rule. - Persistence — Sqlite vs Postgres provider selection, external-Postgres connection-string contract, migration model.
- Authentication — OIDC setup for the admin UI, JWT validation for the API.
- Cutting a Release — maintainer runbook for publishing a new compose bundle.