Torwarden

Install

Installing the agent

One static binary per machine. It makes outbound connections only, never listens on a port, and sends nothing anywhere except the collector URL in its configuration.

Getting the host a key

An agent needs an API key, and a key belongs to a host that exists. There are two ways to get one, and which you want depends on how many machines you have.

One at a time. In the dashboard, Hosts → Add host, enter the hostname, and copy the key — it is shown once and only its hash is stored, so it cannot be shown again.

A fleet. Set an enrolment token once and every machine runs the same command, registering itself. Nobody opens the dashboard, and there is no per-machine secret to distribute or to lose. See enrolling a fleet below.

Which credential is which

Three different things are handed out during a first install, and only one of them is the same on every machine.

CredentialWhere it comes fromGood for
Setup token Printed by the server installer, once, in its log Creating the first administrator, once. It is not an agent credential and no agent ever uses it.
API key (twd_…) Hosts → Add host in the dashboard One host. Shown once; only its hash is stored.
Enrolment token You generate it and set it under Settings → Agents The whole estate. Identical on every machine; each agent swaps it for its own API key.

Do not put one host's API key on a second machine. The server decides where a snapshot belongs from the key it arrives with, not from the hostname inside it — so two machines sharing a key do not become two hosts. They become one host whose processor and memory jump between two machines, with no error anywhere to say why. If you want one command for every machine, that is the enrolment token.

Linux

sudo sh install-agent.sh \
  --url  https://torwarden.example.com \
  --key  twd_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx \
  --hostname web-01

Run it with no arguments and it asks instead. Every flag also has an environment variable — TORWARDEN_URL, TORWARDEN_API_KEY, TORWARDEN_HOSTNAME — which is what you want when the key comes from a secret store rather than a command line that lands in shell history.

Do not pipe this into a shell. The installer asks questions, and a piped script and its prompts would be reading the same stream. Download it, then run it.

The installer writes /etc/torwarden/agent.conf at mode 0600, installs the binary to /usr/local/bin/torwarden-agent and enables a systemd unit. Verify before starting:

torwarden-agent check
sudo systemctl enable --now torwarden-agent

The configuration file

collector_url = https://torwarden.example.com
api_key       = twd_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
hostname      = web-01
interval      = 30s
KeyDefaultWhat it does
collector_url—Base URL of the server. No path on the end.
api_key—The key from host registration. Also readable from TORWARDEN_API_KEY.
hostnamethe machine'sWhat this host is called in Torwarden. Also TORWARDEN_HOSTNAME.
interval30sHow often to collect and ship. Minimum 1s.

The key can come from the environment instead of the file, which is what you want when the configuration is managed by a tool and the credential comes from a secret store:

TORWARDEN_API_KEY=twd_… torwarden-agent -config /etc/torwarden/agent.conf

Windows

Fetch the installer and run it from an elevated PowerShell. It downloads the agent, checks it against the published SHA256SUMS, and registers the service:

Invoke-WebRequest -Uri https://releases.torwarden.com/install-agent.ps1 -OutFile install-agent.ps1
powershell -ExecutionPolicy Bypass -File .\install-agent.ps1 `
  -CollectorUrl "https://torwarden.example.com" `
  -ApiKey       "twd_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

For a fleet, give it an enrolment token instead of a key and the machine registers itself, the same as on Linux — see enrolling a fleet:

$env:TORWARDEN_ENROLMENT_TOKEN = "<token>"
powershell -ExecutionPolicy Bypass -File .\install-agent.ps1 `
  -CollectorUrl "https://torwarden.example.com"

Configuration lives in %ProgramData%\Torwarden\agent.conf — ProgramData rather than Program Files, so an upgrade does not overwrite it. The agent registers as a Windows service and manages itself through the Service Control Manager:

torwarden-agent install      # register the service
torwarden-agent start
torwarden-agent stop
torwarden-agent uninstall

check, version and help work the same on both platforms.

Windows has no load average. Rather than invent one from the processor queue length — a different number wearing the same name — the agent reports nothing, and load charts and load rules simply do not apply to Windows hosts. Everything else does.

Enrolling a fleet

Adding two hundred machines by hand is two hundred trips to a browser, and it makes each key precious: lose one and the only way back used to be deleting the host, which threw away its history. An enrolment token replaces all of that with one secret you already know how to manage.

Generate it, then set it under Settings → Agents and turn Auto-register new agents on. Both switches have to be on; a token by itself registers nobody.

openssl rand -hex 32

Then the same command installs every machine in the estate. Configuration management sets the variable in the environment it runs the script in, which is the case this is built for; by hand, export it first rather than putting it on the command line:

export TORWARDEN_ENROLMENT_TOKEN=<token>
sh install-agent.sh --url https://torwarden.example.com

Windows takes the same token from $env:TORWARDEN_ENROLMENT_TOKEN, or from -EnrolmentToken; the Windows section has the whole command.

Keep the token out of any command line. Arguments are readable by every account on the machine through /proc for as long as the process lives, so --token exposes it, and so does sudo TORWARDEN_ENROLMENT_TOKEN=… — the assignment is one of sudo's arguments, and it is visible for the whole install, not just the moment of registering. export is a shell builtin and starts no process, so it does not. Run as root, or use sudo -E with the variable already exported.

The same holds on Windows for a different reason: -EnrolmentToken puts the secret in a command line, and a managed estate is usually recording those — process creation auditing (event 4688), Sysmon, and PowerShell transcription all capture it, into logs that are kept for years and read by more people than the token was meant for. Set $env:TORWARDEN_ENROLMENT_TOKEN instead: assigning a variable starts no process.

This matters because the enrolment token is the credential that can claim an existing hostname: whoever holds it can take over a monitored host's identity and feed the server whatever it likes for that host. Rotate it if it has been through a command line.

SettingWhat it does
Enrolment tokenThe shared secret an installer presents. Rotating it stops new registrations at once and does not disturb agents already enrolled — they authenticate with their own keys, not with this.
Auto-register new agentsThe switch. Off until you turn it on, whatever the token says.
Auto-approve registered agentsOn by default. Turn it off to stage a large rollout: hosts appear but arrive with monitoring disabled, and a disabled host takes no licence seat until somebody enables it.

A rebuilt machine keeps its history. Registering a hostname that already exists re-keys that host rather than creating a second one, so its id, its metrics, its alert history and its SLA all survive a reinstall. Its previous key stops working at that moment, the server logs the change with the address it came from, and it lands in the audit trail.

Treat the token as a fleet-wide credential. Every machine that can enrol holds it, so anyone holding it can enrol a machine, or re-key an existing one and take over the name. It buys nothing else — it cannot read a host, list the fleet, or reach any other part of the API — but rotate it when a machine leaves the estate, and keep it in whatever already holds the rest of your configuration management's secrets.

Rotating it is one field: set a new value under Settings → Agents. Existing agents carry on untouched.

Once a machine is enrolled it never needs the token again — see upgrading agents for the credential-free form to schedule.

Upgrading agents

Re-run the installer with no server and no credential. It fetches the new binary, verifies it, keeps the existing agent.conf, and restarts the agent:

# Linux
sudo sh install-agent.sh

# Windows, from an elevated PowerShell
powershell -ExecutionPolicy Bypass -File .\install-agent.ps1

That form is the one to put in a scheduled job, because it needs no credential at all. The per-host API key is already in the configuration file, and the enrolment token — which is fleet-wide and can claim any hostname — never has to be distributed for the sake of installing a new binary.

Keep the token out of the upgrade job's environment. Both installers treat a token they can see as an instruction to enrol, not to upgrade, so a job that exports TORWARDEN_ENROLMENT_TOKEN is not running the bare form: it will re-register the host and issue it a new key on every run. Set the variable only where hosts are first installed.

To qualify one release and roll exactly that everywhere, name it. The same flag pins a first install:

sudo sh install-agent.sh --version v0.4.11
powershell -ExecutionPolicy Bypass -File .\install-agent.ps1 -Version "v0.4.11"

An agent keeps reporting to a server running a different version, so servers and agents do not have to be upgraded together.

Linux can defer the restart; Windows cannot. --no-restart replaces the binary and leaves the running agent alone until you restart it yourself. There is no equivalent on Windows, because a running executable is locked there and the service has to stop before the file can be replaced — so on Windows, schedule the whole install inside the maintenance window instead.

When the server is unreachable

The agent keeps what it cannot deliver. A snapshot that fails to send is written to disk, and the backlog goes out oldest-first as soon as the collector answers again — so a server restart, an upgrade or a network partition leaves no gap in the history.

This matters beyond the graphs. A host with no samples is marked not reporting once the agent timeout passes, that state is recorded as no_data, and the availability export counts no_data as downtime. Without buffering, taking the monitoring server down for ten minutes writes a ten-minute outage against every host in the estate — into the report an auditor reads.

SettingDefaultWhat it does
spool_dir/var/lib/torwarden-agent/spool
(%ProgramData%\Torwarden\spool on Windows)
Where undelivered snapshots wait. Empty switches buffering off.
spool_max_bytes8388608 (8 MiB)Cap on that directory — about eighteen hours at the default interval, since a snapshot is roughly 3.5 KB. When it is full the oldest are dropped first, because after a long outage the recent ones are what show the host alive again. A value that would leave the spool uncapped, or one too small to hold a single snapshot, is corrected to the default and noted in the log and in torwarden-agent check — the agent does not refuse to start over it. Clear spool_dir to switch buffering off.

Only failures worth retrying are kept. A refused API key or a snapshot the server called malformed is dropped rather than queued — those never become deliverable, and holding them would push out samples that could still arrive. The same applies to one already in the queue: it is discarded and the rest goes on, because a snapshot the server will never accept sitting at the head would otherwise stop the agent shipping for good.

A backlog is delivered a bounded number at a time. The moment a queue drains is the moment the collector has just come back, and a fleet replaying days of history at a server that has only finished starting is a second outage caused by recovering from the first.

Upgrading an existing Linux agent needs the service definition too. The unit runs under ProtectSystem=strict, so the agent has nowhere to write until the unit declares StateDirectory=torwarden-agent. Re-running the installer updates both the binary and the unit:

sudo sh install-agent.sh

An agent whose binary is newer than its unit still runs and still ships — it just cannot buffer, and says so at startup and in torwarden-agent check.

Installing without internet access

The installers fetch from releases.torwarden.com by default and verify every download against SHA256SUMS. On an estate that does not let servers reach the internet, mirror the release directory internally and point the installer at it:

sudo TORWARDEN_RELEASE_BASE=https://mirror.internal/torwarden \
  sh install-agent.sh --url https://torwarden.example.com --key twd_…

The mirror needs latest/ containing the binaries, SHA256SUMS, and torwarden-agent.service.

Behind a corporate proxy

An agent talks only to your Torwarden server, which is usually on the same network, so most installations need nothing here. If the agent has to cross a proxy to reach the server, add the usual variables to its unit and restart it:

sudo systemctl edit torwarden-agent
[Service]
Environment=HTTPS_PROXY=http://proxy.corp.example:3128
Environment=NO_PROXY=localhost,127.0.0.1,10.0.0.0/8,.corp.example

Keep the server itself in NO_PROXY if it is internal — there is no reason to send your own metrics out through a proxy and back. The server side of this is covered in installing the server.

When the server uses a private CA

Most on-premise servers do. The agent verifies the server's certificate against the operating system trust store and nothing else — there is no insecure switch, because an agent that can be told to skip verification is an agent an attacker can tell to skip verification. Install the issuing CA on each machine instead. It is one file and one command, and it is different on the two families:

FamilyPut the CA hereThen run
RHEL, Rocky, Alma, Fedora/etc/pki/ca-trust/source/anchors/update-ca-trust
Debian, Ubuntu/usr/local/share/ca-certificates/ — the name must end .crtupdate-ca-certificates
SUSE/etc/pki/trust/anchors/update-ca-certificates
# Debian and Ubuntu
sudo cp corp-root-ca.pem /usr/local/share/ca-certificates/corp-root-ca.crt
sudo update-ca-certificates
sudo systemctl restart torwarden-agent

# RHEL, Rocky and Alma
sudo cp corp-root-ca.pem /etc/pki/ca-trust/source/anchors/corp-root-ca.pem
sudo update-ca-trust
sudo systemctl restart torwarden-agent

A PEM file with a .pem name is ignored on Debian and Ubuntu — update-ca-certificates only reads .crt — and that is the single most common way this is got wrong. The command reports how many certificates it added; if it says 0 added, the extension is why.

If the server is using the self-signed certificate it generated for itself, that certificate is its own CA. Copy it off the server with:

openssl s_client -connect torwarden.example.com:443 \
  -servername torwarden.example.com </dev/null 2>/dev/null | openssl x509

Then check the result from the agent's own machine before starting it:

sudo torwarden-agent check

Use the name the certificate was issued for. A certificate covers hostnames, not addresses, unless it was explicitly issued with an IP SAN — so collector_url = https://10.0.0.5 fails verification against a certificate for torwarden.example.com even when the CA is installed correctly. torwarden-agent check says which of the two it is.

In a container

A containerised agent sees its own filesystem, not the machine's: /proc/mounts lists the container's mounts and the root filesystem measures the image. Mount the host's and say so, or a machine at 94% reports a comfortable 12%.

Build the image from the shipped Dockerfile, then:

docker build -f docker/Dockerfile --target agent -t torwarden-agent .

docker run -d --restart=unless-stopped \
  -v /proc:/host/proc:ro -v /:/host/fs:ro \
  -e TORWARDEN_HOST_PROC=/host/proc \
  -e TORWARDEN_HOST_FS=/host/fs \
  -e TORWARDEN_API_KEY=twd_… \
  -e TORWARDEN_HOSTNAME=web-01 \
  torwarden-agent

Note the hostname: inside a container os.Hostname() returns the container id, so a host would register itself under a name that changes on every restart.

When something is wrong

torwarden-agent check names the cause rather than saying it failed. It exits non-zero, so a provisioning run stops rather than carrying on as though it worked.

It saysWhat happened
cannot reach the serverDNS, a firewall, or the wrong port. Nothing answered.
the server’s certificate was refusedThe server answered. Either its CA is not in this machine’s trust store, or the certificate does not cover the name in collector_url. See when the server uses a private CA.
the server rejected this API keyThe host was deleted, or the key was rotated. Register it again.
not on /api/v1/metricscollector_url has a path on the end. It should be the base URL only.
the licence has no free seatThe fleet is at its limit. Remove or disable a host, or install a licence.

The check authenticates without storing a reading, so it is safe to run repeatedly and on a machine that is not in service yet.