Skip to main content
PathMon

Chapter 8 of 15

Installing the PatchMon Agent

Updated Read the full guide

Overview

The PatchMon agent is enrolled through the web UI's Add Host wizard. The wizard provisions a host record in the database, issues a one-time bootstrap token, and builds a ready-to-paste one-liner. Running that command on the target host downloads the platform-specific agent binary over an authenticated HTTPS channel, writes config and credentials, registers a system service, and opens a persistent WebSocket connection back to the server.

This page covers the UI-driven install flow end to end. For everything that happens after the first check-in (CLI commands, service management, logs, updates), see Managing the PatchMon Agent. For bulk enrolment on container hosts, see Proxmox LXC Auto-Enrollment Guide.

How it Works

  1. The web UI calls POST /api/v1/hosts (admin-authenticated) to create a host row and returns a plaintext api_id / api_key pair. This is the only time the plaintext key is ever exposed.
  2. The wizard builds an install URL (GET /api/v1/hosts/install?os=<linux|freebsd|windows>) and pre-shares the credentials via X-API-ID / X-API-KEY headers.
  3. The server responds with an OS-specific install script. A short-lived bootstrap token (5-minute TTL, single-use) is embedded at the top of the script so the host can exchange it for its permanent api_id / api_key via POST /api/v1/hosts/bootstrap/exchange.
  4. The installer auto-detects architecture (via uname -m or PROCESSOR_ARCHITECTURE), downloads the matching agent binary from GET /api/v1/hosts/agent/download, writes /etc/patchmon/config.yml and /etc/patchmon/credentials.yml (or the Windows equivalents), and starts the service.
  5. On first serve, the agent opens a WebSocket to /api/v1/agents/ws and sends an initial report. The wizard polls /api/v1/ws/status/{apiId} every 2 seconds and moves through four states: Waiting for connection → Connected → Receiving initial report → Done. It then redirects you to the host detail page.

Prerequisites

Before you enrol a host, make sure:

  • You can log into PatchMon as a user with the can_manage_hosts permission (admin, superadmin, or a custom role with that permission).
  • The target host can reach the PatchMon server over HTTPS (TCP/443 or whatever port your reverse proxy exposes).
  • The target host's clock is correct. The installer checks this and will warn (or abort interactively) if the system time looks wrong. TLS and signed tokens will both fail silently if the clock drifts more than a few minutes.
  • On Linux / FreeBSD you have root access (or sudo). On Windows you have an elevated PowerShell (Run as Administrator).

Step-by-Step Walkthrough

Step 1: Open the Add Host Wizard

Navigate to Hosts → Add Host in the web UI. A four-step wizard appears:

Step Label What you do
1 Choose OS Pick Linux, FreeBSD, or Windows
2 Host details Friendly name, host groups, optional integrations
3 Copy command Copy the generated install one-liner
4 Connection Wait for the agent to check in

Step 2: Choose the Operating System

Pick the OS of the target host. This determines which installer the server serves and which binary is downloaded.

OS Installer Binary format
Linux POSIX shell script (patchmon_install.sh) patchmon-agent-linux-<arch>
FreeBSD POSIX shell script (same as Linux, with os=freebsd query parameter) patchmon-agent-freebsd-<arch>
Windows PowerShell script (patchmon_install_windows.ps1) patchmon-agent-windows-<arch>.exe

The architecture is auto-detected at install time: you do not pick it in the UI. The installer maps uname -m (or PROCESSOR_ARCHITECTURE on Windows) onto one of amd64, arm64, arm, or 386 and downloads the matching binary.

Windows 32-bit (x86) is not supported. The installer aborts with a clear error. All Microsoft-supported Windows versions as of 2026 are 64-bit only.

Step 3: Host Details

Fill in the form:

Field Required Notes
Friendly Name Yes Free-form label shown in the UI (e.g. web-01.prod). Does not have to match the real hostname; the real hostname is learned from the agent's first report.
Host Groups No Tick one or more groups to pre-tag the host. Groups can be changed later.
Docker integration No Enables Docker container/volume/image reporting. Can be toggled later from the host detail page.
Compliance integration No Enables the OpenSCAP compliance scanner. Can be toggled later.

Click Next. The UI calls POST /api/v1/hosts to create the host record and receives a plaintext api_id and api_key back. These are only rendered into the copy-paste command on the next step. They are never stored or shown again in the UI. If you lose the command before running it, regenerate credentials from the host detail page (see Managing the PatchMon Agent).

Step 4: Copy the Install Command

The wizard now shows a one-liner tailored to the OS you picked. Examples:

Linux one-liner:

curl -s "https://patchmon.example.com/api/v1/hosts/install" \
  -H "X-API-ID: patchmon_a1b2c3d4" \
  -H "X-API-KEY: <64-char-key>" | sudo sh

FreeBSD one-liner (note: no sudo; FreeBSD installs run as root directly; use su - first if you are not root):

curl -s "https://patchmon.example.com/api/v1/hosts/install?os=freebsd" \
  -H "X-API-ID: patchmon_a1b2c3d4" \
  -H "X-API-KEY: <64-char-key>" | sh

Windows one-liner (elevated PowerShell, single line):

Invoke-WebRequest -Uri "https://patchmon.example.com/api/v1/hosts/install?os=windows" -Headers @{"X-API-ID"="patchmon_a1b2c3d4"; "X-API-KEY"="<64-char-key>"} -UseBasicParsing -OutFile "$env:TEMP\patchmon-install.ps1"; & "$env:TEMP\patchmon-install.ps1"

Click Copy command. The wizard advances to Step 5: Connection automatically.

Windows Options

On the Windows step, two optional tick-boxes are shown:

Option When to use
Self-signed certificate (SSL bypass) Your PatchMon server uses a private/internal CA that Windows does not trust. The emitted command sets [Net.ServicePointManager]::ServerCertificateValidationCallback = { $true } before calling the server.
Use curl instead of Invoke-WebRequest Some hardened / older Windows hosts fail with Invoke-WebRequest due to TLS 1.0/1.1 negotiation or chunked-transfer issues. Ticking this box uses curl.exe (shipped with Windows 10 1803+ and Server 2019+) instead.

If your server is using a commercially signed TLS certificate (Let's Encrypt, commercial CA), leave both unticked.

The --force Flag (Linux only)

The Linux installer supports a --force flag that bypasses broken apt packages during dependency installation. The wizard does not surface this by default. If apt-get update or apt-get install curl fails on the target host, re-run the install command with --force appended after sh:

curl -s "https://patchmon.example.com/api/v1/hosts/install?force=true" \
  -H "X-API-ID: ..." -H "X-API-KEY: ..." | sudo sh -s -- --force

Step 5: Run the Command on the Target Host

Paste the command into a terminal on the target host.

Linux / FreeBSD: Run as root or with sudo. The installer refuses to run otherwise.

Windows: Right-click PowerShell → Run as Administrator, then paste.

The installer will:

  1. Verify the system date/time (interactive confirmation when run on a TTY; silent continue when piped).
  2. Detect the package manager (apt, dnf, yum, zypper, pacman, apk, or pkg) and install curl if missing.
  3. Exchange the bootstrap token for the real api_id / api_key via POST /api/v1/hosts/bootstrap/exchange.
  4. Create /etc/patchmon/ (Linux/FreeBSD) or C:\ProgramData\PatchMon\ (Windows) with config.yml and credentials.yml, both with 0600 / Administrator-only permissions.
  5. Download the matching agent binary from GET /api/v1/hosts/agent/download?arch=<arch>&os=<os>.
  6. Run patchmon-agent ping to confirm credentials work.
  7. Register the service:
    • systemd on most Linux distros: /etc/systemd/system/patchmon-agent.service
    • OpenRC on Alpine: /etc/init.d/patchmon-agent
    • rc.d on FreeBSD: /usr/local/etc/rc.d/patchmon_agent
    • Crontab fallback if no init system is detected
    • Windows Service Control Manager on Windows: service name PatchMonAgent, startup type Automatic
  8. Start the service, which opens the WebSocket and sends the initial system report.

On a clean host with a working network, the whole process takes 10–30 seconds.

openSUSE and SLES: the installer detects zypper and completes successfully, but package inventory on SUSE-family hosts is coming soon. The agent has no zypper backend yet, so step 8 fails with unsupported package manager: unknown and the host stays on "Waiting for initial system report" rather than becoming active. A successful install on these systems does not mean the host is being monitored. Follow and vote for zypper support at feedback.patchmon.net.

Step 6: Watch the "Waiting for Connection" Screen

The wizard now shows the connection progress. It polls /api/v1/ws/status/{apiId} every 2 seconds and transitions through four states:

State Meaning
Waiting for connection No WebSocket has been opened yet. The installer is still running on the host, or the host cannot reach the server.
Connected The agent has opened a WebSocket but has not yet sent a report. The initial report runs in the background right after serve starts.
Receiving initial report The agent sent a report and the server is processing it (OS type, hostname, IP, architecture, packages).
Done Enrolment is complete. After a brief "Done" state the wizard redirects you to Hosts → .

If you close the wizard before reaching Done, the enrolment still completes in the background. The host appears in the Hosts list with status "Pending" until the first report lands, then flips to "Active".

What Gets Installed

Path (Linux / FreeBSD) Path (Windows) Purpose
/usr/local/bin/patchmon-agent C:\Program Files\PatchMon\patchmon-agent.exe Agent binary
/etc/patchmon/config.yml C:\ProgramData\PatchMon\config.yml Agent configuration
/etc/patchmon/credentials.yml C:\ProgramData\PatchMon\credentials.yml API credentials (api_id, api_key)
/etc/patchmon/logs/patchmon-agent.log C:\ProgramData\PatchMon\patchmon-agent.log Rolling log file
/etc/systemd/system/patchmon-agent.service (systemd) systemd unit
/etc/init.d/patchmon-agent (OpenRC) OpenRC init script
/usr/local/etc/rc.d/patchmon_agent (FreeBSD) FreeBSD rc.d script
Windows Service PatchMonAgent Service Control Manager entry

For the full reference of every config.yml parameter, see Agent Configuration Reference (config.yml).

Troubleshooting First Check-in

If the "Waiting for connection" screen never moves past its initial state, work through the checks below in order.

1. Host cannot reach the server

From the target host:

curl -v https://patchmon.example.com/health
# expected: HTTP/1.1 200 and body "healthy"

If this fails:

  • DNS: nslookup patchmon.example.com or dig patchmon.example.com. Fix /etc/resolv.conf or update your internal DNS.
  • Routing / firewall: traceroute patchmon.example.com and check outbound TCP/443 is allowed. Corporate firewalls frequently block egress to new hostnames.
  • Proxy: if the host is behind an outbound HTTP proxy, set HTTPS_PROXY and HTTP_PROXY in the environment before pasting the install command.

2. Certificate validation fails

Symptoms: curl: (60) SSL certificate problem on Linux, or Could not establish trust relationship for the SSL/TLS secure channel on Windows.

  • Preferred fix: install your CA into the host's system trust store:
    • Debian/Ubuntu: copy the CA into /usr/local/share/ca-certificates/ and run update-ca-certificates.
    • RHEL/Rocky/Fedora: copy the CA into /etc/pki/ca-trust/source/anchors/ and run update-ca-trust.
    • Alpine: apk add ca-certificates, then copy and update-ca-certificates.
    • Windows: import the CA into Local Computer → Trusted Root Certification Authorities via certlm.msc.
  • Quick bypass (lab only): in the PatchMon web UI, go to Settings → Server → Ignore SSL self-signed and toggle it on. The server will then serve install scripts with curl -sk and inject skip_ssl_verify: true into the generated config.yml. On Windows, tick Self-signed certificate (SSL bypass) on Step 3 of the wizard before copying the command.

Do not use skip_ssl_verify in production. It disables TLS verification entirely and exposes the agent to man-in-the-middle attacks. See the Agent Configuration Reference for more on skip_ssl_verify.

3. "CORS error" in the browser (wizard-side only)

The wizard itself calls /api/v1/hosts/install and /api/v1/ws/status/{apiId} from your browser. If either fails with a CORS error, your server's CORS_ORIGIN env var does not match the URL you are accessing PatchMon with.

Fix: set CORS_ORIGIN in the server .env to the exact origin (protocol + host + port) the browser uses. For example:

CORS_ORIGIN=https://patchmon.example.com

If users reach PatchMon from more than one URL (e.g. an external domain and an internal LAN address), comma-separate the values with no spaces:

CORS_ORIGIN=https://patchmon.example.com,https://patchmon.internal.lan

Then restart the server container (docker compose restart server). See Server Troubleshooting for the full CORS section.

4. Outbound port 443 blocked

Many cloud and enterprise networks allow port 80 but block 443 to arbitrary hosts. Test with:

# TCP reachability
nc -vz patchmon.example.com 443
# or
timeout 5 bash -c "</dev/tcp/patchmon.example.com/443" && echo "open" || echo "blocked"

If blocked, allowlist outbound TCP/443 to your PatchMon server on the egress firewall.

5. Bootstrap token expired

The bootstrap token in the install command is valid for 5 minutes and is single-use. If you copy the command, wait too long, and then run it, you will see:

ERROR: Failed to fetch credentials. Bootstrap token may have expired.
Please request a new installation script.

Go back to Step 4 of the wizard and click Copy command again. This reuses the same host record but mints a fresh token.

6. Installer aborts on clock skew

If the host's clock is more than a few minutes off UTC, TLS handshakes will fail. On Linux:

sudo timedatectl set-ntp true
sudo timedatectl set-timezone Europe/London

On Windows (elevated PowerShell):

w32tm /resync
Set-TimeZone -Name "GMT Standard Time"

Then re-run the install command.

7. Agent installed but stays "Pending"

The binary installed and the service started, but the host never flips to "Active" in the UI. Run on the target host:

sudo patchmon-agent diagnostics
sudo patchmon-agent ping
sudo systemctl status patchmon-agent        # or: rc-service patchmon-agent status
sudo journalctl -u patchmon-agent -n 50     # or: tail -n 50 /etc/patchmon/logs/patchmon-agent.log

For the detailed checks, see Managing the PatchMon Agent: Common Troubleshooting and Agent Troubleshooting.

8. Reverse proxy drops the WebSocket

If the "Connected" state never appears but HTTP requests work fine, your reverse proxy is not forwarding the Upgrade: websocket handshake. The agent opens its WebSocket against /api/v1/agents/ws. See Server Troubleshooting: Agent cannot connect over WebSocket for Nginx / Traefik / Caddy config snippets.

Re-running the Installer on an Already-Enrolled Host

The Linux installer is idempotent. If you paste the install command on a host that is already enrolled and healthy, it will:

  1. Detect existing config.yml, credentials.yml, and binary.
  2. Run patchmon-agent ping.
  3. Exit early with Agent is already configured and ping successful and leave everything untouched.

To force a full reinstall:

sudo rm -f /etc/patchmon/config.yml /etc/patchmon/credentials.yml
# then paste the install command again

Or regenerate credentials from the UI first (this rotates the API key, and the next installer run will pick up the new one).

Next Steps