PatchMon Ubuntu 24.04
Overview
PatchMon 2.0.2 is a self-hosted Linux patch management platform. You register the Linux hosts you want to manage, and PatchMon keeps an inventory of every package on each of them. It shows which hosts have outstanding security and regular updates, groups your hosts, schedules and runs patching jobs, and reports on the patch posture of the whole fleet. It also collects Windows update metadata, runs SCAP-based compliance scanning and provides a browser-based remote console.
This SecureAnyCloud image delivers the PatchMon server on Ubuntu 24.04 LTS, fully installed and managed by systemd, with PostgreSQL 16 and Redis 7 already configured. A working patch management console is available within minutes of launch.
What you get:
- PatchMon 2.0.2, installed from the official upstream release binary and verified by SHA256 checksum at install time
- A single statically linked server that serves both the web console and the REST API on port
3000, with the frontend and all database migrations embedded in the binary - PostgreSQL 16 as the primary database and Redis 7 as the job queue and websocket coordination backend
- The official agent binaries for
linux/amd64,linux/arm64,linux/386andlinux/arm, staged locally so the server can hand each host you add the right one - SCAP Security Guide 0.1.81 datastreams staged for the compliance scanning module
- guacd for the browser-based remote console, started and supervised by PatchMon itself and bound to loopback only
- Secure by default: no administrator account and no shared secret ship in the image. On first boot a unique database password, a unique Redis password, fresh signing and encryption keys and a single unique administrator account are generated and written to a root-only file
- An Ubuntu 24.04 LTS base, patched at build time, with unattended security upgrades enabled
- 24/7 SecureAnyCloud support
This image is the PatchMon server. The PatchMon agent is a separate component that you install on each host you want to manage. PatchMon generates the exact one-line install command for you in the console, and Step 7 walks through it. No agent, enrolment token or host identity is baked into this image.
Prerequisites
You need an active Azure subscription, an SSH key pair, and a VNet with a subnet in the target region.
Standard_B2s (2 vCPU / 4 GiB RAM) is the recommended size for evaluation and for fleets of up to roughly fifty hosts. For larger fleets, choose a size with more memory and raise DB_CONNECTION_LIMIT.
Configure the network security group (NSG) to allow inbound 22/tcp from your management network and 3000/tcp for the web console. Your managed hosts need outbound access to port 3000 on this VM. PatchMon serves plain HTTP on port 3000, so for production or shared access, put a TLS-terminating reverse proxy in front of it (see the final step).
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for PatchMon by SecureAnyCloud, and select Create.
On the Basics tab, pick your subscription, resource group, region and size. Under Administrator account, choose SSH public key and paste your key. Then select Review + create and Create.
Once the VM exists, add an inbound NSG rule that allows 3000/tcp from the networks you will browse from.
Step 2 - Deploy from the Azure CLI
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:patchmon-ubuntu-24-04:default:latest
az vm create \
--resource-group <your-rg> \
--name patchmon \
--image sac1647283583153:patchmon-ubuntu-24-04:default:latest \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_ed25519.pub \
--vnet-name <your-vnet> --subnet <your-subnet> \
--public-ip-sku Standard
# Open the console port so you can reach the web interface
az vm open-port --resource-group <your-rg> --name patchmon --port 3000 --priority 900Step 3 - Retrieve the per-VM administrator credentials
Nothing secret is baked into the image, and PatchMon ships with no user accounts at all. On the very first boot, a one-shot service (patchmon-firstboot.service) generates a fresh database password, a fresh Redis password, fresh JWT, session and encryption keys, and a single administrator account. All of them are unique to your instance.
The service writes them to /root/patchmon-credentials.txt, readable only by root. SSH in as azureuser and read the file:
$
sudo cat /root/patchmon-credentials.txt
# PatchMon - generated on first boot by patchmon-firstboot.service.
# These credentials are unique to THIS VM. Store them somewhere safe. patchmon.url=http://20.55.10.42:3000 patchmon.admin.user=admin patchmon.admin.email=admin@example.com patchmon.admin.pass=Xq7pR2mK9wU4nZ8vB3hL6yD1e5
# Canonical keys consumed by the sac smoke verifier + guide substitution: PATCHMON_URL=http://20.55.10.42:3000 PATCHMON_ADMIN_USER=admin PATCHMON_ADMIN_PASSWORD=<set-at-first-boot>The values above are an example. Your instance has its own unique credentials. Confirm that the file is root-only and that the administrator password entry is present:
sudo stat -c '%a %U:%G %n' /root/patchmon-credentials.txt
sudo grep -c '^PATCHMON_ADMIN_PASSWORD=' /root/patchmon-credentials.txtThe file reports 600 root:root and the grep reports 1.
Step 4 - Confirm the services are running
Check that the PatchMon server, PostgreSQL and Redis are all active:
sudo systemctl is-active patchmon postgresql redis-serverAll three report active. The health endpoint is a real dependency probe. It reports healthy only when both the database and Redis are reachable.
curl -s 'http://localhost:3000/health?format=json'PatchMon publishes the console on port 3000. PostgreSQL, Redis and the guacd remote console daemon all bind to loopback only, so they are never reachable from outside the VM:
sudo ss -tln | grep -E ':(3000|4822|5432|6379) 'Step 5 - Confirm the secure defaults
Confirm that the first-boot service has run to completion and that the application service is running on the secrets it generated. The environment file holding every per-VM secret is readable only by root and the patchmon service account:
sudo systemctl is-active patchmon-firstboot patchmon
sudo stat -c '%a %U:%G %n' /etc/patchmon-server/patchmon.envBoth report active, and the environment file reports 640 root:patchmon.
The application service is deliberately gated on a bootstrap marker that only the first-boot service creates. On a fresh instance, PatchMon cannot start before its secrets have been generated, so there is no window in which vendor defaults are live.
When it finishes, the first-boot service disables itself and is guarded by a sentinel file. It never runs again and never regenerates your credentials on a later reboot. That is why systemctl is-enabled patchmon-firstboot reports disabled on a running instance while is-active reports active: the unit has already done its job.
Step 6 - Sign in to the web console
Browse to http://<vm-ip>:3000 and sign in as admin with the password from /root/patchmon-credentials.txt.
After you sign in, the dashboard summarises your fleet. It shows how many hosts you manage, how many are up to date and how many need updating. It also shows the outstanding packages and security updates across all hosts, and how those numbers are trending.
The dashboard on a new instance starts empty. It fills in as soon as you register your first host, which is the next step.
Step 7 - Register the Linux hosts you want to manage
PatchMon manages a host through a small agent that runs on it and reports its package inventory back to this server. You never have to assemble the install command yourself.
In the console, choose Hosts, then Add Host. Give the host a friendly name and save it. PatchMon creates the host record, issues it a unique API identity and shows a one-line install command that contains a short-lived bootstrap token.
Run that command as root on the host you want to manage, not on this server. The agent installs itself, exchanges the bootstrap token for its own permanent credentials and reports in.
Each host gets its own credentials, so revoking one host never affects the others. Once a host has reported, it appears in the Hosts list with its real operating system, agent version, connection state and update status.
The agent binaries PatchMon hands out are staged locally on this image, so host enrolment does not depend on outbound internet access from the server. List them with:
ls -1 /opt/patchmon/agents/Step 8 - Review what needs patching
Open Packages for the fleet-wide package inventory. It lists every package PatchMon has seen across all your hosts, how many hosts each is installed on, whether an update is available, whether that update is a security update, and the version it would move to. This view answers the question: what is outstanding across my estate, and how much of it is security-relevant?
From Patching, you can schedule or immediately run a patching job against a single host or a group of hosts. You can also review the outcome of every run.
Step 9 - Query the same data from the REST API
Everything in the console is available over the REST API on the same port. Authenticate with the administrator credentials to get a bearer token, then call any endpoint with it. This command reads the real password from the root-only credentials file, so nothing secret has to be typed or pasted:
PW=$(sudo sed -n 's/^PATCHMON_ADMIN_PASSWORD=//p' /root/patchmon-credentials.txt)
TOKEN=$(curl -sS -X POST http://localhost:3000/api/v1/auth/login \
-H 'Content-Type: application/json' \
-d "{\"username\":\"admin\",\"password\":\"$PW\"}" \
| sed -n 's/.*"token":"\([^"]*\)".*/\1/p') test -n "$TOKEN" && echo "authenticated: session token issued"With the token, ask PatchMon for the fleet's patch posture:
PW=$(sudo sed -n 's/^PATCHMON_ADMIN_PASSWORD=//p' /root/patchmon-credentials.txt)
TOKEN=$(curl -sS -X POST http://localhost:3000/api/v1/auth/login \
-H 'Content-Type: application/json' \
-d "{\"username\":\"admin\",\"password\":\"$PW\"}" \
| sed -n 's/.*"token":"\([^"]*\)".*/\1/p')
curl -sS -H "Authorization: Bearer $TOKEN" \
http://localhost:3000/api/v1/dashboard/stats \
| sed -n 's/.*"cards":{\([^}]*\)}.*/\1/p'The API is closed by default. An anonymous request is rejected, and a wrong password issues no session token:
curl -sS -o /dev/null -w 'anonymous GET /api/v1/hosts -> HTTP %{http_code}\n' \
http://localhost:3000/api/v1/hostsBAD=$(curl -sS -X POST http://localhost:3000/api/v1/auth/login \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"wrong-password-1A!"}' \
| sed -n 's/.*"token":"\([^"]*\)".*/\1/p') test -z "$BAD" && echo "wrong password: no session token issued"Step 10 - Where everything lives
- /opt/patchmon/patchmon-server: the PatchMon server binary
- /opt/patchmon/agents/: the official agent binaries served to your hosts
- /opt/patchmon/ssg-content/: SCAP Security Guide datastreams for compliance scanning
- /etc/patchmon-server/patchmon.env: every per-VM secret and setting (0640 root:patchmon)
- /root/patchmon-credentials.txt: the administrator credentials generated on first boot (0600 root:root)
Check the service status and recent logs:
sudo systemctl status patchmon --no-pager | head -5sudo journalctl -u patchmon --since '10 minutes ago' --no-pager | tail -5The database is standard PostgreSQL, so back it up as you would any PostgreSQL database:
sudo -u postgres pg_dump patchmon_db | gzip > /tmp/patchmon-backup.sql.gz && ls -lh /tmp/patchmon-backup.sql.gzStep 11 - Put PatchMon behind TLS on your own domain
For production, terminate TLS in front of PatchMon rather than exposing port 3000 directly. Install your preferred reverse proxy (nginx, Caddy or Traefik) on this VM or in front of it, and obtain a certificate for your domain.
Proxy requests to 127.0.0.1:3000, and forward the Upgrade and Connection headers so that the agent websockets and the remote console keep working.
Then update two settings in /etc/patchmon-server/patchmon.env so that PatchMon agrees with its new address:
CORS_ORIGIN: PatchMon accepts only requests whose address is listed here. Add yourhttps://patchmon.example.comorigin, or the console refuses to load with a host mismatch error.TRUST_PROXY=true: PatchMon reads the real client address and scheme from the proxy's forwarded headers.
Next, set the same URL as the server URL under Settings. The agent install commands PatchMon generates then point at your domain rather than the raw IP. Restart the service:
sudo systemctl restart patchmon && sleep 5 && curl -s http://localhost:3000/healthExisting agents pick up the new address on their next check-in.
Support
This image is published by SecureAnyCloud with 24/7 support for deployment, upgrades, TLS termination, reverse proxy and SSO integration, agent rollout across your fleet, and scaling. Raise a request through the Azure Marketplace listing or contact SecureAnyCloud support directly.
PatchMon is open source software released under the GNU Affero General Public License v3.0. PatchMon is a trademark of its respective owner. All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.
Deploy on Azure
Launch PatchMon on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com