Operations

Run as a system service

Manage Trestle as a systemd system service with trestle service, running under a dedicated unprivileged account that boots independently of any user login.

Machine service (supported)

Run Trestle in the foreground with trestle or trestle serve. To keep it running unattended and boot-safely on a systemd host, install the machine service. The first install requires root and creates the dedicated unprivileged trestle account (nologin, no home), the /var/lib/trestle data directory, the unit, and (optionally) the protected /etc/trestle/trestle.env configuration file:

sudo trestle service install
# Optional install flags:
#   --host HOST
#   --port PORT
#   --listen HOST:PORT
#   --data-dir DIRECTORY
#   --env-file FILE

sudo trestle service start
sudo trestle service stop
sudo trestle service restart
trestle service status
trestle service logs
trestle service logs --follow
sudo trestle service uninstall
  • The system unit is written to /etc/systemd/system/trestle.service and managed with systemctl and journalctl --unit trestle.service.
  • It starts at boot with WantedBy=multi-user.target and does not depend on any user login or on systemd lingering.
  • The binary is installed at /usr/local/bin/trestle. status reports enabled/running state, PID, version, listen address and a live health check of the public GET /system/health endpoint, and exits nonzero when the service is failed or missing.
  • Uninstall removes the service registration and preserves Trestle data and configuration.
  • service install refuses empty, relative or transient executable paths, writes the unit atomically with a versioned SHA-256 integrity header, and never overwrites or removes a foreign or hand-modified unit.
  • It records the canonical --host/--port listener (default 127.0.0.1:7333) and --data-dir (default /var/lib/trestle, created owner-only) in the unit, so the recorded listener is the runtime listener across restart and reboot.
  • The legacy single-address --listen form (and TRESTLE_LISTEN) is retained for compatibility; it cannot be combined with --host/--port, and it records a bootstrap listener resolved through the durable configuration.
  • Existing old units keep their recorded listener until they are reinstalled.

For example, install the machine service bound explicitly to a loopback port behind a reverse proxy:

sudo trestle service install --host 127.0.0.1 --port 7403

  • Install is a transaction.
  • Trestle preserves the prior managed unit bytes and inspects the prior systemd enablement and activity before mutating anything.
  • Only exactly-recreatable states are accepted - enablement enabled, enabled-runtime, disabled and activity active, inactive - and masked, static, linked, generated, transient, failed, reloading and other non-restorable states are refused before any file or lifecycle change; unmask or stop the service first.
  • On a failure, rollback restores the prior unit bytes and reproduces the exact prior enablement and activity states, distinguishing persistent from runtime enablement so runtime-only enablement is restored without leaving a persistent link; a failed fresh install is stopped and disabled while the unit is still loaded, then removed and systemd is reloaded.
  • A changed binary with an unchanged unit is recognised as a changed installation and the service is restarted.
  • A byte-identical unit and binary already enabled and active is a genuine no-op.
  • The complete lifecycle family matches the ecosystem convention: service install|uninstall|start|stop|restart|status|enable|disable|logs|update|rollback. service update ARTIFACT SHA256 replaces the binary with a checksum-verified artifact, preserves the prior running/stopped state and enablement, and retains rollback metadata; service rollback restores the previous version and its operational state.
  • Because the machine service does not inherit your shell environment, provide the remaining TRESTLE_* configuration (PostgreSQL TRESTLE_DATABASE_URL, S3/AWS credentials, trusted proxies, timeouts) through a root-owned environment file:
sudo install -m 0600 /dev/null /etc/trestle/trestle.env
# Edit as root. Common settings:
#   TRESTLE_HOST=127.0.0.1
#   TRESTLE_PORT=7333
#   TRESTLE_DATABASE_URL=postgres://...
#   TRESTLE_S3_*=...
sudo trestle service install --env-file /etc/trestle/trestle.env

The machine configuration file must be an absolute, regular, non-symlink file with exactly 0600 permissions, owned by root:root; systemd reads it via EnvironmentFile= before the process drops to User=trestle, so the service account cannot rewrite its own machine configuration. Secret values are never copied into the unit or printed. The recorded environment file is revalidated before start, restart and status; stop, logs and uninstall remain available even if it is missing. Changing the file takes effect on trestle service restart.

The generated unit applies baseline hardening: NoNewPrivileges, PrivateTmp, ProtectSystem=strict, ProtectHome=true and ReadWritePaths scoped to /var/lib/trestle. Repeated installs preserve the installed listen, data directory and environment file unless a flag is given explicitly; a repeat install restarts the service so changed configuration takes effect.

Boot and recovery

The machine service starts at boot without any user login (systemd system unit, WantedBy=multi-user.target); no systemd lingering is required, Trestle does not run systemctl enable-linger, and it never enables lingering automatically. Confirm enablement structurally:

systemctl is-enabled trestle.service
grep -q 'WantedBy=multi-user.target' /etc/systemd/system/trestle.service

The unit records the absolute path of the trestle executable at install time; moving or deleting that binary breaks the service until you reinstall.

External paths and the service account

The trestle account reads and writes /var/lib/trestle (the SQLite database, uploaded files, backups and the webhook signing key). PostgreSQL and S3 are remote and carry no local filesystem requirement. Development-only path overrides that refer outside /var/lib/trestle (such as TRESTLE_STATIC_DIR) are blocked by ProtectSystem=strict/ProtectHome=true; if one is configured, startup fails with a diagnostic rather than silently running a degraded dashboard.

Shutdown

SIGTERM marks readiness false, stops background workers and drains HTTP work within the configured shutdown timeout.