Recipes
Complete moat.yaml configurations for common project types. Each recipe is a working starting point — copy it and adjust to your project.
Prerequisites
- A working Moat installation with Docker or Apple container runtime
- A
moat.yamlfile in your project directory
Node.js
name: my-node-app
dependencies:
- node@20
- claude-code
- git
- gh
grants:
- claude
- github
env:
ANTHROPIC_MODEL: opus
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: 1
volumes:
- name: node-modules
target: /workspace/node_modules
hooks:
pre_run: npm install
What this demonstrates:
- Volume-cached
node_modules— persists across runs sonpm installis a fast no-op whenpackage-lock.jsonhasn’t changed. The volume is a bind mount from~/.moat/volumes/, so it bypasses the workspace’s shared filesystem (no VirtioFS performance issues). envfor Claude Code configuration — sets the model and enables experimental features via environment variables injected into the containerpre_runhook — installs dependencies on every start, but skips work when the volume is warm
Tip: Run
moat volumes rm my-node-appafter major lockfile changes or Node version bumps to start fresh.
Python
name: my-python-app
dependencies:
- python@3.12
- uv
- claude-code
- git
grants:
- claude
- github
volumes:
- name: venv
target: /workspace/.venv
hooks:
post_build_root: apt-get update -qq && apt-get install -y -qq libpq-dev
pre_run: uv sync
What this demonstrates:
- Volume-cached
.venv— persists the virtual environment across runs, bypassing the workspace’s shared filesystem uvfor fast installs — uv resolves and installs packages significantly faster than pip- System dependencies at build time —
libpq-devis installed inpost_build_rootand cached in the image layer, avoiding reinstallation on every run
Go
name: my-go-service
dependencies:
- go@1.22
- golangci-lint
- claude-code
- git
- gh
grants:
- anthropic
- github
volumes:
- name: gomodcache
target: /home/moatuser/go/pkg/mod
- name: gobuildcache
target: /home/moatuser/.cache/go-build
hooks:
pre_run: go mod download
What this demonstrates:
anthropicgrant for API key auth — uses an Anthropic API key instead of theclaudegrant (which uses Claude subscription OAuth). Useanthropicfor CI or when billing through the API.- Volume-cached module and build caches —
go mod downloadandgo buildreuse cached artifacts across runs - Two separate volumes — the module cache (
go/pkg/mod) and build cache (go-build) have different invalidation patterns; splitting them allows independent cleanup golangci-lintas a dependency — installed at image build time, cached in the Docker layer
Full-stack with services
name: fullstack-app
dependencies:
- node@20
- python@3.12
- uv
- postgres@17
- redis@7
- claude-code
- git
- gh
grants:
- claude
- github
services:
postgres:
env:
POSTGRES_DB: appdb
volumes:
- name: node-modules
target: /workspace/frontend/node_modules
- name: venv
target: /workspace/backend/.venv
hooks:
pre_run: |
set -e
cd /workspace/frontend && npm install
cd /workspace/backend && uv sync
What this demonstrates:
- Multiple runtimes — Node and Python in one container (uses
debian:bookworm-slimbase) - Service dependencies — PostgreSQL and Redis start automatically with readiness checks; connection info injected as
MOAT_POSTGRES_URLandMOAT_REDIS_URL - Per-directory volumes — separate dependency caches for frontend and backend, each bypassing the workspace’s shared filesystem
Tip: Add a
network:block to restrict which hosts the agent can reach. See moat.yaml reference.
Multi-repo with clones
name: my-platform
dependencies:
- node@20
- claude-code
- git
grants:
- claude
- github
- ssh:github.com
volumes:
- name: repos
target: /home/moatuser/repos
hooks:
pre_run: |
set -e
cd /home/moatuser/repos
test -d api/.git || git clone git@github.com:my-org/api.git
test -d shared/.git || git clone git@github.com:my-org/shared.git
cd api && git pull --ff-only
cd ../shared && git pull --ff-only
What this demonstrates:
- SSH grant for private repos —
ssh:github.comproxies SSH agent requests without exposing private keys - Volume-persisted clones — repos are cloned once and updated on subsequent runs with
git pull - Conditional clone —
test -d .git ||skips the clone if the repo already exists in the volume - Workspace separation — cloned repos live in a volume outside
/workspace, keeping the primary workspace clean
Tip: Run
moat volumes rm my-platformto reclone from scratch if the repos get into a bad state.
Claude Code status line
Use a global mount and ~/.moat/claude/settings.json to display a custom status line inside moat containers.
1. Create a status line script:
mkdir -p ~/.moat/scripts
cat > ~/.moat/scripts/statusline.sh << 'EOF'
#!/bin/bash
echo "moat | $(hostname) | $(date +%H:%M)"
EOF
chmod +x ~/.moat/scripts/statusline.sh
2. Mount the script into containers:
# ~/.moat/config.yaml
mounts:
- source: ~/.moat/scripts/statusline.sh
target: /home/user/.claude/moat/statusline.sh
3. Configure Claude Code to use it:
// ~/.moat/claude/settings.json
{
"statusLine": {
"command": "/home/user/.claude/moat/statusline.sh"
}
}
The global mount makes the script available in every container, and the settings passthrough forwards the statusLine config to Claude Code. Step 3 requires the settings passthrough feature.
Cache invalidation
Volumes persist until explicitly removed. Rebuild or clear caches when:
- Lockfile changes significantly — a volume-cached
node_modulesmay have stale or conflicting packages after major dependency changes. Runmoat volumes rm <agent-name>and let the next run reinstall. - Runtime version changes — cached native modules compiled for Node 20 won’t work after switching to Node 22. Clear the volume.
- Image rebuild —
--rebuildrebuilds the image but does not clear volumes. If a rebuild changes something that affects cached dependencies (e.g., a new system library), clear volumes manually.
# Clear all volumes for an agent
$ moat volumes rm my-node-app
# Clear all managed volumes
$ moat volumes prune
# List volumes to see what exists
$ moat volumes ls
Related pages
- moat.yaml reference — Full configuration options
- Dependencies — Available dependencies and layer caching
- Lifecycle hooks — Build-time vs runtime hooks
- SSH access — SSH agent proxying for private repos
- Service dependencies — Running databases and caches alongside agents
- Mount syntax — Mounts, excludes, and volumes