openclaw / Dockerfile
asons's picture
Update Dockerfile
d7433d1 verified
FROM node:22-slim
# 1. 基础依赖补全
RUN apt-get update && apt-get install -y --no-install-recommends \
git openssh-client build-essential python3 python3-pip \
g++ make ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# 2. 安装 HF 数据交互工具
RUN pip3 install --no-cache-dir huggingface_hub --break-system-packages
# 3. 构建环境优化
RUN update-ca-certificates && \
git config --global http.sslVerify false && \
git config --global url."https://github.com/".insteadOf ssh://git@github.com/
# 4. OpenClaw 核心安装
RUN npm install -g openclaw@latest --unsafe-perm
# 5. 环境变量预设
ENV PORT=7860 \
OPENCLAW_GATEWAY_MODE=local \
HOME=/root
# 6. Python 同步引擎(已新增定时任务完整备份)
RUN cat > /usr/local/bin/sync.py << 'EOF'
import os, sys, tarfile
from huggingface_hub import HfApi, hf_hub_download
from datetime import datetime, timedelta
api = HfApi()
repo_id = os.getenv("HF_DATASET")
token = os.getenv("HF_TOKEN")
def restore():
try:
print("🔍 [Restore] Searching for latest backup...")
files = api.list_repo_files(repo_id=repo_id, repo_type="dataset", token=token)
now = datetime.now()
for i in range(7):
day = (now - timedelta(days=i)).strftime("%Y-%m-%d")
name = f"backup_{day}.tar.gz"
if name in files:
print(f"📦 [Restore] Found: {name}")
path = hf_hub_download(repo_id=repo_id, filename=name, repo_type="dataset", token=token)
with tarfile.open(path, "r:gz") as tar:
tar.extractall(path="/root/.openclaw/")
print(f"✅ [Restore] Success: Restored sessions + skills + scheduled tasks + agents from {name}")
return True
print("⚠️ [Restore] No recent backup found.")
except Exception as e:
print(f"❌ [Restore] Error: {e}")
def backup():
try:
day = datetime.now().strftime("%Y-%m-%d")
name = f"backup_{day}.tar.gz"
print(f"📦 [Backup] Starting → {name}")
with tarfile.open(name, "w:gz") as tar:
# 会话
if os.path.exists("/root/.openclaw/agents"):
tar.add("/root/.openclaw/agents", arcname="agents")
session_count = len(os.listdir("/root/.openclaw/agents/main/sessions")) if os.path.exists("/root/.openclaw/agents/main/sessions") else 0
print(f" → Backed up agents/sessions ({session_count} sessions)")
# 技能
if os.path.exists("/root/.openclaw/skills"):
tar.add("/root/.openclaw/skills", arcname="skills")
if os.path.exists("/root/.openclaw/workspace"):
tar.add("/root/.openclaw/workspace", arcname="workspace")
# 定时任务(新增重点)
if os.path.exists("/root/.openclaw/cron"):
tar.add("/root/.openclaw/cron", arcname="cron")
print(" → Backed up cron/ (scheduled tasks)")
if os.path.exists("/root/.openclaw/agents/main/cron"):
tar.add("/root/.openclaw/agents/main/cron", arcname="agents/main/cron")
print(" → Backed up agents/main/cron")
if os.path.exists("/root/.openclaw/workspace/cron"):
tar.add("/root/.openclaw/workspace/cron", arcname="workspace/cron")
print(" → Backed up workspace/cron")
# 其他必要目录
if os.path.exists("/root/.openclaw/openclaw.json"):
tar.add("/root/.openclaw/openclaw.json", arcname="openclaw.json")
if os.path.exists("/root/.openclaw/.clawhub"):
tar.add("/root/.openclaw/.clawhub", arcname=".clawhub")
api.upload_file(path_or_fileobj=name, path_in_repo=name, repo_id=repo_id, repo_type="dataset", token=token)
print(f"✅ [Backup] Success: {name} uploaded (includes scheduled tasks)")
except Exception as e:
print(f"❌ [Backup] Error: {e}")
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == "backup":
backup()
else:
restore()
EOF
# 7. openclaw.json 模板(你的第三方 API 配置)
RUN mkdir -p /root/.openclaw && \
cat > /root/.openclaw/openclaw.json.template << 'EOT'
{
"models": {
"providers": {
"thirdparty": {
"baseUrl": "PLACEHOLDER_BASE_URL",
"apiKey": "PLACEHOLDER_API_KEY",
"api": "openai-completions",
"models": [
{
"id": "gemini-3.1-pro-high",
"name": "Gemini 3.1 Pro (High)",
"contextWindow": 131072
}
]
}
}
},
"agents": {
"defaults": {
"timeoutSeconds": 300, // 整体任务超时,建议 3005分钟)或更高
"model": {
"primary": "thirdparty/PLACEHOLDER_MODEL_ID"
}
}
},
"gateway": {
"mode": "local",
"bind": "lan",
"port": 7860,
"trustedProxies": ["0.0.0.0/0", "10.0.0.0/8", "10.16.0.0/12", "172.16.0.0/12", "192.168.0.0/16"],
"auth": {
"mode": "token",
"token": "PLACEHOLDER_GATEWAY_PASSWORD"
},
"controlUi": {
"enabled": true,
"allowInsecureAuth": true,
"dangerouslyAllowHostHeaderOriginFallback": true,
"dangerouslyDisableDeviceAuth": true
}
}
}
EOT
# 8. 启动脚本(显示定时任务恢复情况)
RUN cat > /usr/local/bin/start-openclaw << 'EOF'
#!/bin/bash
set -e
mkdir -p /root/.openclaw/sessions
echo "🚀 Starting OpenClaw with full persistence..."
# 恢复所有数据
python3 /usr/local/bin/sync.py restore
# 显示恢复后的会话和定时任务
echo "=== Restored Sessions ==="
if [ -d "/root/.openclaw/agents/main/sessions" ]; then
count=$(ls -1 "/root/.openclaw/agents/main/sessions" 2>/dev/null | wc -l)
echo "agents/main/sessions: ${count} sessions"
else
echo "No agents/main/sessions yet"
fi
echo "=== Restored Scheduled Tasks (cron) ==="
ls -la /root/.openclaw/cron 2>/dev/null || echo "No global cron"
ls -la /root/.openclaw/agents/main/cron 2>/dev/null || echo "No agents/main/cron"
ls -la /root/.openclaw/workspace/cron 2>/dev/null || echo "No workspace/cron"
echo "=================================="
# 立即备份一次
echo "📤 Immediate backup after restore..."
python3 /usr/local/bin/sync.py backup
# 生成配置
CLEAN_BASE=$(echo "$OPENAI_API_BASE" | sed 's|/chat/completions||g' | sed 's|/v1/|/v1|g' | sed 's|/v1$|/v1|g')
sed -e "s|PLACEHOLDER_BASE_URL|$CLEAN_BASE|g" \
-e "s|PLACEHOLDER_API_KEY|$OPENAI_API_KEY|g" \
-e "s|PLACEHOLDER_MODEL_ID|$MODEL|g" \
-e "s|PLACEHOLDER_GATEWAY_PASSWORD|$OPENCLAW_GATEWAY_PASSWORD|g" \
/root/.openclaw/openclaw.json.template > /root/.openclaw/openclaw.json
echo "✅ OpenClaw fully started (sessions + skills + scheduled tasks restored)"
# 每20分钟自动备份
(while true; do sleep 1200; python3 /usr/local/bin/sync.py backup; done) &
openclaw doctor --fix
exec openclaw gateway run --port $PORT
EOF
RUN chmod +x /usr/local/bin/start-openclaw
EXPOSE 7860
CMD ["/usr/local/bin/start-openclaw"]