Chapter 8 of 26
Patching Overview
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) orpatch_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 topatch_package;patch_allcannot be dry-run because the agent's bulk upgrade path does not support--dry-runfaithfully. - 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 runsapt-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
pkgon FreeBSD, the agent runspkg upgrade -nor the equivalent install-no-run. - For
pacman, the agent usespacman -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:
- The validation run is marked
approved(terminal) and preserved with its output for audit. - A new patch run is created with
dry_run=false, linked to the validation viavalidation_run_id. - The new run is enqueued against the effective policy (so "Approve & Patch" at 14:00 on a host with a
03:00fixed-time policy produces ascheduledrun, not an immediate one). - 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
apksupport 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
approvedfor that host by the server, plus runswinget upgrade --allfor 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:
- 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.
- 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
patchingmodule is disabled.
You can also enter the Patching UI from:
- A package link in the Packages page. "Patch this package" starts a
patch_packagewizard 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:
- You click Patch all on a Host Detail page. The Patch Wizard opens, pre-loaded with the host.
- You optionally override the policy (e.g. "Run immediately" on a host that has a delayed policy) and click the fire button.
- The browser calls
POST /patching/triggerwithpatch_type=patch_all. Becausepatch_allcannot be dry-run, the run starts inpending_approvalif you ticked "Submit for approval", or goes straight toqueuedotherwise. - The server inserts a
patch_runsrow, snapshots the effective policy onto it, and enqueues arun_patchtask on thepatchingasynq queue. If the policy introduces a delay, asynq schedules the task for the future and the run status showsscheduled. - When the task dequeues, the server sends a
run_patchWebSocket message to the agent connected for that host. - The agent flips the run to
running, callsapt-get --with-new-pkgs upgrade -y(or the equivalent for the OS), and streams stdout/stderr back overPOST /patching/runs/{id}/outputin short chunks. On Debian and Ubuntu,--with-new-pkgslets 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. - The server fans each chunk out to any browsers subscribed to
GET /patching/runs/{id}/stream, and persists the combined output to the database. - On success the agent sends a final
completedstage with the authoritative shell output. The server marks the runcompleted, emits apatch_run_completednotification, and flags the host as "awaiting post-patch report" so the next inventory sync can update the package status. - 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.
Related Documentation
- Running a Patch: step-by-step, from trigger through live log to "patched".
- Patch Policies and Scheduling: configure when patches actually run.
- Patch History and Live Logs: work with the Runs & History table and live terminal output.
- Release Notes: 2.0.0: the release that introduced the patching module.