compose-outdated
Check your Docker Compose stack for image updates without pulling any images. Shows current vs latest versions and fetches changelogs from GitHub.
Features
- Parses
docker-compose.yml/compose.yamlwith fullinclude:recursion - Queries Docker Hub, GHCR, and lscr.io for available tags
- Compares versions within the same tag channel (e.g.
-alpinetags stay in their lane) - Fetches release notes from GitHub for each available update
- Supports environment variable interpolation (
${VAR:-default}) - Concurrent registry checks for fast results
- No images are pulled -- registry APIs only
Setup
Requires Python 3.12+ and uv.
# Clone and install
git clone <repo-url> && cd compose-outdated
uv sync
# Or install directly with uv/pipx
uv tool install .
This installs two equivalent commands: compose-outdated and cod.
Usage
# Check compose file in the current directory
cod
# Check a specific directory or file
cod /path/to/my-stack
cod /path/to/docker-compose.yml
# Skip changelog fetching (faster, table-only output)
cod --no-changelog
# Only check specific services
cod -s traefik -s redis
# Set a GitHub token for higher API rate limits (also reads GITHUB_TOKEN env var)
cod --github-token ghp_xxxxx
# Control concurrent registry requests (default: 5)
cod -n 10
# Show more changelog context per release (default: 8 lines, 0 = unlimited)
cod -l 20
cod -l 0
# Save the full report to a file (always untruncated)
cod -o report.txt
# Compact terminal output but full detail in the file
cod -l 3 -o report.txt
Options
| Flag | Short | Description |
|---|---|---|
--changelog / --no-changelog |
-c / -C |
Enable/disable GitHub changelog fetching (default: on) |
--github-token TEXT |
GitHub API token for higher rate limits | |
--service TEXT |
-s |
Only check named service(s), can be repeated |
--concurrency INT |
-n |
Max concurrent registry checks (default: 5) |
--body-lines INT |
-l |
Max lines per changelog body in terminal (0 = unlimited, default: 8) |
--output PATH |
-o |
Write full report to file (changelogs are never truncated in the file) |
Exit codes
| Code | Meaning |
|---|---|
0 |
All images are up to date |
1 |
Error (compose file not found, parse failure, etc.) |
2 |
Updates are available |
This makes it easy to use in scripts or CI:
cod --no-changelog /path/to/stack || echo "Updates available!"
Supported registries
- Docker Hub (
docker.io) -- uses the Hub REST API, no auth required for public images - GitHub Container Registry (
ghcr.io) -- uses OCI Distribution API with anonymous tokens - LinuxServer (
lscr.io) -- proxied through GHCR - Any OCI-compliant registry should work via the Distribution spec fallback
How changelogs are resolved
The tool tries several strategies to find the GitHub source repo for each image:
- Built-in known mappings for popular images (traefik, postgres, redis, cloudflared, etc.)
- GHCR / lscr.io path inference (image path usually matches the GitHub repo)
docker-library/official-imagesmanifest lookup (for officiallibrary/images)- Docker Hub repository metadata (parses README for GitHub links)
- GitHub search as a last resort
Once a repo is found, GitHub Releases are fetched and filtered to versions between your current tag and the latest.
Compose file support
The tool handles real-world compose setups:
compose.yaml,compose.yml,docker-compose.yaml,docker-compose.yml- Recursive
include:directives (nested includes are followed) - Environment variable interpolation:
$VAR,${VAR},${VAR:-default} - Digest-pinned images (
image:tag@sha256:...) -- the tag is extracted for comparison - Images without explicit tags default to
latest
Version comparison
Tags are compared within "channels" to avoid false positives:
1.25.3-alpineis only compared against other*-alpinetagsv-prefixed and non-prefixed duplicates are deduplicated (v3.6.10and3.6.10count as one)- Non-semver tags like
latest,stable, or commit SHAs are skipped
Development
uv sync # install deps
uv run pytest # run tests
uv run pytest --cov=compose_outdated # run tests with coverage
uv run ruff check . # lint
uv run ruff format . # format
This project uses Conventional Commits. Prefix your commit messages with feat:, fix:, chore:, docs:, test:, ci:, refactor:, etc.
Releasing a new version
- Ensure you're on
masterwith a clean working tree and CI passing. - Run the version bump:
uv run cz bump
This will:- Determine the next version from commit messages since the last tag
- Update
versioninpyproject.toml - Update
CHANGELOG.md - Create a commit and git tag (e.g.
v0.4.0)
- Review the changes:
git log -1 --stat git diff HEAD~1 CHANGELOG.md
- Push the commit and tag:
git push origin master --follow-tags
- The CI
publishjob triggers on the tag, builds the package, and publishes to PyPI via OIDC trusted publisher.
PyPI trusted publisher setup (one-time)
On pypi.org, under the project's "Publishing" settings, add a new "GitLab" trusted publisher:
- GitLab instance URL:
gitlab.com - Project path:
<your-namespace>/compose-outdated - Environment:
pypi - Top-level pipeline or workflow name:
.gitlab-ci.yml
Metadata
Release files for compose-outdated 0.4.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| compose_outdated-0.4.1.tar.gz | 61.6 kB | Without provenance | Details for compose_outdated-0.4.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| compose_outdated-0.4.1-py3-none-any.whl | Python 3 | none | any | Details for compose_outdated-0.4.1-py3-none-any.whl |
Total release size: 79.4 kB
Release files / compose_outdated-0.4.1.tar.gz
| Download URL | compose_outdated-0.4.1.tar.gz |
|---|---|
| Size | 61.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
de25e6ac1c1bb182085d298f87be7a8c0c8dedc9e0c5e83a042d0257fe463b89
|
|
BLAKE2b-256 checksum How to use checksums |
34cc17d9ab8c2c6177dbd9ced3c930aac1ecfc8f36a4b718dbffc3bca27c14e5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / compose_outdated-0.4.1-py3-none-any.whl
| Download URL | compose_outdated-0.4.1-py3-none-any.whl |
|---|---|
| Size | 17.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
da0ea34f9ff1c70abeb52836feb4b020f0cfed8d4e0e5878df6bfbc899d7b394
|
|
BLAKE2b-256 checksum How to use checksums |
7725b0644b459a35eb3b33c16618033eef626e58c06b634f56a351621e9b82ce
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for compose-outdated 0.4.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.