← All articles

OpenClaw Docker Setup Guide: Compose, Storage, Telegram, and Discord

Deploy OpenClaw with Docker Compose. Covers environment variables, persistent storage, health checks, and connecting Telegram or Discord as your agent interface.

Why Docker for OpenClaw

Running OpenClaw via Docker gives you process isolation, consistent environments across machines, and a clean path to production deployment on a VPS. The Docker image handles the Node.js runtime and dependency stack — you bring environment variables, a volume for persistent storage, and a compose file to wire it together.

This guide covers a production-ready Docker Compose setup: environment configuration, persistent volume mounting, health checks, and attaching Telegram or Discord so you can interact with your agent from a mobile device.

If you're starting from scratch and haven't installed OpenClaw yet, see the full installation guide first →.


Prerequisites

  • Docker 24+ and Docker Compose v2 (docker compose, not docker-compose)
  • An AI provider API key (Anthropic recommended)
  • A VPS or server with at least 1 GB RAM and 20 GB disk
  • Ports 3000 open on your firewall (or a reverse proxy in front)

Check your versions:

docker --version
docker compose version

Compose v2 is built into Docker Desktop and recent Docker Engine releases. If docker compose fails, you're on v1 — update Docker.


The Docker Compose File

Create a directory for your OpenClaw deployment:

mkdir openclaw && cd openclaw

Create docker-compose.yml:

version: "3.9"

services:
  openclaw:
    image: openclaw/gateway:latest
    container_name: openclaw
    restart: unless-stopped
    ports:
      - "3000:3000"
    environment:
      - AI_PROVIDER=${AI_PROVIDER}
      - AI_API_KEY=${AI_API_KEY}
      - OPENCLAW_AUTH_TOKEN=${OPENCLAW_AUTH_TOKEN}
      - OPENCLAW_ENV=production
      - LOG_LEVEL=info
    volumes:
      - openclaw_data:/app/data
      - ./config:/app/config:ro
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 15s

volumes:
  openclaw_data:
    driver: local

Key decisions in this configuration:

restart: unless-stopped — The container restarts automatically after crashes or server reboots. Use always if you want it to restart even after a manual docker stop (not recommended for development).

volumes: openclaw_data:/app/data — This is the persistent storage mount. OpenClaw writes agent state, conversation history, and channel credentials to /app/data. Without a volume, this data is lost every time the container restarts. The named volume openclaw_data persists across container updates.

./config:/app/config:ro — Mount a local config directory into the container read-only. This is where you put your openclaw.config.json if you're managing configuration as files rather than environment variables. The :ro flag prevents the container from writing back to your host config.

healthcheck — Docker polls /health every 30 seconds. If the endpoint fails 3 consecutive checks, Docker marks the container unhealthy. You can use this with monitoring tools or restart policies. The start_period: 15s gives OpenClaw time to initialize before health checks begin.


Environment Variables

Create a .env file in the same directory as your compose file:

touch .env
chmod 600 .env
# AI Provider
AI_PROVIDER=anthropic
AI_API_KEY=sk-ant-your-key-here

# Gateway auth — generate with: openssl rand -hex 32
OPENCLAW_AUTH_TOKEN=your_64_character_random_string_here

# Optional: Telegram bot integration
TELEGRAM_BOT_TOKEN=your_telegram_bot_token
TELEGRAM_ALLOWED_USERS=your_telegram_user_id

# Optional: Discord bot integration
DISCORD_BOT_TOKEN=your_discord_bot_token
DISCORD_GUILD_ID=your_server_id
DISCORD_CHANNEL_ID=your_channel_id

Never commit .env to git. Add it to .gitignore:

echo ".env" >> .gitignore

The OPENCLAW_AUTH_TOKEN is the bearer token for authenticating requests to your Gateway. Generate a secure one:

openssl rand -hex 32

For AI_PROVIDER, use anthropic for Claude models. OpenClaw also accepts openai and google. The key format changes depending on the provider — Anthropic keys start with sk-ant-, OpenAI with sk-.


Persistent Storage Explained

OpenClaw stores three categories of data under /app/data:

Agent state — The agent's active context, current task queue, and SOUL.md (the personality/instruction file that defines how the agent behaves). If this is lost, agents start from scratch on every restart.

Conversation history — Indexed records of past sessions used for context retrieval. Agents use this to understand what's been discussed and decided across sessions.

Channel credentials — OAuth tokens and API keys for connected integrations (email, calendar, Slack). These are encrypted at rest by OpenClaw. If you lose the volume, you'll need to re-authenticate every channel.

The named Docker volume persists across docker compose down and container image updates. To verify the volume exists after your first deploy:

docker volume ls
docker volume inspect openclaw_openclaw_data

Backing up the volume:

docker run --rm \
  -v openclaw_openclaw_data:/source:ro \
  -v $(pwd)/backups:/backup \
  alpine tar czf /backup/openclaw-$(date +%Y%m%d).tar.gz -C /source .

Run this on a cron schedule for production deployments. Data loss from a corrupted volume without backups means full re-onboarding of every connected channel.


Starting and Managing the Deployment

Start the stack:

docker compose up -d

Check logs:

docker compose logs -f openclaw

You should see Gateway started on port 3000 within 10–15 seconds. If you see API key errors, check your .env formatting — trailing whitespace after the key value is a common issue.

Test the Gateway:

curl -s \
  -H "Authorization: Bearer $OPENCLAW_AUTH_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Hello"}' \
  http://localhost:3000/v1/chat

Update to a new image version:

docker compose pull
docker compose up -d

Docker Compose pulls the updated image and recreates the container while keeping your volumes intact. The update typically takes under 30 seconds with no manual steps.


Connecting Telegram

Telegram is the most common interface for interacting with a self-hosted OpenClaw agent. It gives you a mobile-accessible chat interface without exposing your Gateway to the public internet.

Create a Telegram bot:

  1. Message @BotFather on Telegram
  2. Send /newbot
  3. Follow the prompts — you'll get a bot token in the format 1234567890:ABCdef...

Add the token to your .env:

TELEGRAM_BOT_TOKEN=1234567890:ABCdef...

Get your Telegram user ID:

Message @userinfobot on Telegram. It returns your numeric user ID. Add it to .env:

TELEGRAM_ALLOWED_USERS=123456789

This allowlist is critical. Without it, anyone who finds your bot can send it commands. OpenClaw's Telegram integration runs in "pairing mode" by default — it rejects requests from any user ID not in the allowlist, which prevents prompt injection attacks from unknown users.

Add Telegram to your compose file under the environment section:

environment:
  # ... existing variables ...
  - TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}
  - TELEGRAM_ALLOWED_USERS=${TELEGRAM_ALLOWED_USERS}
  - TELEGRAM_ENABLED=true

Restart the stack:

docker compose down && docker compose up -d

Find your bot on Telegram (search the bot username you set with BotFather), send /start, and you should receive a gateway confirmation message.


Connecting Discord

Discord is the better choice for team setups where multiple people need access to the agent. You get channel-level access control instead of per-user ID lists.

Create a Discord application and bot:

  1. Go to the Discord Developer Portal → New Application
  2. Under "Bot," add a bot to your application
  3. Copy the bot token
  4. Under "OAuth2 → URL Generator," select bot scope and Send Messages + Read Message History permissions
  5. Use the generated URL to invite the bot to your server

Add to .env:

DISCORD_BOT_TOKEN=your_bot_token
DISCORD_GUILD_ID=your_server_id
DISCORD_CHANNEL_ID=the_channel_id_the_bot_watches

Get your guild ID and channel ID by enabling Developer Mode in Discord settings (Settings → Advanced → Developer Mode), then right-clicking the server or channel and selecting "Copy ID."

Add to compose environment:

environment:
  # ... existing variables ...
  - DISCORD_BOT_TOKEN=${DISCORD_BOT_TOKEN}
  - DISCORD_GUILD_ID=${DISCORD_GUILD_ID}
  - DISCORD_CHANNEL_ID=${DISCORD_CHANNEL_ID}
  - DISCORD_ENABLED=true

Restart and test by posting a message in the watched channel.


Production Security Checklist

Running OpenClaw on a VPS means you're operating a service with access to email, calendar, and potentially other sensitive integrations. These are non-negotiable:

Put a reverse proxy in front of port 3000. Never expose the Gateway directly on a public IP. Use Nginx or Caddy with TLS termination. A basic Caddy config:

openclaw.yourdomain.com {
    reverse_proxy localhost:3000
}

Caddy handles HTTPS automatically via Let's Encrypt.

Firewall port 3000 from public access. Only the reverse proxy should reach it:

ufw allow 80
ufw allow 443
ufw allow 22
ufw deny 3000
ufw enable

Rotate your OPENCLAW_AUTH_TOKEN periodically. Generate a new token, update .env, and restart the stack. Any services calling the Gateway directly will need the updated token.

Audit agent tool permissions. Agents with write access to email can send messages on your behalf. Review each agent's configured tool set and ensure write permissions are explicitly granted, not inherited by default.

Keep the image updated. OpenClaw releases security patches in point versions. A pull-and-restart on a monthly schedule is the minimum maintenance commitment for a production deployment.


The Managed Alternative

A self-hosted Docker deployment gives you full control. It also gives you full responsibility for uptime, security patches, backup management, and debugging when a channel integration breaks after an OAuth token expires.

For a developer who manages infrastructure as part of their role, that's a reasonable trade. For an operator who wants the outcomes — inbox triage, morning brief, calendar protection — without becoming an ops engineer, it's often the wrong use of time.

What a fully managed OpenClaw deployment looks like vs. self-hosted →

The infrastructure question is separate from the outcome question. If what you actually want is 2 hours back per morning without running a server, there's a faster path →.


Skip the ops work. Start your free trial →

Free 3-day trial

Your AI executive assistant is ready.

Morning brief at 7am. Inbox triaged overnight. Calendar protected. Dedicated VPS. No Docker. Live in 60 seconds.

Start free trial → $0 today · $47/mo after 3 days · Cancel anytime

Ready to delegate your inbox?

3-day free trial. No charge today. Live in 60 seconds.

Start your trial →