A security-focused linter for Docker Compose files
Project description
compose-lint
A security-focused linter for Docker Compose files. Catches dangerous misconfigurations before they reach production.
compose-lint targets the same niche Hadolint occupies for Dockerfiles: zero-config, opinionated, fast, and grounded in OWASP and CIS standards.
Installation
pip
pip install compose-lint
Docker — composelint/compose-lint
docker run --rm -v "$(pwd):/src" composelint/compose-lint
Quick Start
Run without arguments to auto-detect compose.yml, compose.yaml, docker-compose.yml, or docker-compose.yaml in the current directory:
compose-lint
Or pass files explicitly:
compose-lint docker-compose.yml docker-compose.prod.yml
Docker equivalent:
docker run --rm -v "$(pwd):/src" composelint/compose-lint docker-compose.prod.yml
Example Output
docker-compose.yml:5 CRITICAL CL-0001 Docker socket mounted via
'/var/run/docker.sock:/var/run/docker.sock'. This gives the container
full control over the Docker daemon.
service: traefik
fix: Use a Docker socket proxy (e.g., tecnativa/docker-socket-proxy)
to expose only the API endpoints your service needs.
ref: https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-1
docker-compose.yml:3 HIGH CL-0005 Port '8080:80' is bound to all
interfaces. Docker bypasses host firewalls (UFW/firewalld), potentially
exposing this port to the public internet.
service: web
fix: Bind to localhost: 127.0.0.1:8080:80
ref: https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html#rule-5a
docker-compose.yml: 1 critical, 1 high
Rules
| ID | Severity | Description | OWASP | CIS |
|---|---|---|---|---|
| CL-0001 | CRITICAL | Docker socket mounted | Rule #1 | 5.31 |
| CL-0002 | CRITICAL | Privileged mode enabled | Rule #3 | 5.4 |
| CL-0003 | MEDIUM | Privilege escalation not blocked | Rule #4 | 5.25 |
| CL-0004 | MEDIUM | Image not pinned to version | Rule #13 | 5.27 |
| CL-0005 | HIGH | Ports bound to all interfaces | Rule #5a | 5.13 |
| CL-0006 | MEDIUM | No capability restrictions | Rule #3 | 5.3 |
| CL-0007 | MEDIUM | Filesystem not read-only | Rule #8 | 5.12 |
| CL-0008 | HIGH | Host network mode | Rule #5 | 5.9 |
| CL-0009 | HIGH | Security profile disabled | Rule #6 | 5.21 |
| CL-0010 | HIGH | Host namespace sharing | Rule #3 | 5.8, 5.15, 5.16, 5.21 |
| CL-0011 | HIGH | Dangerous capabilities added | Rule #3 | 5.5 |
| CL-0012 | MEDIUM | PIDs cgroup limit disabled | — | 5.29 |
| CL-0013 | HIGH | Sensitive host path mounted | Rule #8 | 5.5 |
| CL-0014 | MEDIUM | Logging driver disabled | — | 5.x |
| CL-0015 | LOW | Healthcheck disabled | — | 4.6, 5.27 |
| CL-0016 | HIGH | Dangerous host device exposed | — | 5.18 |
| CL-0017 | MEDIUM | Shared mount propagation | — | 5.20 |
| CL-0018 | MEDIUM | Explicit root user | Rule #7 | 5.x |
| CL-0019 | MEDIUM | Image tag without digest | Rule #13 | 5.27 |
Severity Levels
Findings are rated LOW, MEDIUM, HIGH, or CRITICAL based on exploitability and impact scope. See docs/severity.md for the full scoring matrix.
Configuration
Create .compose-lint.yml to disable rules or adjust severity:
rules:
CL-0001:
enabled: false
CL-0003:
enabled: false
reason: "SEC-1234 — Approved by J. Smith, expires 2026-07-01"
CL-0005:
severity: medium
Disabled rules still run — findings appear as SUPPRESSED without affecting the exit code. The reason field is surfaced in all output formats:
- Text: shown after the
SUPPRESSEDlabel - JSON:
suppression_reasonfield - SARIF:
suppressions[].justification(recognized by GitHub Code Scanning)
To hide suppressed findings from output:
compose-lint --skip-suppressed docker-compose.yml
CLI Reference
compose-lint [OPTIONS] [FILE ...]
--format {text,json,sarif} Output format (default: text)
--fail-on SEVERITY Minimum severity to trigger exit 1 (default: high)
--skip-suppressed Hide suppressed findings from output
--config PATH Path to config file (default: .compose-lint.yml)
--version Show version and exit
Exit Codes
| Code | Meaning |
|---|---|
| 0 | No findings at or above the --fail-on threshold |
| 1 | One or more findings at or above the --fail-on threshold |
| 2 | Usage error (invalid args, file not found, invalid Compose file) |
The default threshold is high — medium and low findings don't fail CI unless you opt in:
compose-lint --fail-on low docker-compose.yml # fail on everything
compose-lint --fail-on critical docker-compose.yml # only critical
CI Integration
GitHub Actions
The easiest path — runs compose-lint and uploads findings to GitHub Code Scanning:
# .github/workflows/lint.yml
name: Compose Lint
on: [push, pull_request]
jobs:
compose-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: tmatens/compose-lint@v0.3.3
with:
sarif-file: results.sarif
Or install from PyPI directly:
- uses: actions/setup-python@v6
with:
python-version: "3.13"
- run: pip install compose-lint
- run: compose-lint docker-compose.yml
SARIF output
compose-lint --format sarif docker-compose.yml > results.sarif
Pre-commit
# .pre-commit-config.yaml
repos:
- repo: https://github.com/tmatens/compose-lint
rev: v0.3.3
hooks:
- id: compose-lint
How it compares
| Tool | Compose security rules | Scope | Zero config |
|---|---|---|---|
| compose-lint | Yes | Docker Compose | Yes |
| KICS | Yes | Broad IaC (Terraform, K8s, Compose, ...) | No |
| Hadolint | No — Dockerfile only | Dockerfile | Yes |
| dclint | Yes — schema/structure only | Docker Compose | Yes |
| Trivy | No — Dockerfile + image scanning | Dockerfiles, images, repos | Yes |
| Checkov | No — no Compose support | Broad IaC (Terraform, K8s, ...) | No |
If you need broad IaC coverage across Terraform, Kubernetes, and more, KICS covers Docker Compose and is worth evaluating. If you want a lightweight, focused tool with zero config and actionable fix guidance for Compose files specifically, this is it.
Contributing
See CONTRIBUTING.md for development setup and how to add rules.
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file compose_lint-0.3.4.tar.gz.
File metadata
- Download URL: compose_lint-0.3.4.tar.gz
- Upload date:
- Size: 80.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
014b2253f4fbfdea80a82c918ebd8a9d7b8a4a673331b5a62de28a4661299532
|
|
| MD5 |
30a500880f55d7f2fa631a3f9e84e879
|
|
| BLAKE2b-256 |
08ebe0e42edf12923a247d3942fb1340928366e52f194d29eecb5c25f79538dd
|
Provenance
The following attestation bundles were made for compose_lint-0.3.4.tar.gz:
Publisher:
publish.yml on tmatens/compose-lint
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
compose_lint-0.3.4.tar.gz -
Subject digest:
014b2253f4fbfdea80a82c918ebd8a9d7b8a4a673331b5a62de28a4661299532 - Sigstore transparency entry: 1291288765
- Sigstore integration time:
-
Permalink:
tmatens/compose-lint@f8df184088dcce073c7bb17bc536012ad0ea8857 -
Branch / Tag:
refs/tags/v0.3.4 - Owner: https://github.com/tmatens
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f8df184088dcce073c7bb17bc536012ad0ea8857 -
Trigger Event:
push
-
Statement type:
File details
Details for the file compose_lint-0.3.4-py3-none-any.whl.
File metadata
- Download URL: compose_lint-0.3.4-py3-none-any.whl
- Upload date:
- Size: 42.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d937608ce94e22ad02361c03d6f034745c07a35ec146010ca8bd70780ede06f4
|
|
| MD5 |
0e572937c7f38adfe130c7b06baed502
|
|
| BLAKE2b-256 |
acd64decebfca4093eaad0f7dbdd5bef83cd6ee5841bec5d4a31675c0e0cd7d7
|
Provenance
The following attestation bundles were made for compose_lint-0.3.4-py3-none-any.whl:
Publisher:
publish.yml on tmatens/compose-lint
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
compose_lint-0.3.4-py3-none-any.whl -
Subject digest:
d937608ce94e22ad02361c03d6f034745c07a35ec146010ca8bd70780ede06f4 - Sigstore transparency entry: 1291288876
- Sigstore integration time:
-
Permalink:
tmatens/compose-lint@f8df184088dcce073c7bb17bc536012ad0ea8857 -
Branch / Tag:
refs/tags/v0.3.4 - Owner: https://github.com/tmatens
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f8df184088dcce073c7bb17bc536012ad0ea8857 -
Trigger Event:
push
-
Statement type: