Compose-Check-Updates
Keep your Docker Compose image tags up to date — like npm-check-updates, but for compose.yaml.
ccu # open the TUI and pick what to update
ccu check # just print what's outdated
ccu check -u # print and write the new tags
Point it at a directory, it scans every Compose file below it, asks each registry
what's newer, and — if you want — rewrites the tags for you.
Install
Download the binary for your platform from the
Releases page.
Linux / macOS
mv ccu-linux-amd64 ccu && chmod +x ccu
sudo mv ccu /usr/local/bin/ # optional
ccu version
Windows — rename it to ccu.exe, optionally put its directory on your PATH,
then check with ccu.exe version.
You can also just run the downloaded file directly (./ccu-linux-amd64) without
installing anything.
Later on, ccu self-update replaces the binary in place — see
Updating ccu.
Usage
cd into the directory holding your stacks and run ccu. All subdirectories are
scanned recursively for Compose files, and the images in their services are
checked against their registries.
There are two ways to use it:
|
|
ccu |
The TUI. Browse what's outdated, pick rows, apply. Default. |
ccu check |
One-shot report for scripts, cron and CI. No UI. |
Nothing is ever written unless you ask for it — A in the TUI, -u for check.
[!NOTE]
Writing creates a backup of every modified Compose file next to it, with a
.ccu extension.
The TUI (default)
ccu # scan the current directory
ccu -d ./stacks # scan somewhere else
A full-screen terminal UI: updates grouped per Compose file, colour-coded by
level, streaming in as the registries answer. Arrow keys to move, space to
select, A to apply. Press ? for everything else.
Nothing is written until you press A, and you decide per row which version gets
written — so a major bump never sneaks in. Afterwards ccu asks once whether the
affected Compose files should be restarted with docker compose up -d.
All keys
| Key |
Action |
↑/↓ or k/j |
Move the selection |
space or enter |
Toggle a row, or fold/unfold a file group |
a / n |
Select all / select none |
z |
Fold/unfold the group under the cursor |
C / E |
Collapse all / expand all groups |
f |
Cycle the display filter (which rows are shown) |
t |
Cycle the target level for all rows |
T/→ or ← |
Cycle the target level for the highlighted row |
d or tab |
Toggle the detail pane |
i |
Show the issues logged during the scan |
A |
Apply the selected updates |
u |
Apply only the highlighted row |
y / n |
Answer the restart prompt |
? / q |
Help / quit |
Filter vs. target — which version actually gets written
show (f) only decides which rows are visible; target (t, T) decides
which version actually gets written. The target defaults to major, so out of
the box you are offered the highest available version. At target minor, an
image on traefik:v2.9.3 that has 3.7.8 available re-points to the latest
2.11.x instead; at patch, to 2.9.4.
T only cycles the levels an image actually has — the (+2) after a version
means two other levels exist. A row with nothing at the current target shows as
[-] … no patch update and cannot be applied.
The TUI always resolves all update levels, regardless of -patch, -minor,
-major or -f. Those flags govern ccu check only.
[!TIP]
The TUI needs a real terminal. When stdout is piped or redirected, ccu runs
the check report instead and says so on stderr — so an old cron entry or CI
job keeps working either way.
ccu check — the non-interactive report
ccu check # report only (patch updates by default)
ccu check -u # write the new tags
ccu check -u -r # write, then restart the affected services
ccu check -f # consider every newer version, not just patches
ccu check -d ./stacks # scan a different directory
Every flag below applies to ccu check; only -d and -exclude also apply to
the TUI, which picks levels in the UI instead.
| Flag |
Description |
Default |
-d |
Directory to scan |
. |
-exclude |
Directories to exclude, comma-separated |
none |
-u |
Update the Compose files with the new image tags |
false |
-r |
Restart the services after updating |
false |
-f |
Full mode — consider every newer version, not just patches |
false |
-major |
Only suggest major version updates |
false |
-minor |
Only suggest minor version updates |
false |
-patch |
Only suggest patch version updates |
true |
All commands
ccu # the TUI
ccu check # the non-interactive report
ccu self-update # download, verify and replace the running binary
ccu check-update # only report whether a newer ccu exists
ccu help # show the help message
ccu version # show version information
The last four act on ccu itself and ignore the flags above.
[!NOTE]
Coming from v0.6.x? The report used to be the default and -i opened the
TUI. That is now the other way round. Both old spellings still work: -i is
accepted as a no-op, and a report-only flag without check (ccu -u) still
runs the report, printing a one-line hint about the new spelling.
Updating ccu
ccu self-update # download, verify and replace the running binary
ccu check-update # only report whether something newer exists
A ccu check run also checks at most once every 24 hours whether a newer
release exists and prints one line to stderr if so — stdout stays clean. It never
installs anything by itself.
Update-check details
The timestamp of the last check lives in <user config dir>/ccu/update-check.json
(%AppData%\ccu\update-check.json on Windows, ~/.config/ccu/update-check.json
on Linux). Set CCU_NO_UPDATE_CHECK=1 to disable the check entirely.
The older -self-update and -check-update flag spellings still work so existing
scripts keep running, but the subcommands above are the supported form.
Not every image publishes semantic versions. Some are pinned by digest, others tag
every build with its commit (e.g. ghcr.io/vert-sh/vert with sha-e1c83ba tags).
For those, ccu compares the image manifest digest instead of the version number:
| In your Compose file |
What ccu does |
image: vert:sha-438f91a |
Moves the tag to the one currently matching latest, e.g. sha-e1c83ba |
image: vert@sha256:abc… |
Rewrites the digest to the one latest now resolves to |
image: vert:1.2.3@sha256:abc… |
Bumps the tag and the digest together, so they stay consistent |
image: vert:latest |
Skipped — a floating tag already resolves to the newest image |
These are reported with the update level digest. A digest change has no
major/minor/patch level, so it is always reported and is unaffected by -major,
-minor and -patch.
[!NOTE]
Finding which tag carries the newest digest requires querying tags individually,
so the first check of such an image is noticeably slower. At most 250 tags of the
same naming scheme are inspected; ccu warns when an image has more.
Troubleshooting
No new versions found, but newer versions exist
By default ccu check only checks for patch versions. With a current tag of
1.0.0 and a latest tag of 1.1.0, there is no newer patch version, so nothing
is suggested. Use ccu check -f to consider every newer version — or just use the
TUI, which always resolves every level.
Image tags with only x.y versions
Some images only publish x.y tags. Alpine has 3.14, 3.14.1 and 3.14.0 — if
you use 3.14, ccu suggests 3.14.1. But Postgres has 13, 13.3 and 13.4:
if you use 13.2, ccu will not suggest 13.4, because 13 is not a valid
semver version.
(This might change in the future behind an additional flag.)
The TUI does not open
ccu falls back to the check report when stdout is not a terminal — inside a
pipe (ccu | less), a redirect (ccu > out.txt), or a CI job. The stderr line
tells you when that happened. Run ccu with its output attached to the terminal
to get the UI.