2026-06-02
Everyone reaches for nohup, tmux, or at when they want to background a job. Almost nobody reaches for the tool that systemd itself uses under the hood: systemd-run. It launches an arbitrary command as a transient service, scope, or timer — giving you journal logging, real cgroup-enforced resource caps, sandboxing, and calendar scheduling without ever writing a .service file.
The basic split: --scope runs the command inside your current terminal as the foreground process. --service (the default) forks a detached unit you talk to via journalctl/systemctl.
Capture stdout, stderr, and exit code in the journal, keyed by a unit name:
systemd-run --user --unit=nightly-ingest ./ingest.sh
journalctl --user -fu nightly-ingest.service
systemctl --user status nightly-ingest.service
Cap a runaway build at 4 GB and two cores — actual kernel enforcement, not ulimit wishful thinking:
systemd-run --user --scope \
-p MemoryMax=4G -p MemorySwapMax=0 \
-p CPUQuota=200% \
cargo build --release
Block until done and return the exit code (great in CI scripts):
systemd-run --user --wait --pipe --collect \
-p MemoryMax=2G ./flaky-converter input.bin
--pipe wires stdin/stdout through so it composes in pipelines. --collect garbage-collects the unit on exit so you don't accumulate failed-unit corpses.
Schedule a one-shot in 90 minutes, no at daemon needed:
systemd-run --user --on-active=90min \
--unit=cache-purge /usr/local/bin/purge.sh
A recurring timer with calendar syntax that beats cron's:
systemd-run --user --on-calendar="Mon..Fri 09:30" \
--unit=workday-report ./report.sh
Drop into a sandboxed shell — no network, private /tmp, read-only system, writable only where you say:
systemd-run --user --pty --same-dir \
-p PrivateNetwork=yes -p PrivateTmp=yes \
-p ProtectSystem=strict \
-p ReadWritePaths=$PWD \
bash
Inspect everything transient currently running:
systemctl --user list-units --type=service "run-*" "*.service"
systemd-cgls --user
systemd-cgtop
Why this beats the alternatives:
nohup gives you a runaway log file and zero resource control.tmux/screen keeps the process alive but can't cap memory or CPU.at can't repeat and offers no sandboxing.daemon-reload, and stick around forever.Gotchas worth knowing:
--user units die at logout unless you run loginctl enable-linger $USER once.--scope is tied to your shell; close the terminal and it's gone. Use --service for true background.-p StandardOutput=file:/var/log/foo.log.systemd.exec(5) documents: IOWeight, Nice, AmbientCapabilities, BindReadOnlyPaths, RuntimeMaxSec… the whole zoo.Once you internalize that "run a command" and "configure a systemd unit" are the same operation, you stop writing .service files for half the things you used to.
