 Autor: [Monika Wojciechowska](/autorzy/monika-wojciechowska) Specjalistka SEO i treści webowych · Zweryfikowano Kwiecień 2026

1.  [Strona główna](/) ›
2.  [Baza wiedzy](/baza-wiedzy/) ›
3.  Zero-downtime deployment z Nginx

# Zero-downtime deployment z Nginx — blue-green, rolling update, health checks i rollback

Opublikowano: 9 kwietnia 2026 · Kategoria: VPS / DevOps

Deploy o 3 w nocy żeby uniknąć użytkowników? Okno maintenance w niedzielę? To już przeszłość. Zero-downtime deployment pozwala wdrażać nowe wersje aplikacji w ciągu dnia roboczego, bez błędów 502 i bez utraty sesji użytkownika. W tym przewodniku omówimy mechanizm graceful reload Nginx, blue-green deployment, rolling update z health checkami i automatyczny rollback gdy coś pójdzie nie tak.

## Nginx graceful reload — jak to działa

Zanim przejdziemy do złożonych strategii — zrozum fundamentalny mechanizm. `nginx -s reload` nie restartuje serwera, lecz zastępuje worker procesy zachowując ciągłość obsługi:

\# Sprawdź PID master procesu przed reload
ps aux | grep "nginx: master"
cat /var/run/nginx.pid

# Przetestuj konfigurację (ZAWSZE przed reload)
sudo nginx -t

# Graceful reload — zero dropped connections
sudo nginx -s reload
# lub:
sudo systemctl reload nginx

# Sprawdź PID po reload — master PID się nie zmienia!
cat /var/run/nginx.pid

# Obserwuj stare workery kończące obsługę żądań
watch -n 0.5 "ps aux | grep nginx"

Stary worker może żyć kilka sekund lub minut (zależy od aktywnych WebSocket i long-polling połączeń). Parametr `worker_shutdown_timeout` ustawia limit:

\# /etc/nginx/nginx.conf
worker\_processes auto;
worker\_shutdown\_timeout 30s;  # Maksymalny czas na graceful shutdown workerów

## Rolling update — wymiana instancji po jednej

Rolling update działa gdy masz load balancer i wiele instancji aplikacji. Wyłączasz backend z rotacji Nginx, wdrażasz nową wersję, weryfikujesz zdrowie, przywracasz do rotacji:

\# /etc/nginx/sites-available/app.conf
upstream app\_backend {
    server 127.0.0.1:3001;  # instancja 1
    server 127.0.0.1:3002;  # instancja 2
    keepalive 32;
}

server {
    listen 80;
    server\_name example.com;

    location / {
        proxy\_pass http://app\_backend;
        proxy\_http\_version 1.1;
        proxy\_set\_header Connection "";
        proxy\_set\_header Host $host;
        proxy\_connect\_timeout 5s;
        proxy\_read\_timeout 30s;

        # Retry na zdrowy backend gdy jeden padnie
        proxy\_next\_upstream error timeout http\_502 http\_503;
        proxy\_next\_upstream\_tries 2;
    }

    # Health check endpoint (Nginx Plus lub przez limit\_except)
    location /nginx-health {
        return 200 "healthy\\n";
        add\_header Content-Type text/plain;
    }
}

Skrypt rolling update — wdrożenie po jednej instancji:

#!/bin/bash
# rolling-deploy.sh
set -euo pipefail

APP\_DIR="/var/www/app"
PORTS=(3001 3002)
NGINX\_CONF="/etc/nginx/sites-available/app.conf"
HEALTH\_URL="http://localhost"
NEW\_VERSION=${1:-"latest"}

log() { echo "\[$(date '+%H:%M:%S')\] $1"; }

health\_check() {
  local port=$1
  local retries=10
  while \[ $retries -gt 0 \]; do
    if curl -sf "http://localhost:${port}/health" > /dev/null 2>&1; then
      return 0
    fi
    sleep 2
    retries=$((retries - 1))
  done
  return 1
}

for PORT in "${PORTS\[@\]}"; do
  log "=== Wdrażam na port $PORT ==="

  # 1. Wyłącz instancję z load balancera
  log "Wyłączam port $PORT z rotacji Nginx..."
  sudo sed -i "s/server 127.0.0.1:${PORT};/server 127.0.0.1:${PORT} down;/" "$NGINX\_CONF"
  sudo nginx -t && sudo nginx -s reload
  sleep 5  # Daj czas na drenaż aktywnych połączeń

  # 2. Zatrzymaj starą instancję
  log "Zatrzymuję starą instancję..."
  pm2 stop "app-${PORT}" || true

  # 3. Wdróż nowy kod
  log "Wdrażam $NEW\_VERSION..."
  cd "$APP\_DIR"
  git pull origin main
  npm ci --production
  PORT=$PORT pm2 start ecosystem.config.js --only "app-${PORT}"

  # 4. Health check nowej instancji
  log "Sprawdzam zdrowie na porcie $PORT..."
  if ! health\_check "$PORT"; then
    log "BŁĄD: Health check nieudany na porcie $PORT — rollback!"
    sudo sed -i "s/server 127.0.0.1:${PORT} down;/server 127.0.0.1:${PORT};/" "$NGINX\_CONF"
    sudo nginx -s reload
    exit 1
  fi

  # 5. Przywróć do rotacji
  log "Przywracam port $PORT do rotacji..."
  sudo sed -i "s/server 127.0.0.1:${PORT} down;/server 127.0.0.1:${PORT};/" "$NGINX\_CONF"
  sudo nginx -t && sudo nginx -s reload

  log "Port $PORT zaktualizowany pomyślnie."
  sleep 10  # Stabilizacja przed przejściem do kolejnej instancji
done

log "=== Rolling deploy zakończony ==="

## Blue-green deployment

Blue-green to dwa kompletne środowiska. Aktywne środowisko (blue) obsługuje ruch produkcyjny. Nowa wersja wdrażana jest na green, testowana, a następnie load balancer przełącza 100% ruchu:

\# Struktura katalogów
/var/www/
  app-blue/      # Blue environment — port 3001
  app-green/     # Green environment — port 3002
  app-current -> app-blue  # Symlink na aktywne środowisko

# /etc/nginx/conf.d/blue-green.conf
# ACTIVE=blue (zmień na green żeby przełączyć)
upstream app\_active {
    server 127.0.0.1:3001;  # blue
    # server 127.0.0.1:3002;  # green (odkomentuj po wdrożeniu)
}

#!/bin/bash
# blue-green-switch.sh
set -euo pipefail

NGINX\_CONF="/etc/nginx/conf.d/blue-green.conf"
BLUE\_PORT=3001
GREEN\_PORT=3002

# Wykryj aktualnie aktywne środowisko
if grep -q "server 127.0.0.1:${BLUE\_PORT};" "$NGINX\_CONF"; then
  ACTIVE="blue"
  ACTIVE\_PORT=$BLUE\_PORT
  STANDBY="green"
  STANDBY\_PORT=$GREEN\_PORT
else
  ACTIVE="green"
  ACTIVE\_PORT=$GREEN\_PORT
  STANDBY="blue"
  STANDBY\_PORT=$BLUE\_PORT
fi

echo "Aktywne: $ACTIVE (port $ACTIVE\_PORT)"
echo "Standby: $STANDBY (port $STANDBY\_PORT)"

# 1. Wdróż na standby
echo "Wdrażam na $STANDBY..."
cd "/var/www/app-${STANDBY}"
git pull origin main
npm ci --production
pm2 restart "app-${STANDBY}" --update-env

# 2. Sprawdź zdrowie standby
echo "Health check na porcie $STANDBY\_PORT..."
sleep 5
HEALTH=$(curl -sf "http://localhost:${STANDBY\_PORT}/health" || echo "FAIL")
if \[ "$HEALTH" = "FAIL" \]; then
  echo "Health check nieudany! Przerywam bez przełączenia."
  exit 1
fi

# 3. Przełącz ruch (atomic swap)
echo "Przełączam ruch z $ACTIVE na $STANDBY..."
sed -i "s/server 127.0.0.1:${ACTIVE\_PORT};/server 127.0.0.1:${STANDBY\_PORT};/" "$NGINX\_CONF"
nginx -t && nginx -s reload

# 4. Aktualizuj symlink
ln -sfn "/var/www/app-${STANDBY}" /var/www/app-current

echo "Przełączono na $STANDBY. Poprzednie środowisko $ACTIVE pozostaje gotowe do rollback."
echo "Rollback: bash blue-green-switch.sh (przełączy z powrotem)"

## Health checks — endpointy aplikacji

Każda aplikacja powinna eksponować endpoint `/health` sprawdzający stan wewnętrznych zależności:

// health.ts — endpoint Node.js/Express
app.get('/health', async (req, res) => {
  const checks = {
    uptime: process.uptime(),
    timestamp: Date.now(),
    db: 'unknown' as string,
    redis: 'unknown' as string,
  };

  try {
    // Sprawdź połączenie DB
    await db.query('SELECT 1');
    checks.db = 'ok';
  } catch {
    checks.db = 'error';
  }

  try {
    // Sprawdź Redis
    await redis.ping();
    checks.redis = 'ok';
  } catch {
    checks.redis = 'error';
  }

  const isHealthy = checks.db === 'ok' && checks.redis === 'ok';
  const status = isHealthy ? 200 : 503;

  res.status(status).json({
    status: isHealthy ? 'ok' : 'degraded',
    checks,
  });
});

## Porównanie strategii deployment

Strategia

Downtime

Zasoby

Rollback

Kiedy używać

In-place restart

Tak (sekundy)

1×

Trudny

Nigdy na produkcji z ruchem

Rolling update

Nie

1×

Możliwy

Standardowy deploy z LB i 2+ instancjami

Blue-green

Nie

2×

Natychmiastowy

Gdy potrzebujesz gwarantowanego rollback w 30s

Canary

Nie

1.1×–2×

Natychmiastowy

Ryzykowne zmiany, testowanie na % ruchu

## Najczęstsze pytania

Czym jest zero-downtime deployment? +

Zero-downtime deployment (ZDD) to wdrożenie nowej wersji aplikacji bez widocznego dla użytkownika przestoju — żadne żądanie HTTP nie dostaje błędu 502/503 ani nie jest odrzucane. Osiąga się to przez utrzymanie przynajmniej jednej działającej instancji podczas wymiany pozostałych. Techniki: rolling update (wymieniaj po jednej instancji), blue-green (przełącz cały ruch na nowe środowisko), canary (5% ruchu na nową wersję, obserwuj, skaluj).

Jak działa nginx -s reload — czy to bezpieczne? +

Komenda nginx -s reload (lub systemctl reload nginx) wysyła sygnał HUP do master procesu Nginx. Master otwiera nowe worker procesy z nową konfiguracją, a stare workery kończą obsługę aktywnych żądań (graceful shutdown) przed zamknięciem. Trwa to kilka milisekund do sekund. Nowe połączenia trafiają od razu do nowych workerów. To jest bezpieczne — zero dropped requests. Natomiast nginx -s stop / systemctl restart nginx natychmiast zabija wszystkie workery — nie używaj tego na produkcji podczas ruchu.

Jaka jest różnica między blue-green a canary deployment? +

Blue-green: masz dwa identyczne środowiska (blue=produkcja, green=staging). Po wdrożeniu na green przełączasz 100% ruchu przez zmianę konfiguracji load balancera. Rollback = przełącz z powrotem na blue. Prosto, ale drogie (podwójne zasoby). Canary: stopniowo przenosisz % ruchu na nową wersję — np. 5%, 20%, 50%, 100%. Obserwujesz metryki na każdym etapie. Wykryjesz problem zanim dotknie wszystkich. Wymaga bardziej zaawansowanego routingu (nagłówki, cookies, weight w Nginx upstream).

Jak wdrożyć zero-downtime dla Node.js z PM2? +

PM2 obsługuje zero-downtime przez cluster mode i graceful reload: pm2 reload app-name. PM2 uruchamia nową instancję, czeka aż zacznie odpowiadać (health check przez listen\_timeout), następnie wysyła SIGINT do starej instancji. Stara instancja obsługuje aktywne żądania przez kill\_timeout (domyślnie 1600ms) i się zamyka. Klucz: aplikacja musi obsługiwać SIGINT gracefully — zamknąć serwer HTTP i poczekać na zakończenie aktywnych połączeń przed exit(0).

## Sprawdź oferty pasujące do tego scenariusza

Poniżej masz szybkie przejścia do ofert i stron z kodami rabatowymi tam, gdzie są dostępne.

Contabo

VPS dla blue-green deployment — postaw dwa środowiska i przełączaj bez przestoju

VPS

[Aktywuj rabat →](/out/contabo)

#Reklama · link partnerski

[Zobacz kod rabatowy →](/kody-rabatowe/contabo)

Mikrus

Tani VPS do testowania strategii deploymentu przed wdrożeniem na produkcję

Dev/Test

[Aktywuj rabat →](/out/mikrus)

#Reklama · link partnerski

[Zobacz kod rabatowy →](/kody-rabatowe/mikrus)

LH.pl

Hosting z Git deploy — automatic deployment przez post-receive hook

Hosting

[Aktywuj rabat →](/out/lh-pl)

#Reklama · link partnerski

[Zobacz kod rabatowy →](/kody-rabatowe/lh-pl)

## Powiązane strony

-   [Nginx load balancer — konfiguracja](/baza-wiedzy/nginx-load-balancer-konfiguracja)
-   [Git deploy — automatyczne wdrożenia](/baza-wiedzy/git-deploy-hosting)
-   [Testowanie wydajności — k6 i ApacheBench](/baza-wiedzy/load-testing-k6-ab)
-   [Wszystkie artykuły](/baza-wiedzy/)