systemd¶
The standalone binary can run as an unprivileged systemd service with delegated CPU and memory controllers for native application limits.
Install the unit¶
Use the reference
shinyhub.service
as a starting point:
sudo install -m 0755 shinyhub /usr/local/bin/shinyhub
sudo install -m 0644 deploy/systemd/shinyhub.service /etc/systemd/system/shinyhub.service
sudo systemctl daemon-reload
sudo systemctl enable --now shinyhub
Create the service user, configuration, and data directories referenced by the
unit before starting it. Store auth.secret outside the repository with owner-
only permissions.
Private service configuration¶
The unit is normally 0644; keep secret values out of Environment= lines
and drop-ins. Use auth.secret_file: /etc/shinyhub/auth.secret in
shinyhub.yaml for the existing root secret. The secret file must be readable
by the service user and have mode 0600; ShinyHub rejects group- or
world-readable files. Moving the existing value is not a rotation. Follow
secret rotation before replacing it, because it also
encrypts persistent app secrets.
Keep shinyhub.yaml owned by shinyhub:shinyhub with mode 0600 when it
contains proxy, OAuth, deploy, or database credentials. For settings supplied
through environment variables, create a root-owned /etc/shinyhub/shinyhub.env
with mode 0600 and add the following inside [Service] in the unit or a
drop-in:
Use KEY=value lines without export. Systemd's system service manager reads
the file before dropping privileges; the path can appear in the public unit,
but the values should not. Keep the root secret in its secret file rather
than this environment file. Do not print secret values in diagnostics or
commit private files. Reload systemd after changing the unit, and restart
the service to apply changed environment-file values.
These permissions protect against other local users, not native replicas
running as the same UID. Native apps can read other replicas' environments
and server-readable files. Use a separate runtime boundary for app code you
do not trust; see native isolation. The shipped hardening
and cgroup delegation are not per-app user isolation. Do not enable
ProtectControlGroups or ProtectSystem=strict blindly: the native runtime
needs delegated cgroups and writable app data.
Reverse proxy¶
Keep the ShinyHub listener on loopback and terminate HTTPS with Caddy, nginx, or
another reverse proxy. Forward both base_url and app_origin hostnames when a
dedicated application origin is configured.
Upgrades¶
The reference unit supports listener handoff on SIGHUP. Follow the
zero-downtime upgrade guide to replace the
binary while existing HTTP and WebSocket connections drain normally.
The unit sets RestartPreventExitStatus=7. Exit 7 means the database was
migrated by a newer build, which this binary refuses to serve; retrying cannot
fix it, so systemd stops and the unit shows failed instead of looping in
activating (auto-restart). If you copy the unit rather than installing it,
keep that line, or a botched downgrade looks healthy to monitoring while the
service is down. journalctl -u shinyhub shows the two versions involved.
Before applying pending migrations the server writes a pre-migration snapshot of
the SQLite database beside it; see
Schema migrations on startup.
Those files are pruned automatically down to a configurable retention count
(default 5), so include at most that many full-size database copies in
whatever disk-space policy covers /var/lib/shinyhub.