Deployer une Application Next.js avec Docker et CI/CD en Production

AI Bot
Par AI Bot ·

Chargement du lecteur de synthèse vocale...

Ce tutoriel vous guide pas a pas pour containeriser une application Next.js avec Docker, mettre en place un pipeline CI/CD avec GitHub Actions, et deployer automatiquement en production sur un VPS Linux. A la fin, chaque push sur votre branche principale declenchera un deploiement automatique.

Objectifs d'apprentissage

A la fin de ce tutoriel, vous serez capable de :

  • Ecrire un Dockerfile optimise multi-stage pour Next.js
  • Configurer Docker Compose pour le developpement local et la production
  • Mettre en place un pipeline CI/CD complet avec GitHub Actions
  • Deployer automatiquement sur un serveur VPS via SSH
  • Gerer les variables d'environnement de maniere securisee
  • Configurer un reverse proxy Nginx avec SSL

Prerequis

Avant de commencer, assurez-vous d'avoir :

  • Node.js 20+ installe localement
  • Docker Desktop installe et fonctionnel (installer Docker)
  • Un compte GitHub avec un depot contenant une application Next.js
  • Un VPS Linux (Ubuntu 22.04+ recommande) avec acces SSH
  • Un nom de domaine pointe vers votre VPS (optionnel mais recommande)
  • Des connaissances de base en Next.js et en ligne de commande

Ce que vous allez construire

Nous allons mettre en place une infrastructure de deploiement complete :

  1. Dockerfile multi-stage — Image Docker optimisee (~150 Mo au lieu de ~1 Go)
  2. Docker Compose — Orchestration des services (app + base de donnees + cache)
  3. Pipeline CI/CD — Tests automatiques, build et deploiement via GitHub Actions
  4. Reverse proxy — Nginx avec certificat SSL Let's Encrypt
  5. Monitoring — Healthchecks et logs structures

Etape 1 : Preparer le projet Next.js

Commencez par verifier que votre projet Next.js fonctionne correctement en local.

# Verifier la version de Node.js
node --version  # v20.x.x requis
 
# Installer les dependances
npm install
 
# Tester le build de production
npm run build
 
# Verifier que le serveur demarre
npm start

Si vous n'avez pas encore de projet Next.js, creez-en un rapidement avec npx create-next-app@latest mon-app --typescript. Ce tutoriel fonctionne avec Next.js 14 et 15.

Ajouter un script de healthcheck

Creez un fichier app/api/health/route.ts pour que Docker puisse verifier l'etat de votre application :

// app/api/health/route.ts
import { NextResponse } from "next/server";
 
export async function GET() {
  return NextResponse.json({
    status: "healthy",
    timestamp: new Date().toISOString(),
    uptime: process.uptime(),
  });
}

Ce endpoint sera utilise par Docker et votre pipeline CI/CD pour valider que l'application fonctionne.


Etape 2 : Ecrire le Dockerfile multi-stage

Le secret d'une image Docker performante pour Next.js est le build multi-stage. Cela permet de separer les outils de build (qui sont lourds) de l'image finale (qui est legere).

Creez un fichier Dockerfile a la racine de votre projet :

# =============================================================================
# Etape 1 : Image de base avec les dependances
# =============================================================================
FROM node:20-alpine AS base
 
# Installer libc6-compat pour les modules natifs
RUN apk add --no-cache libc6-compat
WORKDIR /app
 
# =============================================================================
# Etape 2 : Installer les dependances
# =============================================================================
FROM base AS deps
 
# Copier les fichiers de dependances
COPY package.json package-lock.json* ./
 
# Installer toutes les dependances (y compris devDependencies pour le build)
RUN npm ci
 
# =============================================================================
# Etape 3 : Build de l'application
# =============================================================================
FROM base AS builder
WORKDIR /app
 
# Copier les dependances installees
COPY --from=deps /app/node_modules ./node_modules
 
# Copier le code source
COPY . .
 
# Desactiver la telemetrie Next.js pendant le build
ENV NEXT_TELEMETRY_DISABLED=1
 
# Build de production
RUN npm run build
 
# =============================================================================
# Etape 4 : Image de production (legere)
# =============================================================================
FROM node:20-alpine AS runner
WORKDIR /app
 
# Mode production
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
 
# Creer un utilisateur non-root pour la securite
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
 
# Copier les fichiers publics
COPY --from=builder /app/public ./public
 
# Copier le build standalone de Next.js
# Le mode standalone copie uniquement les fichiers necessaires
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
 
# Utiliser l'utilisateur non-root
USER nextjs
 
# Exposer le port
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
 
# Healthcheck Docker
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:3000/api/health || exit 1
 
# Demarrer l'application
CMD ["node", "server.js"]

Configurer le mode standalone dans Next.js

Pour que le Dockerfile fonctionne, vous devez activer le output standalone dans votre configuration Next.js :

// next.config.js (ou next.config.mjs)
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: "standalone",
};
 
module.exports = nextConfig;

Le mode standalone est essentiel pour Docker. Sans lui, l'image finale necessitera tout le dossier node_modules, ce qui augmentera considerablement sa taille. Avec standalone, Next.js copie uniquement les fichiers necessaires a l'execution.

Creer le fichier .dockerignore

Creez un fichier .dockerignore pour eviter de copier des fichiers inutiles dans l'image :

# .dockerignore
node_modules
.next
.git
.gitignore
*.md
docker-compose*.yml
.env*.local
.vscode
.idea
coverage
.husky

Tester le build Docker en local

# Construire l'image
docker build -t mon-app-nextjs .
 
# Verifier la taille de l'image
docker images mon-app-nextjs
# REPOSITORY        TAG       SIZE
# mon-app-nextjs    latest    ~150MB
 
# Lancer le conteneur
docker run -p 3000:3000 mon-app-nextjs
 
# Tester dans un autre terminal
curl http://localhost:3000/api/health

Si vous obtenez {"status":"healthy"}, votre image Docker fonctionne correctement. La taille devrait etre entre 130 et 200 Mo selon vos dependances.


Etape 3 : Configurer Docker Compose

Docker Compose permet d'orchestrer plusieurs services. Nous allons configurer deux fichiers : un pour le developpement et un pour la production.

Docker Compose pour le developpement

Creez docker-compose.dev.yml :

# docker-compose.dev.yml
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
      target: deps  # Utiliser seulement l'etape des dependances
    command: npm run dev
    ports:
      - "3000:3000"
    volumes:
      - .:/app
      - /app/node_modules  # Exclure node_modules du volume
    environment:
      - NODE_ENV=development
      - DATABASE_URL=postgresql://postgres:password@db:5432/myapp
      - REDIS_URL=redis://cache:6379
    depends_on:
      db:
        condition: service_healthy
      cache:
        condition: service_started
 
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_DB: myapp
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5
 
  cache:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
 
volumes:
  postgres_data:
  redis_data:

Docker Compose pour la production

Creez docker-compose.prod.yml :

# docker-compose.prod.yml
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    restart: unless-stopped
    ports:
      - "3000:3000"
    env_file:
      - .env.production
    healthcheck:
      test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/api/health"]
      interval: 30s
      timeout: 10s
      start_period: 40s
      retries: 3
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: "0.5"
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
 
  db:
    image: postgres:16-alpine
    restart: unless-stopped
    env_file:
      - .env.production
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5
    deploy:
      resources:
        limits:
          memory: 256M
 
  cache:
    image: redis:7-alpine
    restart: unless-stopped
    command: redis-server --maxmemory 128mb --maxmemory-policy allkeys-lru
    volumes:
      - redis_data:/data
    deploy:
      resources:
        limits:
          memory: 128M
 
volumes:
  postgres_data:
  redis_data:

Lancer l'environnement de developpement

# Demarrer tous les services
docker compose -f docker-compose.dev.yml up -d
 
# Voir les logs
docker compose -f docker-compose.dev.yml logs -f app
 
# Arreter les services
docker compose -f docker-compose.dev.yml down

Etape 4 : Configurer le pipeline CI/CD avec GitHub Actions

C'est la partie la plus importante. Nous allons creer un pipeline qui :

  1. Teste le code a chaque pull request
  2. Build l'image Docker
  3. Deploie automatiquement sur le VPS a chaque merge sur main

Creer le workflow principal

Creez le fichier .github/workflows/deploy.yml :

# .github/workflows/deploy.yml
name: CI/CD Pipeline
 
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
 
env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}
 
jobs:
  # =====================================================
  # Job 1 : Linting et tests
  # =====================================================
  test:
    name: Tests et qualite
    runs-on: ubuntu-latest
 
    steps:
      - name: Checkout du code
        uses: actions/checkout@v4
 
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: "npm"
 
      - name: Installer les dependances
        run: npm ci
 
      - name: Linting
        run: npm run lint
 
      - name: Tests unitaires
        run: npm test -- --passWithNoTests
 
      - name: Build de verification
        run: npm run build
 
  # =====================================================
  # Job 2 : Build et push de l'image Docker
  # =====================================================
  build:
    name: Build Docker
    runs-on: ubuntu-latest
    needs: test
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
 
    permissions:
      contents: read
      packages: write
 
    outputs:
      image-tag: ${{ steps.meta.outputs.tags }}
 
    steps:
      - name: Checkout du code
        uses: actions/checkout@v4
 
      - name: Connexion au registre GitHub
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
 
      - name: Extraire les metadonnees Docker
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=sha,prefix=
            type=raw,value=latest
 
      - name: Setup Docker Buildx
        uses: docker/setup-buildx-action@v3
 
      - name: Build et push
        uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
 
  # =====================================================
  # Job 3 : Deploiement sur le VPS
  # =====================================================
  deploy:
    name: Deploiement production
    runs-on: ubuntu-latest
    needs: build
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
 
    environment:
      name: production
      url: https://${{ vars.DOMAIN_NAME }}
 
    steps:
      - name: Checkout du code
        uses: actions/checkout@v4
 
      - name: Deployer via SSH
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.VPS_HOST }}
          username: ${{ secrets.VPS_USER }}
          key: ${{ secrets.VPS_SSH_KEY }}
          script: |
            # Se placer dans le repertoire du projet
            cd /opt/apps/mon-app
 
            # Se connecter au registre GitHub
            echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
 
            # Telecharger la derniere image
            docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
 
            # Mettre a jour le service avec zero downtime
            docker compose -f docker-compose.prod.yml up -d --no-deps --wait app
 
            # Nettoyer les anciennes images
            docker image prune -f
 
            # Verifier le healthcheck
            sleep 10
            curl -f http://localhost:3000/api/health || exit 1
 
      - name: Notification de succes
        if: success()
        run: echo "Deploiement reussi sur ${{ vars.DOMAIN_NAME }}"
 
      - name: Notification d'echec
        if: failure()
        run: echo "Echec du deploiement - verifier les logs"

Configurer les secrets GitHub

Rendez-vous dans Settings > Secrets and variables > Actions de votre depot GitHub et ajoutez :

SecretDescriptionExemple
VPS_HOSTAdresse IP ou domaine du VPS203.0.113.50
VPS_USERUtilisateur SSHdeploy
VPS_SSH_KEYCle privee SSHContenu de ~/.ssh/id_ed25519

Et dans les Variables :

VariableDescriptionExemple
DOMAIN_NAMENom de domainemonapp.example.com

Ne stockez jamais vos cles SSH ou tokens dans le code source. Utilisez exclusivement les secrets GitHub pour les informations sensibles. Generez une cle SSH dediee au deploiement avec ssh-keygen -t ed25519 -C "deploy@github-actions".


Etape 5 : Preparer le serveur VPS

Connectez-vous a votre VPS et preparez l'environnement.

Installer Docker sur le VPS

# Mise a jour du systeme
sudo apt update && sudo apt upgrade -y
 
# Installer Docker via le script officiel
curl -fsSL https://get.docker.com | sudo sh
 
# Ajouter l'utilisateur au groupe docker
sudo usermod -aG docker $USER
 
# Installer Docker Compose (inclus dans Docker Engine moderne)
docker compose version
 
# Redemarrer la session pour appliquer les permissions
exit
# Reconnectez-vous

Creer la structure du projet

# Creer le repertoire de l'application
sudo mkdir -p /opt/apps/mon-app
sudo chown $USER:$USER /opt/apps/mon-app
cd /opt/apps/mon-app
 
# Creer le fichier d'environnement production
cat > .env.production << 'EOF'
NODE_ENV=production
DATABASE_URL=postgresql://postgres:VOTRE_MOT_DE_PASSE_FORT@db:5432/myapp
REDIS_URL=redis://cache:6379
POSTGRES_USER=postgres
POSTGRES_PASSWORD=VOTRE_MOT_DE_PASSE_FORT
POSTGRES_DB=myapp
EOF
 
# Proteger le fichier
chmod 600 .env.production

Copier le fichier Docker Compose

# Copier docker-compose.prod.yml sur le serveur depuis votre machine locale
scp docker-compose.prod.yml deploy@VOTRE_VPS:/opt/apps/mon-app/

Creer un utilisateur dedie au deploiement

# Creer l'utilisateur deploy
sudo adduser --disabled-password deploy
sudo usermod -aG docker deploy
 
# Configurer la cle SSH pour le deploiement
sudo mkdir -p /home/deploy/.ssh
sudo cp ~/.ssh/authorized_keys /home/deploy/.ssh/
# Ajoutez la cle publique generee pour GitHub Actions
echo "VOTRE_CLE_PUBLIQUE_ED25519" | sudo tee -a /home/deploy/.ssh/authorized_keys
sudo chown -R deploy:deploy /home/deploy/.ssh
sudo chmod 700 /home/deploy/.ssh
sudo chmod 600 /home/deploy/.ssh/authorized_keys

Etape 6 : Configurer Nginx comme reverse proxy

Nginx se place devant votre application Next.js pour gerer le SSL, le cache et la compression.

Installer Nginx et Certbot

# Installer Nginx
sudo apt install -y nginx
 
# Installer Certbot pour Let's Encrypt
sudo apt install -y certbot python3-certbot-nginx

Configurer le virtual host

Creez le fichier de configuration Nginx :

# /etc/nginx/sites-available/mon-app
upstream nextjs_upstream {
    server 127.0.0.1:3000;
    keepalive 64;
}
 
server {
    listen 80;
    server_name monapp.example.com;
 
    # Redirection vers HTTPS (sera configure par Certbot)
    location / {
        return 301 https://$server_name$request_uri;
    }
}
 
server {
    listen 443 ssl http2;
    server_name monapp.example.com;
 
    # Les certificats SSL seront ajoutes par Certbot
    # ssl_certificate /etc/letsencrypt/live/monapp.example.com/fullchain.pem;
    # ssl_certificate_key /etc/letsencrypt/live/monapp.example.com/privkey.pem;
 
    # En-tetes de securite
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
 
    # Compression gzip
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types text/plain text/css text/xml application/json application/javascript
               application/xml+rss application/atom+xml image/svg+xml;
 
    # Cache des fichiers statiques de Next.js
    location /_next/static {
        proxy_pass http://nextjs_upstream;
        proxy_cache_valid 60m;
        add_header Cache-Control "public, max-age=31536000, immutable";
    }
 
    # Fichiers publics statiques
    location /images {
        proxy_pass http://nextjs_upstream;
        proxy_cache_valid 60m;
        add_header Cache-Control "public, max-age=86400";
    }
 
    # Proxy vers Next.js
    location / {
        proxy_pass http://nextjs_upstream;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;
 
        # Timeouts
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }
}

Activer le site et le SSL

# Activer le site
sudo ln -s /etc/nginx/sites-available/mon-app /etc/nginx/sites-enabled/
 
# Verifier la configuration
sudo nginx -t
 
# Recharger Nginx
sudo systemctl reload nginx
 
# Obtenir le certificat SSL avec Certbot
sudo certbot --nginx -d monapp.example.com --non-interactive --agree-tos -m votre@email.com
 
# Verifier le renouvellement automatique
sudo certbot renew --dry-run

Certbot modifiera automatiquement votre configuration Nginx pour ajouter les certificats SSL. Le renouvellement est automatique via un timer systemd.


Etape 7 : Deploiement zero-downtime

Pour eviter les interruptions lors des mises a jour, nous allons configurer un deploiement progressif.

Script de deploiement avance

Creez scripts/deploy.sh sur votre VPS :

#!/bin/bash
# scripts/deploy.sh - Deploiement avec zero downtime
 
set -euo pipefail
 
APP_DIR="/opt/apps/mon-app"
HEALTH_URL="http://localhost:3000/api/health"
MAX_RETRIES=30
RETRY_INTERVAL=2
 
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
 
check_health() {
    curl -sf "$HEALTH_URL" > /dev/null 2>&1
}
 
cd "$APP_DIR"
 
log "Demarrage du deploiement..."
 
# Telecharger la nouvelle image
log "Telechargement de la nouvelle image..."
docker compose -f docker-compose.prod.yml pull app
 
# Demarrer le nouveau conteneur
log "Demarrage du nouveau conteneur..."
docker compose -f docker-compose.prod.yml up -d --no-deps app
 
# Attendre que le healthcheck passe
log "Verification du healthcheck..."
retries=0
until check_health; do
    retries=$((retries + 1))
    if [ "$retries" -ge "$MAX_RETRIES" ]; then
        log "ERREUR: Le healthcheck a echoue apres $MAX_RETRIES tentatives"
        log "Rollback en cours..."
        docker compose -f docker-compose.prod.yml rollback app 2>/dev/null || true
        exit 1
    fi
    log "En attente du healthcheck... ($retries/$MAX_RETRIES)"
    sleep "$RETRY_INTERVAL"
done
 
log "Healthcheck OK"
 
# Nettoyer les anciennes images
docker image prune -f
 
log "Deploiement termine avec succes"
# Rendre le script executable
chmod +x /opt/apps/mon-app/scripts/deploy.sh

Etape 8 : Monitoring et logs

Configurer les logs structures

Ajoutez la configuration de logging dans votre application Next.js :

// lib/logger.ts
type LogLevel = "info" | "warn" | "error" | "debug";
 
interface LogEntry {
  level: LogLevel;
  message: string;
  timestamp: string;
  [key: string]: unknown;
}
 
function log(level: LogLevel, message: string, meta?: Record<string, unknown>) {
  const entry: LogEntry = {
    level,
    message,
    timestamp: new Date().toISOString(),
    ...meta,
  };
 
  const output = JSON.stringify(entry);
 
  if (level === "error") {
    console.error(output);
  } else if (level === "warn") {
    console.warn(output);
  } else {
    console.log(output);
  }
}
 
export const logger = {
  info: (msg: string, meta?: Record<string, unknown>) => log("info", msg, meta),
  warn: (msg: string, meta?: Record<string, unknown>) => log("warn", msg, meta),
  error: (msg: string, meta?: Record<string, unknown>) => log("error", msg, meta),
  debug: (msg: string, meta?: Record<string, unknown>) => log("debug", msg, meta),
};

Commandes utiles pour le monitoring

# Voir les logs en temps reel
docker compose -f docker-compose.prod.yml logs -f app
 
# Voir les statistiques des conteneurs
docker stats
 
# Verifier l'etat des conteneurs
docker compose -f docker-compose.prod.yml ps
 
# Verifier l'utilisation disque de Docker
docker system df
 
# Nettoyer les ressources inutilisees
docker system prune -f

Ajouter un healthcheck avance

Enrichissez votre endpoint /api/health pour inclure plus d'informations :

// app/api/health/route.ts
import { NextResponse } from "next/server";
 
export async function GET() {
  const healthData = {
    status: "healthy",
    timestamp: new Date().toISOString(),
    uptime: process.uptime(),
    memory: {
      used: Math.round(process.memoryUsage().heapUsed / 1024 / 1024),
      total: Math.round(process.memoryUsage().heapTotal / 1024 / 1024),
      unit: "MB",
    },
    version: process.env.npm_package_version || "unknown",
    node: process.version,
  };
 
  return NextResponse.json(healthData);
}

Etape 9 : Securiser l'ensemble

Variables d'environnement

Creez un fichier .env.example pour documenter les variables requises sans exposer les valeurs :

# .env.example - Variables d'environnement requises
NODE_ENV=production
DATABASE_URL=postgresql://user:password@db:5432/dbname
REDIS_URL=redis://cache:6379
 
# Base de donnees
POSTGRES_USER=postgres
POSTGRES_PASSWORD=changez_moi
POSTGRES_DB=myapp
 
# Application
NEXTAUTH_SECRET=generez_un_secret_fort
NEXTAUTH_URL=https://monapp.example.com

Bonnes pratiques de securite

Voici les points essentiels a respecter :

  1. Utilisateur non-root dans le conteneur (deja configure dans le Dockerfile)
  2. Secrets GitHub pour toutes les informations sensibles
  3. Certificat SSL via Let's Encrypt (renouvellement automatique)
  4. En-tetes de securite dans Nginx (HSTS, X-Frame-Options, etc.)
  5. Limites de ressources dans Docker Compose (memoire, CPU)
  6. Logs structures pour la detection d'anomalies

Changez toujours les mots de passe par defaut. Utilisez openssl rand -base64 32 pour generer des secrets forts. Ne repoussez jamais les fichiers .env dans votre depot Git.


Tester votre pipeline complet

Voici comment verifier que tout fonctionne de bout en bout :

1. Test local avec Docker

# Build et demarrage
docker compose -f docker-compose.dev.yml up --build -d
 
# Verifier les logs
docker compose -f docker-compose.dev.yml logs -f
 
# Tester le healthcheck
curl http://localhost:3000/api/health

2. Test du pipeline CI/CD

# Creer une branche de test
git checkout -b test/cicd-pipeline
 
# Faire un changement mineur
echo "// test" >> lib/logger.ts
 
# Commit et push
git add . && git commit -m "test: verify CI/CD pipeline"
git push -u origin test/cicd-pipeline
 
# Creer une pull request sur GitHub
gh pr create --title "Test CI/CD pipeline" --body "Verification du pipeline"

3. Verifier le deploiement

# Apres le merge sur main, verifier le deploiement
curl https://monapp.example.com/api/health
 
# Reponse attendue :
# {"status":"healthy","timestamp":"...","uptime":42,"memory":{"used":85,"total":128,"unit":"MB"}}

Depannage

Le build Docker echoue

ProblemeSolution
COPY failed: file not foundVerifiez que .dockerignore n'exclut pas les fichiers necessaires
npm ci echoueAssurez-vous que package-lock.json est present et a jour
Out of memory pendant le buildAugmentez la memoire Docker Desktop ou ajoutez --max-old-space-size=4096

Le pipeline GitHub Actions echoue

ProblemeSolution
Permission denied (SSH)Verifiez que la cle SSH est correctement configuree dans les secrets
Image push refusedVerifiez les permissions packages: write dans le workflow
Healthcheck timeoutAugmentez start-period et verifiez les logs de l'application

Le deploiement ne fonctionne pas

# Verifier les logs du conteneur
docker compose -f docker-compose.prod.yml logs app --tail 50
 
# Verifier que les ports ne sont pas utilises
sudo ss -tulnp | grep 3000
 
# Redemarrer les services
docker compose -f docker-compose.prod.yml restart
 
# Reconstruire completement
docker compose -f docker-compose.prod.yml up -d --build --force-recreate

Prochaines etapes

Maintenant que votre pipeline est en place, voici comment l'ameliorer :

  • Ajouter des tests E2E avec Playwright dans le pipeline CI
  • Configurer des notifications Slack ou Discord pour les deploiements
  • Mettre en place un CDN (Cloudflare) devant Nginx pour le cache global
  • Ajouter du monitoring applicatif avec Sentry ou Datadog
  • Implementer des deploiements blue/green pour un zero-downtime garanti
  • Configurer des backups automatiques de la base de donnees PostgreSQL

Conclusion

Vous disposez maintenant d'une infrastructure de deploiement complete et professionnelle pour votre application Next.js :

  • Docker multi-stage pour des images legeres et securisees
  • Docker Compose pour l'orchestration des services
  • GitHub Actions pour l'integration et le deploiement continu
  • Nginx + SSL pour un reverse proxy securise
  • Monitoring avec des healthchecks et des logs structures

Cette architecture est utilisee en production par de nombreuses entreprises et startups. Elle est fiable, reproductible et facile a maintenir. Chaque push sur main declenche automatiquement un deploiement verifie par des healthchecks.

Le plus important est de commencer simple et d'iterer. Deployez d'abord une version basique, puis ajoutez progressivement les couches de monitoring, de securite et d'optimisation.


Vous voulez lire plus de tutoriels? Découvrez notre dernier tutoriel sur Accelerez le Succes de Votre Application : Construire et Executer avec Firebase.

Discutez de votre projet avec nous

Nous sommes ici pour vous aider avec vos besoins en développement Web. Planifiez un appel pour discuter de votre projet et comment nous pouvons vous aider.

Trouvons les meilleures solutions pour vos besoins.

Articles connexes

Construire un Agent IA Autonome avec Agentic RAG et Next.js

Apprenez a construire un agent IA qui decide de maniere autonome quand et comment recuperer des informations depuis des bases de donnees vectorielles. Un guide pratique complet avec Vercel AI SDK et Next.js, accompagne d'exemples executables.

30 min read·