🚀 Container Image Update Monitoring: Drift Now, Safely
Discover container image update monitoring: detect Docker image updates, semantic version tags & container registry digest drift—audit safely without restarts.

Track Container Image Updates Without Risky Automatic Restarts
A container image can change even when your deployment files don't. For example, a tag like 1.4.2 might be rebuilt upstream with a security fix, while your running service keeps using the older image. The reverse happens too: a mutable tag such as latest can start pointing at a breaking release the next time you deploy.
So teams end up in a tough spot. You can check registries by hand and hope you never miss anything, or you can automate image updates and risk restarting production with an image no one has verified. The clean way out is to separate discovery from deployment: detect that something changed, then decide when - and how - to roll it out.
This guide walks through a safer monitoring pattern and how a lightweight Python notifier can support it. We'll cover version tags, image digests, inspecting running services and Compose files, notification workflows, and the limits of what you can safely assume from an audit script. The point isn't unattended deployment - it's a signal you can trust enough to review.
Why monitor container image updates
A deployment manifest tells you what you asked to run. It doesn't always tell you whether the image behind that reference has changed since the last deployment. That matters because registries often support both versioned tags and mutable tags.
A versioned tag such as 2.3.1 is easier to review, but it isn't automatically immutable. A publisher can replace the image associated with that tag. A mutable tag such as stable or latest is built to move, so a pull (or redeploy) can fetch different content even when the service definition hasn't changed.
The two kinds of drift
It helps to split the problem into two questions:
- Has the declared version advanced? You might see a newer semantic version, like
2.3.2after2.3.1. - Has the image behind the tag changed? The tag may stay the same, while the registry digest updates.
Version progression is only half the story. Same-tag rebuilds are the other half. If you only watch version strings, you can miss rebuilds. If you only watch digests, you'll know "content changed," but you still won't know whether it's a security fix, a routine rebuild, or something that could break assumptions in your app.
Why not deploy every detected update?
It's tempting to treat detection as "low risk" and just restart. But a registry read is harmless; a restart is a change to a running system. If you wire registry events directly into restarts, you can get a restart before you've checked compatibility, release notes, configuration assumptions, or database migration requirements.
That might be reasonable for disposable test environments or a rollout pipeline with health checks and rollback. For a stateful production service, it's a bad default. A database image, for instance, may include upgrade steps that aren't safely reversible just by switching back to a previous tag.
A rule of thumb that works well in practice is: automate the observation; gate the change. Alert when a candidate update appears, then let an operator (or a properly tested deployment pipeline) decide what to do. You get visibility without letting "registry behavior" become "implicit production change."
How to detect version updates and digest drift
A practical monitor needs three answers: which image a service uses, what the registry currently serves for that reference, and whether that difference matters. A notifier such as imagenudge_container_update_notifier.py breaks that into parsing, local inspection, registry checks, comparison, and alerting.
Compare semantic versions carefully
If a tag follows semantic versioning, a parser can split it into major, minor, patch, and prerelease parts. It can normalize a leading v or V, treat omitted minor/patch as zero, then compare the numeric components in order.
For example, a stable 1.8.0 is newer than 1.7.9. A stable release is also considered newer than a prerelease with the same numeric core, like 1.8.0-rc.1. That's useful for common release tags, but it's not a full implementation of every versioning quirk out there. In particular, prerelease label ordering isn't always the same as a simple lexicographic comparison some projects expect.
Also, don't assume every tag is a "version." Tags that include commit hashes, dates, or project-specific labels can't be ordered safely with semantic arithmetic. In those cases, the monitor should say "not comparable" rather than pretending it knows what's newer.
Check the registry digest
A digest identifies image content more precisely than a tag. The notifier can make an HTTP HEAD request for a registry manifest and inspect the returned digest. If authentication is required, it can handle the Bearer challenge, fetch a token, and retry.
If the remote digest differs from what your local runtime metadata reports, that's a solid signal that "the tag now points somewhere else." It's still not an approval. A digest difference means "investigate," not "ship."
Inspect both the runtime and deployment files
A monitor can look at running workloads via the local runtime daemon (for example, by connecting to its UNIX domain socket). It can also read image references from Compose files. Those two views answer different questions:
- Runtime inspection shows what's actually running, within whatever permissions the monitor has.
- Compose inspection shows what a deployment file declares, regardless of whether that service is currently running.
Using both helps you spot mismatches - like a service declared in a file that isn't deployed, or an image running on a host that isn't reflected in what you scanned. Keep those results distinct in alerts: "declared" and "running" aren't the same thing.
How to use a passive image update notifier
A passive notifier is an audit tool. It reads what you have and what the registry serves, then reports findings without pulling images, rewriting configuration, or restarting anything. That separation is the whole point: it makes scheduling safer without giving the monitor any direct authority over production.
Install and verify the script
The notifier described here is a standalone Python script that's built around standard-library modules. It doesn't require third-party packages or its own dedicated virtual environment. Save the file as imagenudge_container_update_notifier.py, then check the options with:
python imagenudge_container_update_notifier.py --help
The script also includes an offline test suite for its parsing and comparison routines. To run those tests:
python imagenudge_container_update_notifier.py
Run this check in the Python environment where you'll actually use the utility. It verifies how the script handles versions locally, but it can't prove your registry access, runtime socket permissions, notification endpoint, or Compose-file assumptions are all wired correctly.
Give it only the access it needs
Runtime inspection depends on permission to read the local daemon's UNIX socket. If the account running the script can't access that socket, runtime queries may fail - but Compose-file scanning can still work. That's usually the better tradeoff than granting broad privileges by default.
Access to the daemon socket should be treated as sensitive. On many systems, it provides substantial control over the host. If you run this regularly, pick an account and execution environment that matches your security model. Don't run as root just to silence a permission error and move on.
Make alerts actionable
The script can send notifications to compatible webhook endpoints, ntfy, or Gotify-style services, and it can also send text summaries via SMTP. Configure alerts so they answer the questions an operator needs to decide what to do:
- Which service and image reference were checked?
- Is the finding a newer semantic tag, a changed digest, or both?
- What is the current local reference or digest, and what did the registry return?
- Did the check succeed, or did authentication/network access fail?
A failed registry request isn't the same thing as "no update." Keep those errors visible and clearly separated from successful comparisons. Otherwise, a quiet notification channel can create a false sense that monitoring is working.
Put the notifier into a review loop
Start with a scheduled audit that sends results to a channel someone actually reviews. For each alert, check release notes and compatibility, test the candidate in a non-production environment when it makes sense, then deploy through your normal process. Also record what happened: did the alert lead to an update, get deferred, or turn out to be irrelevant?
That feedback is how you tune the workflow. If alerts are noisy, figure out whether the cause is an overly broad image list, an unsupported tag format, or a notification rule that treats every digest change as equally urgent. Don't just suppress digest changes to reduce volume. Instead, make the signal more informative and route it based on service criticality.
What the monitoring approach cannot tell you
A read-only monitor reduces deployment risk, but it doesn't eliminate the need to validate what it reads and what it reports. Its output is only as reliable as:
- the image references it can parse,
- the registry responses it can authenticate and fetch,
- and the local metadata it can access.
Compose parsing is intentionally limited
The utility's Compose parser uses regular-expression line matching, not a full YAML parser. It expects conventional indentation and image declarations on a single line. If your Compose setup uses anchors, templating, unusual formatting, or dynamic values, the monitor may not interpret it the way your deployment does.
If that's your situation, compare the monitor's findings with the fully rendered configuration your deployment process actually uses. Scanning a source file isn't always equivalent to what ends up running after environment-variable substitution and templating.
Registry authentication and certificates matter
The registry client supports a Bearer-token flow using the realm/service/scope values from an authentication challenge. It won't cover every registry configuration, including Basic authentication, mutual TLS, or vendor-specific credential helpers.
The HTTP client relies on Python's standard certificate validation and the OS's trusted certificate configuration. If you use a private registry with an internal CA, that CA needs to be trusted by the system (or by the Python environment) used to run the script. Don't treat TLS or authentication errors as proof that an image is current.
A digest change is not a security verdict
A different digest confirms that the referenced content differs. It doesn't tell you whether the new image fixes a vulnerability, whether the change is backward compatible, or whether it's appropriate for your workload. Likewise, a matching digest doesn't prove the running service is healthy or that the current release is secure.
Use update notifications alongside your existing controls - release review, vulnerability scanning, backups, health checks, and rollback procedures. The notifier answers a narrow question: "Is there a candidate change I should inspect?" It's useful, but it isn't a replacement for the rest of your safeguards.
Conclusion
Container image monitoring is most valuable when it tells you about change without making the change for you. Track version progression and digest drift separately, inspect both running services and deployment declarations, and make registry or parsing failures explicit in the report.
A passive notifier gives you that visibility with modest overhead, but it can't validate compatibility, judge security impact, or replace your deployment process. Use the alerts to start a review, then test and deploy through the controls your service needs. That division helps updates not slip by while keeping production decisions where they belong: in a deliberate, observable workflow.