From 667ae3cdcbc93cd479ed72c44780b122ba3bc626 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Thu, 15 Oct 2026 22:25:21 +0200 Subject: [PATCH] fix(docker-cleanup): remove old Coolify images on Docker 29 Remove old helper, realtime and sentinel images by name with --format instead of parsing the default table, which changed in Docker 29. Also remove unused images pulled by digest. Closes #12148 Co-Authored-By: Claude Opus 5.5 --- app/Actions/Server/CleanupDocker.php | 29 ++++++++++++++----- .../ReverbAndTerminalPackagingTest.php | 1 - 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/app/Actions/Server/CleanupDocker.php b/app/Actions/Server/CleanupDocker.php index 2ace4d7426..ba3886d21c 100644 --- a/app/Actions/Server/CleanupDocker.php +++ b/app/Actions/Server/CleanupDocker.php @@ -131,10 +131,7 @@ class CleanupDocker implements ShouldBeUnique } } $helperImageVersion = getHelperVersion(); - $helperImage = coolifyHelperImage(); - $helperImageWithVersion = "$helperImage:$helperImageVersion"; - $helperImageWithoutPrefix = 'coollabsio/coolify-helper'; - $helperImageWithoutPrefixVersion = "coollabsio/coolify-helper:$helperImageVersion"; + $helperImageWithVersion = coolifyHelperImage().":$helperImageVersion"; $buildxMetadataVolume = isDev() && $server->isLocalhost() ? 'coolify-buildx' : '$HOME/.docker/buildx'; @@ -165,8 +162,7 @@ class CleanupDocker implements ShouldBeUnique $imagePruneCmd, 'docker builder prune -af', "docker run --rm -v {$buildxMetadataVolume}:/root/.docker/buildx -v /var/run/docker.sock:/var/run/docker.sock {$helperImageWithVersion} docker buildx prune --builder coolify-railpack -af 2>/dev/null || true", - "docker images --filter before=$helperImageWithVersion --filter reference=$helperImage | grep $helperImage | awk '{print $3}' | xargs -r docker rmi -f", - "docker images --filter before=$helperImageWithoutPrefixVersion --filter reference=$helperImageWithoutPrefix | grep $helperImageWithoutPrefix | awk '{print $3}' | xargs -r docker rmi -f", + $this->buildCoolifyImageCleanupCommand($helperImageVersion), ]; if ($deleteUnusedVolumes) { @@ -221,6 +217,22 @@ class CleanupDocker implements ShouldBeUnique "awk '{print $1}' | xargs -r docker rm"; } + /** + * Build a command that removes unused images of the Coolify helper, realtime and sentinel, + * from any registry. They have the coolify.managed=true label, so the generic image prune + * skips them. The current helper version stays, and docker rmi (without -f) keeps images + * that a container uses, for example the running sentinel. + */ + private function buildCoolifyImageCleanupCommand(string $helperImageVersion): string + { + $escapedHelperVersion = preg_replace('/([.\\\\+*?\[\]^$(){}|])/', '\\\\$1', $helperImageVersion); + + return "docker images --format '{{.Repository}}:{{.Tag}}' | ". + "grep -E '(^|/)coollabsio/(coolify-helper|coolify-realtime|sentinel):' | ". + "grep -v -E ':$|(^|/)coollabsio/coolify-helper:{$escapedHelperVersion}$' | ". + 'xargs -r docker rmi 2>/dev/null || true'; + } + /** * Build a docker image prune command that excludes application image repositories. * @@ -256,17 +268,18 @@ class CleanupDocker implements ShouldBeUnique // - Don't have coolify.managed=true label // Images in use by containers will fail silently with docker rmi // Pattern matches both uuid:tag and uuid_servicename:tag (Docker Compose with build) + // Images pulled by digest have no tag and are not dangling, so they are listed as repo@digest $grepCommands = "grep -v ''"; // Add application repo exclusion if there are applications if ($applicationImageRepos->isNotEmpty()) { - $grepCommands .= " | grep -v -E '^({$appExcludePatterns})[_:].+'"; + $grepCommands .= " | grep -v -E '^({$appExcludePatterns})[_:@].+'"; } // Add infrastructure image exclusion (matches any registry prefix) $grepCommands .= " | grep -v -E '{$infraExcludePattern}'"; - $commands[] = "docker images --format '{{.Repository}}:{{.Tag}}' | ". + $commands[] = "docker images --digests --format '{{if eq .Tag \"\"}}{{.Repository}}@{{.Digest}}{{else}}{{.Repository}}:{{.Tag}}{{end}}' | ". $grepCommands.' | '. "xargs -r -I {} sh -c 'docker inspect --format \"{{index .Config.Labels \\\"coolify.managed\\\"}}\" \"{}\" 2>/dev/null | grep -q true || docker rmi \"{}\" 2>/dev/null' || true"; diff --git a/tests/Feature/ReverbAndTerminalPackagingTest.php b/tests/Feature/ReverbAndTerminalPackagingTest.php index 04a82db3d1..de27b46ee1 100644 --- a/tests/Feature/ReverbAndTerminalPackagingTest.php +++ b/tests/Feature/ReverbAndTerminalPackagingTest.php @@ -254,7 +254,6 @@ it('stops publishing or preserving the obsolete realtime image', function () { ->not->toContain('realtime_version') ->not->toContain('realtime_image') ->and(file_get_contents(app_path('Actions/Server/CleanupDocker.php'))) - ->not->toContain('coolify-realtime') ->not->toContain('realtimeImage') ->and(file_get_contents(base_path('versions.json'))) ->not->toContain('"realtime"')