Skip to main content

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

The ContainerProvider 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.
containerd and nerdctl are not supported runtimes. /run/containerd/containerd.sock was probed as priority 10 until it was measured: containerd serves its own gRPC API over HTTP/2, while Crewship’s client speaks the Docker REST API over HTTP/1.1. A ping to it comes back as an HTTP/2 SETTINGS frame, so the probe could never have succeeded on any version. nerdctl is a client for that same gRPC API, not a Docker-API server, so installing it changes nothing.The candidate is gone. In its place, when no runtime answers and a containerd socket exists, the detection error names the socket and explains why it is not usable, instead of reporting that nothing was found on a machine that visibly has a container runtime.Rancher Desktop is supported in dockerd (moby) mode only. In containerd mode it still creates ~/.rd/docker.sock, but that socket accepts a connection and immediately closes it — detection correctly moves on, and Crewship cannot use the instance. Switch the container engine to dockerd in Rancher Desktop’s preferences.

VM-backed runtimes and host bind mounts

Colima, Rancher Desktop, Docker Desktop and podman 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-sidecar and entrypoint.sh are resolved next to the crewship executable (/opt/homebrew/… for Homebrew, /usr/local/bin for install.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, /output and /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 the docker 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. Set CREWSHIP_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

The CrewConfig 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 inspect and container list

Container Inspection

Apple Containers use a different JSON structure:

Apple Containers Limitations

auto still prefers Docker. A crew on the Apple provider gets the credential tmpfs, the egress fence, an init process and the rlimits, but not CapDrop: ALL, no-new-privileges, noexec mounts or the memory-swap and restart-policy controls — several of which the Apple CLI has no flag for at all. crewship start logs a warning naming exactly what is and is not applied whenever it wires the Apple provider; treat that line as a deployment decision, not noise.

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 the prefix field set to keep container names from colliding.

Storage Providers

State Providers

The state provider stores ephemeral agent run states (running, completed, failed):
BoltDB is an embedded key-value store that requires no external dependencies. It stores state in a single file.
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:
Invalid provider values cause a startup error with a descriptive message.

Choosing a Provider Combination

Simplest setup. No external dependencies beyond Docker.