Bare Metal Install - Kheopsian/Hydranos GitHub Wiki
Applies to Hydranos 4.4.
This page is for you if you want Hydranos on a Linux server or seedbox without Docker, managed by systemd. By the end you will have downloaded and verified the release, installed it with the bundled script or by hand, run it under the shipped systemd unit, and know how to update and uninstall.
Releases ship one static (musl) tarball per architecture, each with a SHA-256 file beside it:
Machine (uname -m) |
Asset |
|---|---|
x86_64 |
hydranos-<tag>-linux-amd64.tar.gz + .sha256
|
aarch64 |
hydranos-<tag>-linux-arm64.tar.gz + .sha256
|
<tag> keeps its v (v4.4.0). Download, check, unpack:
VER=v4.4.1
ARCH=amd64 # arm64 on aarch64
curl -fLO https://github.com/Kheopsian/Hydranos/releases/download/$VER/hydranos-$VER-linux-$ARCH.tar.gz
curl -fLO https://github.com/Kheopsian/Hydranos/releases/download/$VER/hydranos-$VER-linux-$ARCH.tar.gz.sha256
sha256sum -c hydranos-$VER-linux-$ARCH.tar.gz.sha256
tar xzf hydranos-$VER-linux-$ARCH.tar.gz
cd hydranos-$VER-linux-$ARCHThe folder contains:
| File | What it is |
|---|---|
hydranos |
the daemon: web UI, APIs and every engine, in one process |
hydranos-update |
the updater (see Updating) |
default.toml.example |
the config template (the same one hydranos writes itself when the config file is missing) |
hydranos.service |
the systemd unit |
install.sh |
the installer described below |
Releases no longer ship hydranos-engine. ./hydranos --version (or -V) prints hydranos 4.x.y. The command line is:
usage: hydranos [--config <path/to/default.toml>] [--console]
hydranos --version
hydranos reset-password <password> [path/to/default.toml]
hydranos hash-password <password>
--help prints it. Any other argument is reported on stderr (hydranos: ignoring unknown argument …; starting anyway (see hydranos --help)) and the daemon starts. Details: Configuration: Environment and CLI.
Run it as root from inside the unpacked folder (it copies files by relative name):
sudo ./install.shWhat it does, in order:
- Creates the system user
hydranos(no login shell, home/var/lib/hydranos) if it does not exist. - Creates
/opt/hydranos,/etc/hydranosand/var/lib/hydranos, owned byhydranos. - Copies
hydranosandhydranos-updateinto/opt/hydranos. - On first install only, copies
default.toml.exampleto/etc/hydranos/default.tomland setsdata_dir = "/var/lib/hydranos". An existing config is never overwritten. - Installs
hydranos.serviceinto/etc/systemd/system/only if no unit is there yet. If yours differs from the shipped one, yours is kept and the shipped one is put beside it ashydranos.service.new(hydranos: kept your /etc/systemd/system/hydranos.service; the shipped one is at /etc/systemd/system/hydranos.service.new). Then it runssystemctl daemon-reloadandsystemctl enable --now hydranos.
The result:
flowchart LR
UNIT["hydranos.service"] -->|"ExecStart --config"| BIN
subgraph OPT["/opt/hydranos"]
BIN["hydranos"]
UPD["hydranos-update"]
end
subgraph ETC["/etc/hydranos = config dir"]
CFG["default.toml"]
LOG["hydranos.log"]
RACE["race/state.db<br/>race/resume/"]
HOARD["hoard/state.db<br/>hoard/resume/"]
end
subgraph VAR["/var/lib/hydranos = data_dir"]
STORE["hydra.db"]
BENCH["bench.db"]
end
BIN --> CFG
BIN --> LOG
BIN --> RACE
BIN --> HOARD
BIN --> STORE
BIN --> BENCH
BIN -->|"torrent data"| SP["your save paths"]
Note the split: the engines' resume state and the log live beside the config file in /etc/hydranos, not in data_dir. The store hydra.db (the catalogue, including every .torrent) and bench.db live in /var/lib/hydranos. Back up both folders. Why the split exists: Architecture.
At the end the script prints how to finish:
- Create the admin account in the browser. The first-run screen only answers callers on the same machine or a private network (see Installation and First Run).
- On a remote host, set the password from the shell instead, as root:
/opt/hydranos/hydranos reset-password '<newpassword>' /etc/hydranos/default.toml, thensystemctl restart hydranos. The password needs at least 8 characters. The command rewrites the file in place, so it stays owned byhydranos; the password stays in your shell history (derived from code, not tested by the maintainer). - Its upgrade hint,
/opt/hydranos/hydranos-update, is incomplete: run from another folder, the updater looks in the current folder and replaces nothing. Use the full command under Updating (--dirand--config).
The root install.sh in the repository (served by a running instance at /install.sh) is a different script: it enrols a new machine as a node, at the controller's version (HYDRANOS_VERSION_PIN overrides it). When docker is on the PATH it runs a container, and only ever replaces a container it created itself. Otherwise it downloads the same release tarball and installs either as root, into /usr/local/bin with its own system unit, or, with --user or on an account that is not root, into your home with a systemd --user unit (a seedbox account). See Nodes and Multiple Machines.
Same layout as the script, step by step. Only hydranos is required; add hydranos-update if you want the updater.
sudo useradd --system --home-dir /var/lib/hydranos --shell /usr/sbin/nologin hydranos
sudo install -d -o hydranos -g hydranos /opt/hydranos /etc/hydranos /var/lib/hydranos
sudo install -m 0755 hydranos hydranos-update /opt/hydranos/
sudo install -m 0644 -o hydranos -g hydranos default.toml.example /etc/hydranos/default.toml
sudo sed -i 's#^data_dir = .*#data_dir = "/var/lib/hydranos"#' /etc/hydranos/default.tomlThings to know:
-
The config file must be writable by the service user. On first start Hydranos generates the API key and writes it into
[daemon] api_key; settings saved from the UI are written there too. If the file is read-only, the key stays empty and every API call is refused. -
No config file?
hydranos --config /some/dir/default.tomlwrites the template there itself and setsdata_dirto/some/dir/data. Without--configit uses/config/default.toml, which rarely exists on a Linux host: always pass--config. -
Save paths. There is no default download folder. Every torrent needs a save path, usually from its category, and the
hydranosuser must be able to write there:sudo install -d -o hydranos -g hydranos /srv/torrents
- Firewall. Open the engines' listen ports, TCP and UDP (16171 for race, 16172 for hoard). Keep port 8199 on your LAN or behind a tunnel. The full port table is on Installation and First Run.
-
Trying it first without root:
./hydranos --config "$HOME/hydranos/default.toml"runs in the foreground with everything under~/hydranos. Stop it with Ctrl+C (that is a clean stop, see below).
Then install the unit from the next section.
The unit shipped in the tarball (and installed by the script) is the one to run. If you install by hand, copy it as it is:
sudo install -m 0644 hydranos.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now hydranos# /etc/systemd/system/hydranos.service (as shipped)
[Unit]
Description=Hydranos BitTorrent client
Documentation=https://github.com/Kheopsian/Hydranos/wiki
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=hydranos
Group=hydranos
ExecStart=/opt/hydranos/hydranos --config /etc/hydranos/default.toml
# A restart asked from the UI exits with 75, which on-failure brings back.
Restart=on-failure
RestartSec=5
# On SIGTERM Hydranos tells the trackers it is leaving (5 s at most), then
# flushes every engine's resume data, in parallel, within
# HYDRANOS_STOP_TIMEOUT (120 s by default). Keep this above both, or systemd
# kills the flush and the next start re-checks what was already complete.
TimeoutStopSec=150
# Peer connections and files at scale -> raise the fd limit.
LimitNOFILE=1048576
[Install]
WantedBy=multi-user.targetRestart=on-failure is enough. The UI's Restart now and Apply & restart buttons, saving Duplicate data and resetting the settings stop the daemon cleanly and exit with code 75, which systemd counts as a failure and restarts. systemctl stop hydranos (exit 0) stops it for good.
TimeoutStopSec=150. On SIGTERM Hydranos drains the API (5 s at most; the web UI's event stream and live log tail close themselves, so an open tab no longer holds the stop), tells the trackers it is leaving (5 s at most), then writes every engine's resume state in parallel, bounded by HYDRANOS_STOP_TIMEOUT (default 120 s; 120, 90s, 2m, 1m30s, 1h). Past that budget it leaves with what is written. If systemd kills the process first, the next start re-checks the pieces whose progress was lost (up to five minutes of it, the interval of the periodic save). The worst case is 5 + 5 + 120 = 130 s. To see what your instance really takes:
journalctl -u hydranos | grep "shutdown flush"shutdown flush complete took_s=… gives the time; shutdown flush hit HYDRANOS_STOP_TIMEOUT; leaving with what is saved means the budget ran out. If you raise HYDRANOS_STOP_TIMEOUT, raise TimeoutStopSec with it (keep it at least 10 s above), in a drop-in.
LimitNOFILE=1048576: every peer connection and every open data file uses a file descriptor.
The unit gives the service no capability: the network mode WireGuard, managed by Hydranos is refused under it (the reason is shown on the Network tab) until you grant CAP_NET_ADMIN in a drop-in and install ip and wg (iproute2, wireguard-tools). See Managed WireGuard.
install.sh keeps an existing unit (a newer shipped one lands beside it as hydranos.service.new), but changes are easier to carry across upgrades in a drop-in, which nothing touches. For example, a longer flush budget for a large library:
sudo mkdir -p /etc/systemd/system/hydranos.service.d
sudo tee /etc/systemd/system/hydranos.service.d/override.conf >/dev/null <<'EOF'
[Service]
Environment=HYDRANOS_STOP_TIMEOUT=5m
TimeoutStopSec=330
# Managed WireGuard only (not tested by the maintainer):
# AmbientCapabilities=CAP_NET_ADMIN
EOF
sudo systemctl daemon-reload
sudo systemctl restart hydranosjournalctl -u hydranos -f shows the daemon's output. The same lines are also appended to /etc/hydranos/hydranos.log, which rotates itself at 128 MiB and keeps four old files (hydranos.log.1 to .4). To keep only the journal, add Environment=HYDRANOS_LOG_STDOUT=1 to the [Service] section: the variable's presence alone (any value, even 0) disables the file. More: Monitoring and Logs.
bind_interface (all of one engine's traffic on, say, wg0: listener, uTP, dials, announces, DHT, webseeds) works on Linux only, and what it does and does not pin is explained on VPN and Egress. It uses SO_BINDTODEVICE; on kernels older than 5.7 an unprivileged user needs CAP_NET_RAW for that. If an engine refuses to open its listener after you set bind_interface, add AmbientCapabilities=CAP_NET_RAW to the drop-in (not tested by the maintainer). To let Hydranos bring the tunnels up itself instead, see Managed WireGuard.
Open http://<server>:8199/. On a headless server, tunnel the port instead of opening it:
ssh -L 8199:127.0.0.1:8199 [email protected]then browse to http://localhost:8199/. The first-run steps (admin account, API key, first category) are the same on every platform: see First run on Installation and First Run.
Read the release notes on the releases page first. Two ways, both keep your config and data:
With hydranos-update (installed in /opt/hydranos by the script):
sudo /opt/hydranos/hydranos-update --dir /opt/hydranos --config /etc/hydranos/default.toml --check # what is installed, what is available
sudo /opt/hydranos/hydranos-update --dir /opt/hydranos --config /etc/hydranos/default.toml # download, verify, replace
sudo systemctl restart hydranos--config lets its downloads leave the way the daemon's own requests do ([daemon] egress, the kill switch, the network mode); it then prints a network: … line when that route is not the direct one or the kill switch is armed. Without it, the updater looks for default.toml in --dir, which in this layout does not exist, and uses the default route. A config that does not parse is refused. It picks the linux-amd64 or linux-arm64 tarball of the latest GitHub release (--tag v4.4.0 for a specific one, --yes to skip the prompt), refuses to install if the .sha256 is missing or does not match, and replaces only the binaries already present in --dir. On Linux it does not restart anything: the old version keeps running until systemctl restart.
With a new tarball: download and verify it as above, run sudo ./install.sh from the new folder, then sudo systemctl restart hydranos (the script's enable --now does not restart a running service). The script keeps your unit file; if the shipped one changed, compare it with hydranos.service.new.
Coming from 3.x: paths, names and the unit changed; follow Upgrading from 3.x. Backups and the clean-stop details: Operating Hydranos.
sudo systemctl disable --now hydranos
sudo rm -rf /etc/systemd/system/hydranos.service /etc/systemd/system/hydranos.service.d
sudo systemctl daemon-reload
sudo rm -rf /opt/hydranos
# Only if you do not want to keep the config, the catalogue and the resume state:
sudo rm -rf /etc/hydranos /var/lib/hydranos
sudo userdel hydranosYour torrent data in the save paths is not touched. Deleting /var/lib/hydranos/hydra.db loses the catalogue (every .torrent is stored there, nowhere else).
Now do First run on Installation and First Run.