urr — a tiny TCP daemon for remotely rebooting (and checking on) a Linux box,
even when its storage has gone bad.
The name is short for ultima ratio — the last resort. On a machine with a
dying HDD, a normal reboot or systemctl reboot can hang forever waiting to
flush a disk that will never respond, leaving you with a host you can reach but
can't recycle. urr exposes a couple of "don't sync, just go" reboot paths
(syscall_nosync, sysrqb) that force an immediate restart, so you can
power-cycle the box over the network instead of walking to it.
It's a single static binary (~2.5 MB, no runtime deps) run as a systemd service.
⚠️ Security: the shared secret is sent in plaintext over TCP, andINFOneeds no auth at all. Only expose this on a trusted/management network (or over a VPN / SSH tunnel) and firewall the port. See Security model.
No build toolchain required — the installer downloads a prebuilt binary from the
latest GitHub release.
Needs Linux + systemd + root and curl (or wget).
curl -fsSL https://raw.githubusercontent.com/andrianbdn/urr-reboot/main/install.sh \
| sudo REBOOT_SECRET="choose-a-strong-secret" bashDon't want to pick a secret? Omit it and one is generated and printed once:
curl -fsSL https://raw.githubusercontent.com/andrianbdn/urr-reboot/main/install.sh | sudo bashThe installer:
- detects your CPU arch (
amd64/arm64/386) and downloads that binary, - verifies it against the release
SHA256SUMS, - installs it to
/usr/local/sbin/urr, - writes the secret to
/etc/urr.env(mode600, root-only), - installs + enables + starts the
urr.servicesystemd unit, - verifies the service came up.
Pin a specific release with URR_VERSION:
curl -fsSL .../install.sh | sudo URR_VERSION=v1.0.0 REBOOT_SECRET=… bashOne newline-terminated command per TCP connection on port 987:
| Command | Auth | Reply |
|---|---|---|
INFO |
none | uptime=… loadavg=… … … |
REBOOT:<method>:<secret> |
secret | ok or error: … |
| Method | What it does | Syncs disk? | Use when… |
|---|---|---|---|
systemd |
systemctl reboot --force --no-wall |
yes | the box is basically healthy |
syscall |
sync(2) then reboot(2) RESTART |
yes | systemd is wedged but the disk still works |
syscall_nosync |
reboot(2) RESTART, no sync |
no | the disk is hung and you accept data loss |
sysrqb |
echo b > /proc/sysrq-trigger (immediate hard reset) |
no | last resort — instant, like the reset pin |
syscall_nosync and sysrqb skip flushing to disk on purpose. That's the whole
point on a box with failing storage, but it means unsaved data is lost.
Reach for systemd/syscall first; drop to the nosync paths only when a clean
reboot hangs.
sysrqbwrites1to/proc/sys/kernel/sysrqfirst to make sure SysRq is enabled, then triggers the reboot — so it works even if SysRq was switched off. (Writes to/proc/sysrq-triggeralready bypass that mask on most kernels; enabling it first just makes it deterministic. Both are procfs writes, unaffected by a wedged disk.)
From another machine (replace HOST):
# Check it's alive (no secret required)
printf 'INFO\n' | nc HOST 987
# -> uptime=3h12m4s loadavg=0.08 0.03 0.01
# Clean reboot
printf 'REBOOT:systemd:%s\n' "$REBOOT_SECRET" | nc HOST 987
# -> ok
# Disk is hung — force it without syncing
printf 'REBOOT:syscall_nosync:%s\n' "$REBOOT_SECRET" | nc HOST 987
# Nuclear option (immediate, no sync)
printf 'REBOOT:sysrqb:%s\n' "$REBOOT_SECRET" | nc HOST 987Any TCP client works — nc/ncat, socat - TCP:HOST:987, even a raw
/dev/tcp redirect in bash. Each connection has a 10-second deadline.
systemctl status urr
journalctl -u urr -f # watch reboot requests as they arrive
systemctl restart urr # e.g. after changing the secret in /etc/urr.envTo change the secret: edit /etc/urr.env, then systemctl restart urr.
curl -fsSL https://raw.githubusercontent.com/andrianbdn/urr-reboot/main/uninstall.sh | sudo bash…or, from a checkout: sudo ./uninstall.sh. Removes the unit, binary, and
/etc/urr.env.
This daemon can power-cycle the machine, so treat the port as sensitive:
-
Plaintext secret.
REBOOT:…:<secret>travels unencrypted. Anyone who can sniff the connection sees it. Use it only over trusted links (a management VLAN, WireGuard/VPN, or an SSH tunnel). -
Unauthenticated
INFO. Uptime/load is exposed to anyone who can connect. -
No rate limiting / lockout. Don't expose port 987 to the public internet. Restrict it at the firewall, e.g.:
# allow only the management subnet to reach urr iptables -A INPUT -p tcp --dport 987 -s 10.0.0.0/24 -j ACCEPT iptables -A INPUT -p tcp --dport 987 -j DROP -
The secret lives in
/etc/urr.env(mode600), not baked into the systemd unit, so it stays out of world-readable/etc/systemd/system.
You don't need this to install — releases ship prebuilt binaries — but:
make build # native build -> ./urr
make build-all # cross-compile amd64/arm64/386 -> dist/
make dist # build-all + unit + SHA256SUMS (what a release contains)
make vet # go vetRequires a Go toolchain (the daemon is Linux-only; //go:build linux).
.github/workflows/ci.yml— on every push/PR:go vet+ cross-compile all three targets..github/workflows/release.yml— on av*tag: buildsurr-linux-{amd64, arm64,386}, copiesurr.service, generatesSHA256SUMS, and publishes a GitHub release with those assets (the exact files the installer pulls).
Cut a release:
git tag v1.0.0
git push origin v1.0.0urr-reboot/
├── main.go # the daemon (Linux-only; //go:build linux)
├── go.mod / go.sum # module + pinned golang.org/x/sys
├── urr.service # systemd unit (shipped as a release asset)
├── install.sh # pipeable installer (downloads from releases)
├── uninstall.sh # tear down
├── Makefile # build / build-all / dist / vet
├── .github/workflows/ci.yml # vet + cross-compile
├── .github/workflows/release.yml# build + publish release on tag
└── README.md