clusterlint: auditing a live Kubernetes cluster for configuration smells
A best practices checker for Kubernetes clusters. đź¤
At a glance
- What is it?
- clusterlint is a Go linter that queries a running cluster, applies common and platform specific checks to what it finds, and reports problems without changing anything. Checks can be selected by group or by name, silenced with an annotation, and extended locally through Go plugins.
- Who is it for?
- Adopt clusterlint when you operate Kubernetes clusters and want a repeatable, read-only report of configuration problems across namespaces, resource requests and platform specific traps. Skip it if you need admission-time enforcement, since clusterlint is run externally and does not alter resources, or if you need checks it does not ship.
- 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 38 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 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
It reads the cluster rather than the manifests you wrote
clusterlint queries live Kubernetes clusters for resources, executes common and platform specific checks against those resources, and provides actionable feedback to cluster operators. The README stresses that it is a non invasive tool run externally, and that it does not alter the resource configurations. That design choice is the whole product. The reason it looks at a live cluster rather than at your YAML is stated in the background section: Kubernetes resources can be configured and applied in many ways, and that flexibility makes it hard to identify problems across the cluster at the time of configuration. A manifest is one snapshot of intent, while a live cluster is the accumulated result of every apply, every Helm release and every manual kubectl edit. The tool therefore answers the question the manifests cannot, which is what does the cluster actually look like right now. The price is that it needs credentials with read access to a real cluster, and that its findings reflect reality rather than intent, which means a finding can also reveal that the manifests in your repository are out of date.
Checks are grouped so you can run only what applies to your platform
Every check clusterlint performs is categorized into groups, and a check can belong to more than one group. The reason is stated plainly: if a cluster is running on DOKS, running checks specific to AWS does not make sense, so clusterlint can blacklist the AWS related checks while running against a DOKS cluster. The group and check flags are the main verbosity of the tool.
clusterlint run -g basic // runs only checks that are part of the basic group
clusterlint run -G security // runs all checks that are not part of the security group
clusterlint run -c default-namespace // runs only the default-namespace check
clusterlint run -C default-namespace // exclude default-namespace checkThe lowercase and uppercase forms are inverses, which is worth internalising before you script against it. The lower case group flag includes one group, the upper case group flag excludes one group, and the same pairing exists for individual check names. Two commands cover the baseline.
clusterlint list [options] // list all checks available
clusterlint run [options] // run all or specific checksA checks.md file in the repository documents the background on every check the tool performs, and the checks directory holds their implementations, so the catalogue of what it can complain about is readable before you run anything.
Noisy clusters are handled with an annotation, not a config file
Real clusters contain resources that deliberately break the rules. The example the README gives is the kube-system namespace, whose resources often use privileged containers, which creates a lot of noise when an operator is looking for feedback. Rather than requiring a global exclusion list, clusterlint lets you annotate the object in question, and the annotation takes a comma separated list of check names to skip for that resource.
"metadata": {
"annotations": {
"clusterlint.digitalocean.com/disabled-checks" : "noop,bare-pods"
}
}The example skips the noop and bare-pods checks, which tells you the check names are kebab-case strings. Putting the exception on the resource itself rather than in the linter config is the right design for a tool that reads a live cluster, since it travels with the object through GitOps and can be reviewed in the same pull request as the object it silences. The trade-off is that the exemption is visible to anyone who can read the resource, so a blanket annotation on a namespace silences those checks for everything in it, not just the object a reviewer had in mind.
Running it in-cluster as a CronJob
The tool can be built into an image and run from inside the cluster, which is how you would run it on a schedule. The build and push are ordinary Docker steps with your own registry prefix.
docker build -t <hub_username>/clusterlint:<tag> .
docker push <hub_username>/clusterlint:<tag>Inside a Pod you add the --in-cluster flag so the tool uses the Pod's service account to reach the Kubernetes API instead of your local kubeconfig.
clusterlint --in-cluster runThe README gives a CronJob definition that runs it every hour without any RBAC setup, and points to docs/RBAC.md when you do use RBAC.
apiVersion: batch/v1
kind: CronJob
metadata:
name: clusterlint-cron
spec:
schedule: "0 */1 * * *"
concurrencyPolicy: Replace
failedJobsHistoryLimit: 3
successfulJobsHistoryLimit: 1
jobTemplate:
spec:
template:
spec:
containers:
- name: clusterlint
image: docker.io/<hub_username>/clusterlint:<tag>
command: ['/clusterlint', '--in-cluster', 'run']
imagePullPolicy: IfNotPresent
restartPolicy: NeverThe container image is built from a golang builder stage and packaged on a distroless static base running as the nonroot user, with the entrypoint set to the binary. The concurrency policy of Replace means a slow run does not pile up behind the next scheduled one, and the history limits keep the completed Jobs from accumulating. Since the tool only reads, running it on a timer is safe from a change perspective, though a check set that returns hundreds of findings will still generate noise unless you filter by group or annotate.
The checks that catch real operational mistakes
The README gives two examples of the common best practices the tool encodes. The first is namespace usage: using namespaces to limit the scope of resources created by multiple sets of users within a team, since dumping everything into the default namespace can lead to privilege escalation, resource name collisions, latency in operations as resources scale up, and mismanagement of Kubernetes objects, and because namespaces let you enable resource quotas to track node, cpu and memory usage per team. The second is always specifying resource requests and limits on pods, since with requests the scheduler makes better placement decisions and with limits contention on a node is handled in a specified manner rather than by accident. The platform specific checks are where the tool earns its keep, and the README's example is a DOKS one: on upgrade of a cluster on DigitalOcean Kubernetes the worker node hostnames change, so a pod spec that relies on the hostname to schedule onto specific nodes will fail to schedule after the upgrade. That is the class of problem a linter over live state can catch and a static manifest review usually misses.
Local checks through Go plugins, with caveats attached
Organisations often have best practices that do not apply to the wider community, and clusterlint supports that through Go plugins. The README asks that you submit a check to the project instead if it might be useful to anyone else, and reserves the plugin route for a truly specific check. Passing the plugin path on the command line is all that is needed to load it.
$ clusterlint --plugins=/path/to/plugin.so list
$ clusterlint --plugins=/path/to/plugin.so run -c my-plugin-checkNote the ordering, since the plugins flag precedes the subcommand. The example-plugin directory in the repository documents how to write one, and the README points you at a caveats section in that plugin's README and asks you to consider whether you really want to maintain a plugin. That is a sensible instruction for a Go plugin mechanism generally, since plugins are tightly coupled to the Go version and build flags of the host binary. A Go plugin has to be compiled against a matching standard library and the same build configuration, which makes it fragile across the release cadence of the tool itself, and it is the reason the tool also ships an annotation based mechanism that needs no code at all.
Version 0.16, the Kubernetes client it builds against, and what a linter cannot do
The release history is steady and recent: v0.16.0 on 2026-09-15, v0.15.0 on 2026-04-29 and v0.14.0 on 2026-02-04, and the last push to the repository was on 2026-09-15, the same day as the v0.16.0 tag. The repository is not archived and the licence is Apache-2.0. The go.mod pins Go 1.27.0 and the Kubernetes libraries at v0.37.0 for api, apimachinery and client-go, which tells you the tool is built against a specific Kubernetes generation, and the dependency list is deliberately short, with urfave/cli for the command line, testify for tests, fatih/color for terminal output and the external-snapshotter client among them. The module also vendors its dependencies, and the Dockerfile builds with a vendor directory and a GOFLAGS of -mod=vendor, which keeps the container build reproducible. What a linter like this cannot do is worth stating plainly. It is an external, read-only reporter, not an admission controller, so nothing it finds is blocked or fixed automatically. It has no notion of whether your organisation actually cares about a given finding, and it cannot see problems that only appear at runtime, such as an application that ignores its own resource limits. And a scheduled report that nobody reads is worth exactly as much as the discipline of whoever reads it.
Editorial conclusion
Adopt clusterlint when you operate Kubernetes clusters and want a repeatable, read-only report of configuration problems across namespaces, resource requests and platform specific traps. Skip it if you need admission-time enforcement, since clusterlint is run externally and does not alter resources, or if you need checks it does not ship. Verify first that your cluster version matches the client libraries it builds against, that the RBAC permissions in docs/RBAC.md cover what the checks read, and that the group and check names you intend to exclude are the ones the tool actually uses.
Frequently asked questions
How do I install and run clusterlint?
Run go get github.com/digitalocean/clusterlint/cmd/clusterlint, which creates the clusterlint binary in $GOPATH/bin. Then use clusterlint list to see the available checks and clusterlint run to run all of them or a selected subset.
How do I run only the checks that apply to my cloud platform?
Checks are organized into groups and a check can belong to several. Use a lowercase group flag to include one group and an uppercase one to exclude it, for example clusterlint run -g basic or clusterlint run -G security, and the same pairing works for individual check names with -c and -C.
Can clusterlint ignore a specific resource it flags?
Yes. Add the annotation clusterlint.digitalocean.com/disabled-checks with a comma separated list of check names to the resource, for example "noop,bare-pods". This is useful for objects such as those in kube-system that deliberately use privileged containers.
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/digitalocean-clusterlint)Was this useful?