Check your Docker Compose stack for image updates without pulling
Project description
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
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_outdated-0.4.1.tar.gz.
File metadata
- Download URL: compose_outdated-0.4.1.tar.gz
- Upload date:
- Size: 61.6 kB
- Tags: Source
- Uploaded using 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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
de25e6ac1c1bb182085d298f87be7a8c0c8dedc9e0c5e83a042d0257fe463b89
|
|
| MD5 |
e6b948feddc7452751146198ad1aaca9
|
|
| BLAKE2b-256 |
34cc17d9ab8c2c6177dbd9ced3c930aac1ecfc8f36a4b718dbffc3bca27c14e5
|
File details
Details for the file compose_outdated-0.4.1-py3-none-any.whl.
File metadata
- Download URL: compose_outdated-0.4.1-py3-none-any.whl
- Upload date:
- Size: 17.9 kB
- Tags: Python 3
- Uploaded using 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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
da0ea34f9ff1c70abeb52836feb4b020f0cfed8d4e0e5878df6bfbc899d7b394
|
|
| MD5 |
d47bc19e29008ea426d2039501538c33
|
|
| BLAKE2b-256 |
7725b0644b459a35eb3b33c16618033eef626e58c06b634f56a351621e9b82ce
|