Docker 部署是 OpenClaw 从开发环境走向生产环境的关键一步。本文从 Docker 基础概念出发,深入讲解 OpenClaw 的 Docker 镜像构建、多阶段优化、环境变量注入、数据持久化、健康检查配置,以及 docker-compose 多服务编排。通过一个完整的实战案例——将本地 OpenClaw 项目容器化并部署到服务器,你将掌握从 Dockerfile 编写到生产运行的全流程。读完你会发现:容器化不是"加一层 Docker",而是让部署变得可重复、可移植、可扩展。
你一定遇到过这种场景:
Docker 解决的就是这个问题——把应用和它的所有依赖打包成一个镜像,在任何安装了 Docker 的机器上都能跑。

| 维度 | 裸机部署 | Docker 部署 |
|---|---|---|
| 环境一致性 | ? 每台机器不同 | ? 镜像保证一致 |
| 部署速度 | 分钟级(安装依赖) | 秒级(拉镜像+启动) |
| 回滚 | 手动恢复文件 | docker run 上一版本镜像 |
| 资源隔离 | ? 进程级 | ? 容器级(CPU/内存限制) |
| 可移植性 | ? 绑定操作系统 | ? 任何支持 Docker 的系统 |
| 扩展性 | 手动配置多实例 | docker-compose / K8s 编排 |
在写 Dockerfile 之前,先厘清三个核心概念:
| 概念 | 说明 | 类比 |
|---|---|---|
| 镜像(Image) | 应用的只读模板,包含代码+运行时+依赖 | 安装包 ISO |
| 容器(Container) | 镜像的运行实例,有独立的文件系统和网络 | 安装后的运行程序 |
| 仓库(Registry) | 存储和分发镜像的地方 | App Store |

| 指令 | 作用 | 示例 |
|---|---|---|
| FROM | 指定基础镜像 | FROM node:20-alpine |
| WORKDIR | 设置工作目录 | WORKDIR /app |
| COPY | 复制文件到镜像 | COPY . /app |
| RUN | 在构建时执行命令 | RUN npm install |
| ENV | 设置环境变量 | ENV NODE_ENV=production |
| EXPOSE | 声明容器端口 | EXPOSE 18789 |
| CMD | 容器启动时的默认命令 | CMD ["node", "server.js"] |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
# ============================================ # OpenClaw Dockerfile - 基础版本 # 适用于开发环境和快速验证 # ============================================
FROM node:20-alpine
# 设置工作目录 WORKDIR /app
# 安装系统依赖 RUN apk add --no-cache \ python3 \ py3-pip \ git \ curl \ bash
# 安装 OpenClaw RUN npm install -g openclaw
# 创建数据目录 RUN mkdir -p /data/openclaw /etc/openclaw
# 复制配置文件 COPY openclaw.yaml /etc/openclaw/openclaw.yaml
# 声明端口 EXPOSE 18789
# 健康检查 HEALTHCHECK --interval=30s --timeout=10s --retries=3 \ CMD curl -f http://localhost:18789/health || exit 1
# 启动 Gateway CMD ["openclaw", "gateway", "start"] |
构建和运行:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# 构建镜像 docker build -t openclaw:latest .
# 运行容器 docker run -d \ --name openclaw-gateway \ -p 18789:18789 \ -v /data/openclaw:/data/openclaw \ -v /etc/openclaw:/etc/openclaw \ -e OPENAI_API_KEY="sk-xxx" \ openclaw:latest
# 查看日志 docker logs -f openclaw-gateway
# 查看状态 docker ps | grep openclaw |
这个基础版本适合快速验证。但生产环境还需要考虑镜像体积、安全性、多阶段构建等问题。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# ============================================ # OpenClaw Dockerfile - 多阶段构建(生产级) # 优化点:减小镜像体积、分离构建和运行环境 # ============================================
# ===== 阶段1:构建阶段 ===== FROM node:20-alpine AS builder
WORKDIR /build
# 安装构建工具 RUN apk add --no-cache python3 py3-pip git make g++
# 安装 OpenClaw(含编译依赖) RUN npm install -g openclaw@latest
# 安装 Python 依赖 RUN pip3 install --no-cache-dir \ requests \ aiohttp \ websockets
# ===== 阶段2:运行阶段 ===== FROM node:20-alpine AS runner
# 安装运行时依赖(不含编译工具) RUN apk add --no-cache \ python3 \ py3-pip \ curl \ tini \ su-exec
# 创建非 root 用户 RUN addgroup -g 1000 openclaw && \ adduser -u 1000 -G openclaw -s /bin/sh -D openclaw
# 从构建阶段复制 OpenClaw COPY --from=builder /usr/local/lib/node_modules /usr/local/lib/node_modules COPY --from=builder /usr/local/bin/openclaw /usr/local/bin/openclaw
# 复制 Python 包 COPY --from=builder /usr/lib/python3* /usr/lib/python3* COPY --from=builder /usr/local/lib/python3* /usr/local/lib/python3*
# 创建工作目录 WORKDIR /app RUN mkdir -p /data/openclaw /etc/openclaw /app/workspace && \ chown -R openclaw:openclaw /data/openclaw /app/workspace
# 复制配置 COPY --chown=openclaw:openclaw openclaw.yaml /etc/openclaw/openclaw.yaml
# 切换到非 root 用户 USER openclaw
# 健康检查 HEALTHCHECK --interval=30s --timeout=10s --start-period=15s --retries=3 \ CMD curl -sf http://localhost:18789/health || exit 1
# 暴露端口 EXPOSE 18789
# 使用 tini 作为 init 进程(处理僵尸进程和信号转发) ENTRYPOINT ["/sbin/tini", "--"] CMD ["openclaw", "gateway", "start"] |
多阶段构建的优势:
| 指标 | 基础版本 | 多阶段版本 | 优化幅度 |
|---|---|---|---|
| 镜像大小 | ~800MB | ~350MB | -56% |
| 构建时间 | 120s | 90s | -25% |
| 安全 | root 运行 | 非 root 用户 | 更安全 |
| 攻击面 | 含编译工具 | 仅运行时 | 更小 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# openclaw.yaml - 使用环境变量占位符 gateway: port: ${GATEWAY_PORT:-18789} auth_token: ${GATEWAY_AUTH_TOKEN} model: default: ${DEFAULT_MODEL:-gpt-4o-mini} providers: openai: api_key: ${OPENAI_API_KEY} base_url: ${OPENAI_BASE_URL:-https://api.openai.com/v1} channels: feishu: app_id: ${FEISHU_APP_ID} app_secret: ${FEISHU_APP_SECRET} telegram: bot_token: ${TELEGRAM_BOT_TOKEN} logging: level: ${LOG_LEVEL:-info} file: /data/openclaw/logs/gateway.log |
|
1 2 3 4 5 6 7 |
# 使用 env 文件启动 docker run -d \ --name openclaw-prod \ --env-file .env.production \ -p 18789:18789 \ -v openclaw-data:/data/openclaw \ openclaw:latest |
|
1 2 3 4 5 6 7 8 9 |
# .env.production 示例 GATEWAY_PORT=18789 GATEWAY_AUTH_TOKEN=your-secure-token-here DEFAULT_MODEL=gpt-4o OPENAI_API_KEY=sk-prod-xxx FEISHU_APP_ID=cli_xxx FEISHU_APP_SECRET=xxx TELEGRAM_BOT_TOKEN=123:abc LOG_LEVEL=warn |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# docker-compose.yml # OpenClaw 生产环境多服务编排 version: "3.8" services: # ===== OpenClaw Gateway ===== gateway: build: context: . dockerfile: Dockerfile image: openclaw:latest container_name: openclaw-gateway restart: unless-stopped ports: - "${GATEWAY_PORT:-18789}:18789" volumes: # 数据持久化 - openclaw_data:/data/openclaw # 配置文件 - ./config/openclaw.yaml:/etc/openclaw/openclaw.yaml:ro # 工作空间(Skills、插件等) - ./workspace:/app/workspace # 日志目录 - openclaw_logs:/data/openclaw/logs env_file: - .env.production environment: - NODE_ENV=production - TZ=Asia/Shanghai healthcheck: test: ["CMD", "curl", "-sf", "http://localhost:18789/health"] interval: 30s timeout: 10s retries: 3 start_period: 20s networks: - openclaw-net # 资源限制 deploy: resources: limits: cpus: "2" memory: "2G" reservations: cpus: "0.5" memory: "512M" logging: driver: "json-file" options: max-size: "50m" max-file: "5" # ===== Redis(可选:会话缓存) ===== redis: image: redis:7-alpine container_name: openclaw-redis restart: unless-stopped volumes: - redis_data:/data command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 3 networks: - openclaw-net deploy: resources: limits: cpus: "0.5" memory: "512M" # ===== Nginx(可选:反向代理) ===== nginx: image: nginx:alpine container_name: openclaw-nginx restart: unless-stopped ports: - "80:80" - "443:443" volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - ./nginx/ssl:/etc/nginx/ssl:ro depends_on: gateway: condition: service_healthy networks: - openclaw-net deploy: resources: limits: cpus: "0.5" memory: "256M" volumes: openclaw_data: driver: local openclaw_logs: driver: local redis_data: driver: local networks: openclaw-net: driver: bridge |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# nginx/nginx.conf upstream openclaw_gateway { server gateway:18789; keepalive 32; } server { listen 80; server_name your-domain.com; # 强制 HTTPS return 301 https://$host$request_uri; } server { listen 443 ssl http2; server_name your-domain.com; ssl_certificate /etc/nginx/ssl/cert.pem; ssl_certificate_key /etc/nginx/ssl/key.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; # 请求体大小限制 client_max_body_size 50m; # WebSocket 支持 proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; 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; # 超时配置 proxy_connect_timeout 60s; proxy_send_timeout 120s; proxy_read_timeout 120s; location / { proxy_pass http://openclaw_gateway; } # 健康检查端点 location /health { proxy_pass http://openclaw_gateway/health; access_log off; } } |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# 启动所有服务 docker-compose up -d
# 查看服务状态 docker-compose ps
# 查看 Gateway 日志 docker-compose logs -f gateway
# 重启 Gateway(不重启 Redis/Nginx) docker-compose restart gateway
# 更新镜像并重新部署 docker-compose pull gateway docker-compose up -d --no-deps gateway
# 停止所有服务 docker-compose down
# 停止并删除数据卷(?? 危险操作) docker-compose down -v |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
openclaw-docker/ ├── Dockerfile # 镜像构建文件 ├── docker-compose.yml # 多服务编排 ├── .env.production # 生产环境变量 ├── .dockerignore # 构建排除文件 ├── config/ │ └── openclaw.yaml # Gateway 配置 ├── workspace/ # Skills、插件等 │ └── skills/ ├── nginx/ │ ├── nginx.conf # 反向代理配置 │ └── ssl/ # SSL 证书 └── scripts/ ├── deploy.sh # 部署脚本 └── backup.sh # 备份脚本 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
#!/bin/bash # scripts/deploy.sh # OpenClaw Docker 一键部署脚本
set -e
echo "???? 开始部署 OpenClaw..."
# 1. 检查 Docker 环境 if ! command -v docker &> /dev/null; then echo "? Docker 未安装,请先安装 Docker" exit 1 fi
if ! command -v docker-compose &> /dev/null; then echo "? docker-compose 未安装" exit 1 fi
# 2. 检查环境变量文件 if [ ! -f .env.production ]; then echo "? .env.production 文件不存在" echo "???? 请从 .env.example 复制并填写配置" exit 1 fi
# 3. 创建必要目录 mkdir -p workspace/skills nginx/ssl data/backups
# 4. 备份当前运行版本(如果存在) if docker ps | grep -q openclaw-gateway; then echo "???? 备份当前版本..." docker commit openclaw-gateway openclaw:backup-$(date +%Y%m%d-%H%M%S) fi
# 5. 拉取最新镜像并构建 echo "???? 构建镜像..." docker-compose build --pull gateway
# 6. 启动服务 echo "?? 启动服务..." docker-compose up -d
# 7. 等待健康检查通过 echo "? 等待服务就绪..." for i in $(seq 1 30); do if curl -sf http://localhost:18789/health > /dev/null 2>&1; then echo "? OpenClaw Gateway 已就绪" break fi sleep 2 done
# 8. 显示状态 echo "" echo "???? 服务状态:" docker-compose ps
echo "" echo "? 部署完成!" echo " Gateway: http://localhost:18789" echo " 日志: docker-compose logs -f gateway" |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#!/bin/bash # scripts/backup.sh # OpenClaw 数据备份脚本
BACKUP_DIR="./data/backups" TIMESTAMP=$(date +%Y%m%d-%H%M%S) BACKUP_FILE="${BACKUP_DIR}/openclaw-backup-${TIMESTAMP}.tar.gz"
mkdir -p "$BACKUP_DIR"
echo "???? 备份 OpenClaw 数据..."
# 备份数据卷 docker run --rm \ -v openclaw_data:/data:ro \ -v "$(pwd)/${BACKUP_DIR}":/backup \ alpine tar czf "/backup/openclaw-data-${TIMESTAMP}.tar.gz" -C /data .
# 备份配置文件 tar czf "${BACKUP_DIR}/openclaw-config-${TIMESTAMP}.tar.gz" \ config/ .env.production docker-compose.yml
# 清理 7 天前的备份 find "$BACKUP_DIR" -name "*.tar.gz" -mtime +7 -delete
echo "? 备份完成: ${BACKUP_FILE}" echo " 保留最近 7 天的备份" |
| 症状 | 可能原因 | 排查命令 |
|---|---|---|
| 容器启动后立即退出 | 配置文件错误 | docker logs openclaw-gateway |
| 端口无法访问 | 端口映射未生效 | docker port openclaw-gateway |
| 健康检查失败 | 服务未就绪 | docker exec openclaw-gateway curl localhost:18789/health |
| 数据丢失 | 未挂载数据卷 | docker inspect openclaw-gateway | grep Mounts -A 10 |
| 内存溢出 | 资源限制过低 | docker stats openclaw-gateway |
| 权限错误 | 文件 owner 不匹配 | docker exec openclaw-gateway ls -la /data/openclaw |
|
1 2 3 4 5 6 7 8 |
# 进入容器 Shell docker exec -it openclaw-gateway sh # 查看进程 docker exec openclaw-gateway ps aux # 查看资源使用 docker stats openclaw-gateway --no-stream # 查看容器详细信息 docker inspect openclaw-gateway |
本文从零开始,完整走通了 OpenClaw 的 Docker 容器化部署全流程:
核心要点:
思考题: