> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nanoclaw.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Configuration

> Configure a NanoClaw install — environment variables in .env and the service environment, plus per-group container config in the database.

NanoClaw has two configuration layers:

1. **Install-wide environment variables** — identity, container limits, logging, credentials. They apply to the whole install.
2. **Per-group container config** in the database — each agent group has a row in the `container_configs` table controlling its provider, model, mounts, packages, and MCP servers.

## Environment variables

Variables come from two sources, and the distinction matters:

* A **small set is read from `.env`** at the project root by NanoClaw's own parser. These work with a plain "edit `.env`, restart" flow. Values are not loaded into `process.env`, so secrets don't leak to child processes; a value already in the process environment takes precedence.
* **Everything else is read from `process.env` only.** The launchd plist, systemd unit, and nohup wrapper that setup generates set just `PATH` and `HOME` — they do **not** source `.env`. Putting these variables in `.env` has no effect; you have to set them in the service environment (see [Applying changes](#applying-changes)).

### Read from .env

| Variable                   | Default                     | What it does                                                                                                                                                                                                                   |
| -------------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `ASSISTANT_NAME`           | `Andy`                      | The assistant's name. Also sets the default trigger, `@<name>`.                                                                                                                                                                |
| `ASSISTANT_HAS_OWN_NUMBER` | `false`                     | Set `true` when the assistant runs on its own WhatsApp number instead of yours.                                                                                                                                                |
| `DEFAULT_AGENT_PROVIDER`   | `claude`                    | Provider stamped on **new** agent groups at creation. Existing groups are never touched, and the per-group `provider` config still wins.                                                                                       |
| `ONECLI_URL`               | —                           | URL of the OneCLI Agent Vault.                                                                                                                                                                                                 |
| `ONECLI_API_KEY`           | —                           | API key for the vault. See [Credentials](/operate/credentials).                                                                                                                                                                |
| `TZ`                       | system timezone, else `UTC` | IANA timezone for scheduled tasks and message timestamps. Invalid values are skipped, not errored.                                                                                                                             |
| `CONTAINER_CPU_LIMIT`      | *unset* (unbounded)         | Opt-in CPU cap per container, passed to `docker run --cpus` (e.g. `2`). Can be set in `.env` or the process environment.                                                                                                       |
| `CONTAINER_MEMORY_LIMIT`   | *unset* (unbounded)         | Opt-in memory cap per container, passed to `docker run --memory` (e.g. `8g`). Hard cap only on a swapless host. Can be set in `.env` or the process environment.                                                               |
| `NANOCLAW_EGRESS_LOCKDOWN` | `false`                     | Set `true` to force all agent traffic through the OneCLI gateway on an internal Docker network. Fails fast if the gateway isn't running. Can be set in `.env` or the process environment. See [Hardening](/operate/hardening). |
| `NANOCLAW_EGRESS_NETWORK`  | `nanoclaw-egress`           | Name of the locked-down Docker network. Can be set in `.env` or the process environment.                                                                                                                                       |
| `ONECLI_GATEWAY_CONTAINER` | `onecli`                    | Name of the gateway container attached as the only egress hop. Can be set in `.env` or the process environment.                                                                                                                |
| `ANTHROPIC_BASE_URL`       | —                           | Custom Anthropic-compatible endpoint for the Claude provider. Only active when setup has configured a custom endpoint; the real auth token stays on the host and is injected by the OneCLI proxy.                              |

Some channels run in containers and can't see the host's `.env`. For those, setup mirrors `.env` to `data/env/env`, which is mounted into the channel container. Keep the two in sync — the `set-env` setup step handles both:

```bash theme={null}
pnpm exec tsx setup/index.ts --step set-env -- --key ASSISTANT_NAME --value "Jarvis" --sync-container
```

You can also edit `.env` by hand and copy it yourself: `cp .env data/env/env`.

### Process environment only

These are read from `process.env` at startup. Set them in the service definition or export them in your shell before `pnpm run dev` — `.env` won't work.

**Containers**

| Variable               | Default          | What it does                                                                                                                                   |
| ---------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `CONTAINER_IMAGE`      | per-install tag  | Full agent image override (name and tag).                                                                                                      |
| `CONTAINER_IMAGE_BASE` | per-install name | Base image name override. The default includes an install slug so two checkouts on one host don't clobber each other's `nanoclaw-agent` image. |

<Note>
  `CONTAINER_TIMEOUT`, `IDLE_TIMEOUT`, `MAX_CONCURRENT_CONTAINERS`, `CONTAINER_MAX_OUTPUT_SIZE`, and `MAX_MESSAGES_PER_PROMPT` were removed from the code and are no longer read — setting any of them has no effect. Container idle/stuck detection is now handled by the periodic host-sweep loop (`src/host-sweep.ts`), not a per-container timeout env var.
</Note>

**Network and logging**

| Variable       | Default | What it does                                      |
| -------------- | ------- | ------------------------------------------------- |
| `WEBHOOK_PORT` | `3000`  | Port for the local webhook server.                |
| `LOG_LEVEL`    | `info`  | One of `debug`, `info`, `warn`, `error`, `fatal`. |

See [Hardening](/operate/hardening) for the full egress lockdown and sandboxing story.

## Per-group container config

Each agent group has a row in the `container_configs` table (the central SQLite database). At spawn time NanoClaw materializes it to `groups/<folder>/container.json` — don't edit that file by hand, it's overwritten on every spawn. The full per-field reference, including the variable tables for the whole install, lives in the [environment variables](/reference/environment-variables) and [container configuration](/reference/container-config) references.

Fields per group:

| Field                          | What it controls                                                                                                                 |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------- |
| `provider`                     | Which agent CLI runs in the container (e.g. Claude Code, OpenCode). New groups start with the `DEFAULT_AGENT_PROVIDER` value.    |
| `model`, `effort`              | Model and reasoning effort passed to the provider.                                                                               |
| `assistant_name`               | Per-group assistant name override.                                                                                               |
| `max_messages_per_prompt`      | Per-group override of the install-wide batching limit.                                                                           |
| `image_tag`                    | Custom container image for this group.                                                                                           |
| `cli_scope`                    | What the group's agent can do via the internal CLI: `disabled`, `group` (own group only, the default), or `global`.              |
| `skills`                       | Skill list for the group, or `all`.                                                                                              |
| `mcp_servers`                  | MCP servers available inside the container.                                                                                      |
| `packages_apt`, `packages_npm` | Extra packages installed in the container.                                                                                       |
| `additional_mounts`            | Extra host directories mounted into the container (subject to the mount allowlist at `~/.config/nanoclaw/mount-allowlist.json`). |

To change a group's config, ask the agent in that group, or use [`ncl`](/operate/ncl-cli). For adding MCP servers, mounts, and providers, see [Extending NanoClaw](/extend/overview) and [Ollama integration](/extend/providers).

## Applying changes

**`.env`-read variables**: edit `.env`, sync the container copy if any channel runs in a container (`cp .env data/env/env`), then restart the service:

```bash theme={null}
# macOS
launchctl kickstart -k gui/$(id -u)/com.nanoclaw-v2-<slug>

# Linux
systemctl --user restart nanoclaw-v2-<slug>
```

**Process-environment variables**: add them to the service definition, then reload it.

* **macOS**: edit `~/Library/LaunchAgents/com.nanoclaw-v2-<slug>.plist`, add a key to the `EnvironmentVariables` dict, then `launchctl unload` + `launchctl load` the plist.
* **Linux (systemd)**: add an `Environment=KEY=value` line to the unit, then `systemctl --user daemon-reload && systemctl --user restart nanoclaw-v2-<slug>`.
* **Dev shell**: `export LOG_LEVEL=debug` before `pnpm run dev`.

<Warning>
  Re-running the service setup step regenerates the plist or unit file, wiping manual `Environment` edits — re-apply them afterwards.
</Warning>

Per-group container config applies at the next container spawn — it's read fresh from the database each time. A running container keeps its old config until it's restarted, and package changes need an image rebuild (`ncl groups restart --id <group-id> --rebuild`). See the [container configuration reference](/reference/container-config) for per-field semantics.

See [Installation](/installation) for service management details and finding your install slug.
