Skip to main content

Overview

NanoClaw Pro runs as a systemd user service that starts on login and restarts on failure. The service manages the main Node.js process, which handles channel connections, message polling, and spawning containerized agents using Docker.

Prerequisites

Node.js 20 or later
Claude Code CLI authenticated
Docker or Docker Desktop installed and running
Build tools (gcc, make, python3)
Install build tools for native modules:
sudo apt-get update
sudo apt-get install -y build-essential python3

Installation

1

Clone and set up the project

git clone https://github.com/andrewsiah/nanoclaw-pro.git
cd nanoclaw-pro
npm install
npm run build
The setup handles dependency installation and native module compilation (better-sqlite3).
2

Install and configure Docker

Docker Desktop (recommended for desktop Linux):
# Install from https://docs.docker.com/desktop/install/linux-install/

# Start Docker Desktop
systemctl --user start docker-desktop
systemctl --user enable docker-desktop
Docker Engine (for servers):
# Follow: https://docs.docker.com/engine/install/

# Add your user to docker group (avoid sudo for docker commands)
sudo usermod -aG docker $USER
newgrp docker

# Enable and start Docker
sudo systemctl enable docker
sudo systemctl start docker
Verify Docker is running:
docker info
3

Build the agent container image

./container/build.sh
This creates the nanoclaw-agent:latest image with Node.js 20, Claude Code CLI, Chromium (for browser automation), and the agent-runner code.
4

Configure authentication

Create a .env file in the project root:Option 1: Claude Subscription (OAuth)
CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-...
Extract from ~/.claude/.credentials.json if logged into Claude Code.Option 2: Pay-per-use API Key
ANTHROPIC_API_KEY=sk-ant-api03-...
Only authentication variables are extracted and mounted into containers.
5

Create systemd user service

Create ~/.config/systemd/user/nanoclaw.service:
[Unit]
Description=NanoClaw Pro - Personal Claude Assistant
After=network.target docker.service
Wants=docker.service

[Service]
Type=simple
WorkingDirectory=/home/yourusername/nanoclaw-pro
ExecStart=/usr/bin/node /home/yourusername/nanoclaw-pro/dist/index.js
Restart=on-failure
RestartSec=10s
StandardOutput=append:/home/yourusername/nanoclaw-pro/logs/nanoclaw.log
StandardError=append:/home/yourusername/nanoclaw-pro/logs/nanoclaw.error.log

# Environment
Environment="PATH=/home/yourusername/.local/bin:/usr/local/bin:/usr/bin:/bin"
Environment="HOME=/home/yourusername"
Environment="ASSISTANT_NAME=Andy"
Environment="CONTAINER_IMAGE=nanoclaw-agent:latest"
Environment="CONTAINER_TIMEOUT=1800000"
Environment="MAX_CONCURRENT_CONTAINERS=5"

[Install]
WantedBy=default.target
Replace /home/yourusername with your actual home directory path.
Use which node to find your Node.js path and pwd in the project directory for the absolute path.
6

Enable and start the service

# Reload systemd to recognize new service
systemctl --user daemon-reload

# Enable service (start on login)
systemctl --user enable nanoclaw

# Start service now
systemctl --user start nanoclaw

Service Management

Check Service Status

# View service status and recent logs
systemctl --user status nanoclaw

# View logs in real-time
journalctl --user -u nanoclaw -f

# View logs since last boot
journalctl --user -u nanoclaw -b

# View last 100 lines
journalctl --user -u nanoclaw -n 100

Start/Stop/Restart

systemctl --user stop nanoclaw
Stopping the service does not kill running containers. Agent containers run detached with --rm and will finish or timeout naturally.

Service Logs

Systemd captures stdout/stderr to journald. You can also access file logs:
FileContents
logs/nanoclaw.logMain process stdout (redirected by service)
logs/nanoclaw.error.logMain process stderr
groups/{name}/logs/container-*.logPer-agent execution logs
Container logs are timestamped: container-20260307-143022.log

Debugging

# Check if service failed
systemctl --user is-failed nanoclaw

# View detailed failure reason
systemctl --user status nanoclaw

# Check environment variables
systemctl --user show nanoclaw --property=Environment

# Test manually (run outside systemd)
cd ~/nanoclaw-pro
node dist/index.js

Docker vs Podman

NanoClaw Pro uses Docker by default. To switch to Podman:
1

Install Podman

sudo apt-get install -y podman
2

Configure Docker alias (optional)

Podman provides Docker CLI compatibility:
# Add to ~/.bashrc or ~/.zshrc
alias docker=podman
Or create a symlink:
sudo ln -s /usr/bin/podman /usr/local/bin/docker
3

Update container runtime in code

Edit src/container-runtime.ts:
export const CONTAINER_RUNTIME_BIN = 'podman';
Rebuild:
npm run build
systemctl --user restart nanoclaw
Podman is daemonless and rootless by default, making it more secure than Docker. It’s API-compatible but has subtle differences in networking and volume mounts.

Container Runtime Setup

NanoClaw requires a container runtime to isolate agent execution. The service automatically:
  1. Checks if runtime is running (docker info)
  2. Kills orphaned nanoclaw-* containers from previous runs
  3. Spawns fresh containers for each agent invocation with --rm (auto-cleanup)
Each container is named nanoclaw-{groupFolder}-{timestamp} and runs with:
  • Working directory: /workspace/group (mounted from groups/{name}/)
  • Non-root user: node (uid 1000)
  • Network: bridge (default)
  • Auto-remove: Yes (--rm flag)

Volume Mounts

Default mounts for all groups:
groups/{name}/ → /workspace/group (read-write)
data/sessions/{name}/.claude/ → /home/node/.claude/ (read-write)
data/ipc/ → /workspace/ipc (read-write)
Main group additional mounts:
project_root/ → /workspace/project (read-only)
.env → /dev/null (shadowed for security)
Custom mounts via containerConfig in SQLite:
{
  "additionalMounts": [
    {
      "hostPath": "~/projects/webapp",
      "containerPath": "webapp",
      "readonly": false
    }
  ]
}
Appears at /workspace/extra/webapp in container.

Environment Variables

Configure in systemd service file’s [Service] section:
VariableDefaultPurpose
ASSISTANT_NAMEAndyTrigger word for messages (@Andy)
CONTAINER_IMAGEnanoclaw-agent:latestAgent container image
CONTAINER_TIMEOUT1800000 (30 min)Max container runtime (ms)
IDLE_TIMEOUT1800000 (30 min)Keep container alive after last result
MAX_CONCURRENT_CONTAINERS5Max parallel agent containers
PATHStandard + ~/.local/binInclude Claude Code CLI path
Change in service file:
Environment="ASSISTANT_NAME=Bot"
Then reload:
systemctl --user daemon-reload
systemctl --user restart nanoclaw

Startup Sequence

When the service starts:
  1. Ensures Docker is running (docker info, exits if failed)
  2. Cleans up orphaned nanoclaw-* containers
  3. Initializes SQLite database (store/messages.db)
  4. Loads registered groups, sessions, router state
  5. Connects channels (self-registration pattern)
  6. Starts scheduler loop (60s interval)
  7. Starts IPC watcher (monitors data/ipc/)
  8. Recovers unprocessed messages from before restart
  9. Starts message polling loop (2s interval)
If container runtime is not running, the service exits with a fatal error.

Security

The service runs as your user (not root). All agents execute in Docker containers with:
  • Process isolation: cgroups and namespaces
  • Filesystem isolation: Only mounted directories visible
  • Network isolation: Bridge network (can be configured per-group)
  • Non-root user: Runs as node (uid 1000) inside container
  • No privileged mode: Standard container security
  • Mount allowlist: src/mount-security.ts validates paths before mounting
Host .env file is shadowed with /dev/null when mounting project root for main group.

Firewall Configuration

If using a firewall, allow Docker’s bridge network:
# Docker manages iptables rules automatically
# No manual rules needed

WSL2 Considerations

If running on Windows Subsystem for Linux:
  1. Use Docker Desktop for Windows with WSL2 backend (recommended)
  2. Or install Docker Engine inside WSL2 (more complex networking)
  3. Ensure WSL2 has enough memory allocated (default 50% of host RAM)
Edit .wslconfig in Windows home directory:
[wsl2]
memory=8GB
processors=4
Restart WSL:
wsl --shutdown

Next Steps

macOS Deployment

Deploy with launchd on macOS

Troubleshooting

Common issues and solutions

Build docs developers (and LLMs) love