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.serviceand managed withsystemctlandjournalctl --unit trestle.service. - It starts at boot with
WantedBy=multi-user.targetand does not depend on any user login or on systemd lingering. - The binary is installed at
/usr/local/bin/trestle.statusreports enabled/running state, PID, version, listen address and a live health check of the publicGET /system/healthendpoint, and exits nonzero when the service is failed or missing. - Uninstall removes the service registration and preserves Trestle data and configuration.
service installrefuses 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/--portlistener (default127.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
--listenform (andTRESTLE_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,disabledand activityactive,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 SHA256replaces the binary with a checksum-verified artifact, preserves the prior running/stopped state and enablement, and retains rollback metadata;service rollbackrestores the previous version and its operational state. - Because the machine service does not inherit your shell environment, provide the remaining
TRESTLE_*configuration (PostgreSQLTRESTLE_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.