Deploy on a VPS
This is the canonical “RunWisp on a Linux server” recipe. By the end
you’ll have a daemon running under its own non-root user, persisting
to /var/lib/runwisp, supervised by systemd, fronted by a reverse
proxy with HTTPS. Adjust the proxy section for your favourite
(nginx, Caddy, Traefik); the rest is the same.
Prerequisites
Section titled “Prerequisites”- A Linux VPS (Debian / Ubuntu / Fedora / Arch — anything with systemd).
- Root access via SSH.
- A DNS record pointing at the VPS, if you want HTTPS.
1. Install the binary
Section titled “1. Install the binary”Use the installer from the Install page:
curl -fsSL https://get.runwisp.com | shsudo install -m 0755 ./runwisp /usr/local/bin/runwisprunwisp --versionThe binary is static and self-contained — no language runtime, no SQLite to install separately.
2. Create a dedicated user
Section titled “2. Create a dedicated user”Run RunWisp as a system user with no shell. The daemon executes the
shell commands in runwisp.toml with this user’s privileges, so keep
its surface area small.
sudo useradd --system --home /var/lib/runwisp --shell /usr/sbin/nologin runwispsudo install -d -o runwisp -g runwisp -m 0750 /var/lib/runwispsudo install -d -o runwisp -g runwisp -m 0750 /etc/runwispIf your tasks need to write somewhere outside /var/lib/runwisp,
chmod those directories so the runwisp user can write — don’t run
RunWisp as root just to dodge that step.
3. Drop in the config
Section titled “3. Drop in the config”sudo -u runwisp runwisp init --config /etc/runwisp/runwisp.tomlsudo chmod 0640 /etc/runwisp/runwisp.tomlrunwisp init writes an annotated TOML scaffold and prints a generated
password. Edit the file in your editor; remove the demo task; add your
real ones. See the configuration reference for every key.
4. Pick the auth strategy
Section titled “4. Pick the auth strategy”Either let RunWisp manage the password file, or hand it your own via the environment.
Auto-generated (default)
Section titled “Auto-generated (default)”The daemon will create /var/lib/runwisp/password on first start and
print it on stdout. Look for it in journalctl after the first boot:
sudo journalctl -u runwisp --since '1 minute ago' | grep passwordOperator-supplied
Section titled “Operator-supplied”Drop the password into a systemd environment file:
sudo install -m 0640 -o root -g runwisp /dev/stdin /etc/runwisp/runwisp.env <<'EOF'RUNWISP_PASSWORD=<a long random string here>EOFThe systemd unit below loads it via EnvironmentFile=. The daemon
also writes the env-supplied password to /var/lib/runwisp/password
so the TUI and CLI work without re-typing it.
5. Wire up systemd
Section titled “5. Wire up systemd”See the systemd unit page for a copy-paste drop-in. Quick version:
[Unit]Description=RunWisp daemonAfter=network-online.targetWants=network-online.target
[Service]User=runwispGroup=runwispEnvironmentFile=-/etc/runwisp/runwisp.envExecStart=/usr/local/bin/runwisp daemon \ --config /etc/runwisp/runwisp.toml \ --data /var/lib/runwisp \ --host 127.0.0.1 \ --port 9477Restart=on-failureRestartSec=2s
[Install]WantedBy=multi-user.targetBind to 127.0.0.1 (loopback) — the next step puts a reverse proxy in
front to handle TLS and exposure. Bringing the daemon up:
sudo systemctl daemon-reloadsudo systemctl enable --now runwispsudo systemctl status runwisprunwisp status should now answer:
runwisp status# RunWisp is healthy at :94776. Reverse proxy + TLS
Section titled “6. Reverse proxy + TLS”Two example proxies. Pick one.
Caddy is the smallest path to TLS — it provisions a Let’s Encrypt certificate automatically.
runwisp.example.com { reverse_proxy 127.0.0.1:9477}sudo systemctl reload caddyserver { listen 443 ssl http2; server_name runwisp.example.com;
ssl_certificate /etc/letsencrypt/live/runwisp.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/runwisp.example.com/privkey.pem;
location / { proxy_pass http://127.0.0.1:9477; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme;
# SSE log streaming needs request body buffering off and a long timeout. proxy_buffering off; proxy_read_timeout 24h; }}Use certbot --nginx (or your preferred tool) to provision the
certificate.
7. Tell the daemon to trust the proxy
Section titled “7. Tell the daemon to trust the proxy”Setting RUNWISP_TRUST_PROXY is what lets the daemon honour
X-Forwarded-Proto from your proxy and set the Secure cookie
attribute correctly. Without it, the JWT cookie would not be marked
Secure even on HTTPS — and the daemon would rate-limit by the
proxy’s loopback IP instead of the real client.
Append to /etc/runwisp/runwisp.env:
RUNWISP_TRUST_PROXY=127.0.0.1/32,::1/128Then restart:
sudo systemctl restart runwispIf your proxy lives on another host (say a load balancer), use that
host’s IP / CIDR instead. Never set this to 0.0.0.0/0 —
the daemon refuses that range explicitly.
8. Smoke test
Section titled “8. Smoke test”curl -sSf https://runwisp.example.com/health# OKrunwisp status# RunWisp is healthy at :9477Open the Web UI in a browser at https://runwisp.example.com, log in
with the generated (or operator-supplied) password, and you should
see the Web UI tour in action.
What to back up
Section titled “What to back up”The data directory holds run history and on-disk logs. The authoritative reference is Operations: data directory; the short version for systemd:
# Pause writes briefly with a service stop, snapshot, then resume.sudo systemctl stop runwispsudo tar -czf /backups/runwisp-$(date +%F).tgz /var/lib/runwispsudo systemctl start runwispOr, with the daemon running, copy the SQLite triplet
(runwisp.db, -shm, -wal) atomically — see the
backups section.
Where to next
Section titled “Where to next”- Deploy with Docker — same idea, container edition.
- systemd unit — the full unit file with hardening directives.
- Operations: auth — what
RUNWISP_TRUST_PROXYand theSecurecookie are doing. - Operations: troubleshooting — when the install doesn’t go to plan.