Skip to main content

Environment Variables

All Crewship configuration can be set via environment variables. These override values from the YAML config file. The mapping is defined in applyEnvOverrides in internal/config/config.go.
As of PR #446, no env vars are required for first boot — secrets are auto-generated and persisted. Set variables here only to override a default or hand a secret to an external manager. Jump to a group: Server · Container · Storage · Auth · Encryption · Keeper · CLI client · Installer.

Server

IPC

Container

Runtime image pinning

Before starting a crew container, Crewship resolves the runtime image tag to an OCI manifest digest (a registry HEAD) and then pulls that digest, not the tag. A tag can be repointed between the check and the fetch; a digest cannot. The digest is recorded on the crew’s provisioning.step journal entry (payload.digest, plus payload.pinned), stamped with the run’s trace_id, so the audit trail can answer which image produced this run. Read it back with:
No new endpoint or command was needed for this — the digest rides the existing provisioning.step payload, which crewship journal already returns. Offline and air-gapped installs are unaffected. When the registry cannot be reached at all, the digest lookup returns nothing and Crewship keeps using the local copy exactly as before — it just logs the local digest alongside, so even an offline run is attributable. When the local image is provably stale. This is the case that changed. Two different failures land a host in the same state — the registry answered the digest check, and the tag the container will start from resolves to a different manifest. That is provable drift, not a guess, so Crewship refuses to start the container rather than silently running the wrong image. CREWSHIP_ALLOW_STALE_RUNTIME_IMAGE=1 is the single host-wide opt-out for both routes. Route 1 — the pull failed. Reaching a pull with a local copy present means the digest check succeeded and the local copy did not match it:
The most common cause is a registry rate limit (Docker Hub answers HEAD generously and throttles blob pulls). Route 2 — the pull succeeded but the tag could not be moved. A digest-pinned pull leaves the image on disk unnamed, so Crewship re-creates the repo:tag alias itself. If the daemon refuses that and an older copy of the tag is still there, the tag keeps pointing at the old manifest and every downstream lookup — ContainerCreate included — addresses the image by tag. Crewship re-reads the tag before deciding, and refuses on the same terms:
This route is the more recoverable of the two: the manifest you wanted is already on disk, it just has no name, which is why the error hands you the docker tag that fixes it properly. Digests are abbreviated (sha256:aaaa…) in the examples above for width — the real error prints them in full, and the docker tag line it gives you is runnable as printed. What the opt-out does and does not relax. With CREWSHIP_ALLOW_STALE_RUNTIME_IMAGE=1 both routes proceed, log a warning naming both digests, and journal the local digest — what actually ran — with payload.pinned: false. The escape hatch relaxes execution only. It never relaxes the audit record: a provisioning.step row never claims a digest that did not run, opt-out or not. One case is deliberately left alone: if the re-tag fails and the daemon answers 404 for the tag afterward, there is no stale image to run and no digest to record. The provisioning.step row is still written — it just carries no digest and pinned: false — and the daemon’s own error surfaces at container create, which is where it is most legible. A read-back that fails rather than answering 404 is not the same thing and is not treated as one: a timeout or a busy daemon says nothing about what the tag resolves to, so Crewship falls back on what it proved before the pull. If a local copy was present then (which is what sent it to the pull in the first place), the tag is still the stale one and the start is refused. Locally built devcontainer cache images (crewship-cache:*) exist in no registry and therefore have no digest. Their provisioning.step rows omit the digest key entirely; their provenance is the base image recorded on the build’s own steps.

Set by Crewship inside the container

These are not configuration — Crewship sets them on the crew container itself, and an agent (or a postCreateCommand) can read them.
CREWSHIP_CONTAINER_PREFIX is essential for multi-instance deployments. Without it, multiple Crewship instances will create containers with colliding names like crewship-team-engineering.

Storage

State

Public-facing URL

Origin enforcement (CORS / EnforceOrigin)

Rate limiting & proxy trust

Metrics

Webhooks / hooks (SSRF guard)

Profiling & observability

These are off by default and intended for operators debugging performance.

Crash reporting (Sentry)

Email (password recovery)

Google sign-in (optional)

Logging

CLI and runtime context

These names are used by CLI commands, test harnesses, or the agent runtime rather than by the server’s main YAML configuration. They are listed here so a deployment or script can distinguish an intentional context variable from a typo.

Authentication

As of PR #446 (internal/secrets), the three persisted secrets — ENCRYPTION_KEY, NEXTAUTH_SECRET, and CREWSHIP_ADMIN_TOKEN_HMAC_KEY (the managed list in internal/secrets/bootstrap.go) — are bootstrapped on first start: missing values are generated with crypto/rand, persisted atomically to <dataDir>/secrets.env at mode 0600, and re-exported into the process environment so existing os.Getenv call sites see them unchanged. Explicit env vars override the file; partial persistence is handled (one missing key is filled, the other valid keys are preserved). CREWSHIP_INTERNAL_TOKEN is handled separately — the config loader generates it per boot when unset and does not persist it, so it rotates on each restart. For clustered deployments, set all four explicitly via your secret manager.

Auto-Derived Values

  • CREWSHIP_NEXTJS_URL: If not explicitly set, defaults to http://localhost:{CREWSHIP_PORT}. In single-binary mode, the internal resolver calls itself on the same port.
  • CREWSHIP_INTERNAL_TOKEN: Generated fresh per boot by the config loader when unset; not persisted, so it rotates on restart. Pin it explicitly only for multi-instance IPC bus sharing.

Encryption

Composio integrations

Keeper (AI Security Gatekeeper)

Setting KEEPER_OLLAMA_URL auto-enables Keeper (unless KEEPER_ENABLED is explicitly false). This means you only need to set the URL to activate Keeper.

Local models (coding agents)

LLM Proxy

Paymaster (Billing Modes)

These variables tag every cost-ledger row written from inside an agent run. The orchestrator sets them on the agent’s environment from the credential type before exec; the sidecar reads them once at startup and includes them on every POST /api/v1/internal/cost/record call.
Operators do not set these manually for normal use. They are propagated automatically based on the credential’s auth type (API key → metered, OAuth/subscription → flat_rate). They are listed here only for sidecar-image authors and tooling that intercepts the orchestrator handoff.
See Paymaster for the full billing-mode model and Migrations for the v62 cost-ledger schema.

Ollama (Episodic Memory + Keeper)

Feature toggles & runtime tuning

Backup webhooks

Operational skip flags (dev / CI)

These bypass safety or convenience behaviours. Never set them in production.

License

Seed Data

These variables are used by the ./dev.sh seed command:

Container Runtime

The Docker provider checks DOCKER_HOST first, then auto-detects from candidate socket paths.

Devcontainer Cache GC

Crewship runs a background sweeper every 30 minutes that detects three kinds of leaks: temporary provisioning containers orphaned by a crewshipd crash (matched by the crewship-provision-* name and the crewship.temp=provision label — see Devcontainers → Background GC for why the name is the one that authorises deletion), unreferenced crewship-cache:* images left behind when the process is killed between docker commit and the database write that links the image to a crew, and intermediate crewship-feat:* BuildKit feature images (per-feature layers that are regenerable and never directly referenced by a crew row). Temp containers older than one hour are always removed. Cache and feature images are logged only by default; deletion must be explicitly enabled. A 5-minute age floor protects images that were committed in the last few minutes (closing the race between docker commit and the DB update inside Provision()), so enabling auto-delete is safe even on busy systems.
Leave CREWSHIP_CACHE_GC_AUTODELETE unset on developer machines. Enable it on long-running production hosts where reclaiming disk matters more than preserving incidental cached images. Inspect the orphan list in the logs first to verify nothing important would be removed.

CLI client

These configure the crewship CLI on a developer/operator machine (not the server). They override values in ~/.crewship/cli-config.yaml. Resolution order and parsing live in internal/cli/config.go.

Installer

Read by scripts/install.sh (the curl … | bash install path), not by the running server.

Complete Example

Validation

The config is validated at startup (Config.Validate). These constraints are enforced: