This guide provides an Ansible playbook to deploy ZeroClaw with Docker Compose on Debian 10+, Ubuntu 20.04+, and RHEL 9+ compatible hosts. ZeroClaw is an ultra-lightweight Rust AI agent runtime designed for edge devices and resource-constrained environments.
Important: ZeroClaw does not provide an official pre-built Docker image. This playbook builds the Docker image locally from source or uses a pre-built binary in a minimal container.
Official Resources:
Note: Between March 30 and April 2, 2026, the zeroclaw-labs GitHub organization went offline (404 errors) and was subsequently restored.
crates.io note: The crates.io registry lists v0.1.7, which significantly lags behind the latest GitHub release (v0.6.9). This playbook uses GitHub releases for the current version.
---
- name: Deploy ZeroClaw with Docker
hosts: zeroclaw
become: true
vars:
app_root: /opt/zeroclaw
app_name: zeroclaw
app_port: 3000
# Configuration
zeroclaw_provider: "openai"
zeroclaw_api_key: "{{ vault_zeroclaw_api_key }}"
zeroclaw_channels: ['cli']
# ZeroClaw version (from GitHub releases)
zeroclaw_version: "latest"
zeroclaw_arch: "x86_64" # x86_64, aarch64, armv7
tasks:
- name: Install Docker on Debian/Ubuntu
apt:
name:
- docker.io
- docker-compose-plugin
- curl
state: present
update_cache: true
when: ansible_os_family == "Debian"
- name: Install Docker on RHEL family
dnf:
name:
- docker
- docker-compose-plugin
- curl
state: present
when: ansible_os_family == "RedHat"
- name: Enable and start Docker
service:
name: docker
state: started
enabled: true
- name: Create application directory
file:
path: "{{ app_root }}"
state: directory
mode: "0755"
- name: Create config directory
file:
path: "{{ app_root }}/.zeroclaw"
state: directory
mode: "0700"
- name: Get latest ZeroClaw release version
uri:
url: "https://api.github.com/repos/zeroclaw-labs/zeroclaw/releases/latest"
return_content: true
register: release_info
when: zeroclaw_version == "latest"
- name: Set ZeroClaw version
set_fact:
zeroclaw_version: "{{ release_info.json.tag_name }}"
when: zeroclaw_version == "latest"
- name: Download ZeroClaw binary
get_url:
url: "https://github.com/zeroclaw-labs/zeroclaw/releases/download/{{ zeroclaw_version }}/zeroclaw-{{ zeroclaw_version }}-{{ zeroclaw_arch }}-unknown-linux-gnu.tar.gz"
dest: "{{ app_root }}/zeroclaw.tar.gz"
mode: "0644"
register: download_result
ignore_errors: true
- name: Extract ZeroClaw binary
unarchive:
src: "{{ app_root }}/zeroclaw.tar.gz"
dest: "{{ app_root }}"
remote_src: true
when: download_result is succeeded
- name: Make binary executable
file:
path: "{{ app_root }}/zeroclaw"
mode: "0755"
- name: Write ZeroClaw configuration
copy:
dest: "{{ app_root }}/.zeroclaw/config.toml"
mode: "0600"
content: |
api_key = '{{ zeroclaw_api_key }}'
provider = '{{ zeroclaw_provider }}'
channels = {{ zeroclaw_channels | to_json }}
workspace_only = true
log_level = 'info'
- name: Write Dockerfile
copy:
dest: "{{ app_root }}/Dockerfile"
mode: "0644"
content: |
FROM scratch
COPY zeroclaw /zeroclaw
COPY .zeroclaw/config.toml /root/.zeroclaw/config.toml
ENTRYPOINT ["/zeroclaw"]
CMD ["--config", "/root/.zeroclaw/config.toml"]
- name: Write Docker Compose file
copy:
dest: "{{ app_root }}/docker-compose.yml"
mode: "0644"
content: |
version: "3.8"
services:
zeroclaw:
build: .
container_name: {{ app_name }}
restart: unless-stopped
ports:
- "127.0.0.1:{{ app_port }}:3000"
volumes:
- ./data:/root/.zeroclaw
- ./.zeroclaw/config.toml:/root/.zeroclaw/config.toml:ro
read_only: true
tmpfs:
- /tmp
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
deploy:
resources:
limits:
cpus: '1.0'
memory: 256M
- name: Build and start ZeroClaw
shell: |
docker compose build
docker compose up -d
args:
chdir: "{{ app_root }}"
- name: Verify container is running
command: docker ps --filter name={{ app_name }} --format "{{ '{{' }}.Names{{ '}}' }}"
register: container_status
changed_when: false
failed_when: "app_name not in container_status.stdout"
---
- name: Deploy ZeroClaw from Source
hosts: zeroclaw
become: true
vars:
app_root: /opt/zeroclaw
app_name: zeroclaw
app_port: 3000
zeroclaw_repo: "https://github.com/zeroclaw-labs/zeroclaw.git"
# Configuration
zeroclaw_provider: "openai"
zeroclaw_api_key: "{{ vault_zeroclaw_api_key }}"
zeroclaw_channels: ['cli']
tasks:
- name: Install Docker on Debian/Ubuntu
apt:
name:
- docker.io
- docker-compose-plugin
- curl
- git
state: present
update_cache: true
when: ansible_os_family == "Debian"
- name: Install Rust toolchain
shell: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source $HOME/.cargo/env
args:
creates: ~/.cargo/bin/cargo
- name: Install Docker on RHEL family
dnf:
name:
- docker
- docker-compose-plugin
- curl
- git
state: present
when: ansible_os_family == "RedHat"
- name: Enable and start Docker
service:
name: docker
state: started
enabled: true
- name: Clone ZeroClaw repository
git:
repo: "{{ zeroclaw_repo }}"
dest: "{{ app_root }}"
force: true
- name: Create config directory
file:
path: "{{ app_root }}/.zeroclaw"
state: directory
mode: "0700"
- name: Write ZeroClaw configuration
copy:
dest: "{{ app_root }}/.zeroclaw/config.toml"
mode: "0600"
content: |
api_key = '{{ zeroclaw_api_key }}'
provider = '{{ zeroclaw_provider }}'
channels = {{ zeroclaw_channels | to_json }}
workspace_only = true
log_level = 'info'
- name: Write Dockerfile (from source)
copy:
dest: "{{ app_root }}/Dockerfile"
mode: "0644"
content: |
FROM rust:1.75-slim as builder
WORKDIR /app
COPY . .
RUN cargo build --release
FROM debian:bookworm-slim
COPY --from=builder /app/target/release/zeroclaw /usr/local/bin/
COPY .zeroclaw/config.toml /root/.zeroclaw/config.toml
ENTRYPOINT ["/usr/local/bin/zeroclaw"]
CMD ["--config", "/root/.zeroclaw/config.toml"]
- name: Write Docker Compose file
copy:
dest: "{{ app_root }}/docker-compose.yml"
mode: "0644"
content: |
version: "3.8"
services:
zeroclaw:
build: .
container_name: {{ app_name }}
restart: unless-stopped
ports:
- "127.0.0.1:{{ app_port }}:3000"
volumes:
- ./data:/root/.zeroclaw
read_only: true
tmpfs:
- /tmp
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
deploy:
resources:
limits:
cpus: '1.0'
memory: 256M
- name: Build and start ZeroClaw
shell: |
docker compose build
docker compose up -d
args:
chdir: "{{ app_root }}"
Save the playbook as deploy-zeroclaw-docker.yml.
Create an inventory file (inventory.ini):
[zeroclaw]
server1.example.com
server2.example.com
[zeroclaw:vars]
ansible_user=deploy
ansible_ssh_private_key_file=~/.ssh/id_ed25519
Use Ansible Vault to store sensitive values:
# Create vault file
ansible-vault create group_vars/zeroclaw/vault.yml
Add your API key:
vault_zeroclaw_api_key: "sk-your-actual-api-key-here"
# Basic run (binary-based)
ansible-playbook -i inventory.ini deploy-zeroclaw-docker.yml
# With vault
ansible-playbook -i inventory.ini deploy-zeroclaw-docker.yml --ask-vault-pass
# From source
ansible-playbook -i inventory.ini deploy-zeroclaw-docker.yml -e "deployment_method=source"
---
- name: Deploy ZeroClaw with Multiple Channels
hosts: zeroclaw
become: true
vars:
app_root: /opt/zeroclaw
app_port: 3000
zeroclaw_provider: "anthropic"
zeroclaw_api_key: "{{ vault_zeroclaw_api_key }}"
zeroclaw_channels: ['cli', 'telegram']
tasks:
- name: Install Docker
apt:
name:
- docker.io
- docker-compose-plugin
state: present
update_cache: true
when: ansible_os_family == "Debian"
- name: Enable and start Docker
service:
name: docker
state: started
enabled: true
- name: Create directories
file:
path: "{{ item }}"
state: directory
mode: "0755"
loop:
- "{{ app_root }}"
- "{{ app_root }}/.zeroclaw"
- name: Write ZeroClaw configuration (multi-channel)
copy:
dest: "{{ app_root }}/.zeroclaw/config.toml"
mode: "0600"
content: |
api_key = '{{ zeroclaw_api_key }}'
provider = '{{ zeroclaw_provider }}'
channels = {{ zeroclaw_channels | to_json }}
workspace_only = false
log_level = 'debug'
# Channel-specific configuration
[channels.telegram]
bot_token = "{{ vault_zeroclaw_telegram_token }}"
- name: Write Docker Compose file
copy:
dest: "{{ app_root }}/docker-compose.yml"
mode: "0644"
content: |
version: "3.8"
services:
zeroclaw:
build: .
container_name: zeroclaw
restart: unless-stopped
ports:
- "127.0.0.1:{{ app_port }}:3000"
volumes:
- ./data:/root/.zeroclaw
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
deploy:
resources:
limits:
cpus: '0.5'
memory: 128M
- name: Build and start
shell: |
docker compose build
docker compose up -d
args:
chdir: "{{ app_root }}"
---
- name: Deploy ZeroClaw with Nginx
hosts: zeroclaw
become: true
vars:
app_root: /opt/zeroclaw
app_port: 3000
nginx_port: 80
domain_name: "zeroclaw.example.com"
zeroclaw_provider: "openai"
zeroclaw_api_key: "{{ vault_zeroclaw_api_key }}"
tasks:
- name: Install Docker
apt:
name:
- docker.io
- docker-compose-plugin
state: present
update_cache: true
when: ansible_os_family == "Debian"
- name: Enable and start Docker
service:
name: docker
state: started
enabled: true
- name: Create directories
file:
path: "{{ item }}"
state: directory
mode: "0755"
loop:
- "{{ app_root }}"
- "{{ app_root }}/.zeroclaw"
- name: Write ZeroClaw configuration
copy:
dest: "{{ app_root }}/.zeroclaw/config.toml"
mode: "0600"
content: |
api_key = '{{ zeroclaw_api_key }}'
provider = '{{ zeroclaw_provider }}'
channels = ['cli']
workspace_only = true
log_level = 'info'
- name: Write Docker Compose file (with Nginx)
copy:
dest: "{{ app_root }}/docker-compose.yml"
mode: "0644"
content: |
version: "3.8"
services:
zeroclaw:
build: .
container_name: zeroclaw
restart: unless-stopped
expose:
- "3000"
volumes:
- ./data:/root/.zeroclaw
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
nginx:
image: nginx:alpine
container_name: zeroclaw-nginx
restart: unless-stopped
ports:
- "{{ nginx_port }}:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- zeroclaw
- name: Write Nginx configuration
copy:
dest: "{{ app_root }}/nginx.conf"
mode: "0644"
content: |
events {
worker_connections 1024;
}
http {
upstream zeroclaw {
server zeroclaw:3000;
}
server {
listen 80;
server_name {{ domain_name }};
location / {
proxy_pass http://zeroclaw;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}
- name: Build and start
shell: |
docker compose build
docker compose up -d
args:
chdir: "{{ app_root }}"
~/.zeroclaw/config.toml| Variable | Default | Description |
|---|---|---|
app_root |
/opt/zeroclaw |
Application installation directory |
app_name |
zeroclaw |
Container name |
app_port |
3000 |
Exposed port |
zeroclaw_provider |
openai |
AI provider (openai, anthropic, ollama, openrouter, etc.) |
zeroclaw_api_key |
- |
Provider API key (use Ansible Vault) |
zeroclaw_channels |
['cli'] |
List of channels to enable |
zeroclaw_version |
latest |
ZeroClaw version (GitHub release tag) |
zeroclaw_arch |
x86_64 |
Architecture (x86_64, aarch64, armv7) |
| Provider | Config Value | Notes |
|---|---|---|
| OpenAI | openai |
GPT-4, GPT-3.5-turbo |
| Anthropic | anthropic |
Claude models |
| OpenRouter | openrouter |
Multi-provider aggregator |
| Ollama | ollama |
Local models |
| llama.cpp | llama.cpp |
Local llama-server |
| vLLM | vllm |
Local vLLM server |
| Custom | custom |
OpenAI-compatible endpoints |
# SSH to server
ssh user@server
# Check container status
docker ps --filter name=zeroclaw
# View logs
docker logs zeroclaw --tail 50
# Test configuration
docker exec zeroclaw zeroclaw test --config /root/.zeroclaw/config.toml
By default, ZeroClaw binds to localhost on port 3000:
# SSH to server and test
ssh user@server
curl http://127.0.0.1:3000
---
- name: Backup ZeroClaw configuration
hosts: zeroclaw
become: true
vars:
app_root: /opt/zeroclaw
tasks:
- name: Create backup directory
file:
path: "{{ app_root }}/backups"
state: directory
- name: Backup configuration
archive:
path: "{{ app_root }}/.zeroclaw"
dest: "{{ app_root }}/backups/config-{{ ansible_date_time.date }}.tar.gz"
---
- name: Update ZeroClaw
hosts: zeroclaw
become: true
vars:
app_root: /opt/zeroclaw
tasks:
- name: Pull latest binary
get_url:
url: "https://github.com/zeroclaw-labs/zeroclaw/releases/latest/download/zeroclaw-latest-{{ zeroclaw_arch }}-unknown-linux-gnu.tar.gz"
dest: "{{ app_root }}/zeroclaw.tar.gz"
mode: "0644"
- name: Extract and restart
shell: |
tar -xzf zeroclaw.tar.gz
docker compose restart
args:
chdir: "{{ app_root }}"
# SSH to server
ssh user@server
# Check container status
docker ps --filter name=zeroclaw
# View logs
docker logs zeroclaw
docker compose -f /opt/zeroclaw/docker-compose.yml restart
cd /opt/zeroclaw
docker compose pull
docker compose up -d
For detailed security guidance, see ZeroClaw Security.
Any questions?
Feel free to contact us. Find all contact information on our contact page.