Meta Description: Install OpenClaw with Docker in 5 minutes. Drop-in docker-compose.yml, env file structure, volume mounts for memory persistence, and WhatsApp/Telegram socket mapping explained.
Docker turns “works on my machine” into “works everywhere.” One command, your AI agent is running. No dependency hell. No version conflicts. No “but it worked yesterday.”
OpenClaw in Docker means you can spin up an instance in 5 minutes, tear it down just as fast, and move it between servers without reconfiguring anything.
This guide covers the drop-in docker-compose setup, environment file structure, volume mounts for memory persistence, and the gotcha around mapping WhatsApp/Telegram sockets that trips up first-timers.
By the end, you’ll have OpenClaw running in a container with persistent memory and messaging integrations working.
Why Docker for OpenClaw
The traditional install:
- Install Node.js (specific version)
- Install dependencies (hope nothing breaks)
- Configure environment
- Set up system services
- Debug path issues
- Repeat on every server
The Docker install:
- Pull image
- Run container
- Done
Benefits:
Isolation: OpenClaw and dependencies contained. Won’t conflict with other apps on your server.
Portability: Same container runs on your laptop, VPS, or cloud. No environment differences.
Reproducibility: Everyone gets identical setup. No “works for me” bugs.
Rollback: Bad update? Revert to previous image instantly.
Scaling: Need 5 instances? Copy the docker-compose, change ports, run.
The tradeoff: Slightly more disk space (image overhead) and marginal performance cost (containerization). For most users, worth it.
Prerequisites
Install Docker:
Ubuntu/Debian:
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER
newgrp docker
macOS: Download Docker Desktop from docker.com
Windows: Download Docker Desktop from docker.com (requires WSL2)
Verify:
docker --version
docker-compose --version
Should show Docker 20+ and Docker Compose 2+.
The 5-Minute Setup
Step 1: Create Project Directory
mkdir openclaw-docker
cd openclaw-docker
Step 2: Create docker-compose.yml
Create docker-compose.yml:
version: '3.8'
services:
openclaw:
image: openclaw/openclaw:latest
container_name: openclaw
restart: unless-stopped
env_file:
- .env
volumes:
# Memory persistence
- ./data:/app/data
# Configuration
- ./config:/app/config
# Logs
- ./logs:/app/logs
# WhatsApp socket (important!)
- ./whatsapp:/app/.wwebjs_auth
# Telegram session
- ./telegram:/app/telegram-sessions
ports:
- "3000:3000"
# Optional: expose web UI
# - "8080:8080"
networks:
- openclaw-net
networks:
openclaw-net:
driver: bridge
What this does:
- image: Pulls official OpenClaw Docker image
- restart: Auto-restart if container crashes
- env_file: Loads environment variables from .env
- volumes: Maps local directories to container (data persists)
- ports: Exposes OpenClaw API (and optionally web UI)
Step 3: Create .env File
Create .env:
# Core Configuration
NODE_ENV=production
OPENCLAW_API_KEY=your-secret-api-key-here
# Telegram
TELEGRAM_ENABLED=true
TELEGRAM_BOT_TOKEN=123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11
TELEGRAM_PHONE_NUMBER=+15551234567
# WhatsApp
WHATSAPP_ENABLED=true
WHATSAPP_SESSION_NAME=openclaw-session
# Discord
DISCORD_ENABLED=false
DISCORD_BOT_TOKEN=your-discord-bot-token
# Slack
SLACK_ENABLED=false
SLACK_BOT_TOKEN=xoxb-your-slack-token
SLACK_APP_TOKEN=xapp-your-app-token
# Google Calendar
GOOGLE_CALENDAR_ENABLED=false
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-secret
# Memory Backend
MEMORY_BACKEND=sqlite
MEMORY_DB_PATH=/app/data/memory.db
# Logging
LOG_LEVEL=info
LOG_FILE=/app/logs/openclaw.log
Security note: Add .env to .gitignore. Never commit tokens.
Step 4: Create Directory Structure
mkdir -p data config logs whatsapp telegram
Step 5: Start OpenClaw
docker-compose up -d
What happens:
- Docker pulls openclaw/openclaw:latest image (if not cached)
- Creates container from image
- Mounts volumes
- Loads .env
- Starts OpenClaw in background (-d flag)
Check status:
docker-compose ps
Should show:
NAME COMMAND STATUS PORTS
openclaw "node index.js" Up 10 seconds 0.0.0.0:3000->3000/tcp
View logs:
docker-compose logs -f
You should see:
[INFO] OpenClaw starting...
[INFO] Telegram bot connected
[INFO] WhatsApp initializing...
[INFO] Agent ready
Volume Mounts Explained
Why volumes matter: Containers are ephemeral. When container stops, data inside vanishes. Volumes persist data on host machine.
Critical Volumes
./data → /app/data
- Memory database (conversations, user context)
- Agent state
- Cached data
Without this: Every restart = fresh agent with no memory.
./config → /app/config
- Custom skills
- Agent configuration
- Workflow definitions
./whatsapp → /app/.wwebjs_auth
- WhatsApp session data
- QR code auth persistence
- Message encryption keys
Without this: Re-scan QR code every restart.
./telegram → /app/telegram-sessions
- Telegram session files
- Auth tokens
Without this: Re-authenticate every restart.
View Volume Data
ls -la data/
# Shows memory.db, cache files
ls -la whatsapp/
# Shows WhatsApp session
The WhatsApp/Telegram Socket Gotcha
The problem: WhatsApp uses a persistent WebSocket connection. When container restarts, connection breaks. Session must be remounted.
WhatsApp Volume Mapping
Correct:
volumes:
- ./whatsapp:/app/.wwebjs_auth
Maps the ENTIRE .wwebjs_auth directory, not just session file.
Why this matters: WhatsApp stores:
- Session credentials
- Message keys
- Media cache
- Browser session data
If volume is missing: You’ll re-scan QR code on every restart.
First-Time WhatsApp Setup
Start container:
docker-compose up
Watch logs:
docker-compose logs -f openclaw
You’ll see:
[WhatsApp] QR Code:
████ ▄▄▄▄▄ █▀█ █▄▄▀▄▀▀ ▄▄▄▄▄ ████
████ █ █ █▀▀▀█ ▀ ▀█ █ █ ████
████ █▄▄▄█ █▀ █▀▀ ▄ █ █▄▄▄█ ████
...
Scan with WhatsApp mobile app:
- Open WhatsApp on phone
- Settings → Linked Devices → Link a Device
- Scan QR code from terminal
Once connected:
[WhatsApp] Client is ready!
Session is now saved in ./whatsapp/ volume.
Test persistence:
docker-compose restart
Should NOT show QR code again. If it does, volume isn’t mounted correctly.
Telegram Session Persistence
Similar issue with Telegram. Session files must persist.
Correct volume:
- ./telegram:/app/telegram-sessions
First-time auth:
[Telegram] Enter phone number: +15551234567
[Telegram] Enter code from Telegram: 12345
After auth, session saved to ./telegram/ volume.
Subsequent restarts: No re-auth needed.
Environment File Structure
The .env file controls everything. Understanding its structure helps you customize.
Section 1: Core Settings
NODE_ENV=production
OPENCLAW_API_KEY=your-secret-key
NODE_ENV: development or production
- Production: Optimized, less logging
- Development: More logs, hot reload
OPENCLAW_API_KEY: Used if you expose API endpoints. Generate random string:
openssl rand -base64 32
Section 2: Messaging Channels
Each channel has _ENABLED flag:
TELEGRAM_ENABLED=true
WHATSAPP_ENABLED=true
DISCORD_ENABLED=false
Set to false to disable without removing credentials.
Section 3: Memory Backend
MEMORY_BACKEND=sqlite
MEMORY_DB_PATH=/app/data/memory.db
Options:
sqlite: File-based (default, simple)postgres: External database (production)redis: In-memory (fast, requires Redis)
For Docker, SQLite is fine unless you need multi-container setup.
Section 4: Logging
LOG_LEVEL=info
LOG_FILE=/app/logs/openclaw.log
LOG_LEVEL options:
error: Only errorswarn: Warnings and errorsinfo: Normal operation (recommended)debug: Verbose (for troubleshooting)
Hot Reloading Environment
Change .env:
nano .env
# Edit values
Restart container:
docker-compose restart
Changes apply immediately (no rebuild needed).
Building Custom Image
Want to add custom skills or modify OpenClaw?
Create Dockerfile
Create Dockerfile:
FROM openclaw/openclaw:latest
# Copy custom skills
COPY ./custom-skills /app/skills/custom
# Copy custom config
COPY ./my-config.yml /app/config/agent.yml
# Install additional dependencies (if needed)
RUN npm install some-extra-package
# Expose any additional ports
EXPOSE 9000
CMD ["node", "index.js"]
Build Image
docker build -t openclaw-custom:v1 .
Update docker-compose.yml
services:
openclaw:
image: openclaw-custom:v1 # Use your custom image
build:
context: .
dockerfile: Dockerfile
Rebuild and Run
docker-compose up --build -d
Multi-Container Setup
Run multiple OpenClaw instances for different users/use cases.
Create docker-compose-multi.yml
version: '3.8'
services:
openclaw-personal:
image: openclaw/openclaw:latest
container_name: openclaw-personal
env_file:
- .env.personal
volumes:
- ./data-personal:/app/data
- ./whatsapp-personal:/app/.wwebjs_auth
ports:
- "3001:3000"
networks:
- openclaw-net
openclaw-work:
image: openclaw/openclaw:latest
container_name: openclaw-work
env_file:
- .env.work
volumes:
- ./data-work:/app/data
- ./whatsapp-work:/app/.wwebjs_auth
ports:
- "3002:3000"
networks:
- openclaw-net
networks:
openclaw-net:
driver: bridge
Run both:
docker-compose -f docker-compose-multi.yml up -d
Result: Two isolated OpenClaw instances, different configs, different messaging accounts.
Backup and Restore
Backup
Stop container:
docker-compose down
Backup volumes:
tar -czf openclaw-backup-$(date +%Y%m%d).tar.gz data/ whatsapp/ telegram/ config/ .env
Restart:
docker-compose up -d
Restore
Extract backup:
tar -xzf openclaw-backup-20260508.tar.gz
Start container:
docker-compose up -d
All data restored (memory, sessions, config).
Updating OpenClaw
Pull Latest Image
docker-compose pull
Restart with New Image
docker-compose up -d
Docker pulls new version, restarts container. Your data (in volumes) is preserved.
Rollback if Needed
Check image history:
docker images openclaw/openclaw
Run specific version:
services:
openclaw:
image: openclaw/openclaw:v1.2.3 # Specific version
docker-compose up -d
Health Checks
Add health check to docker-compose.yml:
services:
openclaw:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
Check health:
docker inspect --format='{{.State.Health.Status}}' openclaw
Should return: healthy
Common Docker Issues
“Port already in use”
Error:
Error starting userland proxy: listen tcp 0.0.0.0:3000: bind: address already in use
Cause: Another process using port 3000.
Fix 1 – Change port:
ports:
- "3001:3000" # Host port 3001 instead
Fix 2 – Kill process:
sudo lsof -ti:3000 | xargs kill
“Volume mount permission denied”
Error:
permission denied while trying to connect to the Docker daemon socket
Cause: User not in docker group.
Fix:
sudo usermod -aG docker $USER
newgrp docker
“Container keeps restarting”
Check logs:
docker-compose logs openclaw
Common causes:
- Missing required env vars (.env incomplete)
- Bad credentials (Telegram token expired)
- Port conflict
“WhatsApp QR code on every restart”
Cause: Volume not mounted or wrong path.
Fix: Verify volume in docker-compose.yml:
volumes:
- ./whatsapp:/app/.wwebjs_auth # Must match OpenClaw's path
Check directory exists:
ls -la whatsapp/
Should show session files after first QR scan.
Resource Limits
Prevent OpenClaw from consuming all system resources.
Add Resource Constraints
services:
openclaw:
deploy:
resources:
limits:
cpus: '1.0' # Max 1 CPU core
memory: 1024M # Max 1GB RAM
reservations:
cpus: '0.5' # Reserve 0.5 core
memory: 512M # Reserve 512MB
Monitor usage:
docker stats openclaw
Shows real-time CPU, memory, network, disk usage.
Docker Compose Profiles
Run different configurations without multiple compose files.
Add Profiles
services:
openclaw:
profiles: ["production"]
image: openclaw/openclaw:latest
# ... rest of config
openclaw-dev:
profiles: ["development"]
image: openclaw/openclaw:dev
volumes:
- ./src:/app/src # Mount source for hot reload
environment:
- NODE_ENV=development
Run production:
docker-compose --profile production up -d
Run development:
docker-compose --profile development up -d
The PaioClaw Pre-Built Image Alternative
Setup time so far: 5-10 minutes (Docker install, compose file, first QR scan).
What you’ve handled:
- Docker installation
- docker-compose.yml creation
- .env configuration
- Volume mapping for persistence
- WhatsApp/Telegram socket gotcha
- First-time authentication
Ongoing maintenance:
- Image updates (pull, restart)
- Volume backups
- Resource monitoring
- Log rotation
PaioClaw pre-built image:
Docker image included, but fully managed:
- Pre-configured docker-compose
- Automatic volume setup
- One-click messaging auth
- Managed updates
- Built-in monitoring
Total time: 2 minutes (paste API key, scan QR).
Includes:
- Pre-optimized image (smaller, faster)
- Automatic backup to cloud storage
- Resource optimization (CPU/memory tuning)
- Health monitoring and auto-restart
- Log aggregation
Cost: Starts FREE, Smart $15/month, Genius $25/month.
When to DIY Docker:
- You need full container control
- Custom image modifications
- Multi-instance orchestration
- Learning Docker/containers
When to use PaioClaw:
- You want Docker benefits without Docker expertise
- Automatic backups and monitoring
- Managed updates
- Support when things break
The honest truth: Docker setup is straightforward if you know Docker. If docker-compose.yml looks like hieroglyphics, PaioClaw handles the complexity.
The Bottom Line
Docker makes OpenClaw portable, reproducible, and isolated. The setup is genuinely 5 minutes once you understand volume mounts and the WhatsApp/Telegram socket gotcha.
Critical takeaways:
- Volume mounts preserve data across restarts
- WhatsApp/Telegram sessions MUST be in volumes
- .env file controls all configuration
- docker-compose makes management simple
If you followed this guide, you now have OpenClaw running in Docker with persistent memory and working messaging integrations.
Whether you manage your own Docker setup or use PaioClaw’s pre-built image depends on whether you want infrastructure control or infrastructure convenience.
Want Docker without the YAML? PaioClaw’s managed Docker deployment handles compose files, volume mounts, backups, and monitoring automatically. One-click setup, zero Docker knowledge required. Starts FREE, Smart $15/month, Genius $25/month. Start free →

