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.
| Credential | Where it comes from | Good 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
| Key | Default | What 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. |
hostname | the machine's | What this host is called in Torwarden. Also TORWARDEN_HOSTNAME. |
interval | 30s | How 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.
| Setting | What it does |
|---|---|
| Enrolment token | The 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 agents | The switch. Off until you turn it on, whatever the token says. |
| Auto-approve registered agents | On 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.
| Setting | Default | What it does |
|---|---|---|
spool_dir | /var/lib/torwarden-agent/spool( %ProgramData%\Torwarden\spool on Windows) | Where undelivered snapshots wait. Empty switches buffering off. |
spool_max_bytes | 8388608 (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:
| Family | Put the CA here | Then 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 .crt | update-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 says | What happened |
|---|---|
| cannot reach the server | DNS, a firewall, or the wrong port. Nothing answered. |
| the server’s certificate was refused | The 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 key | The host was deleted, or the key was rotated. Register it again. |
| not on /api/v1/metrics | collector_url has a path on the end. It should be the base URL only. |
| the licence has no free seat | The 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.