Self-hosted service
docker/docker-bench-security avatar
docker/docker-bench-security

Docker Bench for Security: a shell script that audits against the CIS benchmark

The Docker Bench for Security is a script that checks for dozens of common best-practices around deploying Docker containers in production.

9,708 stars1,030 forksShellApache-2.0

At a glance

What is it?
A thousand lines of shell that walks a Docker host, checks it against the CIS Docker Benchmark section by section, and prints remediation text alongside pass and warn results. Useful as a periodic report, misleading if you read it as a vulnerability scan.
Who is it for?
Docker Bench for Security earns its place by being the cheapest way to get a second opinion on a Docker host, and its cost is low precisely because it only checks configuration rather than hunting vulnerabilities. Run it on a real host before you change anything, save the JSON log so you have a baseline, and then use the filter flags to keep the run short once you know which sections matter to you.
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 141 days ago.
What is it written in?
Mainly Shell, 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

What the script checks and who wrote the rules

The premise is simple and worth stating plainly before the details. Docker Bench for Security is a script that checks for dozens of common best practices around deploying Docker containers in production. Every test is automated, and the tests are based on the CIS Docker Benchmark v1.6.0, which is a consensus document from the Center for Internet Security rather than something the Docker team invented.

The README is explicit that this is a self-assessment tool made available as an open-source utility so the Docker community can assess hosts and containers against the benchmark. That framing matters. CIS benchmarks are opinionated checklists, and the opinions come from a body that includes practitioners and vendors. Running the script does not make a host compliant with anything except the document version the script implements.

The mapping between script releases and benchmark versions is a small table at the top of the README, and it is a useful piece of history. Release 1.6.0 tracks CIS 1.6.0, 1.5.0 tracks 1.5.0, and going back further the pairs stop matching exactly, with 1.3.6 tracking CIS 1.4.0 and 1.3.0 tracking 1.13.0. So the two have moved independently at times.

The project lives under the docker organisation, is Apache 2.0 licensed, is written in Shell, and has a directory of functions plus a tests directory. There is also a Vagrantfile, which is how anyone reproduced a deliberately insecure host to test the checks against something that fails.

The default branch is master, the project is not archived, and the last push to it landed on 4 June 2026.

Running the check straight on the host with sudo

The quickest path needs no container and no build. The README gives three commands and a warning:

sh
git clone https://github.com/docker/docker-bench-security.git
cd docker-bench-security
sudo sh docker-bench-security.sh

The sudo is not incidental. A large share of the benchmark is about host configuration, kernel parameters, audit rules, and files under /etc, none of which a script running as your own user can read. Running it unprivileged produces a report full of items that are not really failures, which is the fastest way to misread the output.

The script writes two files into a log folder in the current directory: a JSON file named docker-bench-security.log.json and a plain text file named docker-bench-security.log. The JSON one is the machine-readable result and is the one to keep if you intend to compare runs over time or feed the data somewhere else.

There is a note about jq being an optional but recommended dependency, and a second note that when a distribution does not ship auditctl, the audit tests fall back to checking /etc/audit/audit.rules for the presence of a rule. Both of these are the kind of small honest caveat that tells you the script has been run on enough different distributions to have hit their edge cases.

The help output also lists a Docker version requirement of 1.13.0 or later, which is old enough now to read as a floor that will never be raised.

Why the container has to share the host's namespaces

Running the script inside a container is the option people gravitate to in CI, and the README warns about it directly. The container is run with a lot of privilege, sharing the host's filesystem, pid and network namespaces, because portions of the benchmark apply to the running host rather than to any single container.

The command is long and every flag is doing work. Host networking and the host pid namespace are needed to inspect the host. The audit_control capability is needed for audit rules. The bind mounts are all read only, covering /etc, the containerd and runc binaries, systemd units, /var/lib, and the Docker socket. The host user namespace flag reflects Docker Compose and similar cases where the container otherwise sees a different mapping.

sh
docker run --rm --net host --pid host --userns host --cap-add audit_control \
    -e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
    -v /etc:/etc:ro \
    -v /usr/bin/containerd:/usr/bin/containerd:ro \
    -v /usr/bin/runc:/usr/bin/runc:ro \
    -v /usr/lib/systemd:/usr/lib/systemd:ro \
    -v /var/lib:/var/lib:ro \
    -v /var/run/docker.sock:/var/run/docker.sock:ro \
    --label docker_bench_security \
    docker-bench-security

The README then gives platform adjustments, which tells you how much the portability story depends on your distribution. On Ubuntu the systemd units live under /lib/systemd/system rather than /usr/lib/systemd. On macOS the /etc/hostname file is missing and has to be created first, Docker Desktop has no systemd units and none of those Docker binaries, so the reduced command drops most mounts.

Two things follow. First, the docker run form needs adjusting or it silently misses checks. Second, a container with the Docker socket mounted read only is still the Docker socket, and the host mounts give it a great deal of read access to the machine it is auditing. The label docker_bench_security exists so this container can be identified and excluded later.

The README also flags that the published image is out of date and a manual build is required, pointing at issue 405. So the compose path assumes you build first, which the compose file confirms by carrying both a build path and a commented out image reference.

Filtering a long report down to what you care about

A full run is long, and the flags exist so you can run it in a way that fits your workflow. The options block lists them all with one line each:

sh
  -b           optional  Do not print colors
  -c CHECK     optional  Comma delimited list of specific check(s) id
  -e CHECK     optional  Comma delimited list of specific check(s) id to exclude
  -n LIMIT     optional  In JSON output, when reporting lists of items (containers, images, etc.), limit the number of reported items to LIMIT. Default 0 (no limit).

The check selection flags are the important ones, and they work on either a single check id or a named group. Checks derived from the CIS benchmark are named check_<section>_<number>, so something like check_2_6, while community contributed checks are named check_c_<number>. The README gives concrete examples of both directions, including running all checks except the docker_enterprise_configuration group and running the container_images and container_runtime groups together.

sh
sh docker-bench-security.sh -c container_images,container_runtime
sh docker-bench-security.sh -e docker_enterprise_configuration

The other flags narrow by object rather than by check. The include and exclude patterns filter containers and images by name, the label flag filters by container label, and the log flag sends output to a file, which matters when running in a container because logs land at /usr/local/bin/log/ inside it and need a volume to reach the host.

There is one flag that reads like a safety valve for large fleets. The limit flag caps how many items get reported in JSON list output, defaulting to zero for no limit. On a host with thousands of images, that is the difference between a report you can read and one you cannot, and it is the kind of detail that suggests the script has been run against production estates rather than laptops.

Pinned base images and a small Alpine package list

The Dockerfile is short and reveals how little the tool needs to run. The base image is pinned by digest rather than by tag:

dockerfile
FROM alpine:3.18@sha256:eece025e432126ce23f223450a0326fbebde39cdf496a85d8c016293fc851978

The v1.6.1 release notes record the change that introduced this approach, credited to a contributor, and replacing tags with digests is a real improvement for a tool whose output people treat as an audit result. A tag can be repointed; a digest cannot.

The package list is four packages installed with no cache: iproute2, docker-cli, dumb-init, and jq. That is the entire dependency surface, and jq being included is what makes the JSON output work without asking the operator to install anything on the host.

Two more Dockerfile details are worth noting. There is a HEALTHCHECK line whose command is simply an exit with status zero, which makes the container report healthy unconditionally. That is not a bug so much as a signal that the container is not a long running service, and anyone reading orchestration output should not treat that health status as meaning anything. The entrypoint runs the script under dumb-init as PID 1, which is the correct way to get signal handling and zombie reaping without writing a shell wrapper.

The compose file mirrors the run flags, adding the audit_control capability, the host pid namespace, and four read only volume mounts, and it keeps the commented out image line as a reminder that you build this yourself.

The base image being Alpine 3.18 is the one detail here that ages worst. It is a pinned digest of a release that will eventually be out of date, and nothing in the repository suggests an automated dependency update path.

Where a CIS checklist result stops being security assurance

The honest limitation is the one the related search terms keep circling. People searching for this project are looking for a Docker security tool, and Docker Bench for Security is a configuration audit against a fixed document, not a vulnerability scan. It does not look for known CVEs in your images, it does not inspect what a container does at runtime, and it cannot tell you whether an application inside is handling authentication badly.

What it does give you is a fast, repeatable, well-scoped inventory of deployment hygiene: whether the daemon is configured safely, whether users are in the right groups, whether logging is set up, whether content trust is enabled, whether images are built sensibly. For a team that inherited a Docker host and does not know its state, that inventory is genuinely hard to get any other way.

The results are also only as good as the benchmark. CIS documents get revised, and v1.6.1 of the script added check groups for CIS Controls v8 while still being described in the README as based on CIS Docker Benchmark v1.6.0. Two different numbering schemes are now in play, which is exactly the situation where a check id in your ticket stops meaning much.

That is the part to be careful with in practice. Check ids like 2.2 or 4.5 are stable within a script version and mean different things across versions and across other tools, so pinning findings to ids is fragile. The check names in the output, not the numbers, are the durable part.

The related searches also surface kube-bench, which is the Kubernetes counterpart applying CIS benchmarks to a cluster, plus Docker Scout for image scanning. Those fill the two gaps this tool leaves: a scanner for what is inside the images, and a benchmark for the orchestrator rather than the daemon.

If you want a second opinion rather than a checklist, that combination is the honest answer.

Editorial conclusion

Docker Bench for Security earns its place by being the cheapest way to get a second opinion on a Docker host, and its cost is low precisely because it only checks configuration rather than hunting vulnerabilities. Run it on a real host before you change anything, save the JSON log so you have a baseline, and then use the filter flags to keep the run short once you know which sections matter to you. The pinned image and the jq note in the Dockerfile are the two things worth copying into your own tooling. On cadence, the repository saw a push on 4 June 2026 while the newest tagged release remains v1.6.1 from 20 December 2023, and since the CIS Docker Benchmark is not frozen at 1.6.0, read the log for configuration drift rather than treating a clean run as certification against whatever the benchmark has become since then.

Frequently asked questions

Is Docker used for security?

Two different senses. Docker containers are widely used to package and isolate security tooling so it can be run reproducibly, which is the pattern this project itself follows. Docker deployments are also a common audit target, and this script exists to check a host against the CIS Docker Benchmark rather than to scan images for vulnerabilities.

Do I need root to run Docker Bench for Security?

Yes, the README runs it as sudo sh docker-bench-security.sh. Much of the benchmark covers host configuration, kernel parameters and audit rules that an unprivileged user cannot read, so running without root produces failures that are artifacts of the permission rather than the configuration.

Does the published Docker image work for Docker Bench for Security?

Not reliably. The README notes that the docker/docker-bench-security image is out of date and that a manual build is required, pointing at issue 405, which is why the compose file carries a build path and a commented out image reference.

Official sources

  1. docker/docker-bench-security on GitHub
  2. Issues
  3. License: Apache-2.0
  4. README
  5. Releases
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/docker-docker-bench-security.svg)](https://hysenlabs.com/projects/docker-docker-bench-security)

Was this useful?