Automate local OpenClaw Traefik setup and add docs

This commit is contained in:
2026-04-11 00:16:39 +02:00
commit 86204e0836
5 changed files with 502 additions and 0 deletions

296
deploy-local-openclaw.sh Executable file
View File

@@ -0,0 +1,296 @@
#!/usr/bin/env bash
# ==============================================================================
# OpenClaw Local (Bare-Metal) Deployment Script
# ==============================================================================
# Installs OpenClaw directly on the host via npm.
# Source: https://docs.openclaw.ai/install
#
# Usage: deploy-local-openclaw.sh
# deploy-local-openclaw.sh --domain oc.an2.io
# deploy-local-openclaw.sh --remove [--purge] [--yes]
# ==============================================================================
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/common.sh"
# --- Parse arguments ---
parse_args "$@"
# --- Service config ---
SERVICE_NAME="openclaw"
BASE_DIR="/opt/${SERVICE_NAME}"
UNIT_NAME="${SERVICE_NAME}"
# --- Handle --remove ---
if [[ "$ARG_REMOVE" == "1" ]]; then
require_root
echo ""
echo -e "${YELLOW}══════════════════════════════════════════════${NC}"
echo -e "${YELLOW} Removing ${SERVICE_NAME} (local)${NC}"
echo -e "${YELLOW}══════════════════════════════════════════════${NC}"
if [[ "$ARG_PURGE" == "1" ]]; then
echo -e " Mode: ${RED}Purge (delete all data)${NC}"
else
echo -e " Mode: Safe (keep data)"
fi
echo -e " Data: ${BASE_DIR}"
echo -e " Systemd: ${UNIT_NAME}.service"
echo -e "${YELLOW}══════════════════════════════════════════════${NC}"
echo ""
if [[ "$ARG_PURGE" == "1" ]] && [[ -d "$BASE_DIR" ]]; then
backup_script="${SCRIPT_DIR}/backup-${SERVICE_NAME}.sh"
[[ -f "$backup_script" ]] && warn "Back up first? ${backup_script}"
echo ""
fi
unit_file="/etc/systemd/system/${UNIT_NAME}.service"
if [[ -f "$unit_file" ]]; then
systemctl stop "${UNIT_NAME}.service" > /dev/null 2>&1 || true
systemctl disable "${UNIT_NAME}.service" > /dev/null 2>&1 || true
rm -f "$unit_file"
systemctl daemon-reload
ok "Removed systemd unit: ${UNIT_NAME}.service"
fi
remove_traefik_dynamic_config "$SERVICE_NAME"
if [[ "$ARG_PURGE" == "1" ]]; then
echo ""
warn "This will permanently delete: $BASE_DIR"
if [[ "$ARG_YES" != "1" ]]; then
read -rp "Are you sure? [y/N] " confirm
if [[ "${confirm,,}" != "y" ]]; then
info "Purge cancelled."
exit 0
fi
fi
rm -rf "$BASE_DIR"
rm -rf "$HOME/.openclaw" 2>/dev/null
ok "Purged: $BASE_DIR"
fi
echo ""
echo -e "${GREEN}══════════════════════════════════════════════${NC}"
echo -e "${GREEN} ${SERVICE_NAME} removed${NC}"
echo -e "${GREEN}══════════════════════════════════════════════${NC}"
if [[ "$ARG_PURGE" != "1" ]]; then
echo -e " Data preserved at ${BASE_DIR}"
echo -e " To purge: $0 --remove --purge"
fi
echo -e " To redeploy: $0"
echo -e "${GREEN}══════════════════════════════════════════════${NC}"
echo ""
exit 0
fi
# --- Domain resolution ---
if [[ -n "$ARG_DOMAIN" ]]; then
DOMAIN="$ARG_DOMAIN"
elif [[ -f "${BASE_DIR}/.env" ]] && grep -q '^DOMAIN=' "${BASE_DIR}/.env"; then
DOMAIN="$(grep '^DOMAIN=' "${BASE_DIR}/.env" | cut -d= -f2)"
else
DOMAIN="oc.an2.io"
fi
# --- Detect current state for banner ---
install_mode="Fresh install"
if [[ -f "${BASE_DIR}/.env" ]]; then
install_mode="Re-run (preserving data)"
fi
service_status="Not running"
if systemctl is-active --quiet "${UNIT_NAME}.service" 2>/dev/null; then
service_status="Running"
fi
# --- Print deployment plan ---
echo ""
echo -e "${CYAN}══════════════════════════════════════════════${NC}"
echo -e "${CYAN} Deploying OpenClaw (Local)${NC}"
echo -e "${CYAN}══════════════════════════════════════════════${NC}"
echo -e " Domain: ${DOMAIN}"
echo -e " Data: ${BASE_DIR}/data/"
echo -e " Service: ${service_status}"
echo -e " Mode: ${install_mode}"
echo -e "${CYAN}══════════════════════════════════════════════${NC}"
echo ""
# --- Check if already deployed and running ---
if [[ -f "${BASE_DIR}/.env" ]] && \
systemctl is-active --quiet "${UNIT_NAME}.service" 2>/dev/null; then
echo -e "${GREEN} OpenClaw is already running — nothing to do.${NC}"
echo ""
echo -e " Health: curl -fsS http://127.0.0.1:18789/healthz"
echo -e " Logs: journalctl -u ${UNIT_NAME}.service -f"
echo -e " Backup: ${SCRIPT_DIR}/backup-openclaw.sh"
echo -e " Remove: $0 --remove [--purge]"
echo ""
exit 0
fi
# --- Infrastructure ---
require_root
detect_os
# --- Install Node.js 22+ ---
install_nodejs_22() {
if command -v node &>/dev/null; then
local node_major
node_major="$(node --version | sed 's/^v//' | cut -d. -f1)"
if (( node_major >= 22 )); then
ok "Node.js $(node --version) already installed."
return 0
fi
warn "Node.js $(node --version) is too old (need 22+). Upgrading..."
fi
info "Installing Node.js 22..."
case "$DISTRO_FAMILY" in
debian)
curl -fsSL https://deb.nodesource.com/setup_22.x | bash - > /dev/null 2>&1
apt-get install -y -qq nodejs > /dev/null 2>&1
;;
fedora)
curl -fsSL https://rpm.nodesource.com/setup_22.x | bash - > /dev/null 2>&1
dnf install -y -q nodejs > /dev/null 2>&1
;;
esac
ok "Node.js $(node --version) installed."
}
# Install base prerequisites
case "$DISTRO_FAMILY" in
debian)
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq curl openssl jq git ca-certificates > /dev/null 2>&1
;;
fedora)
dnf install -y -q curl openssl jq git ca-certificates > /dev/null 2>&1
;;
esac
install_nodejs_22
# --- Backup restore check ---
if check_and_restore_backup "$SERVICE_NAME"; then
saved_domain="$DOMAIN"
# shellcheck source=/dev/null
source "${BASE_DIR}/.env"
DOMAIN="$saved_domain"
ok "Backup restored."
else
# --- Fresh install ---
if [[ -f "${BASE_DIR}/.env" ]]; then
saved_domain="$DOMAIN"
# shellcheck source=/dev/null
source "${BASE_DIR}/.env"
DOMAIN="$saved_domain"
info "Existing installation found. Updating..."
npm update -g openclaw 2>/dev/null || true
else
info "Installing OpenClaw via npm..."
npm install -g openclaw@latest
mkdir -p "${BASE_DIR}/data"
# Redirect ~/.openclaw to our data dir
rm -rf "$HOME/.openclaw" 2>/dev/null
ln -sfn "${BASE_DIR}/data" "$HOME/.openclaw"
# --- Generate .env ---
cat > "${BASE_DIR}/.env" <<DOTENV
# Generated by deploy-local-openclaw.sh on $(date -Iseconds)
DOMAIN=${DOMAIN}
OPENCLAW_CONFIG_DIR=${BASE_DIR}/data
DOTENV
chmod 600 "${BASE_DIR}/.env"
ok ".env written."
fi
fi
# --- Install systemd unit ---
cat > "/etc/systemd/system/${UNIT_NAME}.service" <<EOF
[Unit]
Description=OpenClaw Gateway
After=network.target
[Service]
Type=simple
Environment=OPENCLAW_CONFIG_DIR=${BASE_DIR}/data
ExecStart=$(command -v openclaw || echo /usr/bin/openclaw) gateway start
Restart=on-failure
RestartSec=10
TimeoutStartSec=60
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable "${UNIT_NAME}.service" > /dev/null 2>&1
systemctl restart "${UNIT_NAME}.service"
ok "Systemd unit installed and started: ${UNIT_NAME}.service"
# --- Wait for health ---
info "Waiting for OpenClaw to become healthy (up to 60s)..."
elapsed=0
while (( elapsed < 60 )); do
if curl -fsS http://127.0.0.1:18789/ > /dev/null 2>&1; then
ok "OpenClaw is responding."
break
fi
sleep 5
(( elapsed += 5 ))
done
if (( elapsed >= 60 )); then
warn "OpenClaw did not respond within 60s. Check: journalctl -u ${UNIT_NAME}.service"
fi
# --- Configure gateway for Traefik/browser access ---
OPENCLAW_CONFIG_JSON="${BASE_DIR}/data/openclaw.json"
if [[ -f "$OPENCLAW_CONFIG_JSON" ]]; then
python3 - <<PY
import json
path = "${OPENCLAW_CONFIG_JSON}"
with open(path) as f:
data = json.load(f)
gw = data.setdefault('gateway', {})
gw['bind'] = 'lan'
cu = gw.setdefault('controlUi', {})
origins = cu.setdefault('allowedOrigins', [])
want = 'https://${DOMAIN}'
if want not in origins:
origins.append(want)
with open(path, 'w') as f:
json.dump(data, f, indent=2)
f.write('\n')
print('updated', path)
PY
systemctl restart "${UNIT_NAME}.service"
ok "Updated OpenClaw gateway bind/origins for Traefik access."
fi
# --- Ensure Traefik and dynamic config ---
ensure_docker_network
ensure_traefik
write_traefik_dynamic_config "$SERVICE_NAME" "$DOMAIN" "http://host.docker.internal:18789"
ok "Traefik route configured for https://${DOMAIN}"
# --- Summary ---
echo ""
echo -e "${GREEN}══════════════════════════════════════════════${NC}"
echo -e "${GREEN} OpenClaw deployed successfully (local)${NC}"
echo -e "${GREEN}══════════════════════════════════════════════${NC}"
echo -e " Local URL: http://localhost:18789"
echo -e " Public URL: https://${DOMAIN}"
echo -e " Data: ${BASE_DIR}/data/"
echo -e " Systemd: systemctl status ${UNIT_NAME}"
echo -e " Logs: journalctl -u ${UNIT_NAME}.service -f"
echo -e " Traefik: ${TRAEFIK_DYNAMIC_DIR}/${SERVICE_NAME}.yml"
echo -e "${GREEN}══════════════════════════════════════════════${NC}"
echo ""