---
title: "Docker setup · intentic docs"
description: "The anatomy every install path shares: the containers, volumes and network created, the capability posture, the nested engine, overlays and updates."
url: "https://intentic.dev/docs/docker/"
updated: "2026-09-15"
---

Run a sandbox

# Docker setup

A sandbox is a Docker container on your own machine. This page is the anatomy behind every install path: what gets created, where your data lives, what the container is and isn't allowed to do, and how to update or remove it.

**On this page (14 sections)**

- [Install commands are on Quickstart](#install-commands-are-on-quickstart)
- [Requirements](#requirements)
- [Installing Docker](#installing-docker)
- [Windows needs more than Docker](#windows-needs-more-than-docker)
- [What gets created](#what-gets-created)
- [Backing them up](#backing-them-up)
- [Ports: none inbound, one on loopback](#ports-none-inbound-one-on-loopback)
- [What the container may do](#what-the-container-may-do)
- [The isolated Docker engine](#the-isolated-docker-engine)
- [Environment overlays](#environment-overlays)
- [Describing this sandbox's shape](#describing-this-sandboxs-shape)
- [The run contract](#the-run-contract)
- [Update, rebuild, cleanup](#update-rebuild-cleanup)
- [Moving between install paths](#moving-between-install-paths)

## Install commands are on Quickstart

Looking for the commands that bring one up? Those are on [Quickstart](https://intentic.dev/docs/quickstart/), which covers all four routes: desktop app, setup command, Docker Compose, and plain `docker run`. Everything below is what those routes have in common.

## Requirements

- **Docker Engine** on Linux, WSL2, or macOS. That's the only dependency.
- Enough headroom for your workload: agent turns, dev servers, and builds run on your hardware, so a sandbox uses what your work uses. There are no imposed CPU or memory caps.

## Installing Docker

The desktop app and the setup command both install Docker for you if it's missing, and both ask first: it's a root-level change beyond the sandbox itself, so it is never silent. On Linux that's `get.docker.com`. In an unattended run set `INSTALL_DOCKER=1` to pre-consent, or [install Docker yourself](https://docs.docker.com/get-docker/) beforehand and every path will use it.

### Windows needs more than Docker

Docker's Linux engine on Windows runs inside **WSL2**, so a Windows PC has a short chain of requirements rather than one: hardware virtualization enabled in firmware, the *Virtual Machine Platform* and *Windows Subsystem for Linux* features turned on, a current WSL2 kernel, and then Docker Desktop itself. Windows 10 version 21H2 or newer, 64-bit Intel or AMD.

Every install path checks that chain in one go before touching anything, shows you the whole list, and asks once. It then turns on what it can (the features, then a restart), installs Docker Desktop (through the Windows package manager where there is one, and straight from docker.com where there isn't), starts its engine and makes sure it is in Linux-container mode. What it cannot do it says so about: a firmware virtualization switch is changed in BIOS/UEFI before Windows boots, and a Windows running inside a virtual machine needs nested virtualization enabled on its host.

The same examination is available on its own, read-only: `ic docker prepare --dry-run`. See [Troubleshooting](https://intentic.dev/docs/troubleshooting/#windows-docker) for what each outcome means.

## What gets created

Two containers, three named volumes, one network: all derived from your sandbox's **slug**, which is the first label of its public hostname (`my-box` in `my-box.intentic.dev`). Every install path derives these identically, which is what lets several sandboxes share a machine and lets one move between paths later.

| Object | Name | What it is |
| --- | --- | --- |
| Container | `intentic-sandbox-<slug>` | The daemon, your agent, your workspace |
| Volume | `intentic-workspace-<slug>` | `/work`: your repos and files |
| Volume | `intentic-history-<slug>` | `/history`: version history and snapshots |
| Volume | `intentic-docker-<slug>` | `/var/lib/docker`: the in-sandbox engine's own storage |
| Network | `intentic-workspace-<slug>` | Where the connector reaches the sandbox by its alias |

Look at yours

```bash
# Everything this machine's sandboxes created, by name.
docker ps -a --filter name=intentic-sandbox
docker volume ls --filter name=intentic-
docker network ls --filter name=intentic-workspace
```

`/history` is a **separate volume mounted outside `/work`**, and that is deliberate: an agent `rm -rf`-ing its workspace cannot reach the history that would let you recover from it. Both survive a container being removed and recreated, which is why update and rebuild can swap the image under a sandbox without touching your files.

### Backing them up

There is no intentic backup service, and there is nothing for one to do: these are ordinary named Docker volumes, so whatever you already use to back up a volume works here. `intentic-workspace-<slug>` is the whole workspace and `intentic-history-<slug>` is its history; `intentic-docker-<slug>` is a rebuildable cache and worth skipping. Your repos' own remotes are the other half of this, and the better half: anything pushed is already off the machine.

## Ports: none inbound, one on loopback

Nothing is published to the network. The sandbox is reached over a private tunnel that its own agent dials *outward*, so there is no port to forward and nothing to expose. Inside the container the daemon serves the tunnel on `8787` and preview servers on `5173`; neither is bound on your machine.

The one exception is a shortcut, not an opening. A browser on the *same machine* shouldn't have to travel out to the tunnel and back, so the container publishes a second listener on `127.0.0.1` only, at a port derived from your sandbox's own id. Nobody announces that address; your browser computes the same number from the token it holds. If something else holds the port, every install path retries without it and the sandbox works over its tunnel as usual.

## What the container may do

The sandbox runs **unprivileged**, with two Linux capabilities and no others. Both are scoped to the container; neither is host access.

- **`SYS_ADMIN`**: lets the daemon give each isolated agent turn its own mount namespace, so a conversation's [worktree](https://intentic.dev/docs/glossary/) can stand in for `/work`. Without it, isolated turns quietly stop being isolated.
- **`SYS_PTRACE`**: lets the daemon be diagnosed in the one place it ever misbehaves: production. When PID 1 stalls, the question is what it's blocked in, and answering it needs a stack.

The **host's Docker socket is never mounted.** That single fact is what bounds everything an agent can do: whatever it runs, it runs inside this container, and it has no handle on the machine outside it.

## The isolated Docker engine

Some work needs Docker *inside* the sandbox: `pnpm db:up`, a `docker compose` stack, a local database. The image bakes its own Docker Engine, dormant until you add the **docker capability**, which flips a single privileged directive and wakes it. The agent's containers then live entirely inside the sandbox's own engine: still no host socket, so nothing it starts can touch the machine outside.

On a Compose-managed sandbox that `privileged: true` stays your own edit to the file. The rebuild flow recreates containers with `docker run`, and would fight Compose over ownership if it wrote it for you.

The nested engine also runs **Ollama**, for people who want that runtime specifically: `docker run -d -p 11434:11434 ollama/ollama` inside the sandbox (add `--gpus=all` once the docker capability's GPU option is on), then point a **Model endpoint** card at `http://127.0.0.1:11434/v1`. If you don't care which runtime serves the weights, the **Local model** card does all of this with one pick and no engine at all. See [Models](https://intentic.dev/docs/models/).

## Environment overlays

A sandbox's tools aren't described in a prompt; they're installed in the image. When a job needs a library or CLI, the agent **proposes** a Dockerfile layer (RUN/ENV only, on a pinned base); nothing changes until **you approve it**, and the rebuild runs *outside* the container against the approved hash. So an agent can never rebuild its own environment without your sign-off. Typical additions: a database client like `psql`, a headless browser, or your language toolchain.

A few capabilities need more than a layer: WireGuard needs a tun device, the nested engine needs `--privileged`, GPU passthrough needs `--gpus`. Those ride in as allowlisted `# intentic:runtime` directive lines in the same approved overlay, so an overlay can't smuggle arbitrary docker flags. A host that can't satisfy an *optional* one (a machine with no NVIDIA runtime) starts the sandbox without it rather than failing the launch, and records that it couldn't.

### Describing this sandbox's shape

The approved overlay is one section of a [`sandbox.toml` definition](https://intentic.dev/docs/sandbox-definitions/), alongside repo remotes, connection shapes, secret names and non-default agent settings. Download it from **Sandbox → Environment** when you want a reviewable recipe for another sandbox; use a private bundle when you need the transcripts and unpublished state too.

## The run contract

Every path composes the same `docker run` from one run contract that **ships inside the image**. The scripts don't hard-code it and neither should your tooling: ask the image, and a stale caller still starts a new image correctly.

Ask the image

```bash
# What every path runs, printed rather than guessed at.
tr '\n' '\0' < sandbox.env | docker run -i --rm --entrypoint intentic \
 ghcr.io/intentic/sandbox:stable sandbox run-command \
 --slug '<SLUG>' --image ghcr.io/intentic/sandbox:stable \
 --base-image ghcr.io/intentic/sandbox:stable --format sh
```

It reads your environment from stdin (NUL-framed, because a private key is multi-line), filters it to the vars a sandbox is allowed to receive, and prints the complete, correctly quoted command, or the raw argv with `--format json`. That is the supported way to run a sandbox from your own scripts; [Quickstart](https://intentic.dev/docs/quickstart/#docker-run) has it as a worked example.

## Update, rebuild, cleanup

The same vanity host that serves the setup script serves the lifecycle helpers:

Maintenance

```bash
# Remove a sandbox. Lists yours, asks before deleting its /work + /history.
curl -fsSL https://intentic.dev/cleanup | sh
curl -fsSL https://intentic.dev/cleanup | sh -s -- <SLUG> # one, by slug
curl -fsSL https://intentic.dev/cleanup | sh -s -- --all -y # every one, no prompt

# Update (pull :stable and recreate in place) and rebuild (apply an
# approved overlay). The platform hands you the exact command on the matching card.
curl -fsSL https://intentic.dev/update | sh -s -- <SLUG>
curl -fsSL https://intentic.dev/rebuild | sh -s -- <SLUG> <SHA256>
```

- **Update** pulls the latest `:stable` image and recreates the container in place, preserving `/work` and `/history`.
- **Rebuild** applies an owner-approved environment overlay, with its hash as the trust anchor.
- **Cleanup** removes a sandbox's containers, named volumes, and networks, which *does* delete its data, so it confirms first and never wipes everything unless you ask.

Each has a PowerShell twin for Windows: add `.ps1` to the path (`/update.ps1`, `/rebuild.ps1`, `/cleanup.ps1`, `/connect.ps1`). The platform surfaces the exact update and rebuild commands on your Sandbox and Environment cards when they apply. In the [desktop app](https://intentic.dev/download/) they are buttons instead: a sandbox can never recreate its own container, and something has to do it on the host.

## Moving between install paths

Because the names are derived rather than invented, a sandbox is not locked to how it was created. Bring one up with the setup command and later manage it with Compose, or the reverse: the containers are recreated, the three volumes reused, your workspace untouched. Stop the old container first (`docker rm -f intentic-sandbox-<slug>`) so the two managers aren't holding the same name.

More in Run a sandbox

[Previous ← Quickstart](https://intentic.dev/docs/quickstart/)[Next Sandbox definitions →](https://intentic.dev/docs/sandbox-definitions/)
