Update ports and persist Homebrew installation across updates
This commit is contained in:
@@ -29,7 +29,7 @@ The add-on container runs three services:
|
|||||||
| Service | Port | Purpose |
|
| Service | Port | Purpose |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **OpenClaw Gateway** | 18789 (configurable) | The AI agent server — handles skills, chat, automations |
|
| **OpenClaw Gateway** | 18789 (configurable) | The AI agent server — handles skills, chat, automations |
|
||||||
| **nginx** (Ingress proxy) | 8099 (fixed) | Serves the landing page inside Home Assistant |
|
| **nginx** (Ingress proxy) | 48099 (fixed) | Serves the landing page inside Home Assistant |
|
||||||
| **ttyd** (Web terminal) | 7681 (configurable) | Provides a browser-based terminal for setup and management |
|
| **ttyd** (Web terminal) | 7681 (configurable) | Provides a browser-based terminal for setup and management |
|
||||||
|
|
||||||
When you open the add-on page in Home Assistant, nginx serves a landing page with:
|
When you open the add-on page in Home Assistant, nginx serves a landing page with:
|
||||||
@@ -46,6 +46,7 @@ When you open the add-on page in Home Assistant, nginx serves a landing page wit
|
|||||||
| `/config/.node_global/` | Yes | User-installed npm packages (skills installed via dashboard) |
|
| `/config/.node_global/` | Yes | User-installed npm packages (skills installed via dashboard) |
|
||||||
| `/config/secrets/` | Yes | Tokens (e.g., `homeassistant.token`) |
|
| `/config/secrets/` | Yes | Tokens (e.g., `homeassistant.token`) |
|
||||||
| `/config/keys/` | Yes | SSH keys (e.g., router SSH key) |
|
| `/config/keys/` | Yes | SSH keys (e.g., router SSH key) |
|
||||||
|
| `/config/.linuxbrew/` | Yes | Homebrew install and brew-installed CLI tools |
|
||||||
| `/usr/lib/node_modules/openclaw/` | No | OpenClaw installation (rebuilt with each image update) |
|
| `/usr/lib/node_modules/openclaw/` | No | OpenClaw installation (rebuilt with each image update) |
|
||||||
|
|
||||||
> **Important**: Everything under `/config/` persists across add-on updates. The container filesystem (`/usr/`, `/opt/`, etc.) is rebuilt each time the image changes.
|
> **Important**: Everything under `/config/` persists across add-on updates. The container filesystem (`/usr/`, `/opt/`, etc.) is rebuilt each time the image changes.
|
||||||
@@ -381,7 +382,7 @@ The connection details are also saved to `/config/CONNECTION_NOTES.txt` for refe
|
|||||||
| User-installed npm skills | `/config/.node_global/` | Yes |
|
| User-installed npm skills | `/config/.node_global/` | Yes |
|
||||||
| SSH keys | `/config/keys/` | Yes |
|
| SSH keys | `/config/keys/` | Yes |
|
||||||
| Tokens | `/config/secrets/` | Yes |
|
| Tokens | `/config/secrets/` | Yes |
|
||||||
| Homebrew-installed tools | `/home/linuxbrew/` | **No** — container filesystem |
|
| Homebrew & brew-installed tools | `/config/.linuxbrew/` | Yes (synced on startup) |
|
||||||
| OpenClaw binary | `/usr/lib/node_modules/openclaw/` | **No** — reinstalled from image |
|
| OpenClaw binary | `/usr/lib/node_modules/openclaw/` | **No** — reinstalled from image |
|
||||||
|
|
||||||
### How built-in skills work
|
### How built-in skills work
|
||||||
@@ -402,7 +403,13 @@ The add-on also configures `pnpm` global directory to persistent storage at `/co
|
|||||||
|
|
||||||
### Homebrew-installed tools
|
### Homebrew-installed tools
|
||||||
|
|
||||||
Some skills depend on CLI tools installed via Homebrew (e.g., `gemini`, `aider`). These are installed into `/home/linuxbrew/` which is **not persistent** — they are lost on image rebuild. Skills will typically reinstall their dependencies automatically when needed.
|
Homebrew (Linuxbrew) and all brew-installed CLI tools (e.g., `gemini`, `aider`, `gh`, `bw`) are now **persisted** across add-on updates. On each startup, the add-on:
|
||||||
|
|
||||||
|
1. Syncs the image's Homebrew install to `/config/.linuxbrew/`
|
||||||
|
2. Creates a symlink from `/home/linuxbrew/.linuxbrew/` to the persistent copy
|
||||||
|
3. On subsequent boots, only newer files are synced (user-installed packages are preserved)
|
||||||
|
|
||||||
|
This means `brew install` packages survive image rebuilds.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -443,7 +450,7 @@ Home Assistant checks for add-on updates automatically. When an update is availa
|
|||||||
**What happens during an update**:
|
**What happens during an update**:
|
||||||
- The container is destroyed and recreated from the new image
|
- The container is destroyed and recreated from the new image
|
||||||
- Everything under `/config/` is preserved (config, skills, workspace, keys)
|
- Everything under `/config/` is preserved (config, skills, workspace, keys)
|
||||||
- Homebrew packages are lost (reinstalled by skills on demand)
|
- Homebrew and brew-installed packages are preserved (synced to `/config/.linuxbrew/`)
|
||||||
- The OpenClaw binary is updated to the version in the new image
|
- The OpenClaw binary is updated to the version in the new image
|
||||||
|
|
||||||
### Checking your version
|
### Checking your version
|
||||||
@@ -490,9 +497,9 @@ Then restart the add-on. It will re-bootstrap a fresh configuration.
|
|||||||
|
|
||||||
Go to **Settings → Add-ons → OpenClaw Assistant → Log** tab. Logs show startup messages, errors, and service status.
|
Go to **Settings → Add-ons → OpenClaw Assistant → Log** tab. Logs show startup messages, errors, and service status.
|
||||||
|
|
||||||
### Port 8099 conflict (add-on page won't load)
|
### Port 48099 conflict (add-on page won't load)
|
||||||
|
|
||||||
**Symptom**: `bind() to 0.0.0.0:8099 failed (98: Address already in use)` in logs.
|
**Symptom**: `bind() to 0.0.0.0:48099 failed (98: Address already in use)` in logs.
|
||||||
|
|
||||||
**Cause**: A stale nginx process from a previous run is still holding the port. This can happen after a crash or unclean restart.
|
**Cause**: A stale nginx process from a previous run is still holding the port. This can happen after a crash or unclean restart.
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: OpenClaw Assistant
|
name: OpenClaw Assistant
|
||||||
version: "0.5.41"
|
version: "0.5.42"
|
||||||
slug: openclaw_assistant
|
slug: openclaw_assistant
|
||||||
description: Run OpenClaw Assistant (OpenClaw-compatible) as a Home Assistant add-on.
|
description: Run OpenClaw Assistant (OpenClaw-compatible) as a Home Assistant add-on.
|
||||||
url: https://github.com/techartdev/OpenClawHomeAssistant
|
url: https://github.com/techartdev/OpenClawHomeAssistant
|
||||||
@@ -16,7 +16,7 @@ host_network: true
|
|||||||
|
|
||||||
# Home Assistant Ingress (UI inside the add-on page)
|
# Home Assistant Ingress (UI inside the add-on page)
|
||||||
ingress: true
|
ingress: true
|
||||||
ingress_port: 8099
|
ingress_port: 48099
|
||||||
panel_title: OpenClaw Assistant
|
panel_title: OpenClaw Assistant
|
||||||
panel_icon: mdi:robot
|
panel_icon: mdi:robot
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ http {
|
|||||||
# Ingress note: keep redirects relative so we stay under HA Ingress.
|
# Ingress note: keep redirects relative so we stay under HA Ingress.
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 8099;
|
listen 48099;
|
||||||
|
|
||||||
# Web terminal (ttyd)
|
# Web terminal (ttyd)
|
||||||
# ttyd base-path is configured as /terminal (no trailing slash).
|
# ttyd base-path is configured as /terminal (no trailing slash).
|
||||||
|
|||||||
@@ -114,6 +114,45 @@ export PNPM_HOME="${PERSISTENT_NODE_GLOBAL}/pnpm"
|
|||||||
mkdir -p "$PNPM_HOME"
|
mkdir -p "$PNPM_HOME"
|
||||||
export PATH="${PNPM_HOME}:${PATH}"
|
export PATH="${PNPM_HOME}:${PATH}"
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Persist Linuxbrew/Homebrew across Docker image rebuilds
|
||||||
|
# Homebrew installs to /home/linuxbrew/.linuxbrew/ which is ephemeral.
|
||||||
|
# We sync it to /config/.linuxbrew and symlink back so brew-installed CLI
|
||||||
|
# tools (gog, gh, bw, etc.) survive add-on updates.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
IMAGE_BREW_DIR="/home/linuxbrew/.linuxbrew"
|
||||||
|
PERSISTENT_BREW_DIR="/config/.linuxbrew"
|
||||||
|
|
||||||
|
if [ -d "$IMAGE_BREW_DIR" ] && [ ! -L "$IMAGE_BREW_DIR" ]; then
|
||||||
|
# Image has a real Homebrew install — sync to persistent storage
|
||||||
|
if [ -d "$PERSISTENT_BREW_DIR" ]; then
|
||||||
|
# Persistent copy exists: sync new/updated files from image (upgrades),
|
||||||
|
# but preserve user-installed packages already in persistent storage.
|
||||||
|
if command -v rsync >/dev/null 2>&1; then
|
||||||
|
rsync -a --update "$IMAGE_BREW_DIR/" "$PERSISTENT_BREW_DIR/" 2>/dev/null || true
|
||||||
|
else
|
||||||
|
cp -ru "$IMAGE_BREW_DIR/"* "$PERSISTENT_BREW_DIR/" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
echo "INFO: Synced Homebrew updates to persistent storage"
|
||||||
|
else
|
||||||
|
# First time: copy entire Homebrew install to persistent storage
|
||||||
|
cp -a "$IMAGE_BREW_DIR" "$PERSISTENT_BREW_DIR" 2>/dev/null || true
|
||||||
|
echo "INFO: Copied Homebrew to persistent storage at $PERSISTENT_BREW_DIR"
|
||||||
|
fi
|
||||||
|
# Replace image dir with symlink to persistent copy
|
||||||
|
rm -rf "$IMAGE_BREW_DIR"
|
||||||
|
ln -sf "$PERSISTENT_BREW_DIR" "$IMAGE_BREW_DIR"
|
||||||
|
elif [ -L "$IMAGE_BREW_DIR" ]; then
|
||||||
|
echo "INFO: Homebrew already linked to persistent storage"
|
||||||
|
elif [ -d "$PERSISTENT_BREW_DIR" ]; then
|
||||||
|
# Image doesn't have Homebrew (failed install?) but persistent copy exists
|
||||||
|
mkdir -p "$(dirname "$IMAGE_BREW_DIR")"
|
||||||
|
ln -sf "$PERSISTENT_BREW_DIR" "$IMAGE_BREW_DIR"
|
||||||
|
echo "INFO: Restored Homebrew symlink from persistent storage"
|
||||||
|
else
|
||||||
|
echo "INFO: Homebrew not available (install may have failed during image build)"
|
||||||
|
fi
|
||||||
|
|
||||||
# Back-compat: some docs/scripts assume /data; point it at /config.
|
# Back-compat: some docs/scripts assume /data; point it at /config.
|
||||||
if [ ! -e /data ]; then
|
if [ ! -e /data ]; then
|
||||||
ln -s /config /data || true
|
ln -s /config /data || true
|
||||||
@@ -341,14 +380,14 @@ if [ -f "$NGINX_PID_FILE" ]; then
|
|||||||
fi
|
fi
|
||||||
rm -f "$NGINX_PID_FILE"
|
rm -f "$NGINX_PID_FILE"
|
||||||
fi
|
fi
|
||||||
# Also kill any orphaned nginx workers that might hold port 8099
|
# Also kill any orphaned nginx workers that might hold port 48099
|
||||||
if command -v pkill >/dev/null 2>&1; then
|
if command -v pkill >/dev/null 2>&1; then
|
||||||
pkill -f "nginx.*-c /etc/nginx/nginx.conf" 2>/dev/null || true
|
pkill -f "nginx.*-c /etc/nginx/nginx.conf" 2>/dev/null || true
|
||||||
sleep 1
|
sleep 1
|
||||||
fi
|
fi
|
||||||
# Verify port 8099 is actually free before proceeding
|
# Verify port 48099 is actually free before proceeding
|
||||||
if command -v ss >/dev/null 2>&1 && ss -tlnp 2>/dev/null | grep -q ':8099 '; then
|
if command -v ss >/dev/null 2>&1 && ss -tlnp 2>/dev/null | grep -q ':48099 '; then
|
||||||
echo "WARN: Port 8099 still in use after cleanup; nginx may fail to start"
|
echo "WARN: Port 48099 still in use after cleanup; nginx may fail to start"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Render nginx config from template.
|
# Render nginx config from template.
|
||||||
@@ -390,7 +429,7 @@ except Exception:
|
|||||||
pass
|
pass
|
||||||
PY
|
PY
|
||||||
|
|
||||||
echo "Starting ingress proxy (nginx) on :8099 ..."
|
echo "Starting ingress proxy (nginx) on :48099 ..."
|
||||||
nginx -g 'daemon off;' &
|
nginx -g 'daemon off;' &
|
||||||
NGINX_PID=$!
|
NGINX_PID=$!
|
||||||
sleep 1
|
sleep 1
|
||||||
|
|||||||
Reference in New Issue
Block a user