# OpenClaw Docker Compose: Complete Configuration Guide

> Originally published on [Remote OpenClaw](https://remoteopenclaw.com/blog/openclaw-docker-compose-guide/).

![OpenClaw Docker Compose: Complete Configuration Guide](/api/og?type=hero&title=OpenClaw%20Docker%20Compose%3A%20Complete%20Configuration%20Guide&subtitle=Complete%20guide%20to%20configuring%20OpenClaw%20with%20Docker%20Compose.%20Full...)

Marketplace

Free skills and AI personas for OpenClaw — browse the marketplace.

[Browse the Marketplace →](https://remoteopenclaw.com/marketplace)

Join the Community

Join 1k+ OpenClaw operators sharing deployment guides, security configs, and workflow automations.

[Join the Community →](https://shorturl.at/3bwc0)

## What Is the Minimum docker-compose.yml?

Here is the simplest working docker-compose.yml for OpenClaw:

```yaml
version: "3.8"

services:
 openclaw:
 image: openclaw/openclaw:latest
 container_name: openclaw
 restart: unless-stopped
 ports:
 - "18789:18789"
 volumes:
 - openclaw_data:/data
 env_file:
 - .env

volumes:
 openclaw_data:
```

And the corresponding .env file:

```bash
ANTHROPIC_API_KEY=sk-ant-your-key-here
GATEWAY_TOKEN=your-secure-random-token
TELEGRAM_BOT_TOKEN=your-telegram-bot-token
```

This gives you a working OpenClaw instance with Claude as the AI model, Telegram as the messaging platform, persistent data storage, and the web UI accessible on port 18789.

Run it with:

```bash
docker compose up -d
```

That is genuinely all you need to get started. Everything below this section covers advanced configuration and optimization.

* * *

## What Environment Variables Does OpenClaw Need?

OpenClaw has dozens of environment variables, but only a few are required. Here is a categorized reference:

**Required:**

```bash
# At least one model provider API key
ANTHROPIC_API_KEY=sk-ant-...
# OR
OPENAI_API_KEY=sk-...
# OR
GOOGLE_AI_API_KEY=...
```

**Recommended:**

```bash
GATEWAY_TOKEN=random-secure-string # API authentication
TELEGRAM_BOT_TOKEN=... # If using Telegram
MODEL_PRIMARY=claude-sonnet-4-20250514 # Default model
```

**Gateway configuration:**

```bash
GATEWAY_BIND=0.0.0.0:18789 # Listen address (default)
GATEWAY_MODE=standard # standard, api_only, or reverse_proxy
```

**Security:**

```bash
TELEGRAM_ALLOWED_USERS=123456789 # Comma-separated user IDs
AUTH_ENABLED=false # Web UI authentication
AUTH_PROVIDER=google # OAuth provider
```

**Sandbox:**

```bash
SANDBOX_BACKEND=docker # docker, ssh, or none
SANDBOX_DOCKER_IMAGE=openclaw/sandbox:latest
```

**Browser:**

```bash
BROWSER_ENABLED=true # Enable browser automation
BROWSER_HEADLESS=true # Run headless (default)
```

Always put sensitive values in a .env file, not directly in docker-compose.yml. The .env file should be in the same directory as docker-compose.yml and should be added to .gitignore if you version control your deployment configuration.

* * *

## How Do Volumes Work?

The `/data` volume is critical. It stores everything that needs to persist between container restarts:

-   `/data/config.json` — Configuration file (if not using env vars exclusively)
-   `/data/sessions/` — Conversation sessions and history
-   `/data/memory/` — Agent memory (vector store and file-based)
-   `/data/skills/` — Installed skills (SKILL.md files)
-   `/data/logs/` — Application logs (if file logging is enabled)

Without a volume, all of this data is lost every time the container restarts. This is the single most common mistake new operators make — they deploy without a volume, everything works great, the container restarts, and all conversation history and configuration is gone.

You have two options for volumes:

**Named volume (recommended for most users):**

```yaml
volumes:
 - openclaw_data:/data
```

Docker manages the storage location. Data survives container updates and recreations. Easy to back up with `docker volume inspect openclaw_data` to find the path.

**Bind mount (for advanced users):**

```yaml
volumes:
 - ./openclaw-data:/data
```

Maps to a specific directory on the host. Easier to access files directly, manage backups, and troubleshoot. But you are responsible for permissions and directory creation.

* * *

## What Ports Does OpenClaw Use?

OpenClaw uses a single port by default: **18789**. This handles all HTTP traffic — web UI, REST API, and webhook callbacks.

```yaml
ports:
 - "18789:18789" # host_port:container_port
```

You can change the host port without changing OpenClaw's internal configuration:

```yaml
ports:
 - "8080:18789" # Access on port 8080, OpenClaw still listens on 18789 internally
```

If you are running behind a reverse proxy and do not need direct external access, you can remove the ports mapping entirely and use Docker's internal networking:

```yaml
# No ports exposed — only accessible via Docker network
# Other services (like Nginx) can reach openclaw:18789 via the Docker network
```

If you are using the sandbox Docker backend, OpenClaw also needs access to the Docker socket:

```yaml
volumes:
 - /var/run/docker.sock:/var/run/docker.sock
```

This lets OpenClaw create sandbox containers. The security implications of mounting the Docker socket are significant — it gives OpenClaw the ability to create, start, stop, and delete containers. Only do this if you understand the risks.

* * *

Marketplace

Free skills and AI personas for OpenClaw — browse the marketplace.

[Browse the Marketplace →](https://remoteopenclaw.com/marketplace)

## How Do You Run OpenClaw with Other Services?

A production deployment often includes companion services. Here is a complete multi-service docker-compose.yml:

```yaml
version: "3.8"

services:
 openclaw:
 image: openclaw/openclaw:latest
 container_name: openclaw
 restart: unless-stopped
 volumes:
 - openclaw_data:/data
 - /var/run/docker.sock:/var/run/docker.sock
 env_file:
 - .env
 depends_on:
 - ollama
 networks:
 - openclaw_net

 caddy:
 image: caddy:2-alpine
 container_name: caddy
 restart: unless-stopped
 ports:
 - "80:80"
 - "443:443"
 volumes:
 - ./Caddyfile:/etc/caddy/Caddyfile
 - caddy_data:/data
 networks:
 - openclaw_net

 ollama:
 image: ollama/ollama:latest
 container_name: ollama
 restart: unless-stopped
 volumes:
 - ollama_data:/root/.ollama
 networks:
 - openclaw_net

volumes:
 openclaw_data:
 caddy_data:
 ollama_data:

networks:
 openclaw_net:
```

This setup runs OpenClaw behind Caddy (a reverse proxy with automatic HTTPS), with Ollama available for local model inference. Caddy handles TLS certificates automatically, and all services communicate over a shared Docker network.

The corresponding Caddyfile:

```text
your-domain.com {
 reverse_proxy openclaw:18789
}
```

* * *

## How Do You Update OpenClaw?

Updating OpenClaw with Docker Compose is straightforward:

```bash
# Pull the latest image
docker compose pull

# Restart with the new image
docker compose up -d
```

Your data persists in the volume — only the application code changes. The container is recreated with the new image, but the /data volume remains attached.

Before updating, always check the [release notes](https://github.com/openclaw/openclaw/releases) for breaking changes. Major updates (like the 3.22 release) can rename environment variables, change config formats, or remove deprecated features.

To pin a specific version instead of using `latest`:

```yaml
image: openclaw/openclaw:3.23.1
```

This prevents unexpected updates and gives you control over when you upgrade. Many operators pin to a specific version in production and test new versions in a staging environment first.

* * *

## What Are the Most Common Docker Issues?

**"Port 18789 is already in use"** — Another process is using the port. Change the host port in docker-compose.yml (e.g., `"8080:18789"`) or stop the conflicting process.

**"Permission denied" on volume mount** — The host directory has wrong ownership. Run `sudo chown -R 1000:1000 ./openclaw-data` (1000 is the default UID inside the container).

**Container restarts in a loop** — Check logs with `docker compose logs openclaw`. Common causes: invalid config, expired API key, or missing required environment variables.

**"No space left on device"** — Docker images, containers, and volumes consume disk space. Run `docker system prune` to clean up unused resources. Also check if conversation logs are filling the data volume.

**Environment variables not updating** — Docker Compose can cache the old environment. Run `docker compose up -d --force-recreate` instead of just `docker compose restart` to ensure the new .env values are loaded.

**"Cannot connect to Docker daemon"** — The Docker daemon is not running, or your user does not have permission. Run `sudo systemctl start docker` and add your user to the docker group with `sudo usermod -aG docker $USER`.
