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. On Linux and FreeBSD this only applies to patch_package; patch_all cannot be dry-run there because the agent's bulk upgrade path does not support --dry-run faithfully. Windows hosts support a dry run for both types.
  • 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). Scheduled runs do not block other runs on the host; when the window arrives the run moves to queued, or is cancelled if another run is still active on the host.
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), or a scheduled run was skipped because another run was active on the host when its window arrived. 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, or found a scheduled, queued or pending_validation run that was not dispatched within 24 hours of becoming due. 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 dnf upgrade --assumeno <packages> (or yum), which prints the transaction plan and stops before committing. Every package in the plan, including dependencies, is listed in the validation result.
  • For pkg on FreeBSD, the agent runs pkg upgrade -n or the equivalent install-no-run.
  • pacman (Arch Linux) has no validation step, because selected patching is not available on Arch and patch_all cannot be dry-run. See Arch Linux patching.
  • For apk on Alpine, the agent runs apk upgrade --simulate <packages>, which opens the package database read-only. A validation fails if apk reports an error (for example an unknown package or a conflict with /etc/apk/world), or if the plan would downgrade or replace a package, or remove one listed in /etc/apk/world.

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 (Linux and FreeBSD patch_package runs only) 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, FreeBSD and Windows patching are 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 Patch all only. See Arch Linux patching.
pkg FreeBSD 13+ (plus freebsd-update for the base system on patch_all) Yes
apk Alpine Linux 3.21 and later (apk-tools 2 on 3.21 and 3.22, apk-tools 3 from 3.23) Yes, with agent 2.1.4 or later. See Alpine patching.
zypper openSUSE Leap 15.6 and 16.0, openSUSE Tumbleweed and Slowroll, SLES 15 and 16 Yes, with agent 2.1.4 or later. See openSUSE and SLES patching.
apt-get and univention-upgrade Univention Corporate Server 5.0 and 5.2 (tested on 5.0-10 and 5.2-7) Yes, with agent 2.1.4 or later. See Univention Corporate Server patching.
Windows Update Agent (WUA) + winget Windows 10/11, Server 2019/2022/2025 Yes, with agent 2.1.4 or later (separate path)

Arch Linux patching

Arch Linux does not support partial upgrades: syncing the package databases and then upgrading only some packages can leave a package linked against libraries that are not installed yet. The Arch Wiki explains this under System maintenance: Partial upgrades are unsupported. Upgrading curl this way on an out-of-date host can stop pacman itself from starting, and then the host cannot be patched at all.

PatchMon therefore offers only Patch all on hosts that use pacman. It runs pacman -Syu --noconfirm, which syncs the databases and upgrades every package in one transaction.

Patch selected is refused at every step, all with the same message: "Arch Linux does not support partial upgrades, so PatchMon cannot patch selected packages on this host. Use Patch all."

  • The UI disables it for Arch hosts and explains why.
  • The server rejects a new patch_package run for an Arch host.
  • A patch_package run for an Arch host that was queued or scheduled before you upgraded PatchMon is marked failed with the same message when it comes due. It is never sent to the agent.
  • The agent refuses one if it still receives it, before running any pacman command. It also refuses a dry run on Arch, because pacman -Syu -p syncs the package databases without upgrading.

Older pending runs. An Arch patch_package run left waiting for validation or approval from before the upgrade can no longer be approved or retried. Delete it from Patching → Runs & History and use Patch all instead.

Alpine patching

Alpine hosts are patched with apk on Alpine 3.21 and later, with agent 2.1.4 or later: apk-tools 2 on 3.21 and 3.22, apk-tools 3 from 3.23. Every run, real or dry, plans before it changes anything:

  1. apk update refreshes the package indexes.

  2. apk upgrade --simulate works out the transaction. On a real run, if apk says the plan is unreliable because apk-tools itself has an update, the agent installs that update first (--self-upgrade-only on apk-tools 2, --preupgrade-only on apk-tools 3), says so in the run output, and plans again. If it cannot tell which apk-tools version is installed, it stops instead.

  3. The agent refuses the run, before changing anything, if the plan would downgrade or replace a package, remove a package listed in /etc/apk/world, or contains a step it does not recognise. The error names each step and its likely cause. Review the plan with apk upgrade --simulate on the host.

    apk also removes packages that nothing needs any more, for example a library a newer package no longer depends on. That is normal clean-up, so the agent lets it happen when the package is not in /etc/apk/world, and names each one in the run output: Removing gdbm 1.26-r0: no longer required by any package and not in /etc/apk/world. A dry run says Would remove instead. Removed packages are listed under the run's affected packages.

  4. A real run then applies the plan with apk upgrade.

Patch selected runs apk upgrade <packages>, never apk add, so /etc/apk/world is not changed. Packages built from the same source as a selected package are upgraded with it: selecting libssl3 also upgrades libcrypto3, because apk will not upgrade one without the other. If apk still leaves a selected package alone, the run output says so; select the packages it needs as well, or use Patch all.

Held packages. A version constraint in /etc/apk/world (for example busybox=1.37.0-r29) holds a package at that version. apk skips held packages, and so does PatchMon: the run succeeds and its output names each one, for example Held by /etc/apk/world: busybox=1.37.0-r29 (1.37.0-r30 available), along with any package held back with it. Held packages are reported as up to date in the package inventory rather than offered for patching.

If the installed version of a package breaks its own constraint in /etc/apk/world, apk refuses every upgrade. PatchMon then fails the run before changing anything and names the line to correct, for example change busybox<1.0 to busybox=1.37.0-r29 or remove the version, then retry.

Security updates on Alpine come from the Alpine security database (secdb.alpinelinux.org). An update is marked as a security update when it moves the package past a version that fixed a published issue. The agent fetches the database for the branch in /etc/apk/repositories at most every 6 hours. If it cannot reach it, it tries again after an hour, and meanwhile the report still goes through, using the last copy it fetched or, without one, marking no updates as security updates. See apk_secdb in the Operator Guide to turn the lookup off.

Reboot required is set when the installed kernel package (linux-lts, linux-virt and so on) is newer than the running kernel. Alpine containers (Docker or LXC) run the host's kernel, so they never report a reboot as required.

Univention Corporate Server patching

Univention Corporate Server (UCS) hosts use the Linux agent: choose Linux in the Add Host wizard. With PatchMon 2.1.4 or later, server and agent, they are listed as Univention Corporate Server with their release, patch level and errata level, for example 5.2-7 errata606. Older agents show UCS 5.0 as Debian 10 and UCS 5.2 without its patch level, but report and patch through apt as they do on Debian.

Inventory comes from apt, as on Debian, so the outdated packages are the errata updates published for the installed release.

Run Command
Patch all univention-upgrade --noninteractive --disable-app-updates --updateto <installed release>, for example --updateto 5.2-7
Patch selected apt-get --only-upgrade install <packages>, as on Debian

Patch all installs every errata update for the installed release through Univention's own updater, which also clears the "updates available" notice in the Univention Management Console. It never starts a release update, such as 5.2-7 to 5.2-8 or 5.0-10 to 5.1-0, and never updates App Center apps. Neither appears in the package list PatchMon shows, and a release update can need preparation and a maintenance window. Install them from the Univention Management Console, or by running univention-upgrade on the host. When a release update is waiting, the run output says so: 5.1-0 is available but updater has been instructed to stop at version 5.0-10.

Security updates. Univention publishes fixes as errata updates, not through a separate security repository, so PatchMon does not mark UCS updates as security updates.

Compliance scanning. UCS 5.2 is scanned with the Debian 12 OpenSCAP content and CIS profiles, which needs agent 2.1.4 or later. UCS 5.0 is based on Debian 10, for which no current OpenSCAP content is published, so compliance scanning is not supported on it.

Reboot required is set when an update installs a newer kernel than the one running.

openSUSE and SLES patching

openSUSE and SLES hosts report inventory and are patched with zypper, with agent 2.1.4 or later. Older agents fail every report on these hosts with unsupported package manager: unknown; update the agent to fix it.

Inventory. Installed packages come from rpm, and available updates from zypper list-updates, which leaves out packages locked with zypper addlock. An update is marked as a security update when a needed patch in the security category updates that package. Tumbleweed and Slowroll publish no patches, and an unregistered SLES host has no update repositories, so neither shows security updates. The repositories list shows each enabled zypper repository, without credentials or the registration token that SUSE Customer Center repositories carry in their URL.

Every run, real or dry, plans before it changes anything:

  1. zypper refresh refreshes the repositories. If a repository cannot be reached, the run says so and carries on with the metadata zypper already has, so one unreachable repository does not block patching.
  2. The agent asks zypper for the plan with --dry-run and prints it in the run output. It refuses the run, before changing anything, if the plan would downgrade a package or change its architecture or vendor. Removals are allowed, because an update can obsolete or replace a package, and each one is named in the run output: Removing libcurl-mini4: zypper's plan removes it, usually because an update obsoletes or replaces it. A dry run says Would remove instead.
  3. A real run applies the plan.
Run openSUSE Leap and SLES Tumbleweed and Slowroll
Patch all zypper patch --with-update: every needed patch, then any update that no patch covers, such as packages from third-party repositories zypper dist-upgrade, the only supported way to update a rolling release
Patch selected zypper update <packages> zypper update <packages>, with a warning that a partial update of a rolling release is not supported upstream

Tumbleweed with Packman or other OBS repositories. zypper dist-upgrade keeps each package with the vendor it came from, and PatchMon refuses any plan that changes a package's vendor. If you switch packages to Packman or another vendor, run zypper dup --allow-vendor-change once by hand to make that move deliberately; later runs keep the new vendor and patch normally.

Patch runs include patches that suggest a reboot, such as kernel updates, and accept licence prompts, so an unattended run does not silently skip them.

zypper updating itself. When a patch updates zypper or libzypp, zypper installs that first and asks to be run again. The agent does so, up to three times in one run, and says so in the run output. A dry run cannot see past that step: it shows only zypper's own update and says that a real run will apply more.

Locked packages. Packages locked with zypper addlock are left alone, and the run output names them. Selecting only locked packages fails the run before anything changes; remove the lock with zypper removelock to patch them.

The zypp lock. If another process holds the zypp lock, for example PackageKit or an administrator running zypper, the agent waits 30 seconds and then fails the run, naming the cause. Retry when it has finished.

Exit codes are reported in plain words. A run that needs a reboot (zypper exit code 102) or skipped a repository it could not refresh (106) succeeds with a note in the output. A run where packages were installed but an RPM script failed (107) is marked failed, so that someone checks the output and runs zypper verify on the host.

Dependency problems. If zypper cannot plan an update without a choice, for example removing a package or skipping patches, PatchMon does not choose for you. The run fails and quotes zypper's problem and the solutions it offers. Resolve it once on the host with zypper, then run the patch again.

Leap 16.0 containers made from the stock Proxmox template hit this on their first Patch all. The template ships systemd-sysvcompat, which the Leap 16.0 repository no longer carries, and the systemd update conflicts with it. Run zypper rm systemd-sysvcompat once on the container, then patch.

Transactional hosts. openSUSE MicroOS, Leap Micro and SUSE Linux Micro have a read-only root filesystem and are updated with transactional-update. They report inventory, but patch runs are refused with an explanation, and nothing is changed.

Reboot required is set when libzypp flags it after installing a kernel or another package that needs a reboot (zypper needs-rebooting), or when the running kernel is older than the newest installed one.

Windows patching

Windows hosts are patched from the same places as Linux hosts: Host Detail, the Packages page and the Patch wizard. The agent on the host must be 2.1.4 or later. For an older agent the server refuses the run with "Update the PatchMon agent on this host to v2.1.4 or later to patch Windows"; update the agent first (Host Info → Update Now).

What can be patched
Item Installed by Shown as
Windows Updates: cumulative, security, .NET, definition, driver and optional updates, and feature upgrades The Windows Update Agent on the host, from the host's configured update source (Microsoft Update or WSUS) Source Windows Update
Applications that winget can upgrade winget, by the app's exact winget Id Source winget
Applications found only in the registry Nothing Not patchable
Microsoft Store apps Nothing: they update through the Microsoft Store The available update, with a Microsoft Store app badge; not selectable
Apps pinned with winget pin Nothing until the pin is removed The available update, with a Pinned in winget badge; not selectable

The agent reads installed applications from the Windows registry and matches them against winget. PatchMon only ever asks winget to upgrade a package by its exact winget Id. An app that winget has no match for has no Id, so there is no safe way to tell winget which package to upgrade. These apps still report their available version, but they cannot be selected; the tooltip reads "Not patchable: this app was not found in winget. Update it with its own updater." Use the vendor's updater or your software deployment tool for them.

Hosts without winget (it ships with the App Installer package, which Windows Server 2019 and 2022, Server Core and LTSC editions often do not have) list every app as Not patchable. Windows Updates still patch. Windows Server 2025 includes winget as standard.

Microsoft Store apps (apps installed from the Store or as MSIX packages) keep showing their available update, but cannot be patched: "Microsoft Store app: updates through the Microsoft Store". The agent runs as the Local System account, and winget run as Local System only updates the machine's staged copy of a Store app, never the copy each user runs, so an upgrade would report success and change nothing. Let the Microsoft Store update them, or manage them with your Store or Intune policy.

Pinned apps. An app you have pinned on the host with winget pin add also keeps showing its update, with a Pinned in winget badge, and cannot be selected: "Pinned with winget pin; remove the pin on the host to update it". Pinned apps are left out of Patch all. Remove the pin (winget pin remove) and wait for the next report to patch it.

If you select a Store app or a pinned app anyway, for example from an out-of-date page, the wizard refuses the run and lists the app with the same reason.

Windows Update rows also carry badges: Security, Optional, Driver and Feature upgrade.

Patch all

On a Windows host the wizard shows two scope boxes, both ticked by default. At least one must stay ticked:

  • Windows Updates: installs every pending Windows update except drivers, optional updates and feature upgrades.
  • Applications (winget): upgrades each app that winget reports an upgrade for, one at a time.

Patch all checks the host again when the run starts, so it installs whatever is pending at that moment, not what was pending when the run was queued or validated. Approving a validated Patch all works the same way.

Drivers, optional updates and feature upgrades are never part of Patch all. Select them individually with Patch selected. Selecting a feature upgrade (for example a move to a new Windows 11 version) shows a warning in the wizard: "Feature upgrades download several GB, can take over an hour and need one or more restarts to finish."

To keep something out of Patch all on one host, hide the update in Windows (hidden updates disappear from PatchMon too), or pin the app with winget pin add. Once the next report has seen the pin, the app is shown as pinned and Patch all leaves it out. If the pin was added after the last report, the agent still checks it before upgrading: the app is not upgraded and the item ends Skipped with "Pinned in winget: remove the pin to update it".

Validate first

Windows supports a dry run for both Patch selected and Patch all, so the wizard offers the Validate step for both. A dry run searches Windows Update and asks winget whether an upgrade is available. Nothing is downloaded or installed. Each item ends Would install, with its expected restart behaviour, Not applicable, or Skipped when it could not be installed as things stand: a Microsoft Store app, an app pinned in winget, an update that needs user input, or an update that cannot install until the host has restarted. You can validate up to 100 packages per Windows host in one run.

Approve the result as you would on Linux. When you approve a validated Patch selected run, only the items that would install are carried into the real run.

Per-item results

Run Detail shows an Updates card above the terminal, with one row per update or app: Name, Source, KB, Status, Result (a hint plus the Windows or winget error code) and Restart. It refreshes while the run is active.

Status Meaning
Installed Installed by this run.
Installed with errors Windows reported the install as successful with errors. Counted as installed.
Installed, restart required Installed earlier and waiting for the host to restart. PatchMon does not install it again.
Already installed Installed before the run reached it, for example by Windows' own automatic updates.
Would install Dry run only: this would be installed.
Not applicable No longer offered by Windows Update (installed, superseded or withdrawn), no upgrade available, or an app installed for a single user (see below).
Skipped The run was stopped, the run time limit was reached, a restart is needed before more updates can install, the update needs interactive input, an earlier update in the run timed out while installing, the app was pinned with winget pin since the last report, or the app is a Microsoft Store app ("Microsoft Store app: it updates through the Store for each user, not as SYSTEM").
Failed / Aborted The install did not succeed. The Result column has the code and a hint, for example "Application is in use: close the app and retry".

One failed item does not stop the others. The run ends Failed if any item failed or was aborted, and its summary lists the first few ("2 of 7 failed: ..."). Skipped items never fail a run. They are listed in the run's summary instead, for example "1 skipped: 7zip.7zip (pinned in winget)", and a run whose only non-successful items were skipped ends Completed with that summary shown as a note.

Restarts

PatchMon never restarts a Windows host. When an update needs a restart:

  • Run Detail shows a banner: "A restart is required to finish installing updates." and the item's Restart column is marked.
  • The host is flagged as needing a reboot as soon as the run ends, without waiting for the next report, and a patch_reboot_required notification is sent.
  • The host keeps showing Reboot pending until it has actually restarted, even if Windows' own restart signals clear earlier, with the reason "Restart required to finish a patch run". PatchMon notices the restart from the host's boot time on its next report or ping.
  • An app upgrade that finished while the app was open may leave some files to be replaced at the next restart. That item is marked as needing a restart with "Installed; some files will be replaced at the next restart."
  • An update that has installed and is waiting for the restart shows as pending restart in the host's package list and is not offered again.

Restart the host when it suits you. If a restart is still pending from an earlier install, Windows may refuse further installs; those items end Skipped with "Restart pending from an earlier install; restart the host and run again."

Stopping a run, and long runs

Stop Run takes effect between items. The update or app being installed at that moment always finishes, because interrupting Windows servicing part-way is worse than waiting; the remaining items end Skipped. Cumulative updates and feature upgrades can run for a long time without output, so the agent writes a progress line every minute and the run is not timed out while it is still working. Each step has its own limit:

Step Limit
Whole run that installs Windows Updates (a Patch all with Windows Updates ticked, or a Patch selected that includes an update) 8 hours
Whole run that only upgrades apps 4 hours
Searching for Windows Updates 15 minutes
Downloading the run's Windows Updates 60 minutes, or 180 minutes when a feature upgrade is included
Installing one Windows Update 180 minutes, or 240 minutes for a feature upgrade
Upgrading one app with winget 30 minutes

A step that runs out of time says so. Windows usually carries on installing an update in the background after its install step times out, so the agent then asks Windows about that update once a minute for up to 10 minutes:

  • If Windows finishes it in that time, the item shows Installed with "Installed; Windows finished after the 180-minute limit", or Installed, restart required with "Installed; Windows finished after the 180-minute limit; restart the host to complete it". The run carries on with the next update.
  • If Windows still has not finished, the item ends Failed with "Install did not finish within 180 minutes. Windows may still be completing it; check after a restart." PatchMon does not start any more Windows Updates in that run: they end Skipped with "Not started: Windows Update was still installing a previous update". App upgrades still run, and the run ends Failed. Check the host after a restart and run the remaining updates again.

Stopping the run during those 10 minutes ends the check straight away. The limits quoted above are for an ordinary update; for a feature upgrade the install limit is 240 minutes.

Apps installed for a single user

The agent runs as the Local System account, so winget acts on apps installed for all users. An app installed for one user only cannot be upgraded by the agent: it ends Not applicable with "Not found for the system account: the app is probably installed for a single user, which the agent cannot upgrade". Upgrade it from that user's session, or reinstall it for all users.

WSUS-managed hosts

If the host gets updates from WSUS, Windows Update only offers updates that WSUS has approved for it, so PatchMon only ever installs WSUS-approved updates. WSUS stays your approval gate and PatchMon decides when they install. If the host cannot reach its update server, items fail with "Cannot reach the update server or WSUS: check proxy, firewall, WSUS URL". WSUS does not affect winget, which uses its own sources.

Licence terms and app sources

Installing an item accepts its licence terms on your behalf: the Windows Update EULA where one applies, and winget's package and source agreements. winget installers come from the winget community repository and run as Local System on the host. You choose which apps to upgrade.

Agent updates during a patch run

Agent updates are held while a patch run is queued, validating or running on the host, on every operating system. Update Now is refused with "A patch run is in progress on this host. Try again when it finishes.", automatic updates wait, and an agent that is told to update mid-run applies the update after the run and its post-patch report have finished. The reverse also holds: a patch run that reaches the agent while it is downloading or installing its own update fails straight away with "Agent update in progress, retry shortly". Run it again once the agent is back.

The terminal pane on Run Detail shows Windows Update and winget progress 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 on Linux and FreeBSD, 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.