Installing the server agent
The server agent installs in one command and enrolls itself. This page covers the install paths, the installer’s options, the network requirements, and hosts that reach the internet only through a forward proxy.
Prerequisites
Section titled “Prerequisites”- A Linux host, x86-64 or arm64, running
systemd219 or newer — every current distribution, and Amazon Linux 2 and CentOS 7 (219). On systemd older than 235, and on cgroup v1 hosts without memory accounting, a few system-service details are not reported. - Outbound HTTPS from the host to the WatchTower15 API, directly or through a forward proxy. No inbound ports.
- Your organisation’s install token (in the dashboard, Servers → Add Server).
- Root for the install step, typically through
sudo. The installer refuses to run without it.
Install
Section titled “Install”The fastest path. Downloads the agent, verifies its checksum, creates the service user, enrolls, and starts monitoring:
curl -fsSL https://api.watchtower15.com/api/agent/install-script | sudo sh -s -- <org_token>In the dashboard, Add Server on the Servers page opens the Adding a server
drawer, which shows this command with your token filled in, and a button that copies it.
It leaves out sudo: run it as root, or put sudo before sh. Members who cannot view
the organisation’s settings see <org_token> in place of the token. For a host behind a
proxy, the drawer adds the proxy for you: see
Behind a forward proxy.
A new server joins your organisation’s default profile, unless the command names
another profile with
--profile=. The drawer adds it for you: pick a profile in its
Profile (optional) list and the command gains --profile=<slug>; Organisation
default adds nothing. Members who cannot view profiles do not see the list. A profile’s
own page shows the command for that profile too (see Assigning a host to a
profile). To move a
server to another profile later, use (change) on the Profile card of the server’s Info
tab (see Changing a server).
For cloud-init, Ansible, Terraform user_data, or the first boot of an instance
built from a golden image, the same installer runs non-interactively. Keep the token
in your secret store and pass it through the environment:
-
Provision the host with outbound HTTPS to the API.
-
Fetch the token from your secret manager into
WT15_ORG_TOKEN(do not bake it into an image layer). -
Run the installer as root. It reads the token from
WT15_ORG_TOKENwhen no token argument is given, and the profile the server joins fromWT15_PROFILE, by the profile’s slug or uuid:Terminal window curl -fsSL https://api.watchtower15.com/api/agent/install-script \| sudo WT15_ORG_TOKEN="$WT15_ORG_TOKEN" WT15_PROFILE=web-tier shName both variables on the
sudoline:sudodrops the ones it is not given (see Behind a forward proxy). Leave outWT15_PROFILEfor your organisation’s default profile. A profile’s slug never changes when the profile is renamed, so your IaC can keep it. -
The agent enrolls, collects self-reported tags from the instance’s cloud/Docker/Kubernetes metadata (in a container, only the
WT15_TAG_<Name>variables), and begins reporting.
Because auto-enrollment collects cloud metadata, a fleet of identical images each enroll with correct, distinct identity tags — no per-host bootstrap script.
Run the installer at each instance’s first boot, never while you build the image: an image must not contain an enrolled agent, or every instance built from it would carry the same server’s credentials.
With Auto Scaling groups or Terraform, archive each instance’s server when the instance is torn down, with one call from a lifecycle hook or a destroy-time provisioner: see Teardown automation.
The installer always downloads the agent from the WatchTower15 API itself, and
the agent needs the API to enroll and report, so there is no separate mirror to
set up. What a host needs is outbound HTTPS to the API, api.watchtower15.com:
directly, or through an HTTP(S) forward proxy that allows CONNECT to it on
port 443.
Allowing the API by address. api.watchtower15.com answers from three fixed
addresses. Where your firewall allows outbound traffic by IP address, allow TCP
port 443 to all three:
3.11.48.155/3218.171.1.44/3218.135.154.177/32These addresses serve api.watchtower15.com only. The dashboard,
app.watchtower15.com, answers from other addresses, which change. So on a host
behind an allow-list, use api.watchtower15.com wherever the dashboard shows
app.watchtower15.com. In the install command the dashboard copies, change only the
host name, and keep the rest as the dashboard wrote it (the token, --profile=,
--tag=, the proxy options):
curl -fsSL https://api.watchtower15.com/api/agent/install-script | sh -s -- <org_token>The same goes for the uninstall command and for the log and
metric ingest URLs. Once installed, the agent sends everything
to api.watchtower15.com, its upgrades included, and teardown
automation calls it there too.
The list, for your automation. https://watchtower15.com/ip-ranges.json holds
the same addresses:
{"version": 1, "updated": "2026-10-19", "notice_days": 90, "agent_ingress": {"hostname": "api.watchtower15.com", "ipv4": ["3.11.48.155/32", "18.171.1.44/32", "18.135.154.177/32"]}}updated is the day the list last changed, and notice_days the notice, in days,
that a new address gets before agents use it (below). Build your firewall rules from
agent_ingress.ipv4, and watch updated to hear of a change.
When the list changes:
- Agents connect only to addresses in the list.
api.watchtower15.commoves between the listed addresses without notice, which is why all three are listed, and why you allow all three. - The one exception, during a large attack:
api.watchtower15.commay answer from Cloudflare’s published ranges (IPv4, IPv6) for a while. No notice is given: it is a defence, not an address change. A server that allows only the three addresses keeps its data on disk meanwhile and sends it onceapi.watchtower15.comis back on them (see When WatchTower15 cannot be reached); a server that also allows Cloudflare’s ranges stays connected throughout. - A new address, beyond these three, is added to the list at least 90 days before any agent is sent to it.
- An address is removed from the list only 90 days after it was last announced as leaving.
- Changes are announced on this page and by the list’s
updated, never by email.
If your hosts reach the internet only through a proxy, see Behind a forward proxy.
Installer options
Section titled “Installer options”Pass options after the token (sh -s -- <org_token> --option). Each option can
also be given as the environment variable in the second column.
| Option | Environment variable | Effect |
|---|---|---|
--proxy=<url> |
WT15_PROXY |
Send the agent’s API traffic, and the installer’s own download, through a forward proxy. See Behind a forward proxy. |
--proxy-port=<port> |
WT15_PROXY_PORT |
The proxy’s port, when the URL does not include one. |
--no-logging |
WT15_DISABLE_LOGGING=1 |
Do not collect or ship logs from this host. |
--no-telemetry |
WT15_DISABLE_TELEMETRY=1 |
Do not collect or send host telemetry. |
--supplier-identifier=<id> |
WT15_SUPPLIER_IDENTIFIER |
The provider’s ID for the host (for example an AWS instance ID). Detected when unset on AWS EC2, on DigitalOcean and in a Docker container. Elsewhere, set a stable ID here (the provider’s instance or server ID, not /etc/machine-id, which an OS reinstall regenerates), and give the same value on every install of that host, so that an agent installed again continues its server. |
--tag=<group>=<tag> |
WT15_TAGS |
A tag the server gets when it enrols, repeatable, at most 20; also --tag=<group-uuid>=<tag> and --tag=<tag-uuid>. A tag that does not exist yet is created. Checked with WatchTower15 before anything is installed: see Tags at install. WT15_TAGS holds the same tags joined by commas; --tag replaces it. It applies only when the host enrols as a new server. |
--profile=<uuid|slug> |
WT15_PROFILE |
The profile the server joins when it enrols, by the profile’s uuid or slug (both on the profile’s page), never its name. Without it, the server joins your organisation’s default profile. Checked with WatchTower15 before anything is installed: see If the profile is not found. It applies only when the host enrols as a new server; --upgrade and --uninstall ignore it. |
--upgrade |
— | Upgrade an installed agent to the latest release in place. See Upgrades. |
--uninstall |
— | Remove the agent and everything the installer created, the uninstall unit included. Takes no token; needs root. In a container it removes nothing, and says so. See Uninstall. |
The installer writes these settings to /etc/wt15-server-agent/agent.env, which
the service reads at start-up, the profile as WT15_PROFILE_UUID or
WT15_PROFILE_SLUG, whichever the value is, and the tags it kept as WT15_TAGS. See the
configuration reference.
If the profile is not found
Section titled “If the profile is not found”Before it downloads or installs anything, the installer asks WatchTower15 whether your token and profile would enrol, and names the profile the server will join:
==> Profile: web-tierIf your organisation has no profile with that uuid or slug (a typo, a deleted profile, or another organisation’s), it stops there, with exit status 1, and names the value:
ERROR: profile "web-teir" was not found in your organisation. Check the uuid or slug on the profile's page.A profile’s name is refused before WatchTower15 is asked:
ERROR: --profile takes a profile's uuid or slug, not its name: "Web tier"Nothing is installed and no server is created: correct the value and run the command
again. The same check stops an install whose token WatchTower15 does not accept, with
ERROR: the install token was not accepted. Copy the command again from Servers → Add Server.
If the installer cannot reach WatchTower15, it tries again after 5, 10, 20 and 30 seconds,
then stops with ERROR: cannot reach <API URL> (HTTP <code>).
On a host that is already enrolled, the agent does not enrol again, so the installer skips
the check. If you gave a profile, it says so and carries on:
==> Credentials found: this host is already enrolled, so --profile applies only if it enrols again as a new server.
To move an enrolled server, use (change) on the Profile card of the server’s Info tab.
Tags at install
Section titled “Tags at install”--tag= gives the server tags when it enrols, as many as you need up to 20: repeat the option, one tag each.
curl -fsSL https://api.watchtower15.com/api/agent/install-script | sudo sh -s -- <org_token> \ --tag=Environment=production --tag=Team=billing-apiEach tag is one of:
<group>=<tag>: a tag group and a tag by name, as on the Tags page. A group or tag that does not exist yet is created when the server enrols.<group-uuid>=<tag>: a group by its uuid, then a tag by name, for a group whose name predates the naming rule (such asMy App).<tag-uuid>: an existing tag by its uuid.
Names follow the naming rule: letters, digits, - and _ only, a group starting with a
letter and a tag with a letter or a digit, up to 100 characters each. In a container, or from an image’s first boot,
set WT15_TAGS to the same tags joined by commas (WT15_TAGS=Environment=production,Team=billing-api); --tag
replaces it when both are given. The Add Server drawer writes these options for you.
Before it installs anything, the installer checks every tag with WatchTower15 and says what will happen to each:
==> Profile: web-servers==> Tag Environment=production (exists)==> Tag Team=billing-api (will be created)It stops, with exit status 1 and nothing installed, on a tag it cannot use, naming it:
ERROR: tag a b=c: Use only letters, digits, - and _, starting with a letter (up to 100 characters).ERROR: tag not-a-uuid: give <group>=<tag>, or a tag's uuid.ERROR: at most 20 tags per install.ERROR: tag 3f2c6a1e-5b7d-4e9f-8a0b-1c2d3e4f5a6b: no such tag (or group) in this organisation.ERROR: tag Team=old-team: a tag group or tag with this name was deleted in WatchTower15, and its name cannot be reused. Choose another name.ERROR: tag Domain=x: its group holds tags for domains, not servers.The first three are checked before WatchTower15 is asked; the others name every refused tag at once. The tags apply
only when the host enrols as a new server. On a host that is already enrolled no request is made: a tag that breaks
the naming rule is only warned about and left out (WARNING: --tag ignored: "<tag>": …), the others are saved, and
the installer carries on. Removing a tag from the server later keeps it removed, until the agent is installed again
with the same command: a host that continues its server adds its install
tags back, and takes none away.
Verify the install
Section titled “Verify the install”systemctl status wt15-server-agent● wt15-server-agent.service Loaded: loaded (/etc/systemd/system/wt15-server-agent.service; enabled) Active: active (running)The host appears in your dashboard within ~60 seconds. The agent writes its own
diagnostics to /var/log/wt15-server-agent.log, and its start-up line there shows
the version it runs. Your system’s logrotate rotates and prunes that file on the
same schedule as your other logs.
The systemd journal receives the agent’s errors, warnings and notices, nothing else. An
outage is one error line when it starts, a warning every hour while it lasts and a notice
when it ends (see When WatchTower15 cannot be
reached), so
journalctl -u wt15-server-agent -p err still lists errors only: one per outage, not one
per failed attempt.
To see what the agent is doing, run it with --status, as root:
$ sudo /var/lib/wt15-server-agent/wt15-server-agent --statusWatchTower15 agent …, state as of 2026-10-06 11:25:04 UTC (written on change)Connection : cannot reach WatchTower15 since 09:12:30 UTC (2 h 12 min): dns_failure last error: lookup api.watchtower15.com: no such hostSpool : on disk, /var/lib/wt15-server-agent/spool, 1.9 MBBacklog : telemetry 528 records (09:12:30 → 11:24:45), system info 1, database snapshots 0, disk usage 0Dropped : noneClock : 1 s behind WatchTower15 (no correction)Profile : etag 3f9a2b7c…, fetched 2026-10-06 08:01 UTC, from the cacheLimits : telemetry 64 MB + 24 MB × 1 database, horizon 72 h, floor 10 % / 512 MB freeIt prints the state the agent last wrote to /var/lib/wt15-server-agent/status.json, and
the first line says when that was: the agent writes the file when its state changes, and at
most once a minute during an outage.
- Connection reads
connected to WatchTower15, or since when and why it is not:cannot reach WatchTower15(the network, DNS, TLS or a proxy),WatchTower15 not accepting data(an HTTP error) orno answer from WatchTower15(a request that got no answer in time), with the error class and the last error. - Spool is where the data waiting to be sent is kept, and how much there is:
on disk, orin memory onlywith the reason (for example below the free-disk floor). - Backlog is what is still to be sent. While WatchTower15 asks agents to wait before
sending more kept data, the line ends with
replay paused until … UTC (WatchTower15 asked to wait). - Dropped counts the records the agent had to drop (at a limit, or refused by WatchTower15 as invalid), with those of the current outage by reason.
- Refused, shown only when there are any, counts the database data the agent kept that
WatchTower15 refused, by reason: for example
database blocks refused: 40, collected after another collector took over; 2, collected while the database was paused. The agent leaves those out and delivers the rest (see Collector pools). - Clock compares the server’s clock with WatchTower15’s, and says when the agent is correcting for it.
- Profile and Limits are the configuration the agent runs: its etag, when it was
fetched and whether it came from the profile kept on the
host, and how much and how long
it keeps. A database this server collected in the last 72 hours still counts in the
telemetry limit, and Limits counts those apart, as in
telemetry 64 MB + 24 MB × 3 databases (1 kept after a lost lease).
--status sends nothing and changes nothing. It exits with status 1 when there is no state
file yet, for example before the agent has enrolled. In a container, run
docker exec <container> /var/lib/wt15-server-agent/wt15-server-agent --status.
Network requirements
Section titled “Network requirements”| Direction | Destination | Purpose |
|---|---|---|
| Outbound HTTPS (443) | WatchTower15 API, api.watchtower15.com |
enrollment, telemetry, logs, config |
| Inbound | — | none required |
Behind a firewall that allows outbound traffic by IP address, allow the three addresses
api.watchtower15.com answers from: see the Restricted egress tab under
Install.
Behind a forward proxy
Section titled “Behind a forward proxy”If your host reaches the internet only through an HTTP(S) forward proxy, give the proxy
to the installer with --proxy=. The installer uses it to download the agent, and
stores it in the agent’s configuration so the agent uses it too. It applies to
WatchTower15 API traffic only, and cloud metadata is never sent through it.
The install script is downloaded before the installer runs, so that download needs
the proxy separately, with curl -x:
curl -fsSL -x http://proxy.corp.example:3128 https://api.watchtower15.com/api/agent/install-script \ | sudo sh -s -- <org_token> --proxy=http://proxy.corp.example:3128You can put the port in the URL, as above, or give it separately:
... | sudo sh -s -- <org_token> --proxy=http://proxy.corp.example --proxy-port=3128The dashboard builds this command for you. The Adding a server drawer (Servers →
Add Server) has two optional fields under the command, Proxy URL and Port. Fill
them in and the command it shows and copies gains -x for the script’s download, with the
port always written out, and --proxy= (and --proxy-port=) for the installer, in the
form above. Leave Port empty when the URL carries one. The drawer checks both fields
as you type, against the port and format rules below, and leaves a value that fails them
out of the command, with the reason under its field.
- Port. A port in the URL and a
--proxy-port=must match. If they differ, the installer stops and names both, rather than picking one.--proxy-port=without--proxy=is also refused. With no port at all, the scheme’s default applies:80forhttp://and443forhttps://. Most proxies listen on neither, so name the port. - Format. The URL is
http://orhttps://, then the host, then an optional port, and nothing after that (no path). - Credentials.
user:password@is supported, but percent-encode any special character in the user name or password. For example,@becomes%40, so the passwordp@ssis written ashttp://user:p%40ss@proxy.corp.example:3128. Apart from the:between user name and password, only letters, digits and- . _ ~may appear unencoded. The installer refuses anything else before it changes the host. The credentials are stored only in the agent’s configuration file, which only the agent’s service user and root can read. They are hidden in the agent’s log and never sent to WatchTower15. sudoand environment variables.sudonormally drops variables such asHTTPS_PROXYandWT15_PROXYfrom your shell. Pass the proxy as a flag, as above, or name the variable explicitly (sudo WT15_PROXY=http://proxy.corp.example:3128 sh -s -- …).- The proxy must allow the API. It must allow
CONNECTto the WatchTower15 API,api.watchtower15.com, on port 443; a proxy that allows by address allows the three addresses in the Restricted egress tab under Install. The agent’s traffic stays TLS-encrypted end to end, and the proxy sees only the hostname. - The server’s IP address. WatchTower15 records the address your server’s requests arrive from. Behind a proxy, that is the proxy’s outbound address, so every server behind the same proxy is shown with the same IP address.
A proxy set in the standard HTTPS_PROXY variable is still honoured, but only as a
fallback when --proxy is not given. See the
configuration reference.
In a container
Section titled “In a container”In a container there is no service: the install script runs the agent in the
foreground, and the container runtime owns its lifecycle. Mount a volume at
/var/lib/wt15-server-agent. It holds the agent’s credentials, its
last profile and the data
it keeps while it cannot reach WatchTower15;
without it, a re-created container enrols again as a new server, starts with no profile
until it reaches WatchTower15, and loses the data it kept during an outage. A re-created container that keeps an
explicit WT15_SUPPLIER_IDENTIFIER and the same host name continues its
server instead.
In a container other than Docker (Podman, LXC, containerd), give each container its own
WT15_SUPPLIER_IDENTIFIER. Without one, every such container on a cloud instance can report the instance’s ID and
Name tag, so the containers share one identity, and one container’s install would take over another’s server.
To choose the profile the container’s server joins,
set WT15_PROFILE in its environment, to the profile’s uuid or slug. The install script
checks it as on any host (If the profile is not found) and
gives it to the agent. The agent’s own WT15_PROFILE_UUID and WT15_PROFILE_SLUG still
work. Once the container has enrolled, its volume holds the server’s credentials, so a
restart keeps its server and its profile, and the check is skipped. A container that starts
the agent without the install script is refused by the agent itself: it logs
profile "<value>" not found in this organisation; not enrolling and exits at once, with
no retries.
Uninstall
Section titled “Uninstall”Run the installer in uninstall mode, as root:
curl -fsSL https://api.watchtower15.com/api/agent/install-script | sudo sh -s -- --uninstallIt needs no token. It stops the agent, then removes its systemd unit, the uninstall
unit and its script, the wt15-server-agent command, the
logrotate drop-in, /var/lib/wt15-server-agent, /etc/wt15-server-agent, the agent’s log and
the wt15-server-agent service user, and prints WatchTower15 agent removed. Anything it could
not remove is named: WatchTower15 agent removed, except: …. A state directory that is a mount
point is emptied and stays. On a host behind a proxy, add -x <proxy> to that curl, as for the
install.
In a container it removes nothing, and prints No systemd here. In a container, remove the container and its volume. Do that instead.
On a host that cannot fetch the script, the same by hand:
sudo systemctl disable --now wt15-server-agentsudo systemctl disable --now wt15-server-agent-uninstall.path # where the uninstall unit is installedsudo rm -f /etc/systemd/system/wt15-server-agent.service \ /etc/systemd/system/wt15-server-agent-uninstall.path \ /etc/systemd/system/wt15-server-agent-uninstall.service \ /usr/local/bin/wt15-server-agent /etc/logrotate.d/wt15-server-agentsudo systemctl daemon-reloadsudo rm -rf /var/lib/wt15-server-agent /etc/wt15-server-agent /usr/local/lib/wt15-server-agent \ /var/log/wt15-server-agent.log*sudo userdel wt15-server-agent/var/lib/wt15-server-agent holds the agent binary, its credentials, its last profile,
the data it keeps while it cannot reach WatchTower15, its log positions (spool/logs/) and the
schema state of the databases it collects (see What the agent keeps on the
host);
/etc/wt15-server-agent holds its configuration, your organisation’s install token included,
and /usr/local/lib/wt15-server-agent the uninstall unit’s script. Removing the agent
stops reporting; the server remains in your dashboard until you delete it there.
Installing the agent again on the same host continues that server, monitored or archived, when the host has an
instance ID (see Lifecycle at a glance); delete the server first to start a
new one. Uninstalling removes /etc/wt15-server-agent, so a host whose ID you set by hand needs the same
--supplier-identifier on the next install, or it enrols a new server. The server keeps its own profile: a
--profile on the new install is not applied, and you change the profile from (change) on the Profile card of the server’s Info tab. The agent reports
the host’s self-reported tags by the profile its install names (your default profile when it names
none), and WatchTower15 filters them by the server’s, so name the server’s own profile with --profile to keep them
arriving as before. Deleting the
server first removes the agent by itself, where its uninstall unit is installed: see Deleting a
server.
Related
Section titled “Related”- Configuration reference — environment variables.
- Fleet & profiles — central configuration.
- Upgrades — keeping agents current.