Service management (rc.d)

Until this round the gateway ran under nohup from the shell that last built it — fine on a bench box, awkward across reboots. An rc.d script and a matching install helper now put e2b-compat on the same footing as any other FreeBSD service: a rc.conf knob enables it, service e2b-compat start starts it, daemon(8) supervises and restarts it on crash, and the log lands in a known place. Nothing exotic; the point is to stop reaching for ps and a fresh nohup every time an operator wants the gateway up.

The rc.d surface

The script lives at tools/rc.d/e2b-compat in this repo and gets installed to /usr/local/etc/rc.d/e2b-compat. The wire format is stock FreeBSD: . /etc/rc.subr, PROVIDE: e2b_compat, REQUIRE: NETWORKING FILESYSTEMS LOGIN, and run_rc_command “$1” at the bottom. service e2b-compat {start,stop,restart,status} behave exactly as you’d expect from any base-system service.

The actual supervision is delegated to daemon(8):

/usr/bin/env $ENV /usr/sbin/daemon \
  -p /var/run/e2b_compat.pid \
  -P /var/run/e2b_compat.supervisor.pid \
  -r -R 2 \
  -f -o /var/log/e2b-compat.log \
  -T e2b_compat \
  /usr/local/bin/e2b-compat --listen ... --zfs-pool ... ...

-r -R 2 respawns the child two seconds after a crash — cheap crash recovery without standing up a supervisor tree. -f -o appends the child’s stdout/stderr to the log file, which newsyslog(8) can rotate. -T e2b_compat tags the lines into syslog too so boot-time failures show up in /var/log/messages even before the logfile exists. The two pidfiles split the roles on purpose: pidfile holds the child pid so rc.subr’s service status grep-for-procname path works, and supervisor_pidfile lets a clean stop reach the supervisor first so -r doesn’t respawn the child we’re trying to kill.

rc.conf knobs

The script honours the usual <name>_enable + per-variable overrides convention. Defaults match honor’s current layout; override in /etc/rc.conf or /etc/rc.conf.d/e2b-compat.

variabledefaultnote
e2b_compat_enableNOSet to YES to bring the service up at boot and allow service e2b-compat start without onestart.
e2b_compat_listen127.0.0.1:3000The SDK’s default; keep bound to loopback and front with an SSH tunnel or a proper proxy for anything reachable off-box.
e2b_compat_zfs_poolzroot/jailsParent dataset for per-sandbox clones; must exist before first start.
e2b_compat_snapshotzroot/jails/_template@baseThe @base snapshot cloned for the empty-templateID path. Bump to the DNS-aware snapshot (…@base-dns-*) on hosts with per-jail resolv.conf plumbing.
e2b_compat_jails_root/jailsMountpoint of the pool — where e2b-<id> clones land.
e2b_compat_templates_root/jailsWhere the registry scans for <name>-template directories at startup and on POST /templates/reload.
e2b_compat_ui_dir/usr/local/share/e2b-compat/uiStatic SPA served under /ui/. The install helper copies e2b-compat/ui/ here so the deployed gateway’s assets live in the base filesystem layout, not /tmp.
e2b_compat_otel_endpointemptyPasses through as OTEL_EXPORTER_OTLP_ENDPOINT. Empty string → the tonic exporter isn’t built (stderr logging only). See tracing.
e2b_compat_logfile/var/log/e2b-compat.logAppend-only target for the child’s stdout + stderr. newsyslog.conf can rotate on a cron just like any other base-system log.

Two more knobs (e2b_compat_otel_service and e2b_compat_env) let an operator set OTEL_SERVICE_NAME and arbitrary extra KEY=VALUE pairs without editing the script; e2b_compat_restart_delay (default 2) controls daemon(8)‘s respawn delay.

Installing on honor

tools/install-e2b-compat-service.sh handles the full copy: the binary to /usr/local/bin/e2b-compat, the UI tree to /usr/local/share/e2b-compat/ui/, and the rc.d script to /usr/local/etc/rc.d/e2b-compat. It’s idempotent — rerun to refresh any of those after a fresh cargo build —release on the honor side. If e2b_compat_enable= isn’t already in /etc/rc.conf it appends e2b_compat_enable=“YES”; otherwise it leaves the existing line alone.

From the dev machine:

mise run e2b:install-service-honor

That task chains e2b:sync-honore2b:build-honor, scps the rc.d script + install helper to honor’s /tmp, and invokes the installer under sudo. The service itself isn’t started — the operator decides when to cut over, usually with a quick service e2b-compat stop + start roundtrip to avoid dropping in-flight sandbox API calls.

Day-to-day

sudo service e2b-compat start
sudo service e2b-compat status     # expects a PID
sudo service e2b-compat restart    # stops, supervisor exits, restart
sudo service e2b-compat stop
tail -f /var/log/e2b-compat.log    # the log daemon(8) is appending to

A clean stop kills the supervisor first so -r doesn’t respawn the child mid-teardown. That’s the reason the rc.d script tracks two pidfiles — rc.subr’s stop_postcmd uses supervisor_pidfile to reach daemon(8) directly.

When it doesn’t start

Files