Skip to main content

Providers

A provider determines where and how a Loom job executes — on your local machine's shell or inside a Docker container. Provider selection is automatic: if your job specifies an image, it runs in Docker; otherwise, it runs on the host.

Why providers matter

Provider selection changes four things about your job's execution environment:

ConcernHost providerDocker provider
IsolationNone — inherits your local process environmentProcess and filesystem isolation via containers
Available toolsWhatever is installed on your machineWhatever is in the container image
Environment variablesYour shell env + Loom variablesOnly Loom variables + image defaults
Failure debuggingFailures reference local paths and toolsFailures reference container paths; check Docker daemon first

Choosing the right provider means choosing the right trade-off between speed (host — no container startup overhead) and reproducibility (Docker — consistent environment regardless of the developer's machine).

How Loom selects a provider

Provider selection is per-job and automatic. The rule is:

Does the job have image:?Provider usedWhat happens
No (omitted or empty)HostJob runs directly in your local shell
Yes (non-empty string)DockerJob runs inside a container using the specified image

There is no manual provider override. If you want a job to run in Docker, set image:. If you want it on the host, remove image:.

The provider name is available at runtime as the LOOM_PROVIDER variable (host or docker), which you can read in your scripts or use for conditional logic.

Host job example

version: v1
stages: [ci]

lint:
stage: ci
target: linux
script:
- golangci-lint run ./...

No image: key — Loom uses the host provider. The golangci-lint binary must be available on your machine.

Docker job example

version: v1
stages: [ci]

test:
stage: ci
target: linux
image: golang:1.22
script:
- go test ./...

The image: golang:1.22 key triggers the Docker provider. Loom pulls the image (if needed), creates a container, mounts your workspace, and runs the script inside it.

Running workflows with providers

Both host and Docker jobs are triggered the same way:

loom run --local --workflow .loom/workflow.yml

Loom inspects each job definition in the compiled graph and routes it to the correct provider automatically. You can verify which provider a job used by checking the provider events in the job's system section:

.loom/.runtime/logs/<run_id>/jobs/<job_id>/system/provider/events.jsonl

Docker workspace mount modes

When a job uses the Docker provider, Loom mounts the workspace into the container using one of two strategies:

ModeHow it worksWhen to use
bind_mountBind-mounts the host workspace directory directly into the containerStandard local development — fast, zero copy overhead
ephemeral_volume (default)Creates a Docker volume, seeds it with the workspace, mounts the volume, then syncs changes back after executionStronger isolation — avoids permission/ownership issues on the host

Configuring the mount mode

MethodExamplePriority
CLI flagloom run --local --docker-workspace-mount bind_mountHighest
Environment variableLOOM_DOCKER_WORKSPACE_MOUNT=bind_mount loom run --localMedium
Defaultephemeral_volumeLowest

The flag always wins. If you set both, the flag takes priority:

LOOM_DOCKER_WORKSPACE_MOUNT=ephemeral_volume loom run --local --docker-workspace-mount bind_mount
# → uses bind_mount (flag takes priority)

Isolation and security model

Host provider

  • Fastest path for local iteration — no container startup overhead.
  • Weakest isolation — the job inherits your full local environment, including credentials, shell configuration, and machine-specific paths.
  • Best for: quick local validation, jobs that need local toolchain access.

Docker provider

  • Stronger isolation — process and filesystem isolation via containers.
  • Your workspace is mounted into the container, so the job can read and write project files.
  • You are still trusting your local Docker daemon and the images you reference.
  • Best for: reproducible builds, testing against specific runtimes, isolating job environments from each other.
HostDocker
Startup speedInstantContainer pull + create overhead
EnvironmentFull local shell envOnly Loom variables + image defaults
Filesystem isolationNoneContainer filesystem with workspace mount
ReproducibilityDepends on local machineConsistent across machines (same image)
When to chooseLocal iteration, native toolchainCI parity, multi-runtime testing
caution

Host jobs inherit your local environment, which may include credentials or sensitive paths. When sharing logs or receipts for help, follow What to share and redact sensitive values.

Common pitfalls

Docker daemon not reachable

  • Symptom: Jobs with image: fail immediately with errors like "cannot connect to Docker daemon" or "pull access denied".
  • Fix: Verify Docker is running and accessible from your shell (docker info). Then retry. If the failure persists, check the provider events in the job's system section: .loom/.runtime/logs/<run_id>/jobs/<job_id>/system/provider/events.jsonl.

Job ran on host but you expected Docker

  • Symptom: The job seems to use host tooling; container-only tools aren't present.
  • Cause: image: is missing, empty, or removed by template resolution.
  • Fix: Ensure the job's resolved definition has a non-empty image: value. If you use templates or default, inspect the resolved workflow via loom compile to confirm the image is present.

Different behavior between host and Docker jobs

  • Symptom: A job works on host but fails in Docker (or the opposite).
  • Cause: Host jobs inherit your shell environment; Docker jobs only get Loom's workflow/job variables plus the image defaults.
  • Fix: Make all inputs explicit using workflow or job variables: (see Variables). Don't assume your shell environment is available inside a container.

Planned

  • Remote providers and runner pools (planned)