openclaw/docker-setup.sh
{Suksham-sharma} 7e47d8d9fa fix(docker): set directory ownership for container user
Add chown commands to set ownership of mounted directories to uid 1000
(the container's node user). This allows the container to write to the
mounted volumes when the host directories are owned by root.

Uses || true to avoid failing when run by non-root users.
2026-01-29 01:33:31 +05:30

214 lines
6.0 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
COMPOSE_FILE="$ROOT_DIR/docker-compose.yml"
EXTRA_COMPOSE_FILE="$ROOT_DIR/docker-compose.extra.yml"
IMAGE_NAME="${CLAWDBOT_IMAGE:-moltbot:local}"
EXTRA_MOUNTS="${CLAWDBOT_EXTRA_MOUNTS:-}"
HOME_VOLUME_NAME="${CLAWDBOT_HOME_VOLUME:-}"
require_cmd() {
if ! command -v "$1" >/dev/null 2>&1; then
echo "Missing dependency: $1" >&2
exit 1
fi
}
require_cmd docker
if ! docker compose version >/dev/null 2>&1; then
echo "Docker Compose not available (try: docker compose version)" >&2
exit 1
fi
mkdir -p "${CLAWDBOT_CONFIG_DIR:-$HOME/.clawdbot}"
mkdir -p "${CLAWDBOT_WORKSPACE_DIR:-$HOME/clawd}"
export CLAWDBOT_CONFIG_DIR="${CLAWDBOT_CONFIG_DIR:-$HOME/.clawdbot}"
export CLAWDBOT_WORKSPACE_DIR="${CLAWDBOT_WORKSPACE_DIR:-$HOME/clawd}"
# Set ownership to container user (uid 1000) so the container can write to mounted volumes
chown -R 1000:1000 "$CLAWDBOT_CONFIG_DIR" 2>/dev/null || true
chown -R 1000:1000 "$CLAWDBOT_WORKSPACE_DIR" 2>/dev/null || true
export CLAWDBOT_GATEWAY_PORT="${CLAWDBOT_GATEWAY_PORT:-18789}"
export CLAWDBOT_BRIDGE_PORT="${CLAWDBOT_BRIDGE_PORT:-18790}"
export CLAWDBOT_GATEWAY_BIND="${CLAWDBOT_GATEWAY_BIND:-lan}"
export CLAWDBOT_IMAGE="$IMAGE_NAME"
export CLAWDBOT_DOCKER_APT_PACKAGES="${CLAWDBOT_DOCKER_APT_PACKAGES:-}"
if [[ -z "${CLAWDBOT_GATEWAY_TOKEN:-}" ]]; then
if command -v openssl >/dev/null 2>&1; then
CLAWDBOT_GATEWAY_TOKEN="$(openssl rand -hex 32)"
else
CLAWDBOT_GATEWAY_TOKEN="$(python3 - <<'PY'
import secrets
print(secrets.token_hex(32))
PY
)"
fi
fi
export CLAWDBOT_GATEWAY_TOKEN
COMPOSE_FILES=("$COMPOSE_FILE")
COMPOSE_ARGS=()
write_extra_compose() {
local home_volume="$1"
shift
local -a mounts=("$@")
local mount
cat >"$EXTRA_COMPOSE_FILE" <<'YAML'
services:
moltbot-gateway:
volumes:
YAML
if [[ -n "$home_volume" ]]; then
printf ' - %s:/home/node\n' "$home_volume" >>"$EXTRA_COMPOSE_FILE"
printf ' - %s:/home/node/.clawdbot\n' "$CLAWDBOT_CONFIG_DIR" >>"$EXTRA_COMPOSE_FILE"
printf ' - %s:/home/node/clawd\n' "$CLAWDBOT_WORKSPACE_DIR" >>"$EXTRA_COMPOSE_FILE"
fi
for mount in "${mounts[@]}"; do
printf ' - %s\n' "$mount" >>"$EXTRA_COMPOSE_FILE"
done
cat >>"$EXTRA_COMPOSE_FILE" <<'YAML'
moltbot-cli:
volumes:
YAML
if [[ -n "$home_volume" ]]; then
printf ' - %s:/home/node\n' "$home_volume" >>"$EXTRA_COMPOSE_FILE"
printf ' - %s:/home/node/.clawdbot\n' "$CLAWDBOT_CONFIG_DIR" >>"$EXTRA_COMPOSE_FILE"
printf ' - %s:/home/node/clawd\n' "$CLAWDBOT_WORKSPACE_DIR" >>"$EXTRA_COMPOSE_FILE"
fi
for mount in "${mounts[@]}"; do
printf ' - %s\n' "$mount" >>"$EXTRA_COMPOSE_FILE"
done
if [[ -n "$home_volume" && "$home_volume" != *"/"* ]]; then
cat >>"$EXTRA_COMPOSE_FILE" <<YAML
volumes:
${home_volume}:
YAML
fi
}
VALID_MOUNTS=()
if [[ -n "$EXTRA_MOUNTS" ]]; then
IFS=',' read -r -a mounts <<<"$EXTRA_MOUNTS"
for mount in "${mounts[@]}"; do
mount="${mount#"${mount%%[![:space:]]*}"}"
mount="${mount%"${mount##*[![:space:]]}"}"
if [[ -n "$mount" ]]; then
VALID_MOUNTS+=("$mount")
fi
done
fi
if [[ -n "$HOME_VOLUME_NAME" || ${#VALID_MOUNTS[@]} -gt 0 ]]; then
write_extra_compose "$HOME_VOLUME_NAME" "${VALID_MOUNTS[@]}"
COMPOSE_FILES+=("$EXTRA_COMPOSE_FILE")
fi
for compose_file in "${COMPOSE_FILES[@]}"; do
COMPOSE_ARGS+=("-f" "$compose_file")
done
COMPOSE_HINT="docker compose"
for compose_file in "${COMPOSE_FILES[@]}"; do
COMPOSE_HINT+=" -f ${compose_file}"
done
ENV_FILE="$ROOT_DIR/.env"
upsert_env() {
local file="$1"
shift
local -a keys=("$@")
local tmp
tmp="$(mktemp)"
declare -A seen=()
if [[ -f "$file" ]]; then
while IFS= read -r line || [[ -n "$line" ]]; do
local key="${line%%=*}"
local replaced=false
for k in "${keys[@]}"; do
if [[ "$key" == "$k" ]]; then
printf '%s=%s\n' "$k" "${!k-}" >>"$tmp"
seen["$k"]=1
replaced=true
break
fi
done
if [[ "$replaced" == false ]]; then
printf '%s\n' "$line" >>"$tmp"
fi
done <"$file"
fi
for k in "${keys[@]}"; do
if [[ -z "${seen[$k]:-}" ]]; then
printf '%s=%s\n' "$k" "${!k-}" >>"$tmp"
fi
done
mv "$tmp" "$file"
}
upsert_env "$ENV_FILE" \
CLAWDBOT_CONFIG_DIR \
CLAWDBOT_WORKSPACE_DIR \
CLAWDBOT_GATEWAY_PORT \
CLAWDBOT_BRIDGE_PORT \
CLAWDBOT_GATEWAY_BIND \
CLAWDBOT_GATEWAY_TOKEN \
CLAWDBOT_IMAGE \
CLAWDBOT_EXTRA_MOUNTS \
CLAWDBOT_HOME_VOLUME \
CLAWDBOT_DOCKER_APT_PACKAGES
echo "==> Building Docker image: $IMAGE_NAME"
docker build \
--build-arg "CLAWDBOT_DOCKER_APT_PACKAGES=${CLAWDBOT_DOCKER_APT_PACKAGES}" \
-t "$IMAGE_NAME" \
-f "$ROOT_DIR/Dockerfile" \
"$ROOT_DIR"
echo ""
echo "==> Onboarding (interactive)"
echo "When prompted:"
echo " - Gateway bind: lan"
echo " - Gateway auth: token"
echo " - Gateway token: $CLAWDBOT_GATEWAY_TOKEN"
echo " - Tailscale exposure: Off"
echo " - Install Gateway daemon: No"
echo ""
docker compose "${COMPOSE_ARGS[@]}" run --rm moltbot-cli onboard --no-install-daemon
echo ""
echo "==> Provider setup (optional)"
echo "WhatsApp (QR):"
echo " ${COMPOSE_HINT} run --rm moltbot-cli providers login"
echo "Telegram (bot token):"
echo " ${COMPOSE_HINT} run --rm moltbot-cli providers add --provider telegram --token <token>"
echo "Discord (bot token):"
echo " ${COMPOSE_HINT} run --rm moltbot-cli providers add --provider discord --token <token>"
echo "Docs: https://docs.molt.bot/providers"
echo ""
echo "==> Starting gateway"
docker compose "${COMPOSE_ARGS[@]}" up -d moltbot-gateway
echo ""
echo "Gateway running with host port mapping."
echo "Access from tailnet devices via the host's tailnet IP."
echo "Config: $CLAWDBOT_CONFIG_DIR"
echo "Workspace: $CLAWDBOT_WORKSPACE_DIR"
echo "Token: $CLAWDBOT_GATEWAY_TOKEN"
echo ""
echo "Commands:"
echo " ${COMPOSE_HINT} logs -f moltbot-gateway"
echo " ${COMPOSE_HINT} exec moltbot-gateway node dist/index.js health --token \"$CLAWDBOT_GATEWAY_TOKEN\""