- Python 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .gitignore | ||
| LICENSE | ||
| README.md | ||
| scr | ||
| scr-logo.png | ||
| scrd | ||
| scrd.service | ||
A small service runner for a single Linux box. scrd is a daemon that systemd starts; it launches your services, restarts them when they crash, and keeps each one in its own tmux session. scr is the command line you use to control it.
scr register aire /srv/aire/main.py --runner uv --user bot --check http://127.0.0.1:8089/health
scr enable aire # start now and at boot
scr status
scr send aire say hello # type into the service's console
scr attach aire # open its tmux session (detach with Ctrl-b d)
It is one Python file for the daemon and one for the CLI, standard library only. It was written for a Raspberry Pi (Debian 13, Python 3.13, tmux 3.5a) and has been tested there only.
Features
- Restart with backoff. A crashed service restarts after 1 s, 2 s, 4 s ... up to 60 s. Five crashes in a row mark it
failedand scr stops trying. A run longer than 30 s resets the counter. - Dependencies.
--after caddystarts the other service first and waits until it is running (and its check passes). Stopping a service stops everything that depends on it. - Health checks.
--check port:8090,--check http://127.0.0.1:8089/health,--check unit:caddy.serviceor--check proc:<user>:<pattern>.scr check <service> -jprints JSON and exits 0 only if the service is running and healthy. - A tmux session per service.
scr attachandscr sendwork on every service that scr runs itself. There is one tmux server per service user. - Runs services as other users.
--userdrops privileges before the service starts. - Two sockets, so ordinary users can look and touch their own services but cannot change the setup (see Security).
Requirements
Linux, Python 3 (tested on 3.13), tmux, and systemd (only to start scrd at boot and for the systemd runner). Run scrd as root if you want --user to work; as a normal user it can only run services as that same user.
Install
sudo install -m 755 scrd /usr/local/bin/scrd
sudo install -m 755 scr /usr/local/bin/scr
sudo install -m 644 scrd.service /etc/systemd/system/scrd.service
sudo systemctl daemon-reload
sudo systemctl enable --now scrd
scrd.service sets a PATH. Edit it so it includes the folders that hold the programs your services need (for example ~/.local/bin for uv).
Files:
| What | Where |
|---|---|
| Service list | /etc/scr/services.json (written on every change) |
| Logs | /var/log/scr/<name>.log |
| Sockets | /run/scr/root.sock, /run/scr/user.sock |
| tmux sockets | /run/scr/tmux/<user>/tmux.sock |
The paths can be changed with the environment variables SCR_DIR, SCR_LOG and SCR_RUN (handy for testing; unix socket paths must stay under about 100 characters).
Commands
| Command | Does |
|---|---|
scr register <name> <target> [options] |
add a service (root only) |
scr enable <service> / disable |
start now and at boot / stop and don't start at boot |
scr start / stop / restart <service> |
control a service |
scr status [-j] |
table of all services, or JSON |
scr check <service> [-j] |
state and health of one service |
scr logs <service> [-n N] |
saved log plus the live screen |
scr attach <service> |
open the tmux session |
scr send <service> <text...> [--no-enter] |
type text (and Enter) into the service |
scr remove <service> |
stop and delete it (root only) |
<service> is a name or an ID. IDs are four digits (0001, 0002 ...), assigned in order, or chosen with --id. They are never reused.
Register options
--runner, --user (default root), --after SERVICE (repeatable), --check SPEC, --args "...", --cwd DIR, --label TEXT (display name), --stop-timeout SECONDS (grace after SIGTERM before SIGKILL, default 15), --id, plus --task (npm), --venv (python), --mem (java).
Runners
| Runner | Runs |
|---|---|
exec (default) |
the target through /bin/sh -c |
uv |
uv run <target> in the target's folder |
npm |
npm run <--task> |
python |
python3 <target>, or <--venv>/bin/python |
node |
node <target> |
java |
java [-Xmx<--mem>] -jar <target> |
cargo |
cargo run --release in the target folder |
systemd |
wraps an existing unit: scr starts, stops and watches it, systemd restarts it (no tmux session) |
external |
not run by scr, only reported from its --check |
derive |
started by another service (the target is that parent's name or ID); scr never starts it, and it counts as running only while the parent runs and its check passes |
--args is appended after the program (after -- for npm and cargo).
Security model
/run/scr/root.sockis mode 600 and root only. It accepts every command./run/scr/user.sockis open to all local users, and checks who is calling withSO_PEERCRED. It allowsstatusandcheckfor every service, andlogs,start,stop,restart,attachandsendonly for services that run as the caller. It cannot register, enable, disable or remove anything, so it cannot be used to run code as another user.- Services run in tmux sessions owned by their own user. Anyone who can attach to a session can type into it, so only the owning user and root can.
scr sendtypes into the console exactly as given. Treat it like a shell for whatever the service is.
Limits
- State such as restart counts lives in memory only. After a reboot
scrdreads the service list and starts the enabled ones. - Stopping
scrdstops the services it started. - The log file is written when a process exits or is stopped (from the tmux scrollback, up to 20 000 lines), not continuously. Use
scr logsorscr attachfor live output. - Not tested:
scr attachfrom a real terminal on every setup, and very large numbers of services.
Contributing
Issues and pull requests welcome. There is no test suite yet; the daemon is written to be tried with dummy services under a throwaway SCR_DIR/SCR_RUN/SCR_LOG.
License
MIT, see LICENSE. Copyright (c) 2026 Nytrix Labs.
