Files
computer-playbook/roles/web-app-openclaw/templates/compose.yml.j2
kevinveenbirkenbachandClaude Opus 5 96408f23a0 perf(healthcheck): let the start window outlast a real boot
start_interval is the cadence while start_period runs, and only while it runs.
Once the window closes the probe falls back to interval, so a service that
becomes ready just after it pays a whole tick anyway and the cadence never
reached it. Six of the seven flavors were shaped that way, Curl worst: a thirty
second window against a one minute interval.

Measured across 151 converge waits in four job logs, 63 sit exactly on a tick
boundary, 36 in the thirty second step and 27 in the sixty second one.
Attributed by application, web-app-seaweedfs is the largest at 1501s over
sixteen waits with a median of 57.7s: it probes with curl and fell straight into
the gap. web-app-zammad probes with connect, whose window is fifteen minutes,
and its 154s median is real boot time with nothing to recover.

So the window moves to ten minutes and the steady state interval to two, and the
flavor overrides that sat below them go: Curl, Wget and Connect lose interval
1m, Http its twenty second window, Nc its ten. Connect keeps fifteen minutes.

Neither number costs anything while a container works. start_period delays
nothing, it only stops failures counting, and interval no longer touches
convergence at all since that hangs on the window alone; it is steady state
load, which drops from 120 probes an hour per service to 30.

Forty six role files follow. Twenty seven declarations go that repeat the new
default or undercut it, an undercut window being the defect itself. Eight
windows longer than ten minutes keep their value and now say why. The literal
blocks and the three Dockerfiles this repository builds spell every timing out,
because outside the lookup an omission takes docker's value and not the
platform's.

A fourth lint rule keeps the shape: start_period must outlast one interval,
checked against the effective values, so raising interval without the window
cannot quietly bring the cliff back.

Two faults in the lint surfaced while building it and are fixed here. It
compared timings as strings, so 600s and 10m read as different values, and it
matched the healthcheck key exactly, so a line carrying a trailing nocheck was
skipped whole, which is how the two swarm-init partials went unexamined.

One regression, bounded. A container that starts and never turns healthy is
neither rejected nor errored, so stack_ready.sh only calls it fatal after three
killed tasks, and each cycle now spans the longer window. That reaches twenty
minutes rather than six, capped there by swarm_stack_converge_retries at 600
with a two second delay, and it lands only on runs that were failing anyway.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 21:54:26 +02:00

33 lines
1.3 KiB
Django/Jinja

{% include 'roles/sys-svc-compose/templates/base.yml.j2' %}
{% set service_name = 'openclaw' %}
{{ service_name }}:
{% include 'roles/sys-svc-container/templates/base.yml.j2' %}
{{ lookup('container_image', application_id, 'openclaw') }}
{{ lookup('compose_only', 'container_name', OPENCLAW_CONTAINER) }}
{% if 'svc-ai-robot' in group_names %}
devices:
{% for device in ROBOT_DEVICE_ALLOWLIST %}
- {{ device }}
{% endfor %}
{% elif lookup('config', application_id, 'services.kata.enabled') | bool %}
{{ lookup('compose_only', 'runtime', SANDBOX_RUNTIME) }}
{% endif %}
user: "0:0"
command: ["node", "dist/index.js", "gateway", "run", "--bind", "lan", "--port", "{{ OPENCLAW_PORT_INTERNAL }}"]
healthcheck:
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:{{ OPENCLAW_PORT_INTERNAL }}/healthz').then((r)=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
interval: 2m
timeout: 5s
retries: 5
start_period: 10m
start_interval: 5s
{{ lookup('container_ports', ['openclaw', 'http', DOCKER_BIND_HOST]) | indent(4) }}
{{ lookup('container_volumes', service_name) | indent(4) }}
{{ lookup('container_networks') | indent(4) }}
{{ lookup('container_depends_on', application_id) | indent(4) }}
{{ lookup('compose_networks') }}
{{ lookup('compose_volumes') }}