feat(claude_desktop): add opt-in cpu_limit and memory_limit options (#3180)

Supervisor exposes no CPU or memory limit for add-ons and `docker update`
is lost when the container is recreated, so apply the limits to a child
cgroup at every start.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
AlexandreandClaude Opus 5.5 authored and GitHub committed 2026-11-01 15:39:25 +01:00
1 parent d5afc983f3
commit b2ee4ceb3b
4 files changed
+75 -1

No files matched your search

+3
View File
@@ -1,4 +1,7 @@
## 07308545.23 (2026-11-01)
- Add the optional `cpu_limit` (cores) and `memory_limit` (for example `6G`) options, which cap the whole add-on and persist across restarts. Unset keeps the current unlimited behaviour
## 07308545.22 (2026-10-31)
- Update to latest version from aaddrick/claude-desktop-debian (changelog : https://github.com/aaddrick/claude-desktop-debian/releases)
- Upstream tag : v3.3.14+claude4.29.0
+10
View File
@@ -125,9 +125,19 @@ Git synchronization hooks. A repository is indexed only when it is listed in
| `enable_ha_api_helper` | `true` | Ship the `ha-cli` Core-API helper and add guidance so Claude can configure Home Assistant without a `/config` mount. |
| `additional_apps` | | Comma-separated Debian apt packages to install at startup. |
| `additional_pip` | | Comma-separated pip packages installed at startup (via `uv`). |
| `cpu_limit` | | Optional cap on the whole add-on, in CPU cores (for example `2` or `1.5`). Unset means unlimited. See [Limiting CPU and memory](#limiting-cpu-and-memory). |
| `memory_limit` | | Optional memory cap on the whole add-on, for example `6G` or `4096M`. Unset means unlimited. |
| `data_location` | `/data/data` | Persistent home directory for Claude and tooling. |
| `env_vars` | `[]` | Additional environment variables exported inside the container. |
### Limiting CPU and memory
Home Assistant has no CPU or memory setting for add-ons, and a manual `docker update` is lost when the add-on restarts. `cpu_limit` and `memory_limit` are applied at every start instead, to everything in the add-on: the desktop, the web interface and all Claude Code sessions with their builds and tests. Changing them requires an add-on restart.
- `cpu_limit` throttles without killing anything, so a low value also slows the web interface.
- `memory_limit` caps RAM, and allows the same amount again in swap (as `docker --memory` does). Beyond that, the kernel kills a process in the add-on, usually the largest, which can be a running session.
- Both need cgroup v2, which Home Assistant OS uses. If it is not available, the add-on logs a warning and starts without limits.
### Permission modes
```yaml
+3 -1
View File
@@ -145,7 +145,9 @@ schema:
cifsdomain: str?
cifspassword: str?
cifsusername: str?
cpu_limit: float(0.1,)?
localdisks: str?
memory_limit: match(^[1-9][0-9]*[MmGg]$)?
networkdisks: str?
github_email: str?
enable_ha_mcp: bool?
@@ -175,5 +177,5 @@ schema:
slug: claude_desktop
udev: true
url: https://github.com/alexbelgium/hassio-addons
version: "07308545.22"
version: "07308545.23"
video: true
@@ -0,0 +1,59 @@
#!/usr/bin/with-contenv bashio
# shellcheck shell=bash
set -e
# Apply the optional cpu_limit / memory_limit options.
#
# Supervisor has no CPU or memory key for add-ons, and `docker update` is lost when it recreates
# the container, so the limits are set on a child cgroup at every start. nsdelegate forbids
# writing cpu.max / memory.max on the container's own cgroup, and a cgroup can only delegate
# controllers once it holds no process, hence moving everything to the child first. This runs
# early because memory already charged stays with the parent.
cpu_limit=""
memory_limit=""
if bashio::config.has_value 'cpu_limit'; then
cpu_limit="$(bashio::config 'cpu_limit')"
fi
if bashio::config.has_value 'memory_limit'; then
memory_limit="$(bashio::config 'memory_limit')"
fi
if [[ -z "$cpu_limit" && -z "$memory_limit" ]]; then
exit 0
fi
cgroup="$(mktemp -d)"
if ! mount -t cgroup2 none "$cgroup"; then
bashio::log.warning "Could not mount cgroup2: cpu_limit and memory_limit are not applied"
rmdir "$cgroup"
exit 0
fi
mkdir -p "$cgroup/addon"
delegated=false
# Retry because processes forked during the move land in the parent and make the write fail
for _ in 1 2 3 4 5; do
xargs -rn1 < "$cgroup/cgroup.procs" > "$cgroup/addon/cgroup.procs" || true
if echo "+cpu +memory" 2> /dev/null > "$cgroup/cgroup.subtree_control"; then
delegated=true
break
fi
done
if [[ "$delegated" != true ]]; then
bashio::log.warning "Could not enable the cgroup v2 controllers: cpu_limit and memory_limit are not applied"
else
if [[ -n "$cpu_limit" ]]; then
awk -v cpus="$cpu_limit" 'BEGIN { printf "%.0f 100000\n", cpus * 100000 }' > "$cgroup/addon/cpu.max"
bashio::log.info "CPU limited to $cpu_limit core(s)"
fi
if [[ -n "$memory_limit" ]]; then
echo "$memory_limit" > "$cgroup/addon/memory.max"
# As with docker --memory: as much swap again, so idle sessions can still be swapped out
echo "$memory_limit" > "$cgroup/addon/memory.swap.max"
bashio::log.info "Memory limited to $memory_limit"
fi
fi
umount "$cgroup"
rmdir "$cgroup"