elonmusk / Dockerfile
GGSheng's picture
feat: deploy Gemma 4 to hf space
3b47d98 verified
# syntax=docker/dockerfile:1.7
# enable BuildKit cache mounts
# use with: docker buildx build --load .
FROM ubuntu:24.04
ARG DEBIAN_FRONTEND=noninteractive
ARG OPENCLAW_VERSION
ENV OPENCLAW_VERSION=$OPENCLAW_VERSION
# ============================================
# Runtime Override Variables
# docker run -e VAR=value or docker build --build-arg VAR=value
# ============================================
# ----- HuggingFace Core -----
ARG HF_TOKEN
ARG OPENCLAW_HF_SPACE_ID
ARG OPENCLAW_BACKUP_DATASET_REPO
ARG OPENCLAW_RESTORE_DATASET_REPO
ARG OPENCLAW_BACKUP_REPO_TYPE
ARG OPENCLAW_BACKUP_PATH_PREFIX
# ----- Backup Enable/Disable -----
ARG OPENCLAW_BACKUP_ENABLED
ARG OPENCLAW_BACKUP_NPM_ENABLED
ARG OPENCLAW_RESTORE_NPM_ENABLED
# ----- Backup Schedule -----
ARG OPENCLAW_BACKUP_CRON
ARG OPENCLAW_INCREMENTAL_BACKUP
ARG OPENCLAW_INCREMENTAL_INTERVAL_MINUTES
ARG OPENCLAW_FULL_BACKUP_INTERVAL_HOURS
ARG OPENCLAW_MAX_INCREMENTAL_BACKUPS
# ----- Backup Encryption -----
ARG OPENCLAW_BACKUP_ENCRYPTION_ENABLED
ARG OPENCLAW_BACKUP_ENCRYPTION_PASSWORD
# ----- Backup Retention & Compression -----
ARG OPENCLAW_BACKUP_KEEP_COUNT
ARG OPENCLAW_BACKUP_COMPRESSION_LEVEL
ARG OPENCLAW_BACKUP_SPLIT_SIZE
ARG OPENCLAW_BACKUP_SIZE_WARNING_MB
# ----- Dynamic Backup Strategy -----
ARG OPENCLAW_DYNAMIC_BACKUP
ARG OPENCLAW_DYNAMIC_SMALL_THRESHOLD_MB
ARG OPENCLAW_DYNAMIC_MEDIUM_THRESHOLD_MB
ARG OPENCLAW_DYNAMIC_HIGH_CHANGE_RATE
ARG OPENCLAW_DYNAMIC_LOW_CHANGE_RATE
ARG OPENCLAW_DYNAMIC_MIN_CHANGED_FILES
ARG OPENCLAW_DYNAMIC_MIN_CHANGED_SIZE_KB
# ----- Backup Extra Paths -----
ARG OPENCLAW_BACKUP_EXTRA_DIRS
ARG OPENCLAW_BACKUP_EXTRA_FILES
# ----- Restore & Watchdog -----
ARG OPENCLAW_RESTORE_TIMEOUT
ARG WATCHDOG_INTERVAL
ARG MAX_BACKUP_AGE_MINUTES
ARG FORCE_BACKUP_INTERVAL
# ----- SSH & SSHX -----
ARG OPENCLAW_SSH_AGENT_AUTOSTART
ARG OPENCLAW_SSHX_AUTO_START
ARG ROOT_PASSWORD
# ----- LLM Configuration -----
ARG OPENCLAW_LLM_BASE_URL
ARG OPENCLAW_LLM_MODEL
# ----- BT Panel -----
ARG BT_PANEL_PORT
ARG BT_PANEL_USERNAME
ARG BT_PANEL_PASSWORD
ARG BT_PANEL_SAFE_PATH
ARG BT_PANEL_TIMEZONE
# ============================================
# Environment Variables (with defaults)
# ============================================
# ----- HuggingFace Core -----
ENV HF_TOKEN=${HF_TOKEN:-}
ENV OPENCLAW_HF_SPACE_ID=${OPENCLAW_HF_SPACE_ID:-}
ENV OPENCLAW_BACKUP_DATASET_REPO=${OPENCLAW_BACKUP_DATASET_REPO:-}
ENV OPENCLAW_RESTORE_DATASET_REPO=${OPENCLAW_RESTORE_DATASET_REPO:-}
ENV OPENCLAW_BACKUP_REPO_TYPE=${OPENCLAW_BACKUP_REPO_TYPE:-dataset}
ENV OPENCLAW_BACKUP_PATH_PREFIX=${OPENCLAW_BACKUP_PATH_PREFIX:-backups}
# ----- Backup Enable/Disable -----
ENV OPENCLAW_BACKUP_ENABLED=${OPENCLAW_BACKUP_ENABLED:-false}
ENV OPENCLAW_BACKUP_NPM_ENABLED=${OPENCLAW_BACKUP_NPM_ENABLED:-true}
ENV OPENCLAW_RESTORE_NPM_ENABLED=${OPENCLAW_RESTORE_NPM_ENABLED:-true}
# ----- Backup Schedule -----
ENV OPENCLAW_BACKUP_CRON=${OPENCLAW_BACKUP_CRON:-"*/10 * * * *"}
ENV OPENCLAW_INCREMENTAL_BACKUP=${OPENCLAW_INCREMENTAL_BACKUP:-true}
ENV OPENCLAW_INCREMENTAL_INTERVAL_MINUTES=${OPENCLAW_INCREMENTAL_INTERVAL_MINUTES:-15}
ENV OPENCLAW_FULL_BACKUP_INTERVAL_HOURS=${OPENCLAW_FULL_BACKUP_INTERVAL_HOURS:-1}
ENV OPENCLAW_MAX_INCREMENTAL_BACKUPS=${OPENCLAW_MAX_INCREMENTAL_BACKUPS:-15}
# ----- Backup Encryption -----
ENV OPENCLAW_BACKUP_ENCRYPTION_ENABLED=${OPENCLAW_BACKUP_ENCRYPTION_ENABLED:-false}
ENV OPENCLAW_BACKUP_ENCRYPTION_PASSWORD=${OPENCLAW_BACKUP_ENCRYPTION_PASSWORD:-}
# ----- Backup Retention & Compression -----
ENV OPENCLAW_BACKUP_KEEP_COUNT=${OPENCLAW_BACKUP_KEEP_COUNT:-48}
ENV OPENCLAW_BACKUP_COMPRESSION_LEVEL=${OPENCLAW_BACKUP_COMPRESSION_LEVEL:-6}
ENV OPENCLAW_BACKUP_SPLIT_SIZE=${OPENCLAW_BACKUP_SPLIT_SIZE:-500M}
ENV OPENCLAW_BACKUP_SIZE_WARNING_MB=${OPENCLAW_BACKUP_SIZE_WARNING_MB:-1500}
# ----- Dynamic Backup Strategy -----
ENV OPENCLAW_DYNAMIC_BACKUP=${OPENCLAW_DYNAMIC_BACKUP:-true}
ENV OPENCLAW_DYNAMIC_SMALL_THRESHOLD_MB=${OPENCLAW_DYNAMIC_SMALL_THRESHOLD_MB:-500}
ENV OPENCLAW_DYNAMIC_MEDIUM_THRESHOLD_MB=${OPENCLAW_DYNAMIC_MEDIUM_THRESHOLD_MB:-2000}
ENV OPENCLAW_DYNAMIC_HIGH_CHANGE_RATE=${OPENCLAW_DYNAMIC_HIGH_CHANGE_RATE:-10}
ENV OPENCLAW_DYNAMIC_LOW_CHANGE_RATE=${OPENCLAW_DYNAMIC_LOW_CHANGE_RATE:-2}
ENV OPENCLAW_DYNAMIC_MIN_CHANGED_FILES=${OPENCLAW_DYNAMIC_MIN_CHANGED_FILES:-5}
ENV OPENCLAW_DYNAMIC_MIN_CHANGED_SIZE_KB=${OPENCLAW_DYNAMIC_MIN_CHANGED_SIZE_KB:-100}
# ----- Backup Extra Paths -----
ENV OPENCLAW_BACKUP_EXTRA_DIRS=${OPENCLAW_BACKUP_EXTRA_DIRS:-}
ENV OPENCLAW_BACKUP_EXTRA_FILES=${OPENCLAW_BACKUP_EXTRA_FILES:-}
# ----- Restore & Watchdog -----
ENV OPENCLAW_RESTORE_TIMEOUT=${OPENCLAW_RESTORE_TIMEOUT:-5400}
ENV WATCHDOG_INTERVAL=${WATCHDOG_INTERVAL:-600}
ENV MAX_BACKUP_AGE_MINUTES=${MAX_BACKUP_AGE_MINUTES:-30}
ENV FORCE_BACKUP_INTERVAL=${FORCE_BACKUP_INTERVAL:-14400}
# ----- SSH & SSHX -----
ENV OPENCLAW_SSH_AGENT_AUTOSTART=${OPENCLAW_SSH_AGENT_AUTOSTART:-true}
ENV OPENCLAW_SSHX_AUTO_START=${OPENCLAW_SSHX_AUTO_START:-false}
ENV ROOT_PASSWORD=${ROOT_PASSWORD:-"lauer3912"}
# ----- LLM Configuration -----
ENV OPENCLAW_LLM_BASE_URL=${OPENCLAW_LLM_BASE_URL:-}
ENV OPENCLAW_LLM_MODEL=${OPENCLAW_LLM_MODEL:-}
# ----- BT Panel -----
ENV BT_PANEL_PORT=${BT_PANEL_PORT:-7860}
ENV BT_PANEL_USERNAME=${BT_PANEL_USERNAME:-btadmin}
ENV BT_PANEL_PASSWORD=${BT_PANEL_PASSWORD:-}
ENV BT_PANEL_SAFE_PATH=${BT_PANEL_SAFE_PATH:-}
ENV BT_PANEL_TIMEZONE=${BT_PANEL_TIMEZONE:-Asia/Shanghai}
# ----- Supervisor -----
ENV SUPERVISOR_HTTP_AUTH=${SUPERVISOR_HTTP_AUTH:-supervisor}
# ============================================
# BuildKit cache mounts for faster builds
# ============================================
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
--mount=type=cache,target=/root/.cache/pip,sharing=locked \
--mount=type=cache,target=/root/.go/cache,sharing=locked \
set -eux; \
echo "BuildKit cache mounts enabled"
RUN printf 'Acquire::Retries "5";\nAcquire::http::Timeout "30";\nAcquire::https::Timeout "30";\n' \
> /etc/apt/apt.conf.d/80-openclaw-retries && \
set -eux; \
for attempt in 1 2 3 4 5; do \
if apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
cmake \
cron \
curl \
sudo \
supervisor \
g++ \
gh \
git \
lsof \
gnupg \
gosu \
openssh-client \
hostname \
jq \
libasound2t64 \
libatk-bridge2.0-0 \
libatk1.0-0 \
libatspi2.0-0 \
libcairo2 \
libcups2t64 \
libdbus-1-3 \
libgbm1 \
libglib2.0-0 \
libnspr4 \
libnss3 \
libpango-1.0-0 \
libx11-6 \
libxcb1 \
libxcomposite1 \
libxdamage1 \
libxext6 \
libxfixes3 \
libxkbcommon0 \
libxrandr2 \
make \
neovim \
openssl \
procps \
python3 \
python3-pip \
tar \
unzip \
vim \
ffmpeg \
imagemagick \
sshpass \
wget \
libbz2-dev \
libcurl4-openssl-dev \
libffi-dev \
libfreetype-dev \
libgdbm-dev \
libicu-dev \
libjpeg-dev \
liblzma-dev \
libncurses-dev \
libpcap-dev \
libpcre3-dev \
libpng-dev \
libreadline-dev \
libsqlite3-dev \
libssl-dev \
libwebp-dev \
libxml2-dev \
libxslt-dev \
tk-dev \
zlib1g-dev; then \
break; \
fi; \
if [ "$attempt" -eq 5 ]; then \
exit 1; \
fi; \
echo "apt bootstrap failed on attempt $attempt, retrying..." >&2; \
rm -rf /var/lib/apt/lists/*; \
sleep $((attempt * 5)); \
done; \
rm -rf /var/lib/apt/lists/*
# Install Go 1.25 (official binary release)
RUN ARCH="$(dpkg --print-architecture)" && \
case "$ARCH" in \
amd64) GO_ARCH="amd64" ;; \
arm64) GO_ARCH="arm64" ;; \
*) echo "unsupported arch for go: $ARCH" >&2; exit 1 ;; \
esac && \
curl -fsSL "https://go.dev/dl/go1.25.9.linux-${GO_ARCH}.tar.gz" -o /tmp/go.tar.gz && \
rm -rf /usr/local/go && \
tar -C /usr/local -xzf /tmp/go.tar.gz && \
rm /tmp/go.tar.gz && \
/usr/local/go/bin/go version
# Install Rust stable via rustup
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable && \
. "$HOME/.cargo/env" && \
rustc --version && \
cargo --version
RUN curl -fsSL https://deb.nodesource.com/setup_24.x | bash - && \
apt-get install -y --no-install-recommends nodejs && \
node --version && \
npm --version && \
rm -rf /var/lib/apt/lists/*
RUN /bin/bash -lc 'set -euo pipefail; \
CFT_VERSION="$(curl -fsSL https://googlechromelabs.github.io/chrome-for-testing/LATEST_RELEASE_STABLE)"; \
DEB_ARCH="$(dpkg --print-architecture)"; \
case "$DEB_ARCH" in \
amd64) CFT_ARCH=linux64 ;; \
arm64) CFT_ARCH=linux-arm64 ;; \
*) echo "unsupported architecture for chromium: $DEB_ARCH" >&2; exit 1 ;; \
esac; \
CFT_ZIP="chrome-${CFT_ARCH}.zip"; \
CFT_DIR="chrome-${CFT_ARCH}"; \
CFT_URL="https://storage.googleapis.com/chrome-for-testing-public/${CFT_VERSION}/${CFT_ARCH}/${CFT_ZIP}"; \
curl -fsSL "$CFT_URL" -o "/tmp/${CFT_ZIP}"; \
rm -rf "/opt/${CFT_DIR}"; \
unzip -q "/tmp/${CFT_ZIP}" -d /opt; \
rm -f "/tmp/${CFT_ZIP}"; \
ln -sf "/opt/${CFT_DIR}/chrome" /usr/local/bin/chromium; \
chromium --version >/dev/null'
RUN /bin/bash -lc 'set -euo pipefail; \
OPENCLAW_VERSION_VALUE="${OPENCLAW_VERSION:-latest}"; \
HOME=/root curl -fsSL https://openclaw.ai/install.sh | bash -s -- --no-onboard --version "$OPENCLAW_VERSION_VALUE"'
RUN /bin/bash -lc 'set -euo pipefail; \
OPENCLAW_BIN="$(command -v openclaw || true)"; \
if [[ -z "$OPENCLAW_BIN" ]] && [[ -x /root/.npm-global/bin/openclaw ]]; then \
OPENCLAW_BIN=/root/.npm-global/bin/openclaw; \
elif [[ -z "$OPENCLAW_BIN" ]] && [[ -x /root/.local/bin/openclaw ]]; then \
OPENCLAW_BIN=/root/.local/bin/openclaw; \
fi; \
if [[ -z "$OPENCLAW_BIN" ]]; then \
echo "openclaw command not found after installer" >&2; \
exit 1; \
fi; \
ln -sf "$OPENCLAW_BIN" /usr/local/bin/openclaw; \
/usr/local/bin/openclaw --help >/dev/null'
RUN /bin/bash -lc 'set -euo pipefail; \
if ! command -v npm >/dev/null; then \
echo "npm command not found; cannot install developer CLIs" >&2; \
exit 1; \
fi; \
npm install -g --no-audit --no-fund opencode-ai @openai/codex @anthropic-ai/claude-code @larksuite/cli; \
npx skills add larksuite/cli -y -g; \
NPM_PREFIX="$(npm config get prefix)"; \
NPM_BIN="${NPM_PREFIX%/}/bin"; \
for cmd in opencode codex claude; do \
CLI_BIN="$(command -v "$cmd" || true)"; \
if [[ -z "$CLI_BIN" ]] && [[ -x "$NPM_BIN/$cmd" ]]; then \
CLI_BIN="$NPM_BIN/$cmd"; \
elif [[ -z "$CLI_BIN" ]] && [[ -x /root/.npm-global/bin/"$cmd" ]]; then \
CLI_BIN=/root/.npm-global/bin/"$cmd"; \
elif [[ -z "$CLI_BIN" ]] && [[ -x /root/.local/bin/"$cmd" ]]; then \
CLI_BIN=/root/.local/bin/"$cmd"; \
fi; \
if [[ -z "$CLI_BIN" ]]; then \
echo "$cmd command not found after npm install" >&2; \
exit 1; \
fi; \
ln -sf "$CLI_BIN" "/usr/local/bin/$cmd"; \
"/usr/local/bin/$cmd" --help >/dev/null; \
done'
# Install PM2 for process management
RUN npm install -g --no-audit --no-fund pm2 && pm2 --version
RUN python3 -m pip install --no-cache-dir --break-system-packages "huggingface_hub[cli]>=0.31.1" "uv>=0.6.0" "Pillow>=10.0.0" && \
hf --help >/dev/null && \
uv --version >/dev/null
RUN python3 --version >/dev/null && \
gh --version >/dev/null && \
nvim --version >/dev/null && \
command -v chromium >/dev/null
RUN HOME=/root /bin/bash -lc "curl -sSf https://sshx.io/get | sh" && \
if [ -x /root/.local/bin/sshx ]; then ln -sf /root/.local/bin/sshx /usr/local/bin/sshx; fi && \
sshx --help >/dev/null
# Install DDNS-GO for dynamic DNS updates (non-critical, don't fail build)
RUN /bin/bash -lc ' \
DDNS_VERSION="$(curl -fsSL https://api.github.com/repos/jeessy2/ddns-go/releases/latest 2>/dev/null | python3 -c "import sys,json; print(json.load(sys.stdin)[\"tag_name\"])" 2>/dev/null || echo "v6.1.0")"; \
DEB_ARCH="$(dpkg --print-architecture)"; \
case "$DEB_ARCH" in \
amd64) DDNS_ARCH=amd64 ;; \
arm64) DDNS_ARCH=arm64 ;; \
*) echo "unsupported architecture for ddns-go: $DEB_ARCH" >&2; exit 0 ;; \
esac; \
curl -fsSL "https://github.com/jeessy2/ddns-go/releases/download/${DDNS_VERSION}/ddns-go_${DDNS_VERSION#v}_linux_${DDNS_ARCH}.tar.gz" -o /tmp/ddns-go.tar.gz && \
tar -xzf /tmp/ddns-go.tar.gz -C /tmp && \
mv /tmp/ddns-go /usr/local/bin/ddns-go && \
chmod +x /usr/local/bin/ddns-go && \
rm -f /tmp/ddns-go.tar.gz && \
ddns-go --version || true'
WORKDIR /root
# Copy all scripts and configs first
COPY scripts/openclaw-entrypoint.sh /usr/local/bin/openclaw-entrypoint.sh
COPY scripts/openclaw-backup-cron.sh /usr/local/bin/openclaw-backup-cron.sh
COPY scripts/openclaw-backup-health.sh /usr/local/bin/openclaw-backup-health.sh
COPY scripts/openclaw-backup-watchdog.sh /usr/local/bin/openclaw-backup-watchdog.sh
COPY scripts/openclaw-restore.sh /usr/local/bin/openclaw-restore.sh
COPY scripts/openclaw-gateway-ctl /usr/local/bin/openclaw-gateway-ctl
COPY scripts/hf-entrypoint.sh /usr/local/bin/hf-entrypoint.sh
COPY scripts/hf-storage.sh /usr/local/bin/hf-storage.sh
COPY scripts/hf-storage.py /usr/local/bin/hf-storage.py
COPY scripts/ssh-agent-autostart.sh /usr/local/bin/ssh-agent-autostart.sh
COPY scripts/save-env.sh /usr/local/bin/save-env.sh
COPY scripts/update-env-from-secrets.sh /usr/local/bin/update-env-from-secrets.sh
COPY scripts/openclaw-env-sync.sh /usr/local/bin/openclaw-env-sync.sh
COPY scripts/supervisord.conf /etc/supervisor/supervisord.conf
COPY pm2/ecosystem.config.js /app/pm2/ecosystem.config.js
COPY scripts/server.js /app/hf-server.js
COPY openclaw_hf /opt/openclaw-hf/openclaw_hf
# Set execute permissions on all scripts
RUN chmod 755 /usr/local/bin/openclaw-entrypoint.sh \
/usr/local/bin/openclaw-backup-cron.sh \
/usr/local/bin/openclaw-backup-health.sh \
/usr/local/bin/openclaw-backup-watchdog.sh \
/usr/local/bin/openclaw-restore.sh \
/usr/local/bin/openclaw-gateway-ctl \
/usr/local/bin/hf-entrypoint.sh \
/usr/local/bin/hf-storage.sh \
/usr/local/bin/hf-storage.py \
/usr/local/bin/ssh-agent-autostart.sh \
/usr/local/bin/save-env.sh \
/usr/local/bin/update-env-from-secrets.sh \
/usr/local/bin/openclaw-env-sync.sh && \
mkdir -p /root/.openclaw/workspace /var/log/openclaw /root/.pm2 /var/log/supervisor /var/run /root/.ssh && \
chmod 700 /root/.ssh
ENV NODE_ENV=production
ENV PM2_HOME=/root/.pm2
ENV PYTHONUNBUFFERED=1
ENV HOME=/root
ENV PATH="/usr/local/go/bin:/root/.cargo/bin:$PATH"
ENV GOPROXY=https://goproxy.cn,direct
ENV OPENCLAW_HOME=/root
ENV OPENCLAW_USER=root
ENV OPENCLAW_GROUP=root
ENV OPENCLAW_STATE_DIR=/root/.openclaw
ENV OPENCLAW_WORKSPACE_DIR=/root/.openclaw/workspace
ENV OPENCLAW_CONFIG_PATH=/root/.openclaw/openclaw.json
ENV OPENCLAW_GATEWAY_BIND=lan
ENV OPENCLAW_GATEWAY_PORT=18789
ENV OPENCLAW_INIT_GATEWAY_MODE=local
ENV OPENCLAW_GATEWAY_AUTH_MODE=token
ENV OPENCLAW_GATEWAY_CONTROLUI_DANGEROUSLY_ALLOW_HOST_HEADER_ORIGIN_FALLBACK=true
ENV OPENCLAW_GATEWAY_CONTROLUI_ALLOW_INSECURE_AUTH=false
ENV OPENCLAW_GATEWAY_CONTROLUI_DANGEROUSLY_DISABLE_DEVICE_AUTH=false
ENV OPENCLAW_LLM_PROVIDER=thirdparty
ENV OPENCLAW_LLM_API=openai-completions
ENV OPENCLAW_BACKUP_SOURCE_DIR=/root/.openclaw
ENV OPENCLAW_BACKUP_ROOT_CONFIG_DIR=/root/.config
ENV OPENCLAW_BACKUP_ROOT_CODEX_DIR=/root/.codex
ENV OPENCLAW_BACKUP_ROOT_CLAUDE_DIR=/root/.claude
ENV OPENCLAW_BACKUP_ROOT_CARGO_DIR=/root/.cargo
ENV OPENCLAW_BACKUP_ROOT_PIP_DIR=/root/.pip
ENV OPENCLAW_BACKUP_ROOT_RUSTUP_DIR=/root/.rustup
ENV OPENCLAW_BACKUP_ROOT_AGENTS_DIR=/root/.agents
ENV OPENCLAW_BACKUP_ROOT_SSH_DIR=/root/.ssh
ENV OPENCLAW_BACKUP_ROOT_ENV_DIR=/root/.env.d
ENV OPENCLAW_BACKUP_ROOT_NPM_DIR=/root/.npm
ENV OPENCLAW_BACKUP_ROOT_LARK_CLI_DIR=/root/.lark-cli
ENV OPENCLAW_BACKUP_PRIVATE=true
# Backup health check configuration
ENV OPENCLAW_BACKUP_HEALTH_CHECK_ENABLED=false
ENV OPENCLAW_BACKUP_HEALTH_CHECK_BEFORE=false
ENV OPENCLAW_BACKUP_HEALTH_CHECK_AFTER=false
ENV OPENCLAW_BACKUP_MAX_RETRIES=3
ENV OPENCLAW_MAX_BACKUP_AGE_MINUTES=90
ENV OPENCLAW_MAX_FAILED_ATTEMPTS=3
# ============================================
# Labels
# ============================================
ARG BUILD_VERSION
ENV BUILD_VERSION=${BUILD_VERSION:-latest}
LABEL org.opencontainers.image.title="GGSheng ClawCopilot HF Space"
LABEL org.opencontainers.image.description="GGSheng ClawCopilot with BT Panel on HuggingFace Spaces"
LABEL org.opencontainers.image.version="${BUILD_VERSION:-latest}"
LABEL org.opencontainers.image.authors="GGSheng ClawCopilot Team"
LABEL org.opencontainers.image.source="https://github.com/ClawCopilot/Openclaw-Hf-Docker"
# ============================================
# Healthcheck
# ============================================
# 检查宝塔面板和SSH服务
HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
CMD /usr/local/bin/check_ssh_health.sh --no-response >/dev/null 2>&1 && \
curl -f http://localhost:7860 >/dev/null 2>&1 || exit 1
# BT Panel default port (使用变量支持构建时自定义)
EXPOSE ${BT_PANEL_PORT:-7860}
# Openclaw default gateway port (使用变量支持构建时自定义)
EXPOSE ${OPENCLAW_GATEWAY_PORT:-18789}
# Install BT Panel (宝塔面板) with custom root route
# 使用本地修改后的宝塔源码和依赖
COPY scripts/bt_install_panel_custom.sh /tmp/bt-install-panel.sh
COPY bt-source/panel /www/server/panel
COPY bt-source/init/bt6.init /www/server/panel/install/src/bt6.init
COPY bt-source/init/bt7.init /www/server/panel/install/src/bt7.init
COPY bt-source/install/public.sh /www/server/panel/install/public.sh
COPY bt-source/conf/softList.conf /www/server/panel/install/conf/softList.conf
COPY index.html /www/server/panel/index.html
# SSH Stability Optimization
COPY scripts/optimize_ssh.sh /tmp/optimize_ssh.sh
COPY scripts/ssh_service_watchdog.sh /usr/local/bin/ssh_service_watchdog.sh
COPY scripts/check_ssh_health.sh /usr/local/bin/check_ssh_health.sh
RUN chmod +x /tmp/optimize_ssh.sh && \
chmod +x /usr/local/bin/ssh_service_watchdog.sh && \
chmod +x /usr/local/bin/check_ssh_health.sh && \
if [ -f /etc/ssh/sshd_config ]; then \
bash /tmp/optimize_ssh.sh || echo "SSH optimization completed with warnings"; \
fi && \
rm -f /tmp/optimize_ssh.sh && \
echo "[SSH-WATCHDOG] SSH服务看门狗脚本已安装"
RUN chmod 755 /tmp/bt-install-panel.sh && \
echo "[BT-PANEL] 开始自动化安装..." && \
echo "[BT-PANEL] 使用本地修改后的源码和依赖" && \
echo " - PANEL_PORT: ${BT_PANEL_PORT:-7860}" && \
export PANEL_PORT="${BT_PANEL_PORT:-7860}" && \
export PANEL_USER="${BT_PANEL_USERNAME:-bt}" && \
export PANEL_PASSWORD="${BT_PANEL_PASSWORD:-}" && \
export SAFE_PATH="${BT_PANEL_SAFE_PATH:-}" && \
export BT_TIMEZONE="${BT_PANEL_TIMEZONE:-Asia/Shanghai}" && \
export go=y && \
export yes=yes && \
bash /tmp/bt-install-panel.sh; \
rm -f /tmp/bt-install-panel.sh
# HF Spaces entrypoint: bt-panel as PID 1, PM2 manages openclaw
ENTRYPOINT ["/usr/local/bin/hf-entrypoint.sh"]
CMD []