Providers
Crewship uses a provider abstraction for containers, storage, and state management. Each provider implements a Go interface and can be swapped via configuration.These are not LLM providers. “Provider” on this page means the
internal/provider/ abstraction over container runtimes, object storage and
state — Docker, Apple Containers, BoltDB. If you came looking for Anthropic /
OpenAI / Ollama, model endpoints, API keys or what a token costs, that is
Multi-provider LLM configuration. The two
vocabularies share a word and nothing else.Container Providers
TheContainerProvider interface (internal/provider/) defines the contract for container lifecycle management.
Docker Provider
Config key:container.provider: "docker"
The Docker provider (internal/provider/docker/docker.go) is the primary container backend. It auto-detects Docker-compatible runtimes and manages crew containers.
Auto-Detection
The provider probes multiple socket paths in priority order:
Each socket gets a 1.5-second ping timeout (
socketPingTimeout) to prevent hung daemons from blocking detection.
Podman masquerades as Docker. The provider checks server components for “Podman Engine” to correctly identify it, even through Docker-compatible sockets.
A runtime that takes over
/var/run/docker.sock is still named correctly. OrbStack (from its privileged helper), Rancher Desktop (with administrative access on) and Docker Desktop all offer to point /var/run/docker.sock at their own socket. That path is priority 2 and matches on the path alone, so the runtime behind it would otherwise be reported as a plain docker — the same product landing under two different labels depending only on whether its helper is installed. Detection resolves the symlink and prefers the label of the specific socket it points at. A link to an unrecognised target, or no link at all, keeps docker.This is not cosmetic: knownRuntimeGaps switches on the detected runtime, so a misnamed runtime is one whose known gaps would never be applied.VM-backed runtimes and host bind mounts
Colima, Rancher Desktop, Docker Desktop andpodman machine run the daemon
inside a VM that only sees the host directories it was configured to share;
OrbStack shares the whole filesystem. A crew container’s bind sources are
resolved inside that VM, so a path outside the share set does not exist as far
as the daemon is concerned — and the daemon reports it as
bind source path does not exist, for a file that is plainly on your disk.
Two mechanisms keep this from being the operator’s problem:
- The mandatory binds are staged into the data dir.
crewship-sidecarandentrypoint.share resolved next to thecrewshipexecutable (/opt/homebrew/…for Homebrew,/usr/local/binforinstall.sh) — outside every default share set. At startup the provider copies them to<OutputBasePath>/.runtime/and bind-mounts the copies, so the daemon only has to see the one host subtree it already needed for/workspace,/outputand/crew. The copy preserves the source’s mtime, so the startup stale-sidecar check keeps working. - Anything still unreachable is named. The provider probes the mandatory binds at construction and, on any bind failure, rewrites the daemon’s error into one that says which runtime, which path, that the path does exist on this host, and the exact command that adds it to the VM’s share set.
Which daemon builds the image
Devcontainer provisioning builds with thedocker CLI but creates and
cache-checks through the provider’s own socket. The build is pinned to the
provider’s endpoint (DOCKER_HOST set on the build subprocess, DOCKER_CONTEXT
stripped), so the operator’s docker context cannot send the image to a
different daemon than the one the container is created on.
Crew containers stranded by a runtime switch
Changing runtime under a running instance does not stop the previous daemon’s crew containers, and they stay bind-mounted to the same live host crew directories. At startup the provider enumerates every other reachable Docker-API daemon, finds crew containers carrying this instance’s container prefix, logs them with both runtimes named, and stops them — stopped, not removed, so they remain inspectable. SetCREWSHIP_STRANDED_CREWS=report to
report without stopping.
Runtime Selection
Network Management
The provider auto-creates a Docker bridge network (default:crewship-agents) for inter-container communication:
Image Management
The runtime image is pulled automatically if not present locally. The provider lists all local images and checks manually (Docker Desktop can block on filtered queries to remote registries).Container Naming
Containers follow the naming convention{prefix}-team-{crew-slug}-{crew-id}, built by crewResourceName in internal/provider/docker/docker.go. The prefix defaults to crewship when container.container_prefix is empty. The crew id (a globally-unique UUID) is appended to prevent cross-tenant container-name collisions (audit C1).
Provider Interfaces
The Docker provider implements three interfaces:CrewConfig Fields
TheCrewConfig struct is passed to EnsureCrewRuntime when creating or ensuring a crew container:
CrewConfig (internal/provider/container.go) carries additional
devcontainer-derived fields applied to the container’s HostConfig at
create time — Privileged, Init, CapAdd, SecurityOpt,
ExtraMounts, PostStartCommands, InitHookEnabled, and Services
(sidecar service containers). The table above lists the fields most
operators set directly; the rest are populated from the crew’s
devcontainer feature resolution.Apple Container Provider
Config key:container.provider: "apple"
The Apple provider (internal/provider/apple/apple.go) uses Apple Containers on macOS Tahoe+. Since there is no Go SDK, all operations shell out to the container CLI.
For the operator’s view of this runtime — prerequisites, how provisioning
builds the image instead of committing one, build performance, and the limits
in one place — see Apple Containers on macOS.
This section is the provider’s internals.
Apple Container Features
- Lightweight VM isolation (stronger than Docker namespaces)
- Native macOS integration
- IPv4 networking with gateway
- JSON output from
container inspectandcontainer list
Container Inspection
Apple Containers use a different JSON structure:Apple Containers Limitations
Crew delivery mounts
Crew containers get the same three delivery mounts on both providers: an in-memory/secrets for file-delivered credentials, the crewship-sidecar
binary that enforces restricted network mode, and entrypoint.sh forced as
the container entrypoint with an init process to reap orphans. Both host
artefacts are mandatory — the Apple provider refuses to create a crew container
without them rather than starting one whose egress mode nothing enforces.
The one difference worth knowing is /secrets ownership. Docker mounts it
owned by the agent UID with mode 0700; Apple’s container create --mount
accepts only type, size, mode, source, destination and readonly, so
the mount root is root-owned and has to be world-writable for the agent to use
it at all. The credentials themselves keep the same protection on both — the
per-agent directory is created 0700 and each file 0400/0600, owned by the
agent — but on Apple another UID inside the container can list /secrets and
see agent slugs.
Crew settings the Apple provider does not apply
Starting a crew hands the provider the crew’s full runtime configuration. The Apple Containers provider acts on the crew id, slug, memory, CPU count, network mode and init; the remaining settings below are not applied. None of them blocks the crew — it starts either way — but none of them is dropped in silence any more.
Each of these is logged at
WARN by the daemon when the crew starts, naming
the setting and what was lost, and is streamed into the chat as a status line.
A crew that sets none of them sees nothing.
Two entries came out of this table when the gaps behind them closed, and the
direction matters: an entry that outlives its gap is not a harmless
over-report, because it feeds the crew read paths and the agent’s own system
prompt. The container is created from the crew’s provisioned image
(CachedImage > Image > the provider default, the same chain the Docker
provider uses), and devcontainer containerEnv is passed to container create
as --env.
network_mode: restricted is enforced here. It reaches the container the
same way it does under Docker: the crewship-sidecar proxy binary is
bind-mounted read-only into the crew container, the orchestrator starts it, and
every exec carries HTTP_PROXY/HTTPS_PROXY pointing at it.Because the binary is a hard requirement of container creation, there is no
state in which a crew starts on this provider with restricted configured and
nothing enforcing it: either the mount is there, or the container is not
created. If a container created before the mount existed is still running, the
sidecar cannot launch inside it and the run fails with sidecar health check failed rather than proceeding unfenced — recreate the crew’s container to
clear it.The reporting built for the unenforced case remains and still fires for the
settings in the table above: GET /api/v1/crews carries network_mode_enforced
next to network_mode, crewship crew list marks an unenforced fence
restricted (NOT ENFORCED), and an agent whose fence is not real is told so in
its system prompt. On this provider those now report enforced.Auto Provider
Config key:container.provider: "auto"
Auto-detection tries Docker first, then falls back to Apple Containers. Useful for development environments where the runtime may vary.
The order is deliberate and not merely a preference: several crew isolation
controls exist on the Docker path only — CapDrop: ALL, no-new-privileges,
noexec mounts, the memory-swap and restart-policy limits — and the Apple CLI
has no flag for some of them at all (see the limitations table above). The
restricted egress fence, the credential tmpfs, the init process and the
rlimits are on both. On a host with both runtimes installed, auto therefore
still selects the more hardened one; Apple Containers are the fallback for
hosts that have nothing else. Set container.provider: "apple" explicitly if
you want that path regardless of what else is installed.
Apple Containers additionally require macOS 26 (Tahoe) or newer. On an older
host the container CLI can be installed and its system service can answer
while providing none of the per-container networking this provider assumes, so
detection rejects it and auto falls through as though the runtime were
absent.
Kubernetes Provider — v0.2 roadmap
A Kubernetes provider that runs each crew as a pod (or job) is on the v0.2 roadmap for distributed deployments. Until it lands, production-at-scale runs use Docker on a dedicated host or a small fleet of VMs with theprefix field set to keep container names from colliding.
Storage Providers
State Providers
The state provider stores ephemeral agent run states (running, completed, failed):A PostgreSQL state provider is on the v0.2 roadmap for multi-server, high-throughput deployments.
Provider Validation
At startup, the config validates all provider selections:Choosing a Provider Combination
- Development
- Production (Single Server)
- Production (Distributed) — v0.2 roadmap