Project Configuration

Every project has a small set of configuration that shapes how its agents run: a setup script that prepares the workspace, an optional Docker Compose file for services your code depends on, a nested Docker switch for projects whose own tooling starts containers, and an execution target that decides where agents run.

You manage the first three under Settings → Sandbox in your project, and the execution target under Settings → Execution. Saved values apply to every agent session for that project.

This guide explains each setting, what it supports, and where the limits are.

The sandbox

Every agent session gets a fresh, isolated sandbox. Your repository is cloned into it at /workspace, your setup script runs, and then the agent starts work. When the session ends the sandbox and everything in it is destroyed, so nothing carries over between sessions — no cached state, no leftover containers, no files written outside your repository.

That freshness is the thing to design around: anything your agents need must either be in your repository or installed by your setup script, every time.

The sandbox is a Debian-based Linux environment with a common toolchain already present:

  • Node.js (with corepack, so pnpm and yarn are one command away)
  • git, curl, bash, jq, ripgrep
  • python3 and build-essential for native builds
  • tar, gzip, zip/unzip, xz
  • the docker CLI

Agents run as a non-root user with sudo available, and apt-get works if you need a system package. Language runtimes and project toolchains beyond Node are not preinstalled — install what you need in the setup script.

Setup script

The setup script runs at the start of every agent session, before the agent begins work. It runs under bash from /workspace, with your repository already checked out there, so it is the place to get the workspace ready: install dependencies, fetch tools, and do any one-time preparation your code needs to build and test.

Typical uses:

  • Install dependencies — pnpm install, npm ci, pip install -r requirements.txt, bundle install.
  • Install a language runtime or CLI your project needs that is not preinstalled.
  • Run a framework or build step that prepares the project — code generation, schema or client generation, asset compilation.

A short example:

#!/usr/bin/env bash
set -euo pipefail

corepack enable
pnpm install --frozen-lockfile
pnpm prisma generate

If the setup script exits non-zero, the session fails. That is deliberate — an agent working in a half-prepared workspace produces confusing failures much later. Use set -euo pipefail so a failing step surfaces immediately rather than being masked by a later success.

The corollary: if a step is genuinely optional, make that explicit so a transient failure cannot end the session. A browser download for end-to-end tests is the common case:

pnpm exec playwright install --with-deps chromium \
  || echo "WARN: browser install failed; visual checks unavailable"

Leave the script empty if your project needs no preparation.

Keeping it fast

The setup script runs on every session, so its cost is paid every time an agent picks up work. Prefer the fast path where your tooling offers one — a frozen lockfile install over a full resolve, a prebuilt binary over compiling from source, and only the parts of a monorepo the agents actually need.

Docker Compose services

Some projects need other services running alongside the agent — a Postgres database, a Redis cache, a message broker, a mock API your tests talk to. The Docker Compose field lets you declare those as sibling services. They are started before your agent and torn down when the session ends.

The file is standard Docker Compose. Declare each dependency under services::

services:
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: dev
      POSTGRES_PASSWORD: dev
      POSTGRES_DB: dev
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U dev"]
      interval: 3s
      retries: 30

Your agent reaches each service over the network, by its service name — the example above is available at the hostname postgres on port 5432. Point your test configuration at those names rather than at localhost.

Use a healthcheck on anything your tests connect to immediately. Without one, a session can start before the service is accepting connections.

A compose file does not give your project a Docker daemon. It declares services for SetForth to run on your behalf; it does not let your own code run docker commands. If your build or tests start containers themselves, that is the separate setting described in nested Docker below.

Sharing the repository with a service

Your repository lives on a volume named workspace. If a service needs to read the checked-out code — one that loads fixtures, say, or runs against your source — mount that volume:

services:
  fixtures:
    image: my-loader:latest
    volumes:
      - workspace:/repo

Mounting the named workspace volume is the supported way to give a service access to your repository. Binding a path from the host machine is not allowed.

Reserved service names

Three service names are reserved and cannot be used:

  • agent
  • workspace-init
  • docker

A file using one of these is rejected on save. Pick another name — db rather than agent, for instance.

What compose files may not do

Services run next to your agent, so each file is checked against a few safety rules before it is saved. Within these bounds you can run essentially any service:

  • No privileged containers. A service may not set privileged: true.
  • No host filesystem bind mounts. Services may use named volumes only. A service may not mount a path from the host machine, and a named volume may not be defined as a bind to a host path.
  • No host namespace sharing. A service may not share the host's or another container's network, PID, IPC, or UTS namespace, and may not set userns_mode: host. In practice: no network_mode: host, pid: host, ipc: host, uts: host, or container:-style values.
  • No added capabilities or devices. A service may not use cap_add, devices, device_cgroup_rules, security_opt, cgroup_parent, or group_add.

Most services built from public images — databases, caches, brokers, and the like — use none of these, so a standard service definition passes as-is.

If a file is rejected, the dashboard names the service and the directive that caused it, for example that a service "must not bind-mount the host filesystem." Remove the flagged directive, switch a host bind mount to a named volume, or rename a reserved service, and save again.

Availability

Compose services currently run on self-hosted runners only. A project that declares services and runs in the cloud will start its session without them, and anything depending on them will fail.

If your project needs sibling services today, set its execution target to self-hosted. If you want to stay on the cloud, two options usually work:

  • Start the dependency from your own tooling and turn on nested Docker below. Testcontainers and similar libraries do exactly this.
  • Use an in-process or file-backed substitute for tests, such as SQLite in place of Postgres, where your test suite supports it.

When your project starts its own containers

Turn on This project starts its own containers when your build or tests launch containers themselves, rather than talking to services you declared above. Common cases:

  • testcontainers and similar libraries that start a database per test run.
  • Frameworks that provision their own dependencies. Some frameworks start their own services, such as a database or a message broker, when you run their dev server or test command.
  • A build or test step that shells out to Docker, such as docker build, docker compose up, or a script that calls them.

With it on, the session gets its own Docker daemon, isolated from the machine running it and from every other session. Your tooling finds it through the standard docker CLI and the usual environment variables, so nothing in your repository needs to change. The daemon and everything it started are destroyed with the session.

This works on both cloud and self-hosted runners, and applies to the sessions that build and test your code — implementation, review, and planning.

Leave it off if the compose services above are all you need. Declaring services is the better tool when it fits: it grants no daemon, and the session starts faster.

Choosing between the two

The two settings answer different questions, and a project can need one, both, or neither:

If your tests connect to a Postgres they never start, that is a compose service. If your tests start a Postgres themselves, that is nested Docker.

What to expect

  • Sessions take slightly longer to start. The daemon has to come up before your setup script runs. Leave the setting off if nothing needs it.
  • Images are pulled fresh every session. There is no image cache between sessions, so a large base image is paid for each time. Prefer small images — an -alpine or -slim tag over a full one — where your tests allow it.
  • The daemon is private to the session. It cannot see images, containers, or volumes from your other sessions or from the machine running it.
  • If the daemon cannot start, the session fails with an error saying so, rather than continuing without it.

Configure with Agent

Rather than writing the setup script and compose file by hand, you can use Configure with Agent at the top of the Sandbox settings. An agent inspects your repository — package manifests, lockfiles, CI configuration, test setup — and fills in all three settings for you.

It is a good starting point, particularly for a project you are onboarding. Review what it produces before relying on it, and edit anything it got wrong; the saved values are yours to change at any time.

Execution target

The execution target decides where your agents run. You choose it per project and can change it later.

  • Cloud — agents run in managed, isolated environments that SetForth provisions for you. There is nothing to install or keep online, and on eligible plans an environment starts automatically when a task is ready.
  • Self-hosted — agents run on a runner you operate on your own hardware. This requires a registered, online runner for your organization; see the Self-Hosted Runner guide. Use it when you need your own toolchain, access to private network resources, compose services, or to keep code on machines you control.
  • Auto — either the cloud or one of your online self-hosted runners takes the work, whichever is available, preferring your own runners and falling back to the cloud when none is online.

If you choose self-hosted and have no runner online, agent work waits until one is available, so register a runner before relying on it.

Troubleshooting

The setup script fails and the session ends. Check the exit code in the session's logs. The most common causes are a missing tool that is not preinstalled, a lockfile that does not match the manifest, or a step that needs a credential the sandbox does not have. Reproduce it locally by running the same script against a fresh clone.

Tests cannot reach a compose service. Confirm the project runs self-hosted, that your tests use the service name as the hostname rather than localhost, and that the service has a healthcheck so the session waits for it to be ready.

Tests fail with a Docker connection error. Your tooling is trying to start a container. Turn on This project starts its own containers.

Sessions are slow to start. Look at what the setup script does on every run. Frozen-lockfile installs, prebuilt binaries, and smaller images all help, as does turning off nested Docker if nothing needs it.