Eraser deletes the images you list from every node in the cluster
🧹 Cleaning up images from Kubernetes nodes
At a glance
- What is it?
- Eraser is a Kubernetes operator that removes a list of non-running images from all the nodes in a cluster, pairing a Trivy based scanner with collector, remover and manager components. The interesting engineering is in how the operator decides what is unused and how it coordinates node level cleanup across a fleet.
- Who is it for?
- Eraser suits cluster administrators who have decided which images must never sit on a node, whether because of a vulnerability advisory or an end-of-life version, and who want that list enforced everywhere rather than on one machine at a time. It is the wrong tool if your problem is disk pressure from images nobody has cleaned up in months, since it works from a list you provide rather than from a general garbage collection policy.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A list of images, erased from every node
The project describes itself in one sentence: Eraser helps Kubernetes admins remove a list of non-running images from all Kubernetes nodes in a cluster.
Read that carefully, because it defines both the scope and the limit. The target is a list, so the decision about what to remove is made by the administrator rather than by the tool. The target is non-running images, so the tool is careful about what it considers removable. And the target is all nodes, which is the part that distinguishes it from a per-node cleanup job.
The topic list makes the security framing explicit, with image-security, vulnerabilities, vulnerability-scanner, images, containers and kubernetes-operator among the topics, and trivy named directly.
The audience is therefore a cluster administrator with a specific job: some images are not allowed on their nodes, whether because they carry a known vulnerability, because their upstream version is past end of life, or because a policy says so, and those images have to be removed from every machine, including the nodes that have been running for months and never been touched.
That last case is the one that motivates the project. An image that was scheduled for deletion six months ago is still sitting on the node that has been up since a kernel upgrade, and no amount of node-local tidying fixes that. The demo animation in the repository is there to show that flow.
The README is short, and it points you at a quick start guide in the Eraser documentation and a setup page for the development environment, which is where the operational detail lives rather than here.
Four component images built from one repository
The build outputs are visible in the Makefile, which names the images it publishes and the tags it applies to them. The version variable is set to the release version, and each component has its own repository under the ghcr.io/eraser-dev organisation with its own tag:
VERSION := v1.5.0-beta.1The four images are the manager, the collector, the remover and the trivy scanner. Their repositories are listed separately in the Makefile, with the scanner under ghcr.io/eraser-dev/eraser-trivy-scanner and the others under ghcr.io/eraser-dev/eraser-manager, ghcr.io/eraser-dev/remover and ghcr.io/eraser-dev/collector.
The Dockerfile shows the same four as build stages, manager-build, collector-build, remover-build and trivy-scanner-build, and those stage names are also collected into a variable used for a build-time SBOM scan. Each Go binary is compiled separately from the same source tree, for example the manager from main.go and the collector from the pkg/collector package, with GOOS and GOARCH set from build arguments so the same Dockerfile produces per-platform binaries.
Separating the binaries is not cosmetic. A component that only lists what is present on a node has different permissions and a different failure mode from the component that deletes images, and the scanner has an entirely different dependency footprint because it carries a vulnerability database tool. The Makefile even sets a separate ldflags value for the scanner that stamps in the Trivy version it was built against, which is the kind of detail that matters when you need to know which scanner produced a report.
The build also caches the Go module downloads and the build cache with BuildKit cache mounts, so a source change does not force a full dependency download.
Trivy is the scanner, and its version is pinned in the image
The scanner stage is not a from-source build. The Dockerfile sets the Trivy binary image as an argument and comments that the upstream Trivy release image is used rather than rebuilding its binary with local dependency overrides:
ARG TRIVY_BINARY_IMG="ghcr.io/aquasecurity/trivy:0.72.0"So the scan capability of Eraser is exactly the capability of Trivy 0.72.0 as published by Aqua Security, and the Makefile keeps the same version in its own variable. The choice to consume a released image rather than rebuild the binary is a supply chain decision: the scanner comes from its upstream release artefact rather than from a local build of that dependency.
The repository carries a .trivyignore file at the root, which is the conventional place to list findings that should not be reported, and the scanner image is built and pushed separately under its own repository and tag, so it can be versioned and rolled independently of the Go components.
For a cluster administrator, the practical sequence is that the scanner identifies images that should not be on a node, and the remover acts on the list. Whether those two steps are driven by one object or two is a question for the documentation, but the separation in the build is a strong hint that they are separate processes with separate lifecycles.
A controller-runtime operator with four API versions
The code is a Kubernetes operator, and the go.mod makes the machinery explicit. It depends on sigs.k8s.io/controller-runtime, on the Kubernetes API, apimachinery and client-go at 0.31.2, and on oras-go v2, which is the library for working with OCI registries and would be the tool for pulling images rather than only deleting them.
The API versions are declared in the Makefile:
API_VERSIONS ?= ./api/v1alpha1,./api/v1,./api/v1alpha2,./api/v1alpha3Four versions of the API in one repository, with v1alpha1, v1alpha2 and v1alpha3 alongside a v1. That ordering tells a story on its own: the project has iterated on its custom resource definition, and the older alpha versions are still present for compatibility. The api, controllers, config and charts directories follow the standard operator layout, with PROJECT and Makefile at the root, and there is a deploy directory and a manifest_staging directory for the rendered manifests.
Observability is not an afterthought either. The module pulls in OpenTelemetry for metrics and traces, with an OTLP HTTP metric exporter, plus zap for logging and logr as the logging interface.
The test stack is visible too: ginkgo and gomega, testify for unit assertions, the Kubernetes e2e-framework and kind for cluster level tests, and an envtest Kubernetes version pinned separately from the main client library version.
The CRI client is pinned for a reason stated in the manifest
One line in go.mod deserves attention, because it is the kind of comment that saves you an afternoon:
// keeping this on 0.25 as updating to 0.26 will remove CRI v1alpha2 versionThe dependency is k8s.io/cri-api, pinned at 0.27.1 while everything else Kubernetes related sits at 0.31.2. The CRI is the Container Runtime Interface, the API a node agent exposes for the kubelet to ask the runtime to list, pull and delete images, and Eraser is exactly the kind of tool that talks to it. The version it needs is the one that still offers the v1alpha2 API.
That detail has consequences beyond the comment. A cluster whose node runtime no longer serves CRI v1alpha2 is a compatibility problem for this version of Eraser, and the release notes for whatever version replaces this pin would be the place to check. For a project that deletes images from live nodes, choosing an API surface that still exists in your runtime is the difference between a working cleanup and a component that cannot talk to the kubelet at all.
It is also a good illustration of the project's maturity overall. The go.mod requires go 1.26.0 with a toolchain of go1.26.5, which sets a recent Go floor for anyone building from source.
Windows workers run on a host process base image
The Dockerfile carries a Windows story, which is unusual for a Kubernetes tool and worth spelling out.
There is an argument for a HostProcess base image, taken from Microsoft's kubernetes windows host process containers base image, used for the Windows workers. The comment beside it explains the sizing: it is around 22 kB because a HostProcess container runs directly on the host and ships no operating system of its own.
The logic is that a Windows node has no Linux binary to run, so an ordinary Linux container image cannot be deployed there. A HostProcess container is the supported way to run a Windows binary on a Windows node, and the image chosen is the minimal one so that the worker does not carry a full OS layer per node.
For an administrator, that means Eraser can cover mixed clusters, or clusters with Windows nodes, without a separate tool for the Windows side. The cost is a second set of images to build and publish, which is why the Makefile keeps a platform variable and builds for multiple targets.
The rest of the image build is conventional and reproducible by design: the Go builder stage pins the golang 1.26.6 bookworm image by digest, disables cgo, downloads modules in a separate cached step, and the final stages are built from that one builder so that the manager, collector and remover all come from the same source snapshot.
What the README does not define, and how the project is run
Two gaps are visible in the documentation, and both matter for an evaluation.
The first is the policy. The README says Eraser removes a list of non-running images, but it does not define how that list is expressed, whether it comes from a custom resource, a scanner report, or a configuration file, and it does not describe what happens when a node does not have the image, or when an image is in use. The quick start guide and the API directories in the repository are where those answers live.
The second is the operating model. The repository has a charts directory for Helm, a deploy directory, and a manifest_staging directory, which points to the standard practice of publishing both a chart and rendered manifests, but the README does not explain how the components are deployed or scaled.
On the community side, the project is organised in the usual CNCF fashion. The README carries an OpenSSF Best Practices badge, an OpenSSF Scorecard badge and a FOSSA licence badge, and it states that the project has adopted the CNCF Code of Conduct. There is a mailing list for release and security notifications, biweekly community meetings for development, issues and use cases, and a channel on the Kubernetes Slack. Issues and feature requests go through GitHub Issues, with the instruction to search existing issues first.
The licence is Apache-2.0. The release tags are v1.5.0-beta.1 on 2026-09-15, v1.4.2 on 2026-08-07 and v1.4.1 on 2025-12-02, and the last push was on 2026-10-21, with the repository not archived. A beta tag alongside stable ones means the 1.5 line is where new work is landing, and a cluster operator pinning a version should decide deliberately whether to sit on 1.4 or move to the beta.
Editorial conclusion
Eraser suits cluster administrators who have decided which images must never sit on a node, whether because of a vulnerability advisory or an end-of-life version, and who want that list enforced everywhere rather than on one machine at a time. It is the wrong tool if your problem is disk pressure from images nobody has cleaned up in months, since it works from a list you provide rather than from a general garbage collection policy. Verify first how the list of images to erase is expressed in the CRD you plan to use, then confirm the scanner reports the vulnerable and end-of-life images you expect, because the removal decisions and the scan results are separate concerns in this project.
Frequently asked questions
What does Eraser actually remove from Kubernetes nodes?
It removes a list of non-running images from all Kubernetes nodes in a cluster, so the images that are in use stay where they are. The README points to the quick start guide in the documentation for the details of how the list is defined.
Which components does Eraser deploy?
The Makefile names four images, the manager, the collector, the remover and the trivy scanner, published under separate repositories in the ghcr.io/eraser-dev organisation. The Dockerfile builds each one as its own stage, manager-build, collector-build, remover-build and trivy-scanner-build.
Does Eraser scan images for vulnerabilities, and with what tool?
Yes. The scanner stage uses the upstream Trivy release image, with the version pinned as an argument in the Dockerfile, and the scanner image is published separately as ghcr.io/eraser-dev/eraser-trivy-scanner. The repository also carries a .trivyignore file.
Why does Eraser pin an older Kubernetes CRI API?
The go.mod carries a comment saying the CRI API is kept on 0.25 because updating to 0.26 would remove the CRI v1alpha2 version, which is the version Eraser relies on to talk to the node runtime.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/eraser-dev-eraser)Was this useful?