From 0c46da0a23708205917e5eb830459882a60bdc0e Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Fri, 12 Dec 2025 11:28:53 +0100 Subject: [PATCH 01/22] Fix Docker container race condition during upgrades MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stop and remove existing Coolify containers before starting new ones to prevent conflicts when project name changes from 'source' to 'coolify'. This resolves volume and container name conflicts during upgrades from older installations. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 --- other/nightly/upgrade.sh | 11 +++++++++++ scripts/upgrade.sh | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/other/nightly/upgrade.sh b/other/nightly/upgrade.sh index bfcd11095..667cf4162 100644 --- a/other/nightly/upgrade.sh +++ b/other/nightly/upgrade.sh @@ -64,6 +64,17 @@ if [ -f /root/.docker/config.json ]; then DOCKER_CONFIG_MOUNT="-v /root/.docker/config.json:/root/.docker/config.json" fi +# Stop and remove existing Coolify containers to prevent conflicts +# This handles both old installations (project "source") and new ones (project "coolify") +echo "Stopping existing Coolify containers..." >>"$LOGFILE" +for container in coolify coolify-db coolify-redis coolify-realtime; do + if docker ps -a --format '{{.Names}}' | grep -q "^${container}$"; then + docker stop "$container" >>"$LOGFILE" 2>&1 || true + docker rm "$container" >>"$LOGFILE" 2>&1 || true + echo " - Removed container: $container" >>"$LOGFILE" + fi +done + if [ -f /data/coolify/source/docker-compose.custom.yml ]; then echo "docker-compose.custom.yml detected." >>"$LOGFILE" docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock ${DOCKER_CONFIG_MOUNT} --rm ${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:${LATEST_HELPER_VERSION} bash -c "LATEST_IMAGE=${LATEST_IMAGE} docker compose --project-name coolify --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml -f /data/coolify/source/docker-compose.custom.yml up -d --remove-orphans --wait --wait-timeout 60" >>"$LOGFILE" 2>&1 diff --git a/scripts/upgrade.sh b/scripts/upgrade.sh index f091d2fdb..36746679b 100644 --- a/scripts/upgrade.sh +++ b/scripts/upgrade.sh @@ -64,6 +64,17 @@ if [ -f /root/.docker/config.json ]; then DOCKER_CONFIG_MOUNT="-v /root/.docker/config.json:/root/.docker/config.json" fi +# Stop and remove existing Coolify containers to prevent conflicts +# This handles both old installations (project "source") and new ones (project "coolify") +echo "Stopping existing Coolify containers..." >>"$LOGFILE" +for container in coolify coolify-db coolify-redis coolify-realtime; do + if docker ps -a --format '{{.Names}}' | grep -q "^${container}$"; then + docker stop "$container" >>"$LOGFILE" 2>&1 || true + docker rm "$container" >>"$LOGFILE" 2>&1 || true + echo " - Removed container: $container" >>"$LOGFILE" + fi +done + if [ -f /data/coolify/source/docker-compose.custom.yml ]; then echo "docker-compose.custom.yml detected." >>"$LOGFILE" docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock ${DOCKER_CONFIG_MOUNT} --rm ${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:${LATEST_HELPER_VERSION} bash -c "LATEST_IMAGE=${LATEST_IMAGE} docker compose --project-name coolify --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml -f /data/coolify/source/docker-compose.custom.yml up -d --remove-orphans --wait --wait-timeout 60" >>"$LOGFILE" 2>&1 From c45cbc04c8c9627c299d5a0e8463aaf47a5f5121 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Fri, 12 Dec 2025 14:54:24 +0100 Subject: [PATCH 02/22] Pull images before stopping containers during upgrade MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ensures images are available before taking down the system. If pull fails (rate limits, network issues, expired tokens), upgrade aborts safely without leaving Coolify in a broken state. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- other/nightly/upgrade.sh | 11 +++++++++++ scripts/upgrade.sh | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/other/nightly/upgrade.sh b/other/nightly/upgrade.sh index 667cf4162..aa0c82865 100644 --- a/other/nightly/upgrade.sh +++ b/other/nightly/upgrade.sh @@ -64,6 +64,17 @@ if [ -f /root/.docker/config.json ]; then DOCKER_CONFIG_MOUNT="-v /root/.docker/config.json:/root/.docker/config.json" fi +# Pull all required images before stopping containers +# This ensures we don't take down the system if image pull fails (rate limits, network issues, etc.) +echo "Pulling required Docker images..." >>"$LOGFILE" +docker pull "${REGISTRY_URL:-ghcr.io}/coollabsio/coolify:${LATEST_IMAGE}" >>"$LOGFILE" 2>&1 || { echo "Failed to pull Coolify image. Aborting upgrade." >>"$LOGFILE"; exit 1; } +docker pull "${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:${LATEST_HELPER_VERSION}" >>"$LOGFILE" 2>&1 || { echo "Failed to pull Coolify helper image. Aborting upgrade." >>"$LOGFILE"; exit 1; } +docker pull postgres:15-alpine >>"$LOGFILE" 2>&1 || { echo "Failed to pull PostgreSQL image. Aborting upgrade." >>"$LOGFILE"; exit 1; } +docker pull redis:7-alpine >>"$LOGFILE" 2>&1 || { echo "Failed to pull Redis image. Aborting upgrade." >>"$LOGFILE"; exit 1; } +# Pull realtime image - version is hardcoded in docker-compose.prod.yml, extract it or use a known version +docker pull "${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-realtime:1.0.10" >>"$LOGFILE" 2>&1 || { echo "Failed to pull Coolify realtime image. Aborting upgrade." >>"$LOGFILE"; exit 1; } +echo "All images pulled successfully." >>"$LOGFILE" + # Stop and remove existing Coolify containers to prevent conflicts # This handles both old installations (project "source") and new ones (project "coolify") echo "Stopping existing Coolify containers..." >>"$LOGFILE" diff --git a/scripts/upgrade.sh b/scripts/upgrade.sh index 36746679b..3ce426548 100644 --- a/scripts/upgrade.sh +++ b/scripts/upgrade.sh @@ -64,6 +64,17 @@ if [ -f /root/.docker/config.json ]; then DOCKER_CONFIG_MOUNT="-v /root/.docker/config.json:/root/.docker/config.json" fi +# Pull all required images before stopping containers +# This ensures we don't take down the system if image pull fails (rate limits, network issues, etc.) +echo "Pulling required Docker images..." >>"$LOGFILE" +docker pull "${REGISTRY_URL:-ghcr.io}/coollabsio/coolify:${LATEST_IMAGE}" >>"$LOGFILE" 2>&1 || { echo "Failed to pull Coolify image. Aborting upgrade." >>"$LOGFILE"; exit 1; } +docker pull "${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:${LATEST_HELPER_VERSION}" >>"$LOGFILE" 2>&1 || { echo "Failed to pull Coolify helper image. Aborting upgrade." >>"$LOGFILE"; exit 1; } +docker pull postgres:15-alpine >>"$LOGFILE" 2>&1 || { echo "Failed to pull PostgreSQL image. Aborting upgrade." >>"$LOGFILE"; exit 1; } +docker pull redis:7-alpine >>"$LOGFILE" 2>&1 || { echo "Failed to pull Redis image. Aborting upgrade." >>"$LOGFILE"; exit 1; } +# Pull realtime image - version is hardcoded in docker-compose.prod.yml, extract it or use a known version +docker pull "${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-realtime:1.0.10" >>"$LOGFILE" 2>&1 || { echo "Failed to pull Coolify realtime image. Aborting upgrade." >>"$LOGFILE"; exit 1; } +echo "All images pulled successfully." >>"$LOGFILE" + # Stop and remove existing Coolify containers to prevent conflicts # This handles both old installations (project "source") and new ones (project "coolify") echo "Stopping existing Coolify containers..." >>"$LOGFILE" From 6a9027dcbf52ffe1711ce3be43928d9f675237b0 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Fri, 12 Dec 2025 15:18:57 +0100 Subject: [PATCH 03/22] Add human-friendly output to upgrade script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Show clear progress with numbered steps (1/6 through 6/6) - Display header and footer banners - Show individual image pull progress - Show which containers are being stopped - Display final success message with version and log location - Keep detailed logging to file for debugging 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- app/Actions/Server/UpdateCoolify.php | 12 ++---- scripts/upgrade.sh | 64 ++++++++++++++++++++++------ 2 files changed, 55 insertions(+), 21 deletions(-) diff --git a/app/Actions/Server/UpdateCoolify.php b/app/Actions/Server/UpdateCoolify.php index a26e7daaa..b5ebd92b2 100644 --- a/app/Actions/Server/UpdateCoolify.php +++ b/app/Actions/Server/UpdateCoolify.php @@ -30,7 +30,6 @@ public function handle($manual_update = false) if (! $this->server) { return; } - CleanupDocker::dispatch($this->server, false, false); // Fetch fresh version from CDN instead of using cache try { @@ -117,17 +116,12 @@ public function handle($manual_update = false) private function update() { - $helperImage = config('constants.coolify.helper_image'); - $latest_version = getHelperVersion(); - instant_remote_process(["docker pull -q {$helperImage}:{$latest_version}"], $this->server, false); - - $image = config('constants.coolify.registry_url').'/coollabsio/coolify:'.$this->latestVersion; - instant_remote_process(["docker pull -q $image"], $this->server, false); - + $latestHelperImageVersion = getHelperVersion(); $upgradeScriptUrl = config('constants.coolify.upgrade_script_url'); + remote_process([ "curl -fsSL {$upgradeScriptUrl} -o /data/coolify/source/upgrade.sh", - "bash /data/coolify/source/upgrade.sh $this->latestVersion", + "bash /data/coolify/source/upgrade.sh $this->latestVersion $latestHelperImageVersion", ], $this->server); } } diff --git a/scripts/upgrade.sh b/scripts/upgrade.sh index 3ce426548..73514214f 100644 --- a/scripts/upgrade.sh +++ b/scripts/upgrade.sh @@ -11,13 +11,22 @@ ENV_FILE="/data/coolify/source/.env" DATE=$(date +%Y-%m-%d-%H-%M-%S) LOGFILE="/data/coolify/source/upgrade-${DATE}.log" +echo "" +echo "==========================================" +echo " Coolify Upgrade - ${DATE}" +echo "==========================================" +echo "" + +echo "1/6 Downloading latest configuration files..." curl -fsSL -L $CDN/docker-compose.yml -o /data/coolify/source/docker-compose.yml curl -fsSL -L $CDN/docker-compose.prod.yml -o /data/coolify/source/docker-compose.prod.yml curl -fsSL -L $CDN/.env.production -o /data/coolify/source/.env.production +echo " Done." # Backup existing .env file before making any changes if [ "$SKIP_BACKUP" != "true" ]; then if [ -f "$ENV_FILE" ]; then + echo " Creating backup of .env file..." echo "Creating backup of existing .env file to .env-$DATE" >>"$LOGFILE" cp "$ENV_FILE" "$ENV_FILE-$DATE" else @@ -25,6 +34,8 @@ if [ "$SKIP_BACKUP" != "true" ]; then fi fi +echo "" +echo "2/6 Updating environment configuration..." echo "Merging .env.production values into .env" >>"$LOGFILE" awk -F '=' '!seen[$1]++' "$ENV_FILE" /data/coolify/source/.env.production > "$ENV_FILE.tmp" && mv "$ENV_FILE.tmp" "$ENV_FILE" echo ".env file merged successfully" >>"$LOGFILE" @@ -48,12 +59,13 @@ echo "Checking and updating environment variables if necessary..." >>"$LOGFILE" update_env_var "PUSHER_APP_ID" "$(openssl rand -hex 32)" update_env_var "PUSHER_APP_KEY" "$(openssl rand -hex 32)" update_env_var "PUSHER_APP_SECRET" "$(openssl rand -hex 32)" +echo " Done." # Make sure coolify network exists # It is created when starting Coolify with docker compose if ! docker network inspect coolify >/dev/null 2>&1; then if ! docker network create --attachable --ipv6 coolify 2>/dev/null; then - echo "Failed to create coolify network with ipv6. Trying without ipv6..." + echo "Failed to create coolify network with ipv6. Trying without ipv6..." >>"$LOGFILE" docker network create --attachable coolify 2>/dev/null fi fi @@ -64,31 +76,59 @@ if [ -f /root/.docker/config.json ]; then DOCKER_CONFIG_MOUNT="-v /root/.docker/config.json:/root/.docker/config.json" fi -# Pull all required images before stopping containers -# This ensures we don't take down the system if image pull fails (rate limits, network issues, etc.) +echo "" +echo "3/6 Pulling Docker images..." +echo " This may take a few minutes depending on your connection." echo "Pulling required Docker images..." >>"$LOGFILE" -docker pull "${REGISTRY_URL:-ghcr.io}/coollabsio/coolify:${LATEST_IMAGE}" >>"$LOGFILE" 2>&1 || { echo "Failed to pull Coolify image. Aborting upgrade." >>"$LOGFILE"; exit 1; } -docker pull "${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:${LATEST_HELPER_VERSION}" >>"$LOGFILE" 2>&1 || { echo "Failed to pull Coolify helper image. Aborting upgrade." >>"$LOGFILE"; exit 1; } -docker pull postgres:15-alpine >>"$LOGFILE" 2>&1 || { echo "Failed to pull PostgreSQL image. Aborting upgrade." >>"$LOGFILE"; exit 1; } -docker pull redis:7-alpine >>"$LOGFILE" 2>&1 || { echo "Failed to pull Redis image. Aborting upgrade." >>"$LOGFILE"; exit 1; } -# Pull realtime image - version is hardcoded in docker-compose.prod.yml, extract it or use a known version -docker pull "${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-realtime:1.0.10" >>"$LOGFILE" 2>&1 || { echo "Failed to pull Coolify realtime image. Aborting upgrade." >>"$LOGFILE"; exit 1; } -echo "All images pulled successfully." >>"$LOGFILE" -# Stop and remove existing Coolify containers to prevent conflicts -# This handles both old installations (project "source") and new ones (project "coolify") +echo " - Pulling Coolify image..." +docker pull "${REGISTRY_URL:-ghcr.io}/coollabsio/coolify:${LATEST_IMAGE}" >>"$LOGFILE" 2>&1 || { echo " ERROR: Failed to pull Coolify image. Aborting upgrade."; echo "Failed to pull Coolify image. Aborting upgrade." >>"$LOGFILE"; exit 1; } + +echo " - Pulling Coolify helper image..." +docker pull "${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:${LATEST_HELPER_VERSION}" >>"$LOGFILE" 2>&1 || { echo " ERROR: Failed to pull helper image. Aborting upgrade."; echo "Failed to pull Coolify helper image. Aborting upgrade." >>"$LOGFILE"; exit 1; } + +echo " - Pulling PostgreSQL image..." +docker pull postgres:15-alpine >>"$LOGFILE" 2>&1 || { echo " ERROR: Failed to pull PostgreSQL image. Aborting upgrade."; echo "Failed to pull PostgreSQL image. Aborting upgrade." >>"$LOGFILE"; exit 1; } + +echo " - Pulling Redis image..." +docker pull redis:7-alpine >>"$LOGFILE" 2>&1 || { echo " ERROR: Failed to pull Redis image. Aborting upgrade."; echo "Failed to pull Redis image. Aborting upgrade." >>"$LOGFILE"; exit 1; } + +echo " - Pulling Coolify realtime image..." +docker pull "${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-realtime:1.0.10" >>"$LOGFILE" 2>&1 || { echo " ERROR: Failed to pull realtime image. Aborting upgrade."; echo "Failed to pull Coolify realtime image. Aborting upgrade." >>"$LOGFILE"; exit 1; } + +echo "All images pulled successfully." >>"$LOGFILE" +echo " All images pulled successfully." + +echo "" +echo "4/6 Stopping existing containers..." echo "Stopping existing Coolify containers..." >>"$LOGFILE" for container in coolify coolify-db coolify-redis coolify-realtime; do if docker ps -a --format '{{.Names}}' | grep -q "^${container}$"; then + echo " - Stopping ${container}..." docker stop "$container" >>"$LOGFILE" 2>&1 || true docker rm "$container" >>"$LOGFILE" 2>&1 || true echo " - Removed container: $container" >>"$LOGFILE" fi done +echo " Done." +echo "" +echo "5/6 Starting new containers..." if [ -f /data/coolify/source/docker-compose.custom.yml ]; then + echo " Custom docker-compose.yml detected." echo "docker-compose.custom.yml detected." >>"$LOGFILE" docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock ${DOCKER_CONFIG_MOUNT} --rm ${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:${LATEST_HELPER_VERSION} bash -c "LATEST_IMAGE=${LATEST_IMAGE} docker compose --project-name coolify --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml -f /data/coolify/source/docker-compose.custom.yml up -d --remove-orphans --wait --wait-timeout 60" >>"$LOGFILE" 2>&1 else docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock ${DOCKER_CONFIG_MOUNT} --rm ${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:${LATEST_HELPER_VERSION} bash -c "LATEST_IMAGE=${LATEST_IMAGE} docker compose --project-name coolify --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml up -d --remove-orphans --wait --wait-timeout 60" >>"$LOGFILE" 2>&1 fi +echo " Done." + +echo "" +echo "6/6 Upgrade complete!" +echo "" +echo "==========================================" +echo " Coolify has been upgraded to ${LATEST_IMAGE}" +echo "==========================================" +echo "" +echo " Log file: ${LOGFILE}" +echo "" From 7dc93001e3e4d1a089a7dcac3120b4e68669582d Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Fri, 12 Dec 2025 15:32:49 +0100 Subject: [PATCH 04/22] Improve log file format with timestamps and sections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add log() helper for timestamped entries - Add log_section() for clear section headers - Include upgrade metadata at start (version, registry, etc.) - Log each step with clear descriptions - Add completion timestamp at end - Track container operations individually 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- scripts/upgrade.sh | 129 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 111 insertions(+), 18 deletions(-) diff --git a/scripts/upgrade.sh b/scripts/upgrade.sh index 73514214f..f922983c6 100644 --- a/scripts/upgrade.sh +++ b/scripts/upgrade.sh @@ -11,34 +11,63 @@ ENV_FILE="/data/coolify/source/.env" DATE=$(date +%Y-%m-%d-%H-%M-%S) LOGFILE="/data/coolify/source/upgrade-${DATE}.log" +# Helper function to log with timestamp +log() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >>"$LOGFILE" +} + +# Helper function to log section headers +log_section() { + echo "" >>"$LOGFILE" + echo "============================================================" >>"$LOGFILE" + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >>"$LOGFILE" + echo "============================================================" >>"$LOGFILE" +} + echo "" echo "==========================================" echo " Coolify Upgrade - ${DATE}" echo "==========================================" echo "" +# Initialize log file with header +echo "============================================================" >>"$LOGFILE" +echo "Coolify Upgrade Log" >>"$LOGFILE" +echo "Started: $(date '+%Y-%m-%d %H:%M:%S')" >>"$LOGFILE" +echo "Target Version: ${LATEST_IMAGE}" >>"$LOGFILE" +echo "Helper Version: ${LATEST_HELPER_VERSION}" >>"$LOGFILE" +echo "Registry URL: ${REGISTRY_URL}" >>"$LOGFILE" +echo "============================================================" >>"$LOGFILE" + +log_section "Step 1/6: Downloading configuration files" echo "1/6 Downloading latest configuration files..." +log "Downloading docker-compose.yml from ${CDN}/docker-compose.yml" curl -fsSL -L $CDN/docker-compose.yml -o /data/coolify/source/docker-compose.yml +log "Downloading docker-compose.prod.yml from ${CDN}/docker-compose.prod.yml" curl -fsSL -L $CDN/docker-compose.prod.yml -o /data/coolify/source/docker-compose.prod.yml +log "Downloading .env.production from ${CDN}/.env.production" curl -fsSL -L $CDN/.env.production -o /data/coolify/source/.env.production +log "Configuration files downloaded successfully" echo " Done." # Backup existing .env file before making any changes if [ "$SKIP_BACKUP" != "true" ]; then if [ -f "$ENV_FILE" ]; then echo " Creating backup of .env file..." - echo "Creating backup of existing .env file to .env-$DATE" >>"$LOGFILE" + log "Creating backup of .env file to .env-$DATE" cp "$ENV_FILE" "$ENV_FILE-$DATE" + log "Backup created: ${ENV_FILE}-${DATE}" else - echo "No existing .env file found to backup" >>"$LOGFILE" + log "WARNING: No existing .env file found to backup" fi fi +log_section "Step 2/6: Updating environment configuration" echo "" echo "2/6 Updating environment configuration..." -echo "Merging .env.production values into .env" >>"$LOGFILE" +log "Merging .env.production values into .env" awk -F '=' '!seen[$1]++' "$ENV_FILE" /data/coolify/source/.env.production > "$ENV_FILE.tmp" && mv "$ENV_FILE.tmp" "$ENV_FILE" -echo ".env file merged successfully" >>"$LOGFILE" +log "Environment file merged successfully" update_env_var() { local key="$1" @@ -47,82 +76,140 @@ update_env_var() { # If variable "key=" exists but has no value, update the value of the existing line if grep -q "^${key}=$" "$ENV_FILE"; then sed -i "s|^${key}=$|${key}=${value}|" "$ENV_FILE" - echo " - Updated value of ${key} as the current value was empty" >>"$LOGFILE" + log "Updated ${key} (was empty)" # If variable "key=" doesn't exist, append it to the file with value elif ! grep -q "^${key}=" "$ENV_FILE"; then printf '%s=%s\n' "$key" "$value" >>"$ENV_FILE" - echo " - Added ${key} with default value as the variable was missing" >>"$LOGFILE" + log "Added ${key} (was missing)" fi } -echo "Checking and updating environment variables if necessary..." >>"$LOGFILE" +log "Checking environment variables..." update_env_var "PUSHER_APP_ID" "$(openssl rand -hex 32)" update_env_var "PUSHER_APP_KEY" "$(openssl rand -hex 32)" update_env_var "PUSHER_APP_SECRET" "$(openssl rand -hex 32)" +log "Environment variables check complete" echo " Done." # Make sure coolify network exists # It is created when starting Coolify with docker compose +log "Checking Docker network 'coolify'..." if ! docker network inspect coolify >/dev/null 2>&1; then + log "Network 'coolify' does not exist, creating..." if ! docker network create --attachable --ipv6 coolify 2>/dev/null; then - echo "Failed to create coolify network with ipv6. Trying without ipv6..." >>"$LOGFILE" + log "Failed to create network with IPv6, trying without IPv6..." docker network create --attachable coolify 2>/dev/null + log "Network 'coolify' created without IPv6" + else + log "Network 'coolify' created with IPv6 support" fi +else + log "Network 'coolify' already exists" fi # Check if Docker config file exists DOCKER_CONFIG_MOUNT="" if [ -f /root/.docker/config.json ]; then DOCKER_CONFIG_MOUNT="-v /root/.docker/config.json:/root/.docker/config.json" + log "Docker config mount enabled: /root/.docker/config.json" fi +log_section "Step 3/6: Pulling Docker images" echo "" echo "3/6 Pulling Docker images..." echo " This may take a few minutes depending on your connection." -echo "Pulling required Docker images..." >>"$LOGFILE" echo " - Pulling Coolify image..." -docker pull "${REGISTRY_URL:-ghcr.io}/coollabsio/coolify:${LATEST_IMAGE}" >>"$LOGFILE" 2>&1 || { echo " ERROR: Failed to pull Coolify image. Aborting upgrade."; echo "Failed to pull Coolify image. Aborting upgrade." >>"$LOGFILE"; exit 1; } +log "Pulling image: ${REGISTRY_URL:-ghcr.io}/coollabsio/coolify:${LATEST_IMAGE}" +if docker pull "${REGISTRY_URL:-ghcr.io}/coollabsio/coolify:${LATEST_IMAGE}" >>"$LOGFILE" 2>&1; then + log "Successfully pulled Coolify image" +else + log "ERROR: Failed to pull Coolify image" + echo " ERROR: Failed to pull Coolify image. Aborting upgrade." + exit 1 +fi echo " - Pulling Coolify helper image..." -docker pull "${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:${LATEST_HELPER_VERSION}" >>"$LOGFILE" 2>&1 || { echo " ERROR: Failed to pull helper image. Aborting upgrade."; echo "Failed to pull Coolify helper image. Aborting upgrade." >>"$LOGFILE"; exit 1; } +log "Pulling image: ${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:${LATEST_HELPER_VERSION}" +if docker pull "${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:${LATEST_HELPER_VERSION}" >>"$LOGFILE" 2>&1; then + log "Successfully pulled Coolify helper image" +else + log "ERROR: Failed to pull Coolify helper image" + echo " ERROR: Failed to pull helper image. Aborting upgrade." + exit 1 +fi echo " - Pulling PostgreSQL image..." -docker pull postgres:15-alpine >>"$LOGFILE" 2>&1 || { echo " ERROR: Failed to pull PostgreSQL image. Aborting upgrade."; echo "Failed to pull PostgreSQL image. Aborting upgrade." >>"$LOGFILE"; exit 1; } +log "Pulling image: postgres:15-alpine" +if docker pull postgres:15-alpine >>"$LOGFILE" 2>&1; then + log "Successfully pulled PostgreSQL image" +else + log "ERROR: Failed to pull PostgreSQL image" + echo " ERROR: Failed to pull PostgreSQL image. Aborting upgrade." + exit 1 +fi echo " - Pulling Redis image..." -docker pull redis:7-alpine >>"$LOGFILE" 2>&1 || { echo " ERROR: Failed to pull Redis image. Aborting upgrade."; echo "Failed to pull Redis image. Aborting upgrade." >>"$LOGFILE"; exit 1; } +log "Pulling image: redis:7-alpine" +if docker pull redis:7-alpine >>"$LOGFILE" 2>&1; then + log "Successfully pulled Redis image" +else + log "ERROR: Failed to pull Redis image" + echo " ERROR: Failed to pull Redis image. Aborting upgrade." + exit 1 +fi echo " - Pulling Coolify realtime image..." -docker pull "${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-realtime:1.0.10" >>"$LOGFILE" 2>&1 || { echo " ERROR: Failed to pull realtime image. Aborting upgrade."; echo "Failed to pull Coolify realtime image. Aborting upgrade." >>"$LOGFILE"; exit 1; } +log "Pulling image: ${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-realtime:1.0.10" +if docker pull "${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-realtime:1.0.10" >>"$LOGFILE" 2>&1; then + log "Successfully pulled Coolify realtime image" +else + log "ERROR: Failed to pull Coolify realtime image" + echo " ERROR: Failed to pull realtime image. Aborting upgrade." + exit 1 +fi -echo "All images pulled successfully." >>"$LOGFILE" +log "All images pulled successfully" echo " All images pulled successfully." +log_section "Step 4/6: Stopping existing containers" echo "" echo "4/6 Stopping existing containers..." -echo "Stopping existing Coolify containers..." >>"$LOGFILE" for container in coolify coolify-db coolify-redis coolify-realtime; do if docker ps -a --format '{{.Names}}' | grep -q "^${container}$"; then echo " - Stopping ${container}..." + log "Stopping container: ${container}" docker stop "$container" >>"$LOGFILE" 2>&1 || true + log "Removing container: ${container}" docker rm "$container" >>"$LOGFILE" 2>&1 || true - echo " - Removed container: $container" >>"$LOGFILE" + log "Container ${container} stopped and removed" + else + log "Container ${container} not found (skipping)" fi done +log "Container cleanup complete" echo " Done." +log_section "Step 5/6: Starting new containers" echo "" echo "5/6 Starting new containers..." if [ -f /data/coolify/source/docker-compose.custom.yml ]; then echo " Custom docker-compose.yml detected." - echo "docker-compose.custom.yml detected." >>"$LOGFILE" + log "Using custom docker-compose.yml" + log "Running docker compose up with custom configuration..." docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock ${DOCKER_CONFIG_MOUNT} --rm ${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:${LATEST_HELPER_VERSION} bash -c "LATEST_IMAGE=${LATEST_IMAGE} docker compose --project-name coolify --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml -f /data/coolify/source/docker-compose.custom.yml up -d --remove-orphans --wait --wait-timeout 60" >>"$LOGFILE" 2>&1 else + log "Using standard docker-compose configuration" + log "Running docker compose up..." docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock ${DOCKER_CONFIG_MOUNT} --rm ${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:${LATEST_HELPER_VERSION} bash -c "LATEST_IMAGE=${LATEST_IMAGE} docker compose --project-name coolify --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml up -d --remove-orphans --wait --wait-timeout 60" >>"$LOGFILE" 2>&1 fi +log "Docker compose up completed" echo " Done." +log_section "Step 6/6: Upgrade complete" +log "Coolify upgrade completed successfully" +log "Version: ${LATEST_IMAGE}" + echo "" echo "6/6 Upgrade complete!" echo "" @@ -132,3 +219,9 @@ echo "==========================================" echo "" echo " Log file: ${LOGFILE}" echo "" + +# Final log entry +echo "" >>"$LOGFILE" +echo "============================================================" >>"$LOGFILE" +echo "Upgrade completed: $(date '+%Y-%m-%d %H:%M:%S')" >>"$LOGFILE" +echo "============================================================" >>"$LOGFILE" From f3ccacb2da6da8b502bcb472bfb0bc6a7c776068 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Fri, 12 Dec 2025 15:36:01 +0100 Subject: [PATCH 05/22] Stop coolify container last during upgrade MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reorder container stop sequence to stop dependencies first (db, redis, realtime) before stopping the main coolify container. This prevents the upgrade process from being interrupted when triggered from Coolify UI. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- other/nightly/upgrade.sh | 3 ++- scripts/upgrade.sh | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/other/nightly/upgrade.sh b/other/nightly/upgrade.sh index aa0c82865..ba21c82ff 100644 --- a/other/nightly/upgrade.sh +++ b/other/nightly/upgrade.sh @@ -77,8 +77,9 @@ echo "All images pulled successfully." >>"$LOGFILE" # Stop and remove existing Coolify containers to prevent conflicts # This handles both old installations (project "source") and new ones (project "coolify") +# Stop coolify last to allow upgrade process to complete gracefully echo "Stopping existing Coolify containers..." >>"$LOGFILE" -for container in coolify coolify-db coolify-redis coolify-realtime; do +for container in coolify-db coolify-redis coolify-realtime coolify; do if docker ps -a --format '{{.Names}}' | grep -q "^${container}$"; then docker stop "$container" >>"$LOGFILE" 2>&1 || true docker rm "$container" >>"$LOGFILE" 2>&1 || true diff --git a/scripts/upgrade.sh b/scripts/upgrade.sh index f922983c6..204240bb6 100644 --- a/scripts/upgrade.sh +++ b/scripts/upgrade.sh @@ -175,7 +175,8 @@ echo " All images pulled successfully." log_section "Step 4/6: Stopping existing containers" echo "" echo "4/6 Stopping existing containers..." -for container in coolify coolify-db coolify-redis coolify-realtime; do +# Stop coolify last to allow upgrade process to complete gracefully +for container in coolify-db coolify-redis coolify-realtime coolify; do if docker ps -a --format '{{.Names}}' | grep -q "^${container}$"; then echo " - Stopping ${container}..." log "Stopping container: ${container}" From f4dbae180536f897185c7933f1904e8b9d39efff Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Fri, 12 Dec 2025 15:39:08 +0100 Subject: [PATCH 06/22] Revert container stop order to original MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- other/nightly/upgrade.sh | 3 +-- scripts/upgrade.sh | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/other/nightly/upgrade.sh b/other/nightly/upgrade.sh index ba21c82ff..aa0c82865 100644 --- a/other/nightly/upgrade.sh +++ b/other/nightly/upgrade.sh @@ -77,9 +77,8 @@ echo "All images pulled successfully." >>"$LOGFILE" # Stop and remove existing Coolify containers to prevent conflicts # This handles both old installations (project "source") and new ones (project "coolify") -# Stop coolify last to allow upgrade process to complete gracefully echo "Stopping existing Coolify containers..." >>"$LOGFILE" -for container in coolify-db coolify-redis coolify-realtime coolify; do +for container in coolify coolify-db coolify-redis coolify-realtime; do if docker ps -a --format '{{.Names}}' | grep -q "^${container}$"; then docker stop "$container" >>"$LOGFILE" 2>&1 || true docker rm "$container" >>"$LOGFILE" 2>&1 || true diff --git a/scripts/upgrade.sh b/scripts/upgrade.sh index 204240bb6..f922983c6 100644 --- a/scripts/upgrade.sh +++ b/scripts/upgrade.sh @@ -175,8 +175,7 @@ echo " All images pulled successfully." log_section "Step 4/6: Stopping existing containers" echo "" echo "4/6 Stopping existing containers..." -# Stop coolify last to allow upgrade process to complete gracefully -for container in coolify-db coolify-redis coolify-realtime coolify; do +for container in coolify coolify-db coolify-redis coolify-realtime; do if docker ps -a --format '{{.Names}}' | grep -q "^${container}$"; then echo " - Stopping ${container}..." log "Stopping container: ${container}" From 1f7888f515da8d67ebad655c35e38ed544cc0543 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Fri, 12 Dec 2025 15:41:09 +0100 Subject: [PATCH 07/22] Use nohup for container restart to survive SSH disconnect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When upgrade is triggered from Coolify UI, the SSH connection is lost when the coolify container stops. Using nohup ensures the container stop/start sequence continues in the background even after the connection drops. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- other/nightly/upgrade.sh | 42 ++++++++++----- scripts/upgrade.sh | 113 ++++++++++++++++++++++++--------------- 2 files changed, 98 insertions(+), 57 deletions(-) diff --git a/other/nightly/upgrade.sh b/other/nightly/upgrade.sh index aa0c82865..0d3896647 100644 --- a/other/nightly/upgrade.sh +++ b/other/nightly/upgrade.sh @@ -77,18 +77,32 @@ echo "All images pulled successfully." >>"$LOGFILE" # Stop and remove existing Coolify containers to prevent conflicts # This handles both old installations (project "source") and new ones (project "coolify") -echo "Stopping existing Coolify containers..." >>"$LOGFILE" -for container in coolify coolify-db coolify-redis coolify-realtime; do - if docker ps -a --format '{{.Names}}' | grep -q "^${container}$"; then - docker stop "$container" >>"$LOGFILE" 2>&1 || true - docker rm "$container" >>"$LOGFILE" 2>&1 || true - echo " - Removed container: $container" >>"$LOGFILE" - fi -done +# Use nohup to ensure the script continues even if SSH connection is lost +echo "Starting container restart sequence (detached)..." >>"$LOGFILE" -if [ -f /data/coolify/source/docker-compose.custom.yml ]; then - echo "docker-compose.custom.yml detected." >>"$LOGFILE" - docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock ${DOCKER_CONFIG_MOUNT} --rm ${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:${LATEST_HELPER_VERSION} bash -c "LATEST_IMAGE=${LATEST_IMAGE} docker compose --project-name coolify --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml -f /data/coolify/source/docker-compose.custom.yml up -d --remove-orphans --wait --wait-timeout 60" >>"$LOGFILE" 2>&1 -else - docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock ${DOCKER_CONFIG_MOUNT} --rm ${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:${LATEST_HELPER_VERSION} bash -c "LATEST_IMAGE=${LATEST_IMAGE} docker compose --project-name coolify --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml up -d --remove-orphans --wait --wait-timeout 60" >>"$LOGFILE" 2>&1 -fi +nohup bash -c " + LOGFILE='$LOGFILE' + DOCKER_CONFIG_MOUNT='$DOCKER_CONFIG_MOUNT' + REGISTRY_URL='$REGISTRY_URL' + LATEST_HELPER_VERSION='$LATEST_HELPER_VERSION' + LATEST_IMAGE='$LATEST_IMAGE' + + # Stop and remove containers + echo 'Stopping existing Coolify containers...' >>\"\$LOGFILE\" + for container in coolify coolify-db coolify-redis coolify-realtime; do + if docker ps -a --format '{{.Names}}' | grep -q \"^\${container}\$\"; then + docker stop \"\$container\" >>\"\$LOGFILE\" 2>&1 || true + docker rm \"\$container\" >>\"\$LOGFILE\" 2>&1 || true + echo \" - Removed container: \$container\" >>\"\$LOGFILE\" + fi + done + + # Start new containers + if [ -f /data/coolify/source/docker-compose.custom.yml ]; then + echo 'docker-compose.custom.yml detected.' >>\"\$LOGFILE\" + docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock \${DOCKER_CONFIG_MOUNT} --rm \${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:\${LATEST_HELPER_VERSION} bash -c \"LATEST_IMAGE=\${LATEST_IMAGE} docker compose --project-name coolify --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml -f /data/coolify/source/docker-compose.custom.yml up -d --remove-orphans --wait --wait-timeout 60\" >>\"\$LOGFILE\" 2>&1 + else + docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock \${DOCKER_CONFIG_MOUNT} --rm \${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:\${LATEST_HELPER_VERSION} bash -c \"LATEST_IMAGE=\${LATEST_IMAGE} docker compose --project-name coolify --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml up -d --remove-orphans --wait --wait-timeout 60\" >>\"\$LOGFILE\" 2>&1 + fi + echo 'Upgrade completed.' >>\"\$LOGFILE\" +" >>"$LOGFILE" 2>&1 & diff --git a/scripts/upgrade.sh b/scripts/upgrade.sh index f922983c6..97073712a 100644 --- a/scripts/upgrade.sh +++ b/scripts/upgrade.sh @@ -172,56 +172,83 @@ fi log "All images pulled successfully" echo " All images pulled successfully." -log_section "Step 4/6: Stopping existing containers" +log_section "Step 4/6: Stopping and restarting containers" echo "" -echo "4/6 Stopping existing containers..." -for container in coolify coolify-db coolify-redis coolify-realtime; do - if docker ps -a --format '{{.Names}}' | grep -q "^${container}$"; then - echo " - Stopping ${container}..." - log "Stopping container: ${container}" - docker stop "$container" >>"$LOGFILE" 2>&1 || true - log "Removing container: ${container}" - docker rm "$container" >>"$LOGFILE" 2>&1 || true - log "Container ${container} stopped and removed" +echo "4/6 Stopping containers and starting new ones..." +echo " This step will restart all Coolify containers." +echo " Check the log file for details: ${LOGFILE}" + +# From this point forward, we need to ensure the script continues even if +# the SSH connection is lost (which happens when coolify container stops) +# We use a subshell with nohup to ensure completion +log "Starting container restart sequence (detached)..." + +nohup bash -c " + LOGFILE='$LOGFILE' + DOCKER_CONFIG_MOUNT='$DOCKER_CONFIG_MOUNT' + REGISTRY_URL='$REGISTRY_URL' + LATEST_HELPER_VERSION='$LATEST_HELPER_VERSION' + LATEST_IMAGE='$LATEST_IMAGE' + + log() { + echo \"[\$(date '+%Y-%m-%d %H:%M:%S')] \$1\" >>\"\$LOGFILE\" + } + + # Stop and remove containers + for container in coolify coolify-db coolify-redis coolify-realtime; do + if docker ps -a --format '{{.Names}}' | grep -q \"^\${container}\$\"; then + log \"Stopping container: \${container}\" + docker stop \"\$container\" >>\"\$LOGFILE\" 2>&1 || true + log \"Removing container: \${container}\" + docker rm \"\$container\" >>\"\$LOGFILE\" 2>&1 || true + log \"Container \${container} stopped and removed\" + else + log \"Container \${container} not found (skipping)\" + fi + done + log \"Container cleanup complete\" + + # Start new containers + echo '' >>\"\$LOGFILE\" + echo '============================================================' >>\"\$LOGFILE\" + log 'Step 5/6: Starting new containers' + echo '============================================================' >>\"\$LOGFILE\" + + if [ -f /data/coolify/source/docker-compose.custom.yml ]; then + log 'Using custom docker-compose.yml' + log 'Running docker compose up with custom configuration...' + docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock \${DOCKER_CONFIG_MOUNT} --rm \${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:\${LATEST_HELPER_VERSION} bash -c \"LATEST_IMAGE=\${LATEST_IMAGE} docker compose --project-name coolify --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml -f /data/coolify/source/docker-compose.custom.yml up -d --remove-orphans --wait --wait-timeout 60\" >>\"\$LOGFILE\" 2>&1 else - log "Container ${container} not found (skipping)" + log 'Using standard docker-compose configuration' + log 'Running docker compose up...' + docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock \${DOCKER_CONFIG_MOUNT} --rm \${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:\${LATEST_HELPER_VERSION} bash -c \"LATEST_IMAGE=\${LATEST_IMAGE} docker compose --project-name coolify --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml up -d --remove-orphans --wait --wait-timeout 60\" >>\"\$LOGFILE\" 2>&1 fi -done -log "Container cleanup complete" -echo " Done." + log 'Docker compose up completed' -log_section "Step 5/6: Starting new containers" + # Final log entry + echo '' >>\"\$LOGFILE\" + echo '============================================================' >>\"\$LOGFILE\" + log 'Step 6/6: Upgrade complete' + echo '============================================================' >>\"\$LOGFILE\" + log 'Coolify upgrade completed successfully' + log 'Version: \${LATEST_IMAGE}' + echo '' >>\"\$LOGFILE\" + echo '============================================================' >>\"\$LOGFILE\" + echo \"Upgrade completed: \$(date '+%Y-%m-%d %H:%M:%S')\" >>\"\$LOGFILE\" + echo '============================================================' >>\"\$LOGFILE\" +" >>"$LOGFILE" 2>&1 & + +# Give the background process a moment to start +sleep 2 +log "Container restart sequence started in background (PID: $!)" echo "" -echo "5/6 Starting new containers..." -if [ -f /data/coolify/source/docker-compose.custom.yml ]; then - echo " Custom docker-compose.yml detected." - log "Using custom docker-compose.yml" - log "Running docker compose up with custom configuration..." - docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock ${DOCKER_CONFIG_MOUNT} --rm ${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:${LATEST_HELPER_VERSION} bash -c "LATEST_IMAGE=${LATEST_IMAGE} docker compose --project-name coolify --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml -f /data/coolify/source/docker-compose.custom.yml up -d --remove-orphans --wait --wait-timeout 60" >>"$LOGFILE" 2>&1 -else - log "Using standard docker-compose configuration" - log "Running docker compose up..." - docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock ${DOCKER_CONFIG_MOUNT} --rm ${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:${LATEST_HELPER_VERSION} bash -c "LATEST_IMAGE=${LATEST_IMAGE} docker compose --project-name coolify --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml up -d --remove-orphans --wait --wait-timeout 60" >>"$LOGFILE" 2>&1 -fi -log "Docker compose up completed" -echo " Done." - -log_section "Step 6/6: Upgrade complete" -log "Coolify upgrade completed successfully" -log "Version: ${LATEST_IMAGE}" - -echo "" -echo "6/6 Upgrade complete!" +echo "5/6 Containers are being restarted in the background..." +echo "6/6 Upgrade process initiated!" echo "" echo "==========================================" -echo " Coolify has been upgraded to ${LATEST_IMAGE}" +echo " Coolify upgrade to ${LATEST_IMAGE} in progress" echo "==========================================" echo "" +echo " The upgrade will continue in the background." +echo " Coolify will be available again shortly." echo " Log file: ${LOGFILE}" -echo "" - -# Final log entry -echo "" >>"$LOGFILE" -echo "============================================================" >>"$LOGFILE" -echo "Upgrade completed: $(date '+%Y-%m-%d %H:%M:%S')" >>"$LOGFILE" -echo "============================================================" >>"$LOGFILE" From 30ac4e079c9b8e23401ae7b6a3594f5c5e19e6a4 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Fri, 12 Dec 2025 17:22:01 +0100 Subject: [PATCH 08/22] Fix variable expansion in upgrade log message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use double quotes for LATEST_IMAGE variable in log output so it expands correctly inside the nohup subshell. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- scripts/upgrade.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/upgrade.sh b/scripts/upgrade.sh index 97073712a..b06cbc412 100644 --- a/scripts/upgrade.sh +++ b/scripts/upgrade.sh @@ -221,7 +221,7 @@ nohup bash -c " else log 'Using standard docker-compose configuration' log 'Running docker compose up...' - docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock \${DOCKER_CONFIG_MOUNT} --rm \${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:\${LATEST_HELPER_VERSION} bash -c \"LATEST_IMAGE=\${LATEST_IMAGE} docker compose --project-name coolify --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml up -d --remove-orphans --wait --wait-timeout 60\" >>\"\$LOGFILE\" 2>&1 + docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock \${DOCKER_CONFIG_MOUNT} \${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:\${LATEST_HELPER_VERSION} bash -c \"LATEST_IMAGE=\${LATEST_IMAGE} docker compose --project-name coolify --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml up -d --remove-orphans --wait --wait-timeout 60\" >>\"\$LOGFILE\" 2>&1 fi log 'Docker compose up completed' @@ -231,7 +231,7 @@ nohup bash -c " log 'Step 6/6: Upgrade complete' echo '============================================================' >>\"\$LOGFILE\" log 'Coolify upgrade completed successfully' - log 'Version: \${LATEST_IMAGE}' + log \"Version: \${LATEST_IMAGE}\" echo '' >>\"\$LOGFILE\" echo '============================================================' >>\"\$LOGFILE\" echo \"Upgrade completed: \$(date '+%Y-%m-%d %H:%M:%S')\" >>\"\$LOGFILE\" From 92326c09ea28af6abf427b32a256dbd997ad7133 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Fri, 12 Dec 2025 17:26:08 +0100 Subject: [PATCH 09/22] Improve upgrade process UX with better progress visibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add step-by-step progress indicator (Preparing → Helper → Image → Restart) - Display elapsed time during upgrade (MM:SS format) - Show version transition in header (v4.0.0-beta.454 → v4.0.0-beta.456) - Add expandable changelog preview before upgrading - Reduce reload delay from 5s to 3s with countdown timer - Add "Reload Now" button to skip countdown - Improve status messages with step-specific descriptions - Add success state with clear indication when upgrade completes - Create new upgrade-progress component for visual step tracking 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 --- app/Livewire/Upgrade.php | 37 +++ .../components/upgrade-progress.blade.php | 143 ++++++++++ resources/views/livewire/upgrade.blade.php | 266 ++++++++++++++---- 3 files changed, 398 insertions(+), 48 deletions(-) create mode 100644 resources/views/components/upgrade-progress.blade.php diff --git a/app/Livewire/Upgrade.php b/app/Livewire/Upgrade.php index f13baa7a7..1b145b244 100644 --- a/app/Livewire/Upgrade.php +++ b/app/Livewire/Upgrade.php @@ -4,6 +4,7 @@ use App\Actions\Server\UpdateCoolify; use App\Models\InstanceSettings; +use App\Services\ChangelogService; use Livewire\Component; class Upgrade extends Component @@ -14,21 +15,57 @@ class Upgrade extends Component public string $latestVersion = ''; + public string $currentVersion = ''; + + public array $changelogEntries = []; + protected $listeners = ['updateAvailable' => 'checkUpdate']; + public function mount() + { + $this->currentVersion = config('constants.coolify.version'); + } + public function checkUpdate() { try { $this->latestVersion = get_latest_version_of_coolify(); + $this->currentVersion = config('constants.coolify.version'); $this->isUpgradeAvailable = data_get(InstanceSettings::get(), 'new_version_available', false); if (isDev()) { $this->isUpgradeAvailable = true; } + $this->loadChangelog(); } catch (\Throwable $e) { return handleError($e, $this); } } + public function loadChangelog() + { + try { + $service = app(ChangelogService::class); + $currentVersion = str_replace('v', '', $this->currentVersion); + + $this->changelogEntries = $service->getEntries(1) + ->filter(function ($entry) use ($currentVersion) { + $entryVersion = str_replace('v', '', $entry->tag_name); + + return version_compare($entryVersion, $currentVersion, '>'); + }) + ->take(3) + ->map(fn ($entry) => [ + 'tag_name' => $entry->tag_name, + 'title' => $entry->title, + 'content_html' => $entry->content_html, + ]) + ->values() + ->toArray(); + } catch (\Throwable $e) { + $this->changelogEntries = []; + } + } + public function upgrade() { try { diff --git a/resources/views/components/upgrade-progress.blade.php b/resources/views/components/upgrade-progress.blade.php new file mode 100644 index 000000000..13eca4f5b --- /dev/null +++ b/resources/views/components/upgrade-progress.blade.php @@ -0,0 +1,143 @@ +@props(['step' => 0]) + +
+
+ {{-- Step 1: Preparing --}} +
+
+
+ + + +
+ Preparing +
+
+
+ + {{-- Step 2: Helper --}} +
+
+
+ + + +
+ Helper +
+
+
+ + {{-- Step 3: Image --}} +
+
+
+ + + +
+ Image +
+
+
+ + {{-- Step 4: Restart --}} +
+
+
+ + + +
+ Restart +
+
+
+
diff --git a/resources/views/livewire/upgrade.blade.php b/resources/views/livewire/upgrade.blade.php index 37e43935d..101c4cf94 100644 --- a/resources/views/livewire/upgrade.blade.php +++ b/resources/views/livewire/upgrade.blade.php @@ -1,5 +1,8 @@
+ x-init="$wire.checkUpdate" x-data="upgradeModal({ + currentVersion: @js($currentVersion), + latestVersion: @js($latestVersion) + })"> @if ($isUpgradeAvailable)
x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100" x-transition:leave-end="opacity-0 -translate-y-2 sm:scale-95" class="relative w-full py-6 border rounded-sm min-w-full lg:min-w-[36rem] max-w-fit bg-neutral-100 border-neutral-400 dark:bg-base px-7 dark:border-coolgray-300"> + + {{-- Header --}}
-

Upgrade confirmation

+
+

+
+ {{ $currentVersion }} {{ $latestVersion }} +
+
-
-

Are you sure you would like to upgrade your instance to {{ $latestVersion }}?

-
- -

Any deployments running during the update process will - fail. Please ensure no deployments are in progress on any server before continuing. -

-
-
-

You can review the changelogs here.

-
-

If something goes wrong and you cannot upgrade your instance, You can check the following - guide on what to do. -

-
-

Progress

-
-
+ {{-- Content --}} +
+ {{-- Progress View --}} + + + {{-- Confirmation View --}} +
+ + {{-- Footer Actions --}}
Cancel
- Continue + + Upgrade Now
@@ -89,23 +185,57 @@ class="w-24 dark:bg-coolgray-200 dark:hover:bg-coolgray-300">Cancel From 0aa7e376b29b7b912dc771a2c237a8cd8c65d7eb Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Fri, 12 Dec 2025 17:41:14 +0100 Subject: [PATCH 10/22] Simplify upgrade modal and improve help text MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove changelog preview section to streamline the UI - Simplify warning message - Add reference to upgrade logs location on server - Minor formatting improvements 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 --- resources/views/livewire/upgrade.blade.php | 87 ++++++++-------------- 1 file changed, 29 insertions(+), 58 deletions(-) diff --git a/resources/views/livewire/upgrade.blade.php b/resources/views/livewire/upgrade.blade.php index 101c4cf94..eedadbb56 100644 --- a/resources/views/livewire/upgrade.blade.php +++ b/resources/views/livewire/upgrade.blade.php @@ -8,17 +8,15 @@ @@ -81,14 +80,21 @@ class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5
@@ -99,7 +105,8 @@ class="absolute top-0 right-0 flex items-center justify-center w-8 h-8 mt-5 mr-5 @@ -168,6 +188,7 @@ class="w-24 dark:bg-coolgray-200 dark:hover:bg-coolgray-300">Cancel elapsedTime: 0, currentStep: 0, upgradeComplete: false, + upgradeError: false, successCountdown: 3, currentVersion: config.currentVersion || '', latestVersion: config.latestVersion || '', @@ -178,12 +199,16 @@ class="w-24 dark:bg-coolgray-200 dark:hover:bg-coolgray-300">Cancel this.currentStep = 1; this.currentStatus = 'Starting upgrade...'; this.startTimer(); + // Trigger server-side upgrade script via Livewire this.$wire.$call('upgrade'); + // Start client-side status polling this.upgrade(); - window.addEventListener('beforeunload', (event) => { + // Prevent accidental navigation during upgrade + this.beforeUnloadHandler = (event) => { event.preventDefault(); event.returnValue = ''; - }); + }; + window.addEventListener('beforeunload', this.beforeUnloadHandler); }, startTimer() { @@ -259,6 +284,11 @@ class="w-24 dark:bg-coolgray-200 dark:hover:bg-coolgray-300">Cancel clearInterval(this.elapsedInterval); this.elapsedInterval = null; } + // Remove beforeunload handler now that upgrade is complete + if (this.beforeUnloadHandler) { + window.removeEventListener('beforeunload', this.beforeUnloadHandler); + this.beforeUnloadHandler = null; + } this.upgradeComplete = true; this.currentStep = 5; @@ -278,6 +308,38 @@ class="w-24 dark:bg-coolgray-200 dark:hover:bg-coolgray-300">Cancel window.location.reload(); }, + showError(message) { + // Stop all intervals + if (this.checkHealthInterval) { + clearInterval(this.checkHealthInterval); + this.checkHealthInterval = null; + } + if (this.checkUpgradeStatusInterval) { + clearInterval(this.checkUpgradeStatusInterval); + this.checkUpgradeStatusInterval = null; + } + if (this.elapsedInterval) { + clearInterval(this.elapsedInterval); + this.elapsedInterval = null; + } + // Remove beforeunload handler so user can close modal + if (this.beforeUnloadHandler) { + window.removeEventListener('beforeunload', this.beforeUnloadHandler); + this.beforeUnloadHandler = null; + } + + this.upgradeError = true; + this.currentStatus = `Error: ${message}`; + }, + + closeErrorModal() { + this.modalOpen = false; + this.showProgress = false; + this.upgradeError = false; + this.currentStatus = ''; + this.currentStep = 0; + }, + upgrade() { if (this.checkUpgradeStatusInterval) return true; this.currentStep = 1; @@ -294,7 +356,7 @@ class="w-24 dark:bg-coolgray-200 dark:hover:bg-coolgray-300">Cancel } else if (data.status === 'complete') { this.showSuccess(); } else if (data.status === 'error') { - this.currentStatus = `Error: ${data.message}`; + this.showError(data.message); } } catch (error) { // Service is down - switch to health check mode diff --git a/scripts/upgrade.sh b/scripts/upgrade.sh index 8ade89669..ad3f32009 100644 --- a/scripts/upgrade.sh +++ b/scripts/upgrade.sh @@ -244,7 +244,7 @@ nohup bash -c " else log 'Using standard docker-compose configuration' log 'Running docker compose up...' - docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock \${DOCKER_CONFIG_MOUNT} \${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:\${LATEST_HELPER_VERSION} bash -c \"LATEST_IMAGE=\${LATEST_IMAGE} docker compose --project-name coolify --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml up -d --remove-orphans --wait --wait-timeout 60\" >>\"\$LOGFILE\" 2>&1 + docker run -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock \${DOCKER_CONFIG_MOUNT} --rm \${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:\${LATEST_HELPER_VERSION} bash -c \"LATEST_IMAGE=\${LATEST_IMAGE} docker compose --project-name coolify --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml up -d --remove-orphans --wait --wait-timeout 60\" >>\"\$LOGFILE\" 2>&1 fi log 'Docker compose up completed' From c6945c86eadefd95e676ca22ccb1953180882695 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Sat, 13 Dec 2025 22:12:33 +0100 Subject: [PATCH 21/22] Parse Docker images dynamically from docker-compose files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace hardcoded image pulls (postgres:15-alpine, redis:7-alpine, coolify-realtime:1.0.10) with dynamic extraction using `docker compose config --images`. This ensures the upgrade script automatically handles new services and version changes without manual updates. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- scripts/upgrade.sh | 99 +++++++++++++++++++++++----------------------- 1 file changed, 49 insertions(+), 50 deletions(-) diff --git a/scripts/upgrade.sh b/scripts/upgrade.sh index ad3f32009..4259aded2 100644 --- a/scripts/upgrade.sh +++ b/scripts/upgrade.sh @@ -59,6 +59,30 @@ curl -fsSL -L $CDN/.env.production -o /data/coolify/source/.env.production log "Configuration files downloaded successfully" echo " Done." +# Extract all images from docker-compose configuration +log "Extracting all images from docker-compose configuration..." +COMPOSE_FILES="-f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml" + +# Check if custom compose file exists +if [ -f /data/coolify/source/docker-compose.custom.yml ]; then + COMPOSE_FILES="$COMPOSE_FILES -f /data/coolify/source/docker-compose.custom.yml" + log "Including custom docker-compose.yml in image extraction" +fi + +# Get all unique images from docker compose config +# LATEST_IMAGE env var is needed for image substitution in compose files +IMAGES=$(LATEST_IMAGE=${LATEST_IMAGE} docker compose --env-file "$ENV_FILE" $COMPOSE_FILES config --images 2>/dev/null | sort -u) + +if [ -z "$IMAGES" ]; then + log "ERROR: Failed to extract images from docker-compose files" + write_status "error" "Failed to parse docker-compose configuration" + echo " ERROR: Failed to parse docker-compose configuration. Aborting upgrade." + exit 1 +fi + +log "Images to pull:" +echo "$IMAGES" | while read img; do log " - $img"; done + # Backup existing .env file before making any changes if [ "$SKIP_BACKUP" != "true" ]; then if [ -f "$ENV_FILE" ]; then @@ -130,60 +154,35 @@ echo "" echo "3/6 Pulling Docker images..." echo " This may take a few minutes depending on your connection." -echo " - Pulling Coolify image..." -log "Pulling image: ${REGISTRY_URL:-ghcr.io}/coollabsio/coolify:${LATEST_IMAGE}" -if docker pull "${REGISTRY_URL:-ghcr.io}/coollabsio/coolify:${LATEST_IMAGE}" >>"$LOGFILE" 2>&1; then - log "Successfully pulled Coolify image" +# Also pull the helper image (not in compose files but needed for upgrade) +HELPER_IMAGE="${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:${LATEST_HELPER_VERSION}" +echo " - Pulling $HELPER_IMAGE..." +log "Pulling image: $HELPER_IMAGE" +if docker pull "$HELPER_IMAGE" >>"$LOGFILE" 2>&1; then + log "Successfully pulled $HELPER_IMAGE" else - log "ERROR: Failed to pull Coolify image" - write_status "error" "Failed to pull Coolify image" - echo " ERROR: Failed to pull Coolify image. Aborting upgrade." + log "ERROR: Failed to pull $HELPER_IMAGE" + write_status "error" "Failed to pull $HELPER_IMAGE" + echo " ERROR: Failed to pull $HELPER_IMAGE. Aborting upgrade." exit 1 fi -echo " - Pulling Coolify helper image..." -log "Pulling image: ${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:${LATEST_HELPER_VERSION}" -if docker pull "${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-helper:${LATEST_HELPER_VERSION}" >>"$LOGFILE" 2>&1; then - log "Successfully pulled Coolify helper image" -else - log "ERROR: Failed to pull Coolify helper image" - write_status "error" "Failed to pull Coolify helper image" - echo " ERROR: Failed to pull helper image. Aborting upgrade." - exit 1 -fi - -echo " - Pulling PostgreSQL image..." -log "Pulling image: postgres:15-alpine" -if docker pull postgres:15-alpine >>"$LOGFILE" 2>&1; then - log "Successfully pulled PostgreSQL image" -else - log "ERROR: Failed to pull PostgreSQL image" - write_status "error" "Failed to pull PostgreSQL image" - echo " ERROR: Failed to pull PostgreSQL image. Aborting upgrade." - exit 1 -fi - -echo " - Pulling Redis image..." -log "Pulling image: redis:7-alpine" -if docker pull redis:7-alpine >>"$LOGFILE" 2>&1; then - log "Successfully pulled Redis image" -else - log "ERROR: Failed to pull Redis image" - write_status "error" "Failed to pull Redis image" - echo " ERROR: Failed to pull Redis image. Aborting upgrade." - exit 1 -fi - -echo " - Pulling Coolify realtime image..." -log "Pulling image: ${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-realtime:1.0.10" -if docker pull "${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-realtime:1.0.10" >>"$LOGFILE" 2>&1; then - log "Successfully pulled Coolify realtime image" -else - log "ERROR: Failed to pull Coolify realtime image" - write_status "error" "Failed to pull Coolify realtime image" - echo " ERROR: Failed to pull realtime image. Aborting upgrade." - exit 1 -fi +# Pull all images from compose config +# Using a for loop to avoid subshell issues with exit +for IMAGE in $IMAGES; do + if [ -n "$IMAGE" ]; then + echo " - Pulling $IMAGE..." + log "Pulling image: $IMAGE" + if docker pull "$IMAGE" >>"$LOGFILE" 2>&1; then + log "Successfully pulled $IMAGE" + else + log "ERROR: Failed to pull $IMAGE" + write_status "error" "Failed to pull $IMAGE" + echo " ERROR: Failed to pull $IMAGE. Aborting upgrade." + exit 1 + fi + fi +done log "All images pulled successfully" echo " All images pulled successfully." From 918959b7f5a3de05732fa55a455697ce171845f2 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Sat, 13 Dec 2025 22:26:31 +0100 Subject: [PATCH 22/22] Remove unused ChangelogService import from Upgrade component --- app/Livewire/Upgrade.php | 1 - 1 file changed, 1 deletion(-) diff --git a/app/Livewire/Upgrade.php b/app/Livewire/Upgrade.php index 37cc8ec28..36bee2a23 100644 --- a/app/Livewire/Upgrade.php +++ b/app/Livewire/Upgrade.php @@ -5,7 +5,6 @@ use App\Actions\Server\UpdateCoolify; use App\Models\InstanceSettings; use App\Models\Server; -use App\Services\ChangelogService; use Livewire\Component; class Upgrade extends Component