Version 2.1.0
A performance, reliability and security release, tested against fleets of 1,000+ hosts.
Read this before upgrading
Three lines in your docker-compose.yml changed. docker compose pull will not apply them, because your compose file is yours.
In the server service:
ports:
- "${PORT:-3000}:${PORT:-3000}" # was "3000:3000"
hostname: patchmon-server # newIn the guacd service:
image: guacamole/guacd:1.6.0 # was :latestThen:
docker compose pull
docker compose up -dSkip those lines and PatchMon still starts, but changing PORT will break the healthcheck and remote console will not run on ARM.
Migrations run at startup and there are no other steps. Large fleets should expect a pause on first boot while the database rebuilds indexes: a few seconds on SSD or NVMe, a few minutes on slower storage. PatchMon does not serve requests while this runs, so give it time rather than assuming it has hung.
Afterwards, update your agents and set the TLS mode on each email destination.
Two dashboard numbers will change, both because they were wrong before:
- Hosts PatchMon holds no package data for were counted as Up to date. They now appear under a new Awaiting data slice, so your "Up to date" figure may drop.
- GetHomepage widget totals left out hosts you had created but not yet enrolled. The widget now counts the same way as the dashboard, so the total may rise.
The feedback portal is live
Ideas, voting, the roadmap and the changelog now live at feedback.patchmon.net. GitHub Issues stays for bug reports.
Agents send far less, and you can see what they sent
- Agents upload only what has actually changed. A routine check-in drops from around 2 MB to around 1 KB.
- The Package Reports and Agent Queue tabs are replaced by a single Agent Activity timeline: every check-in, what kind it was, and which sections were updated or skipped. History is kept for 30 days by default and is configurable.
- Update your agents to get the reduced traffic. Older agents keep working as they are.
You can tell at a glance what is actually wrong
- Four separate status pills per host: connection, reporting, reboot pending and updates. A dropped connection no longer looks the same as a dead host.
- Status reads correctly without relying on colour alone.
- Uptime is live, rather than frozen at whatever the last report said.
- The host down alert watches the live connection instead of inferring from how long ago a host last reported. It reacts in seconds and stops flagging hosts that are plainly still connected. It is now called Host Agent Down.
Large fleets are fast again
- The Packages page loads in a fraction of a second where it previously took around ten.
- Hosts, Packages, Repositories and Alerts are paginated, with a page size that sticks between visits.
- The dashboard and sidebar no longer pull your entire host list just to show a count.
- The package total is no longer capped at 10,000, so the figure matches your database.
- Package search no longer errors out on Docker installs.
Patch runs no longer get stuck
- Stalled runs are timed out and cleared automatically instead of sitting at "running" for ever, and the timeout is configurable.
- A run is marked as disconnected if the agent drops mid-patch, and corrected if the agent comes back with a result.
- Stop Run always stops the run, even when the agent is offline, unresponsive, or holding open output from a child process.
- Long-running jobs that are genuinely still working are no longer marked as timed out.
Your distribution is read correctly
A large sweep through how the agent reads each package manager, so counts match what the host itself reports.
- Fedora and RHEL family: correct upgrade commands rather than install, banner lines no longer parsed as packages, duplicate upgradable entries removed.
- Rocky Linux:
RLSA-security advisories are recognised, so security updates are classified correctly. - Debian and Ubuntu: deb822 sources files are read properly, security updates are identified from the origin rather than the whole line, and patch all applies upgrades that pull in new packages instead of leaving them pending for ever.
- Raspberry Pi: security updates are no longer misfiled as ordinary updates, and multiple installed kernel variants no longer produce a false reboot required.
- Arch and Manjaro: inventory is collected without needing
pacman-contrib, kernel detection is fixed, and a broken pacman database is reported as an error rather than as no updates. - FreeBSD: the correct installer is served.
- LXC containers: uptime is the container's own rather than the host's.
- A host that failed to check for updates is reported as failed, rather than as fully patched.
Windows
- Windows hosts that never appeared in PatchMon at all should now appear. Some installers write stray null characters into their registry entries, and a single one anywhere in a host's package list caused every report from that host to be rejected. MeshCentral's agent is a common source, so this may account for missing hosts in mixed fleets. The same protection now covers Docker and compliance data.
- The agent no longer resets its own server address on first start.
- Updating the agent no longer fails with "Access is denied".
- The uninstall script works on PowerShell 5.1.
- Non-English characters are handled correctly in reports, collectors and install scripts.
- Reboot detection reads the registry consistently, and the agent reports its real version instead of
0.0.0. - Windows builds, linting and runtime tests now run in CI.
Email that tells you why it failed
- SMTP destinations have a proper TLS choice (STARTTLS, SSL, none, or automatic) instead of a single toggle. Existing destinations keep working, but picking the right one means a broken or tampered relay fails rather than quietly sending anyway.
- Port 465 servers requiring implicit TLS now work, instead of failing with an end-of-file error.
- A new Send test email button does a real send and tells you which step failed, whether that is the connection, TLS, the login or delivery, with the mail server's own message.
- Long lines and long subjects are encoded properly rather than being mangled in transit.
Security
- Client IP addresses could be spoofed behind a reverse proxy, which affected rate limiting, login lockouts and activity log entries. A new trusted proxy setting controls which upstream addresses are believed, and the default is correct for standard deployments.
- The rate limiter could leave a counter with no expiry, stranding an address at HTTP 429 with no way back.
- Every issued token is bound to a session, and refresh tokens are no longer accepted in place of a sign-in token, so signing out, revoking a session, changing a password and deactivating an account take effect immediately.
- Accounts created through the initial setup or the sign-up page could not be signed out remotely, because their session had no way to be revoked.
- Linking a Discord or single sign-on identity to an existing account now requires a verified email address and explicit confirmation from the account owner, rather than happening automatically on an email match.
- Two-factor verification requires the first sign-in step to be completed successfully before the code is accepted.
- Sign-in messages are uniform, and no longer vary depending on whether an account exists for the address entered.
- Single sign-on fixes: an error during sign-on could assign the wrong role to a new account, logins were rejected when the provider reported email verification in an unexpected format, and saving settings while a user was signing in applied the wrong auto-create behaviour to that login.
- The idle session timeout setting now works.
- Server address, port and package name inputs are validated more strictly before they reach the installer scripts or the system package manager.
- Repository credentials are redacted from agent logs, including when an update check fails.
- Images and release binaries are published with an SBOM and build provenance.
The API now agrees with the interface
If you have ever queried the API and found a host reported as active while the interface showed it as Inactive, that is fixed.
The status field was never a liveness signal. It records how far a host got through enrolment, so once a host has checked in it stays active for ever, even if it never reports again. The interface has always calculated liveness separately, on the fly, from when the host last reported.
Rather than change what status means and break anyone relying on it, host responses now carry an extra effective_status field holding exactly what the interface displays: pending, active or inactive. It is available on the scoped hosts API, the admin host list, the hosts-in-a-group listing and host detail. Alongside it, reporting_state tells you how fresh a host's data is (reporting, overdue, stale) and update_state tells you whether it needs patching.
The API documentation now sets out which of these four fields answers which question.
Also in this release
- Compliance scanner installs complete reliably, including on Ubuntu 24.04 and Rocky Linux 8 and 9. Scan history and rules search and sort properly, timings are correct, and stalled scans are swept hourly rather than daily.
- The daily compliance content check no longer re-downloads the same archive every day.
- Remote console runs on ARM again.
- Changing the
PORTsetting works, rather than breaking the healthcheck. - Background jobs honour Redis TLS and Redis usernames, so they no longer fail while the rest of the server connects fine.
- A sweep through every filter, sort and clickable stat card in the app so they all do what they say.
- Refresh now refreshes the whole screen rather than a single panel, and routine actions no longer wipe unsaved edits elsewhere on the page.
- The legacy bare-metal installer is removed.
- Dependency and toolchain updates across the server, agent and frontend.
New and changed settings
All new settings are optional and defaulted correctly. None needs setting on a standard install.
Setting | Default | Purpose |
|---|---|---|
| empty | CIDRs of chained proxies, for real client IP resolution. Only needed when proxies are chained, for example Cloudflare in front of Nginx Proxy Manager. |
| 30 (minimum 5) | Minutes before a stuck patch run is marked timed out. |
| 30 (range 7 to 365) | How long Agent Activity history is kept. |
| off | Go profiling endpoints, on a loopback-only listener. |
| 6060 | Profiling port. Never published by compose. |
| unset | Existed already but had no effect. Now functional. 0 disables it. |
One default has changed. ENABLE_LOGGING now defaults to true. The server previously wrote no application logs at all unless this was explicitly turned on, which made every "check the logs" instruction useless. If you have ENABLE_LOGGING=false in your .env, or logging turned off in Settings, that is still honoured. If you never set it, you will start seeing logs after upgrading.
Every fix in this release
Changes since v2.0.2. Numbers refer to issues and pull requests on github.com/PatchMon/PatchMon. Where a fix landed without a pull request, the commit is given instead.
Agent: package managers and OS detection
Issue | PR | Fix |
|---|---|---|
LXC containers reported the host's uptime rather than their own | ||
dnf banner lines parsed as packages on the RHEL family, inflating outdated counts | ||
Kernel detection broken on Arch | ||
False reboot required on Raspberry Pi 5 with multiple kernel variants | ||
Patch all now applies apt upgrades that pull in new packages, so kernel meta-packages stop reappearing as pending | ||
Wrong update command on Fedora ( | ||
Windows reboot registry key read inconsistently | ||
Problem updating a VM | ||
Raspberry Pi security updates classed as normal updates | ||
Successful yum dry runs misread as patch validation failures | ||
Agent behind an outgoing proxy | ||
Manjaro and Arch needed | ||
Rocky Linux | ||
deb822 sources files required | ||
Architecture token reported as the current version for newly seen packages | ||
Installed kernel version regressed on Debian 13, masking a real pending reboot |
Agent: Windows
Issue | PR | Fix |
|---|---|---|
Agent reported its version as | ||
Uninstall script failed on PowerShell 5.1 | ||
A single null character in a registry-sourced package name caused every report from that host to be rejected, so the host never appeared in PatchMon at all | ||
Agent update failed with "Access is denied" | ||
Agent reset its own server address on first start | ||
Package names with non-English characters arrived mangled or cut short, for example "Microsoft Visual C++ 2015-2022 Redistributable (x64" | ||
Wrong install command shown when setup was interrupted | ||
Null character handling extended to the Docker and compliance endpoints |
Agent: connectivity and reporting
Issue | PR | Fix |
|---|---|---|
Unhandled curl exit code 6 (unknown host) | ||
Agent did not reconnect over WSS, and the reconnect loop stalled silently | ||
- | Report failing with status 429 | |
Installer used a deprecated | ||
Auto-enrolment installer IP address and hostname handling | ||
Proxmox auto-enrolment script documented a |
Patching
Issue | PR | Fix |
|---|---|---|
Patch all button not removed | ||
Patching policy exclusions | ||
- | Patch runs stuck in Running indefinitely and never cleaned up | |
Stop and timeout had no effect while a child process held the output pipes |
Compliance
Issue | PR | Fix |
|---|---|---|
Incorrect timing shown | ||
- | OpenSCAP install failed but reported ready | |
Could not install OpenSCAP on Ubuntu 24.04 | ||
Daily content check re-downloaded the same archive every day | ||
No SCAP content found for Rocky Linux 8 and 9 | ||
Stalled scans were reaped after three hours but swept only daily | ||
Bulk-scan host picker always showed a red status dot |
Dashboard, hosts and interface
Issue | PR | Fix |
|---|---|---|
Packages and Installations hover with no action | ||
Packages summary cards scoped to their heading, so the count and the list agree | ||
Column picker | ||
Several issues on the Automations page | ||
- | Reporting and alerts not loading | |
Double scroll bar on Hosts and Packages | ||
- | Package count capped at 10,000 | |
Errored segment counted stale-reporting hosts rather than failed jobs | ||
Errored segment navigated to the wrong filter | ||
Connection status on Hosts not clickable | ||
Loading states overflowed the page | ||
Host setup modal sat behind the sidebar | ||
Group selection cut off | ||
Hosts with no package data counted as Up to date, now shown under a new Awaiting data bucket with a matching list filter | ||
- | A host added but not yet enrolled showed green Reporting and Up to date pills. Never-reported hosts now read Awaiting report and No package data on host detail, the Hosts table and cards, and the compliance host picker, with a new Awaiting report option on the Reporting filter |
Integration API
Issue | PR | Fix |
|---|---|---|
Scoped API reported | ||
GetHomepage | ||
GetHomepage statistics query rewritten: around 277 ms to around 30 ms at 3,000 hosts and 3.74M package rows, with no spill to disk |
Sign-in, users and settings
Issue | PR | Fix |
|---|---|---|
Password rules not applied properly | ||
- | Single sign-on: provider unreachable | |
Last login not recorded on local sign-in | ||
- | Admin users lost privileges after a full stack restart | |
| ||
A correct single sign-on configuration logged nothing at startup, so operators following the guide assumed it had failed. One confirmation line is now logged, and the operator guide documents that Authentik needs a Signing Key set on the provider |
Issue | PR | Fix |
|---|---|---|
- | End-of-file error on port 465 implicit TLS | |
Email lines too long | ||
- | Use TLS tickbox never persisted, so STARTTLS was skipped |
Server, Docker and infrastructure
Issue | PR | Fix |
|---|---|---|
Background jobs ignored | ||
Background jobs ignored | ||
guacd not running on arm64 | ||
Changing | ||
Auto-enrolment docs linked to the wrong section | ||
Dev Docker image showed "Build frontend first" | ||
Rate limiter leaked Redis counters, stranding an address at HTTP 429 |
Changes without an issue number
PR | Change |
|---|---|
Redis-backed distributed registry for agent presence | |
Quoted paths in the agent Makefile | |
Windows agent update | |
Compliance scans rejected when the scanner is unavailable | |
Autofocus on the login form | |
Logic defect audit | |
Version resolved from the git tag, release manifest parsing, pre-release numbering | |
Filter and sort audit across the interface | |
Compliance installation improvements | |
Isolation and security improvements | |
Reboot detection across Debian, Arch, Raspberry Pi and Windows | |
Redis ACL, dnf makecache, apt only-upgrade, dev Dockerfile | |
SMTP transport security resolved identically on all three send paths | |
Windows text encoding across reports, collectors and install scripts | |
Windows build, lint and runtime test coverage in CI | |
Correct installer served for FreeBSD hosts | |
Host OS sent when requesting the install script | |
Broken pacman database no longer reported as no updates | |
Both connection counts filter the host list | |
Installer detects whether systemd is running, not merely installed | |
One copy of the agent scripts served, not two | |
SBOM and build provenance published for images and release binaries | |
Refresh refreshes the whole screen, not one query |
What is coming next
Bug fixes are tracked in GitHub milestones. Each milestone lists the issues targeted at that release, so you can see which fixes are queued for the next version and follow anything you have reported through to the release it lands in. The milestone for this release records every fix that went into it.
Feature requests are not tracked on GitHub. They live on feedback.patchmon.net, where you can propose an idea, vote on the ones already there, and see what has been accepted onto the roadmap.
Please do note, that future releases will be small and in more of an agile format, the community got together and we discussed this and other project status / plans - the recording of this is on the YouTube channel.
We understand that there are still some bugs under Compliance, Windows integration and Docker integration. These are going to be addressed over the next few more minor releases.
Questions about this release
This release has its own discussion thread, linked at the top of this release page and filed under New Release Discussions.
If something here is unclear, an upgrade step has not gone to plan, or you think this release has changed behaviour you were relying on, please check that thread first. Others are likely to have hit the same thing, and answers about this specific release are collected there rather than spread across new issues.
Open a new issue for a bug you can reproduce that is not already covered in the thread.
Many thanks, from Iby and the PatchMon team.
