fix(postgres): preserve Coolify image tag during upgrade

Keep the running Coolify image tag when restarting the stack after
PostgreSQL upgrade or rollback so compose does not fall back to latest.
This commit is contained in:
Andras Bacsai
2026-06-03 14:54:23 +02:00
parent 08735e6cc8
commit bc165f1976
3 changed files with 125 additions and 6 deletions
+27 -3
View File
@@ -136,6 +136,20 @@ current_postgres_image() {
docker inspect coolify-db --format '{{.Config.Image}}' 2>/dev/null
}
current_coolify_image_tag() {
local image
local image_without_digest
local last_segment
image=$(docker inspect coolify --format '{{.Config.Image}}' 2>/dev/null || true)
image_without_digest="${image%@*}"
last_segment="${image_without_digest##*/}"
if [[ "$last_segment" == *:* ]]; then
printf '%s' "${last_segment##*:}"
fi
}
write_override_file() {
local image="$1"
local volume="$2"
@@ -175,11 +189,12 @@ EOF
}
start_stack() {
local coolify_image_tag="${1:-${LATEST_IMAGE:-latest}}"
local files
files=$(compose_files)
# shellcheck disable=SC2086
LATEST_IMAGE="${LATEST_IMAGE:-latest}" docker compose --env-file "$ENV_FILE" $files up -d --remove-orphans --wait --wait-timeout 120
LATEST_IMAGE="$coolify_image_tag" docker compose --env-file "$ENV_FILE" $files up -d --remove-orphans --wait --wait-timeout 120
}
print_rollback_instructions() {
@@ -220,10 +235,13 @@ rollback_postgres() {
[ -n "${PREVIOUS_MOUNT_PATH:-}" ] || fail "Rollback metadata is missing PREVIOUS_MOUNT_PATH."
[ -n "${PREVIOUS_OVERRIDE_PRESENT:-}" ] || fail "Rollback metadata is missing PREVIOUS_OVERRIDE_PRESENT."
CURRENT_COOLIFY_IMAGE_TAG=$(current_coolify_image_tag)
log "Rolling back Coolify internal PostgreSQL."
log "Previous image: ${PREVIOUS_IMAGE}"
log "Previous volume: ${PREVIOUS_VOLUME}"
log "Previous mount path: ${PREVIOUS_MOUNT_PATH}"
log "Current Coolify image tag: ${CURRENT_COOLIFY_IMAGE_TAG:-latest}"
docker volume inspect "$PREVIOUS_VOLUME" >/dev/null 2>&1 || fail "Previous volume '${PREVIOUS_VOLUME}' does not exist."
@@ -242,7 +260,7 @@ rollback_postgres() {
fi
log "Starting Coolify stack with rollback database volume."
start_stack >>"$LOGFILE" 2>&1 || fail "Could not start Coolify stack after rollback. See ${LOGFILE}."
start_stack "$CURRENT_COOLIFY_IMAGE_TAG" >>"$LOGFILE" 2>&1 || fail "Could not start Coolify stack after rollback. See ${LOGFILE}."
log "Rollback completed successfully."
cat <<EOF | tee -a "$LOGFILE"
@@ -283,6 +301,7 @@ upgrade_postgres() {
PREVIOUS_VOLUME=$(current_postgres_mount_name)
PREVIOUS_MOUNT_PATH=$(current_postgres_mount_path)
PREVIOUS_IMAGE=$(current_postgres_image)
CURRENT_COOLIFY_IMAGE_TAG=$(current_coolify_image_tag)
[ -n "$PREVIOUS_VOLUME" ] || fail "Could not detect current PostgreSQL Docker volume."
[ -n "$PREVIOUS_MOUNT_PATH" ] || fail "Could not detect current PostgreSQL mount path."
@@ -298,6 +317,7 @@ upgrade_postgres() {
log "Current active volume: ${PREVIOUS_VOLUME}"
log "Current image: ${PREVIOUS_IMAGE}"
log "Current mount path: ${PREVIOUS_MOUNT_PATH}"
log "Current Coolify image tag: ${CURRENT_COOLIFY_IMAGE_TAG:-latest}"
if [ "$CURRENT_MAJOR" -eq "$TARGET_MAJOR" ]; then
log "PostgreSQL is already on major ${TARGET_MAJOR}. Nothing to do."
@@ -358,12 +378,16 @@ upgrade_postgres() {
docker rm -f coolify-db >>"$LOGFILE" 2>&1 || true
log "Starting Coolify stack with PostgreSQL ${TARGET_MAJOR}."
start_stack >>"$LOGFILE" 2>&1 || fail "Could not start Coolify stack with upgraded PostgreSQL. See ${LOGFILE}."
start_stack "$CURRENT_COOLIFY_IMAGE_TAG" >>"$LOGFILE" 2>&1 || fail "Could not start Coolify stack with upgraded PostgreSQL. See ${LOGFILE}."
log "Coolify internal PostgreSQL upgrade completed successfully."
print_rollback_instructions
}
if [ "${COOLIFY_POSTGRES_UPGRADE_SOURCE_ONLY:-false}" = "true" ] && [ "${BASH_SOURCE[0]}" != "$0" ]; then
return 0
fi
case "$COMMAND" in
rollback)
rollback_postgres
+27 -3
View File
@@ -136,6 +136,20 @@ current_postgres_image() {
docker inspect coolify-db --format '{{.Config.Image}}' 2>/dev/null
}
current_coolify_image_tag() {
local image
local image_without_digest
local last_segment
image=$(docker inspect coolify --format '{{.Config.Image}}' 2>/dev/null || true)
image_without_digest="${image%@*}"
last_segment="${image_without_digest##*/}"
if [[ "$last_segment" == *:* ]]; then
printf '%s' "${last_segment##*:}"
fi
}
write_override_file() {
local image="$1"
local volume="$2"
@@ -175,11 +189,12 @@ EOF
}
start_stack() {
local coolify_image_tag="${1:-${LATEST_IMAGE:-latest}}"
local files
files=$(compose_files)
# shellcheck disable=SC2086
LATEST_IMAGE="${LATEST_IMAGE:-latest}" docker compose --env-file "$ENV_FILE" $files up -d --remove-orphans --wait --wait-timeout 120
LATEST_IMAGE="$coolify_image_tag" docker compose --env-file "$ENV_FILE" $files up -d --remove-orphans --wait --wait-timeout 120
}
print_rollback_instructions() {
@@ -220,10 +235,13 @@ rollback_postgres() {
[ -n "${PREVIOUS_MOUNT_PATH:-}" ] || fail "Rollback metadata is missing PREVIOUS_MOUNT_PATH."
[ -n "${PREVIOUS_OVERRIDE_PRESENT:-}" ] || fail "Rollback metadata is missing PREVIOUS_OVERRIDE_PRESENT."
CURRENT_COOLIFY_IMAGE_TAG=$(current_coolify_image_tag)
log "Rolling back Coolify internal PostgreSQL."
log "Previous image: ${PREVIOUS_IMAGE}"
log "Previous volume: ${PREVIOUS_VOLUME}"
log "Previous mount path: ${PREVIOUS_MOUNT_PATH}"
log "Current Coolify image tag: ${CURRENT_COOLIFY_IMAGE_TAG:-latest}"
docker volume inspect "$PREVIOUS_VOLUME" >/dev/null 2>&1 || fail "Previous volume '${PREVIOUS_VOLUME}' does not exist."
@@ -242,7 +260,7 @@ rollback_postgres() {
fi
log "Starting Coolify stack with rollback database volume."
start_stack >>"$LOGFILE" 2>&1 || fail "Could not start Coolify stack after rollback. See ${LOGFILE}."
start_stack "$CURRENT_COOLIFY_IMAGE_TAG" >>"$LOGFILE" 2>&1 || fail "Could not start Coolify stack after rollback. See ${LOGFILE}."
log "Rollback completed successfully."
cat <<EOF | tee -a "$LOGFILE"
@@ -283,6 +301,7 @@ upgrade_postgres() {
PREVIOUS_VOLUME=$(current_postgres_mount_name)
PREVIOUS_MOUNT_PATH=$(current_postgres_mount_path)
PREVIOUS_IMAGE=$(current_postgres_image)
CURRENT_COOLIFY_IMAGE_TAG=$(current_coolify_image_tag)
[ -n "$PREVIOUS_VOLUME" ] || fail "Could not detect current PostgreSQL Docker volume."
[ -n "$PREVIOUS_MOUNT_PATH" ] || fail "Could not detect current PostgreSQL mount path."
@@ -298,6 +317,7 @@ upgrade_postgres() {
log "Current active volume: ${PREVIOUS_VOLUME}"
log "Current image: ${PREVIOUS_IMAGE}"
log "Current mount path: ${PREVIOUS_MOUNT_PATH}"
log "Current Coolify image tag: ${CURRENT_COOLIFY_IMAGE_TAG:-latest}"
if [ "$CURRENT_MAJOR" -eq "$TARGET_MAJOR" ]; then
log "PostgreSQL is already on major ${TARGET_MAJOR}. Nothing to do."
@@ -358,12 +378,16 @@ upgrade_postgres() {
docker rm -f coolify-db >>"$LOGFILE" 2>&1 || true
log "Starting Coolify stack with PostgreSQL ${TARGET_MAJOR}."
start_stack >>"$LOGFILE" 2>&1 || fail "Could not start Coolify stack with upgraded PostgreSQL. See ${LOGFILE}."
start_stack "$CURRENT_COOLIFY_IMAGE_TAG" >>"$LOGFILE" 2>&1 || fail "Could not start Coolify stack with upgraded PostgreSQL. See ${LOGFILE}."
log "Coolify internal PostgreSQL upgrade completed successfully."
print_rollback_instructions
}
if [ "${COOLIFY_POSTGRES_UPGRADE_SOURCE_ONLY:-false}" = "true" ] && [ "${BASH_SOURCE[0]}" != "$0" ]; then
return 0
fi
case "$COMMAND" in
rollback)
rollback_postgres
@@ -0,0 +1,71 @@
<?php
use Illuminate\Support\Str;
function runPostgresUpgradeScriptCommand(string $script): array
{
$root = base_path();
$workingDirectory = sys_get_temp_dir().'/coolify-postgres-upgrade-test-'.Str::random(12);
$binDirectory = $workingDirectory.'/bin';
mkdir($binDirectory, 0777, true);
file_put_contents($binDirectory.'/docker', <<<'BASH'
#!/bin/bash
if [ "$1" = "inspect" ]; then
printf '%s\n' "${MOCK_DOCKER_IMAGE}"
exit 0
fi
if [ "$1" = "compose" ]; then
printf '%s\n' "${LATEST_IMAGE}" > "${MOCK_LATEST_IMAGE_OUTPUT}"
exit 0
fi
exit 1
BASH);
chmod($binDirectory.'/docker', 0755);
$outputFile = $workingDirectory.'/latest-image.txt';
$command = sprintf(
'PATH=%s:$PATH MOCK_LATEST_IMAGE_OUTPUT=%s COOLIFY_POSTGRES_UPGRADE_SOURCE_ONLY=true bash -c %s 2>&1',
escapeshellarg($binDirectory),
escapeshellarg($outputFile),
escapeshellarg('cd '.escapeshellarg($root).'; '.$script),
);
exec($command, $output, $exitCode);
return [$exitCode, implode("\n", $output), is_file($outputFile) ? trim(file_get_contents($outputFile)) : null];
}
it('detects the current Coolify image tag from the running container image', function (string $scriptPath, string $image, string $expectedTag) {
[$exitCode, $output] = runPostgresUpgradeScriptCommand(sprintf(
'MOCK_DOCKER_IMAGE=%s; export MOCK_DOCKER_IMAGE; source %s; current_coolify_image_tag',
escapeshellarg($image),
escapeshellarg($scriptPath),
));
expect($exitCode)->toBe(0)
->and($output)->toBe($expectedTag);
})->with([
'nightly standard registry image' => ['other/nightly/upgrade-postgres.sh', 'ghcr.io/coollabsio/coolify:4.0.0-beta.420', '4.0.0-beta.420'],
'nightly registry with port' => ['other/nightly/upgrade-postgres.sh', 'registry.example.com:5000/coollabsio/coolify:4.0.1', '4.0.1'],
'nightly digest suffix' => ['other/nightly/upgrade-postgres.sh', 'ghcr.io/coollabsio/coolify:4.0.2@sha256:abcdef', '4.0.2'],
'scripts standard registry image' => ['scripts/upgrade-postgres.sh', 'ghcr.io/coollabsio/coolify:4.0.0-beta.420', '4.0.0-beta.420'],
'scripts registry with port' => ['scripts/upgrade-postgres.sh', 'registry.example.com:5000/coollabsio/coolify:4.0.1', '4.0.1'],
'scripts digest suffix' => ['scripts/upgrade-postgres.sh', 'ghcr.io/coollabsio/coolify:4.0.2@sha256:abcdef', '4.0.2'],
]);
it('passes the preserved Coolify image tag to docker compose when starting the stack', function (string $scriptPath) {
[$exitCode, $output, $latestImage] = runPostgresUpgradeScriptCommand(
sprintf('source %s; start_stack 4.0.0-beta.420', escapeshellarg($scriptPath)),
);
expect($exitCode)->toBe(0)
->and($output)->toBe('')
->and($latestImage)->toBe('4.0.0-beta.420');
})->with([
'nightly script' => 'other/nightly/upgrade-postgres.sh',
'release script' => 'scripts/upgrade-postgres.sh',
]);