The VPS runs the public API and the collection scheduler as two systemd
services on the same host. .ops/deploy.sh deploy is unchanged: it stops,
switches and restarts eden-api, and the unit dependencies below carry
eden-scheduler along.
| Unit | Role | Memory cap |
|---|---|---|
eden-api.service |
uvicorn, public API, pilot usage recording (SCHEDULER_ENABLED=false) |
400M |
eden-scheduler.service |
python -m app.scheduler, same jobs and locks as before |
600M |
eden-api-watchdog.timer |
every 30 s: kills eden-api when loopback /internal/health fails twice |
– |
eden-scheduler is PartOf= and After= eden-api, and eden-api Wants=
eden-scheduler, so every systemctl stop|start|restart eden-api in the deploy
and schema-finalization paths applies to both, in the right order. A scheduler
crash or memory-cap kill restarts only the scheduler.
The files ship with every release under api/deploy/. Install or refresh them:
cd /opt/eden/current/api/deploy
install -o root -g eden -m 640 systemd/api.env /opt/eden/shared/api.env
install -d -m 755 /etc/systemd/system/eden-api.service.d
install -m 644 systemd/eden-api.service.d/*.conf /etc/systemd/system/eden-api.service.d/
install -m 644 systemd/eden-scheduler.service systemd/eden-api-watchdog.service systemd/eden-api-watchdog.timer /etc/systemd/system/
install -m 755 bin/eden-api-watchdog /usr/local/sbin/eden-api-watchdog
systemctl daemon-reload
systemctl enable --now eden-api-watchdog.timer eden-scheduler.service
systemctl restart eden-api # applies api.env; restarts eden-scheduler via PartOfCheck: systemctl show eden-scheduler -p PartOf prints eden-api.service,
curl -s 127.0.0.1:8000/internal/readiness shows "scheduler_enabled":false,
and journalctl -u eden-scheduler shows scheduler_process_started.
eden-phase1-soak.timer and eden-phase2-soak.timer keep sampling every five
minutes; each sample now records eden-scheduler next to the required services
and derives scheduler_enabled from the API readiness flag plus the service
state. Both evaluators restart their window whenever the release symlink
changes, so a deploy resets the 24-hour and 7-day clocks.
The Phase 1 gate needs probes from a scheduler-off phase inside the same window. With the split that phase is "scheduler service stopped, API serving":
systemctl stop eden-scheduler # eden-api keeps serving
systemd-run --wait --pipe --collect --uid=eden --gid=eden \
-p WorkingDirectory=/opt/eden/current/api -p EnvironmentFile=/opt/eden/shared/.env \
-p "UnsetEnvironment=MIGRATION_DB_USER MIGRATION_DB_PASSWORD VPS_PASSWORD" \
/opt/eden/current/api/.venv/bin/python -m app.operations phase1-soak baseline --iterations 20
systemctl start eden-schedulerRun it right after a timer tick so the two-minute gap holds no timer sample. A
systemctl stop/start pair does not count as a restart, but a Phase 2 sample
taken while the service is stopped is a scheduler_disabled violation that
stays in the sliding window for seven days.
systemctl disable --now eden-scheduler.service
rm -f /etc/systemd/system/eden-api.service.d/30-scheduler-split.conf /opt/eden/shared/api.env
sed -i 's/^MemoryMax=.*/MemoryMax=900M/' /etc/systemd/system/eden-api.service.d/20-memory-guard.conf
systemctl daemon-reload && systemctl restart eden-api