Skip to main content
PathMon

Chapter 8 of 26

Patching Overview

Updated Read the full guide

What Patching Is

Patching is the PatchMon feature that lets you deploy package and security updates to your Linux and FreeBSD hosts on demand or on a schedule, with validation, approval, stop, retry, and live log streaming over WebSocket. You drive it from the Patching page in the web UI, or from the Patching tab on any Host Detail page.

Patching in 2.0 is a first-class module rather than a side-feature. Runs are persisted, queued through Redis/asynq, executed by the agent on the host, and streamed back to the browser live.


Module Gate

Both halves of the feature are gated by a capability module. If the module is not enabled on your plan, the relevant UI appears locked with an "Upgrade required" placeholder and the corresponding API routes reject the request.

UI area Required module
Patching dashboard, Runs & History, trigger a patch run, approve, stop, retry patching
Policies tab, policy assignments, exclusions, scheduled runs patching_policies

Fleet-wide the Patching page is hidden from the sidebar when patching is not enabled. The Policies tab inside the page is shown with a tier badge when patching is enabled but patching_policies is not.


Who Can Use It

Patching uses two RBAC permissions on top of the module gate:

Action Permission Route pattern
View dashboard, list runs, open a run, watch the live stream can_view_hosts GET /patching/*
Trigger a run, approve, retry validation, stop, delete a run can_manage_patching POST /patching/trigger, POST /patching/runs/{id}/approve, etc.
View policies can_view_hosts GET /patching/policies
Create, edit, delete policies and policy assignments / exclusions can_manage_patching POST/PUT/DELETE /patching/policies/*

If your role has can_view_hosts but not can_manage_patching, you will see the Patching page read-only. The action buttons (Patch all, Approve, Stop, Delete) either do not appear or return a 403.


The Three Core Concepts

Everything in the Patching module revolves around three concepts:

1. Patch Run

A patch run is one unit of patching work against a single host. Every time you click "Patch all" on a host, approve a submission, or retry a validation, a run row is created in the database.

Each run has:

  • A type: patch_all (install all available package updates) or patch_package (install a specific package or set of packages).
  • A dry-run flag: if dry_run=true, the agent reports what would change without actually installing anything. This only applies to patch_package; patch_all cannot be dry-run because the agent's bulk upgrade path does not support --dry-run faithfully.
  • A status that moves through the run lifecycle (see below).
  • A persisted shell output that captures every line of stdout/stderr produced by the package manager. Live subscribers get it streamed over WebSocket; everyone else gets the full blob on completion.
  • Optional policy metadata: the effective patch policy is snapshotted onto the run at trigger time, so you can see in run detail "which policy was in effect when this was queued".

Run statuses

The server moves a run through these statuses, visible as badges in the Runs & History table and in the run detail header:

Status Meaning
queued The execution task has been enqueued on the asynq queue, waiting for the worker to pick it up.
pending_validation A dry-run was queued for validation but has not completed yet (the host may be offline).
validated The dry-run finished successfully; the run is waiting for an operator to approve it.
pending_approval A patch run was submitted for approval without a dry-run (e.g. a patch_all that cannot be dry-run). An approver needs to sign off before the run is queued.
approved The original validation run after someone approved it. A new execution run (linked by validation_run_id) is created alongside this row and queued.
scheduled The run has been accepted but is waiting for its run_at timestamp (delayed or fixed-time policy).
running The agent is currently executing the package manager command on the host. This is the status that opens the live WebSocket stream.
completed The run finished successfully. The persisted shell_output is now authoritative.
dry_run_completed A dry-run finished successfully (terminal state for dry-runs that aren't turned into a real run).
failed The run finished with a non-zero exit status or the host reported an error.
cancelled The run was stopped by an operator clicking Stop Run (or deleted before execution). The cancel is applied authoritatively in the database first; if the agent is connected the server also sends a courtesy patch_run_stop so the running subprocess is interrupted. With this ordering, an offline or unresponsive agent can't leave the row stuck in running.
timed_out The periodic patch-run cleanup found this run still in running state past the configured stall timeout (PATCH_RUN_STALL_TIMEOUT_MIN, default 30 minutes) and marked it as timed out. The cleanup sweep runs every 10 minutes.
agent_disconnected The agent's WebSocket dropped while this run was running. The server marks every in-flight run for that host as agent_disconnected so the row doesn't sit at running indefinitely. If the agent reconnects and posts a late completed / failed / cancelled for the same run, the server will update the row to that final state.

2. Patch Policy

A patch policy controls when an approved patch run actually fires. Policies are optional; a host with no policy attached gets the implicit "Default" policy, which runs patches immediately on trigger.

Each policy has a delay type:

  • Immediate: run as soon as the task is dequeued.
  • Delayed: wait N minutes after the trigger before running (useful for "give me 30 minutes to change my mind").
  • Fixed time: run at a specific wall-clock time (HH:MM) interpreted as local time in the organization timezone (Settings → General → Timezone). Used for maintenance windows.

Policies are assigned to hosts or host groups. You can also add per-host exclusions to carve specific hosts out of a group-assigned policy. See Patch Policies and Scheduling for full details.

3. Dry-Run / Validation

A dry-run (also called a validation run) asks the agent to simulate the package installation without applying it. It exists to catch problems before you touch the host:

  • For apt-get, the agent runs apt-get -s install <packages> and parses the simulation output.
  • For dnf/yum, the agent runs the planning step that reports "what would be installed" without committing.
  • For pkg on FreeBSD, the agent runs pkg upgrade -n or the equivalent install-no-run.
  • For pacman, the agent uses pacman -S -p / pacman -Syu -p (print-only) as the validation step.

When the dry-run completes the run transitions to validated and shows you the list of packages that would be installed, including dependencies that were pulled in. If more packages would be installed than you originally asked for, the UI badges the run with Extra deps and surfaces the full list in the run detail "Packages affected" panel so you can review before approving.

Approval is the step that turns a validated dry-run into a real patching run. On approval:

  1. The validation run is marked approved (terminal) and preserved with its output for audit.
  2. A new patch run is created with dry_run=false, linked to the validation via validation_run_id.
  3. The new run is enqueued against the effective policy (so "Approve & Patch" at 14:00 on a host with a 03:00 fixed-time policy produces a scheduled run, not an immediate one).
  4. You can override the policy at approval time by picking Immediate in the approve wizard, which bypasses the delay.

Multi-OS Coverage

The agent chooses the patching back-end by detecting the host's package manager. Linux and FreeBSD patching are fully supported; Windows patching has its own path.

Package manager OSes Supported for patching
apt-get Debian, Ubuntu, Raspbian Yes
dnf RHEL 8+, Rocky, AlmaLinux, Fedora Yes
yum RHEL 7, CentOS 7 Yes
pacman Arch Linux, Manjaro Yes
pkg FreeBSD 13+ (plus freebsd-update for the base system on patch_all) Yes
apk Alpine Linux No. The agent reports apk inventory but rejects patch runs with package manager "apk" not supported for patching (apt, dnf, yum, pkg, pacman required). Alpine hosts are visible in PatchMon and get compliance scans, but patch runs on them fail on the agent side.
zypper openSUSE Leap, openSUSE Tumbleweed, SLES No, and no inventory either. Coming soon. The installer detects zypper and completes, and the agent detects the OS correctly, but the agent has no zypper backend. Package collection therefore fails with unsupported package manager: unknown and the host never completes its first report, so it sits at "Waiting for initial system report". Follow and vote for zypper support at feedback.patchmon.net.
Windows Update Agent (WUA) + WinGet Windows 10/11, Server 2019/2022/2025 Yes (separate path)

Note: The 2.0 release notes describe Linux patching generally. If you need to patch Alpine hosts, track the package manager roadmap or use your existing Alpine tooling until apk support lands.

Windows patching

When the agent detects it is running on Windows, patch runs are handled by the WUA + WinGet path rather than the Linux package-manager path:

  • Patch all installs every Windows Update currently marked approved for that host by the server, plus runs winget upgrade --all for WinGet-managed applications.
  • Patch package routes by name: strings that look like a KB... / GUID update are sent via WUA, anything else is treated as a WinGet package ID.
  • Reboot state, superseded-update cleanup, and approved-GUID sync all go through dedicated /patching/windows-updates/* endpoints used by the beta Windows agent.

Windows patching is flagged beta in 2.0 and the Run Detail page renders the same way regardless of OS. The terminal pane simply shows PowerShell / winget output instead of apt-get output.


Where Patching Lives in the UI

There are two ways into patching from the left-hand navigation:

  1. Patching (top-level sidebar item): the fleet-wide view. This is the page described in Running a Patch, Patch Policies and Scheduling, and Patch History and Live Logs.
  2. Hosts → select a host → Patching tab: the per-host view. Start a Patch all run for that host, watch its packages list, open any previous patch run for this host. The tab is hidden when the patching module is disabled.

You can also enter the Patching UI from:

  • A package link in the Packages page. "Patch this package" starts a patch_package wizard pre-loaded with the selected package and the hosts it is installed on.
  • The Dashboard patching cards (queued/running counts), which deep-link into the Runs & History tab with the appropriate status filter applied.

What Happens When You Click "Patch All"

The end-to-end flow for a single patch_all run is:

  1. You click Patch all on a Host Detail page. The Patch Wizard opens, pre-loaded with the host.
  2. You optionally override the policy (e.g. "Run immediately" on a host that has a delayed policy) and click the fire button.
  3. The browser calls POST /patching/trigger with patch_type=patch_all. Because patch_all cannot be dry-run, the run starts in pending_approval if you ticked "Submit for approval", or goes straight to queued otherwise.
  4. The server inserts a patch_runs row, snapshots the effective policy onto it, and enqueues a run_patch task on the patching asynq queue. If the policy introduces a delay, asynq schedules the task for the future and the run status shows scheduled.
  5. When the task dequeues, the server sends a run_patch WebSocket message to the agent connected for that host.
  6. The agent flips the run to running, calls apt-get --with-new-pkgs upgrade -y (or the equivalent for the OS), and streams stdout/stderr back over POST /patching/runs/{id}/output in short chunks. On Debian and Ubuntu, --with-new-pkgs lets the upgrade install packages it does not already have, which is what a kernel ABI bump needs. Without it apt holds those upgrades back and they would keep reappearing as outdated after every run.
  7. The server fans each chunk out to any browsers subscribed to GET /patching/runs/{id}/stream, and persists the combined output to the database.
  8. On success the agent sends a final completed stage with the authoritative shell output. The server marks the run completed, emits a patch_run_completed notification, and flags the host as "awaiting post-patch report" so the next inventory sync can update the package status.
  9. The Run Detail page swaps the green Live pill for a subtle Awaiting inventory report pill, then for New report received once the agent sends its next scheduled inventory report and the system knows the on-host packages reflect reality.

See Running a Patch for the step-by-step operator walkthrough, and Patch History and Live Logs for everything to do with the terminal pane and log stream.