From 540bce911fa784bd5d6d6223ef8f03717d92af07 Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Thu, 30 Jul 2026 18:40:42 +0200 Subject: [PATCH] fix(deploy): free PORT before PM2 start to clear EADDRINUSE orphans Co-authored-by: Cursor --- .gitea/workflows/deploy.yaml | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/.gitea/workflows/deploy.yaml b/.gitea/workflows/deploy.yaml index 60c17d9624..6fef1f7563 100644 --- a/.gitea/workflows/deploy.yaml +++ b/.gitea/workflows/deploy.yaml @@ -381,16 +381,35 @@ jobs: # Next.js prefers an already-set process PORT over .env. PM2 may still # have PORT=3000 from an older start, while .env (and nginx) expect 3002. - # Export PORT before reload so --update-env aligns the running process. + # Export PORT before start so the process matches health checks. DEPLOY_PORT="$(grep -E '^[[:space:]]*PORT=' "${LIVE}/.env" 2>/dev/null | tail -1 | cut -d= -f2- || true)" DEPLOY_PORT="$(printf '%s' "${DEPLOY_PORT}" | tr -cd '0-9')" DEPLOY_PORT="${DEPLOY_PORT:-3002}" export PORT="${DEPLOY_PORT}" echo "PM2/health PORT=${PORT}" - echo "Starting PM2 (zero-downtime reload)..." + free_tcp_port() { + local port="$1" + [ -n "${port}" ] || return 0 + if command -v fuser >/dev/null 2>&1; then + fuser -k "${port}/tcp" 2>/dev/null || true + elif command -v lsof >/dev/null 2>&1; then + # Portable fallback when fuser is unavailable. + lsof -tiTCP:"${port}" -sTCP:LISTEN 2>/dev/null | xargs -r kill -9 2>/dev/null || true + fi + } + + echo "Starting PM2 with a clean PORT=${PORT} listener..." cd "${LIVE}" - pm2 reload next --update-env || pm2 start pnpm --name next --update-env -- start + # Cutover already stopped the app, but failed deploys can leave orphans + # on 3002 (or an old PM2 env still bound to 3000). + pm2 stop next --kill-timeout 10000 2>/dev/null || true + free_tcp_port "${PORT}" + free_tcp_port 3000 + sleep 1 + pm2 delete next 2>/dev/null || true + PORT="${PORT}" pm2 start pnpm --name next -- start + pm2 save 2>/dev/null || true sleep 3 if ! pm2 show next 2>/dev/null | grep -q 'online'; then @@ -415,7 +434,8 @@ jobs: if [ "${HEALTH_OK}" != "1" ]; then echo "ERROR: Health check failed after deploy (${HEALTH_URL})" >&2 echo "Last body: ${BODY:-}" >&2 - echo "PM2 env PORT (should match .env):" >&2 + echo "Listeners on PORT ${PORT}:" >&2 + ss -tlnp 2>/dev/null | grep ":${PORT} " >&2 || netstat -tlnp 2>/dev/null | grep ":${PORT} " >&2 || true pm2 env 0 2>/dev/null | grep -E '^PORT=' >&2 || true pm2 logs next --lines 40 --nostream >&2 || true exit 1