> ## Documentation Index
> Fetch the complete documentation index at: https://ai-development-environment.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Docker

> Run the control plane and its Linux control agent from production GHCR images.

Release tags publish two multi-architecture images for `linux/amd64` and `linux/arm64`:

| Image                                                        | Purpose               |
| ------------------------------------------------------------ | --------------------- |
| `ghcr.io/bludesign/ai-development-environment`               | Next.js control plane |
| `ghcr.io/bludesign/ai-development-environment-control-agent` | Linux control agent   |

Use `latest` for the newest semantic-version release or a version tag such as `0.0.73` to pin a deployment. Public packages can be pulled without signing in.

## Docker Compose

Create `compose.yaml`:

```yaml theme={null}
services:
  server:
    image: ghcr.io/bludesign/ai-development-environment:latest
    restart: unless-stopped
    environment:
      HOSTNAME: 0.0.0.0
      PORT: 3090
      AGENT_WS_HOSTNAME: 0.0.0.0
      AGENT_WS_PORT: 3091
      DATABASE_URL: file:/data/production.db
      APP_SECRET: ${APP_SECRET:?set APP_SECRET}
      APP_ORIGINS: ${APP_ORIGINS:-localhost:3090}
      AUTH_MODE: ${AUTH_MODE:-password}
    ports:
      - "3090:3090"
      - "3091:3091"
    volumes:
      - server-data:/data

  agent:
    image: ghcr.io/bludesign/ai-development-environment-control-agent:latest
    restart: unless-stopped
    depends_on:
      server:
        condition: service_healthy
    volumes:
      - agent-data:/data
      - agent-home:/home/node
      - ./repositories:/workspace/repositories

volumes:
  server-data:
  agent-data:
  agent-home:
```

The server stores its SQLite database in `server-data`. The agent stores its enrollment in `agent-data` and AI-provider credentials in `agent-home`. The agent opens outbound connections only and does not publish a port.

Create a `.env` file beside `compose.yaml` before starting:

```bash theme={null}
printf 'APP_SECRET=%s\n' "$(openssl rand -base64 32)" > .env
printf 'APP_ORIGINS=localhost:3090\n' >> .env
```

Keep `.env` out of source control and back it up securely. `APP_SECRET` also encrypts stored credentials, so losing it costs more than a round of re-authentication — see [Environment variables](/reference/environment-variables).

Behind a reverse proxy, set `APP_ORIGINS` to the public hostname users reach, and set `TRUST_PROXY_HEADERS=true` only if that proxy sets `x-forwarded-host` and strips client-supplied copies. Add the provider variables from [Authentication](/reference/authentication#configure-oauthoidc) when using `AUTH_MODE=oidc` or `both`.

<Steps>
  <Step title="Pull and start the server">
    ```bash theme={null}
    docker compose pull
    docker compose up -d server
    ```

    Open `http://localhost:3090` and create the first user. Registration closes after that account succeeds. Then go to **Agents** and create a one-time enrollment token.
  </Step>

  <Step title="Enroll the container agent">
    ```bash theme={null}
    docker compose run --rm agent \
      enroll \
      --server http://server:3090 \
      --websocket-server ws://server:3091/graphql \
      --enrollment-token <one-time-token>
    ```

    Enrollment writes `/data/config.json` into the persistent `agent-data` volume.
  </Step>

  <Step title="Start the agent">
    ```bash theme={null}
    docker compose up -d agent
    ```

    Follow its connection with `docker compose logs -f agent`.
  </Step>
</Steps>

## Give the agent repository storage

Every repository the agent manages must be mounted into its container. The Compose example mounts local `./repositories` at `/workspace/repositories`; register codebases with their container paths beneath `/workspace/repositories`.

To use an external macOS disk mounted at `/Volumes/Development`, replace or add this agent volume:

```yaml theme={null}
services:
  agent:
    volumes:
      - /Volumes/Development:/Volumes/Development
```

Recreate the agent after changing its mounts:

```bash theme={null}
docker compose up -d --force-recreate agent
docker compose exec agent ls -la /Volumes/Development
```

Docker Desktop or OrbStack may require you to allow access to the external path in its file-sharing settings. On Linux, use the equivalent mounted path, such as `/mnt/development:/mnt/development`.

<Note>
  The agent currently reports capacity for its container root filesystem. A
  bind-mounted disk is available to jobs and repositories, but its capacity is
  not included in the agent's disk-usage field.
</Note>

## Runtime capabilities

The Linux agent image runs as the non-root `node` user and includes Git, SSH, Codex, OpenCode, and the Claude Agent SDK. It can manage codebases, worktrees, commands, workflows, coverage, skills, and AI runs.

Xcode build data, iOS builds, and signing-asset jobs are macOS-only. Install the control agent through Homebrew or npm on a Mac that needs those capabilities.

## Operate and update

```bash theme={null}
docker compose ps
docker compose logs -f server
docker compose logs -f agent
docker compose pull
docker compose up -d
```

Both images carry OCI source, version, revision, and description metadata. Each release also publishes an SBOM and provenance attestation. Manifest tools may show extra `unknown/unknown` descriptors beside `linux/amd64` and `linux/arm64`; those descriptors are attestations, not runnable image variants.

If a package is private, authenticate before pulling:

```bash theme={null}
gh auth token | docker login ghcr.io \
  --username <github-username> \
  --password-stdin
```

## Server configuration

The image supports the same environment variables as the npm server, including Better Auth and OIDC settings, `PORT`, `HOSTNAME`, `AGENT_WS_HOSTNAME`, `AGENT_WS_PORT`, `DATABASE_URL`, credential-storage settings, and Vault settings. It applies pending Prisma migrations before starting.

See [Hosting and networking](/reference/hosting) before exposing the control plane beyond localhost.
