Linux
The binary, a service account, and a systemd unit.
View as MarkdownThe whole install is four things: a binary on PATH, an unprivileged user to run it, a
directory holding config.yaml, and a unit that points WorkingDirectory at that directory.
Download and verify
VERSION=v0.1.0
ARCH=$(uname -m); case "$ARCH" in x86_64) ARCH=amd64 ;; aarch64) ARCH=arm64 ;; esac
curl -fsSLO "https://github.com/savedhq/local-worker/releases/download/${VERSION}/local-worker-linux-${ARCH}"
curl -fsSLO "https://github.com/savedhq/local-worker/releases/download/${VERSION}/SHA256SUMS"
grep "local-worker-linux-${ARCH}\$" SHA256SUMS | sha256sum -c -That prints OK or it prints FAILED. Do not skip it and do not accept the second answer.
sudo install -m 0755 "local-worker-linux-${ARCH}" /usr/local/bin/local-workerThe binary is statically linked, so there is no distribution to match and no package to add.
The dump tools are separate: install postgresql-client, mysql-client or redis for
the source types you actually use.
Create the service account
The worker needs no privileges of its own. It needs to read one file and reach your sources.
sudo useradd --system --no-create-home --shell /usr/sbin/nologin saved
sudo install -d -m 0700 -o saved -g saved /etc/saved
sudo install -m 0600 -o saved -g saved config.yaml /etc/saved/config.yamlconfig.yaml holds your source passwords and the worker key. Mode 0600 owned by saved
is the whole of its protection: the token is on your disk and nowhere else, so nothing else
on the host should be able to read it.
On RHEL-family distributions the locked shell is /sbin/nologin. If the account already
exists, useradd fails harmlessly and the two install lines are what matter.
The account also has to reach the sources. A file or folder backup reads as saved, and a
script source runs as saved, so grant that user the access those paths need rather than
running the worker as root.
Run it once by hand
Before writing a unit, prove the config works. The worker reads config.yaml from its
working directory, so run it from there.
sudo -u saved sh -c 'cd /etc/saved && /usr/local/bin/local-worker'A healthy start logs the tools it resolved, the hub it dialled, and how many backups the
config declares, then goes quiet.
Troubleshooting shows exactly what
those lines look like. backups=0 means it is connected and will fail every run it receives.
Stop it with Ctrl-C once it looks right.
The unit
[Unit]
Description=saved.sh local worker
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=saved
Group=saved
WorkingDirectory=/etc/saved
ExecStart=/usr/local/bin/local-worker
Restart=always
RestartSec=5s
# /var/lib/saved, created 0700 and owned by User. Point local_temp_path inside it.
StateDirectory=saved
StateDirectoryMode=0700
UMask=0077
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectControlGroups=true
PrivateDevices=trueWorkingDirectory is not optional. It is the only way the worker finds its config.
Set the staging directory to match, in /etc/saved/config.yaml:
local_temp_path: /var/lib/saved/tmpsudo systemctl daemon-reload
sudo systemctl enable --now saved-worker
sudo journalctl -u saved-worker -fProtectSystem=strict makes the whole filesystem read-only except the state directory.
Reads are unaffected, so file and folder backups still work. A script source that
writes somewhere other than $SAVED_OUTPUT will not. Drop the line, or add a
ReadWritePaths=, if you have a script that needs to write.
PrivateTmp=true is safe here only because local_temp_path is under
/var/lib/saved. Leave the default and the worker stages dumps in the unit's private
/tmp, which you cannot inspect from outside the unit and which is destroyed on restart.
Set one or the other, not neither.
Check it took
systemctl is-active saved-worker
journalctl -u saved-worker | grep 'local-worker starting'Then confirm from our side that the worker is polling:
sctl worker listAn instance count of exactly one is what you want. Two means a second copy of the same config is running somewhere, which breaks runs.