Moving from Setup to Production
Getting OpenClaw running on Azure is one thing β keeping it running reliably is another. This post collects the operational lessons I learned after deploying OpenClaw across multiple Azure VMs.
Understanding Startup Timing
One of the most confusing behaviors is the βconnection reset by peerβ that happens right after docker compose up -d. Hereβs whatβs actually going on:
Timeline after docker compose up -d:
0s β Container created, port mapped via docker-proxy
1s β Port is OPEN (docker-proxy listening) but app not ready
2s β curl gets "Connection reset by peer" β NORMAL
5s β Node.js process binds to port, starts hooks
7s β Gateway fully initialized, serving Control UI
8s β curl returns HTTP 200 β SUCCESSThe fix: wait before testing
docker compose up -d
sleep 10 # Give the gateway time to initialize
curl -I http://127.0.0.1:18789Or use a retry loop:
docker compose up -d
for i in $(seq 1 30); do
if curl -sS -o /dev/null -w "%{http_code}" http://127.0.0.1:18789 2>/dev/null | grep -q "200"; then
echo "Gateway ready after ${i}s"
break
fi
sleep 1
doneCLI as One-Shot, Not a Service
The default docker-compose.yml starts both openclaw-gateway and openclaw-cli as long-running services. The CLI doesnβt need to run as a daemon β itβs a utility tool.
Problem
Running docker compose up -d starts both:
openclaw-openclaw-cli-1 Up
openclaw-openclaw-gateway-1 UpThe CLI container consumes resources and can cause volume contention with the gateway.
Solution: Start only the gateway
# Start only the gateway as a daemon
docker compose up -d openclaw-gateway
# Use CLI only when needed (one-shot)
docker compose run --rm openclaw-cli config get
docker compose run --rm openclaw-cli dashboard --no-open
docker compose run --rm openclaw-cli security auditPermanent fix: Profile-gate the CLI
Edit docker-compose.yml:
services:
openclaw-gateway:
# ... existing config ...
openclaw-cli:
profiles: ["cli"]
# ... existing config ...Now docker compose up -d only starts the gateway. To explicitly use the CLI service:
docker compose --profile cli run --rm openclaw-cli config getOr just keep using docker compose run --rm openclaw-cli ... β it works regardless of profiles.
Alternative LLM Providers
While this series used GitHub Copilot (via device-flow auth), OpenClaw supports multiple LLM backends. Hereβs a quick overview:
| Provider | Auth Method | Config Key | Notes |
|---|---|---|---|
| GitHub Copilot | Device flow | (built-in) | Requires Copilot subscription |
| OpenAI | API key | providers.openai.apiKey | GPT-4o, GPT-4 Turbo |
| Anthropic | API key | providers.anthropic.apiKey | Claude family |
| Google Gemini | API key or CLI auth | providers.google.apiKey | Gemini Pro/Ultra |
| Local (Ollama) | No auth | providers.ollama.baseUrl | Self-hosted models |
| Azure OpenAI | API key + endpoint | providers.azureOpenai.* | Enterprise |
Switching providers
# Example: Switch to OpenAI
docker compose run --rm openclaw-cli config set \
providers.openai.apiKey "sk-..."
# Example: Switch to a local Ollama instance
docker compose run --rm openclaw-cli config set \
providers.ollama.baseUrl "http://host.docker.internal:11434"
# Restart to apply
docker compose down
docker compose up -d openclaw-gatewayMonitoring and Health Checks
Basic health monitoring
# Check container status
docker compose ps
# Check if gateway is responding
curl -sS -o /dev/null -w "%{http_code}" http://127.0.0.1:18789
# Check gateway logs (last 50 lines)
docker compose logs --tail=50 openclaw-gateway
# Check resource usage
docker stats openclaw-openclaw-gateway-1 --no-streamAutomated health check script
Create ~/openclaw/healthcheck.sh:
#!/bin/bash
HTTP_CODE=$(curl -sS -o /dev/null -w "%{http_code}" \
http://127.0.0.1:18789 2>/dev/null)
if [ "$HTTP_CODE" != "200" ]; then
echo "$(date): Gateway unhealthy (HTTP $HTTP_CODE), restarting..."
cd ~/openclaw
docker compose down
docker compose up -d openclaw-gateway
sleep 10
NEW_CODE=$(curl -sS -o /dev/null -w "%{http_code}" \
http://127.0.0.1:18789 2>/dev/null)
echo "$(date): After restart: HTTP $NEW_CODE"
else
echo "$(date): Gateway healthy (HTTP 200)"
fiAdd to cron for periodic checks:
chmod +x ~/openclaw/healthcheck.sh
crontab -e
# Add: */5 * * * * /home/azureuser/openclaw/healthcheck.sh >> /home/azureuser/openclaw/health.log 2>&1Persistent Data and Backups
What needs to be backed up
| Path (inside container) | Host mount | Contains |
|---|---|---|
/home/node/.openclaw/ | Docker volume | Config, conversation history, device pairs |
~/openclaw/.env | Host filesystem | Gateway token, bind settings, env vars |
Backup commands
# Backup config from the container volume
docker compose run --rm openclaw-cli config get > ~/openclaw-config-backup.json
# Backup the .env file
cp ~/openclaw/.env ~/openclaw/.env.backup.$(date +%Y%m%d)
# Full volume backup
docker run --rm \
-v openclaw_openclaw-data:/data \
-v $(pwd):/backup \
alpine tar czf /backup/openclaw-data-$(date +%Y%m%d).tar.gz -C /data .Common Operational Tasks
Updating OpenClaw
cd ~/openclaw
git pull
docker compose down
docker compose up -d --build --force-recreate openclaw-gateway
docker compose logs --tail=50 openclaw-gatewayRotating the gateway token
NEW_TOKEN=$(openssl rand -hex 32)
sed -i "s/OPENCLAW_GATEWAY_TOKEN=.*/OPENCLAW_GATEWAY_TOKEN=$NEW_TOKEN/" .env
docker compose down
docker compose up -d openclaw-gateway
echo "New token: $NEW_TOKEN"Re-enabling Discord (after fixing intents)
# 1. Fix Discord Developer Portal settings first
# 2. Then re-enable in OpenClaw
docker compose run --rm openclaw-cli config set channels.discord.enabled true
docker compose down
docker compose up -d openclaw-gateway
docker compose logs --tail=100 openclaw-gateway
# Verify no "Fatal Gateway error: 4014"Running the doctor
docker compose run --rm openclaw-cli doctor --fixRunning a security audit
docker compose run --rm openclaw-cli security auditSuppressing CLAUDE_* Warnings
Those WARN messages about unset CLAUDE_* variables are harmless but noisy:
WARN[0000] The "CLAUDE_AI_SESSION_KEY" variable is not set. Defaulting to a blank string.
WARN[0000] The "CLAUDE_WEB_SESSION_KEY" variable is not set. Defaulting to a blank string.
WARN[0000] The "CLAUDE_WEB_COOKIE" variable is not set. Defaulting to a blank string.Fix: Add empty values to .env
cat >> ~/openclaw/.env << 'EOF'
CLAUDE_AI_SESSION_KEY=
CLAUDE_WEB_SESSION_KEY=
CLAUDE_WEB_COOKIE=
EOFAfter this, the warnings disappear from every docker compose command.
Quick Reference: Common Commands
# Start gateway only
docker compose up -d openclaw-gateway
# Stop everything
docker compose down
# View logs (live)
docker compose logs -f openclaw-gateway
# View logs (last 100 lines)
docker compose logs --tail=100 openclaw-gateway
# Check status
docker compose ps
# Run CLI command
docker compose run --rm openclaw-cli <command>
# Get current config
docker compose run --rm openclaw-cli config get
# Set a config value
docker compose run --rm openclaw-cli config set <key> <value>
# Generate dashboard URL
docker compose run --rm openclaw-cli dashboard --no-open
# List/approve devices
docker compose run --rm openclaw-cli devices list
docker compose run --rm openclaw-cli devices approve <requestId>
# Security audit
docker compose run --rm openclaw-cli security audit
# Doctor (diagnose + fix)
docker compose run --rm openclaw-cli doctor --fix
# Rebuild from source
docker compose up -d --build --force-recreate openclaw-gatewaySeries Summary
This 10-part series covered the complete journey of deploying OpenClaw on Azure:
- What is OpenClaw? β Overview and architecture
- Azure VM Setup β Creating and configuring the VM
- Docker Installation β Docker + Compose setup
- Gateway Configuration β Bind modes and Control UI origins
- Discord Integration β Bot setup and Fatal Gateway error 4014
- Control UI Access β SSH tunnel and public access
- Security Hardening β Multi-layer security setup
- Troubleshooting β Common errors and fixes
- Copilot Authentication β Device flow on headless servers
- Production Tips (this post) β Operations and monitoring
Every error message, configuration option, and workaround in this series was verified on a real Azure VM deployment. If you encounter issues not covered here, the openclaw-cli doctor --fix and openclaw-cli security audit commands are your best starting point.

