Kamal 2 : Déployer Next.js sur un VPS avec zéro downtime

Introduction
Déployer une application web en production ne devrait pas nécessiter un cluster Kubernetes, un ingénieur DevOps dédié ou une facture cloud à quatre chiffres. Pourtant, en 2026, beaucoup de développeurs se retrouvent piégés entre deux extrêmes : les plateformes managées coûteuses (Vercel, Railway) et la complexité de Kubernetes.
Kamal 2 est la réponse de 37signals (les créateurs de Ruby on Rails et Basecamp) à ce dilemme. Anciennement connu sous le nom de MRSK, Kamal est un outil de déploiement open-source qui permet de déployer des applications conteneurisées sur n'importe quel serveur — VPS, bare metal ou cloud — avec zéro downtime, des rollbacks instantanés et un SSL automatique via Traefik.
Ce qui rend Kamal 2 particulièrement intéressant :
- Pas de vendor lock-in — fonctionne sur tout serveur avec SSH et Docker
- Zéro downtime — les nouvelles versions sont lancées avant de couper les anciennes
- Simple à configurer — un seul fichier YAML (
config/deploy.yml) - Rollback en une commande — retour à la version précédente instantanément
- Multi-serveur — déployez sur plusieurs machines en parallèle
- Accessory support — gérez vos bases de données et services annexes
Dans ce tutoriel, vous allez :
- Installer et configurer Kamal 2 sur votre machine locale
- Préparer un VPS pour le déploiement
- Conteneuriser une application Next.js avec un Dockerfile optimisé
- Configurer Kamal pour un déploiement zéro downtime
- Mettre en place le SSL automatique avec Traefik
- Déployer, surveiller et effectuer des rollbacks
Prérequis
Avant de commencer, assurez-vous de disposer de :
- Un VPS avec au moins 1 Go de RAM et 1 vCPU (DigitalOcean, Hetzner, OVH, Contabo, etc.)
- Ubuntu 22.04 ou 24.04 installé sur le VPS
- Un nom de domaine pointant vers votre serveur (enregistrement DNS A)
- Docker installé localement (pour construire les images)
- Ruby 3.1+ installé localement (Kamal est écrit en Ruby)
- Une application Next.js prête à déployer
- Un compte Docker Hub (ou un autre registre de conteneurs)
- Connaissances de base en ligne de commande Linux et Docker
Ce que vous allez construire
À la fin de ce tutoriel, vous aurez une infrastructure de déploiement complète :
- Un VPS configuré automatiquement par Kamal
- Une application Next.js en production avec SSL
- Un proxy Traefik gérant le routage et les certificats
- Un pipeline de déploiement zéro downtime
- La capacité de rollback en une seule commande
Étape 1 : Installer Kamal 2
Kamal est distribué comme une gem Ruby. Installez-le globalement sur votre machine de développement :
gem install kamalVérifiez que Kamal est correctement installé :
kamal versionVous devriez voir quelque chose comme 2.4.0 ou une version plus récente.
Si vous préférez ne pas installer Ruby, vous pouvez utiliser Kamal via Docker :
alias kamal='docker run -it --rm -v "${PWD}:/workdir" -v "/run/host-services/ssh-auth.sock:/run/host-services/ssh-auth.sock" -e SSH_AUTH_SOCK="/run/host-services/ssh-auth.sock" -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/basecamp/kamal:latest'Étape 2 : Préparer le VPS
2.1 Créer le serveur
Créez un VPS chez votre fournisseur préféré. Pour ce tutoriel, un serveur avec ces spécifications minimales suffit :
| Ressource | Minimum | Recommandé |
|---|---|---|
| RAM | 1 Go | 2 Go |
| CPU | 1 vCPU | 2 vCPUs |
| Stockage | 20 Go SSD | 40 Go SSD |
| OS | Ubuntu 22.04 | Ubuntu 24.04 |
2.2 Configurer le DNS
Pointez votre domaine vers le serveur :
Type: A
Nom: app (ou @ pour la racine)
Valeur: <IP_DE_VOTRE_SERVEUR>
TTL: 300
2.3 Configurer SSH
Assurez-vous que vous pouvez vous connecter au serveur via SSH avec votre clé :
ssh root@votre-serveur.comKamal se connecte en SSH pour configurer Docker et déployer vos conteneurs. Il gère automatiquement l'installation de Docker sur le serveur lors du premier déploiement — vous n'avez rien à installer manuellement.
Étape 3 : Préparer l'application Next.js
3.1 Créer le projet (si nécessaire)
Si vous partez de zéro :
npx create-next-app@latest mon-app-kamal --typescript --tailwind --app
cd mon-app-kamal3.2 Créer le Dockerfile
Créez un Dockerfile optimisé pour la production à la racine du projet :
# Étape 1 : Installer les dépendances
FROM node:20-alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --only=production
# Étape 2 : Builder l'application
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
ENV NODE_ENV=production
RUN npm run build
# Étape 3 : Image de production
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]3.3 Configurer Next.js pour le mode standalone
Modifiez votre next.config.js (ou next.config.ts) pour activer le mode standalone :
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone',
}
module.exports = nextConfigLe mode standalone génère un serveur Node.js autonome qui inclut uniquement les fichiers nécessaires, réduisant considérablement la taille de l'image Docker.
3.4 Ajouter un fichier .dockerignore
Créez un fichier .dockerignore pour accélérer les builds :
node_modules
.next
.git
.gitignore
*.md
docker-compose*.yml
.env*.local
Étape 4 : Initialiser Kamal
4.1 Générer la configuration
Depuis la racine de votre projet Next.js :
kamal initCette commande crée les fichiers suivants :
config/
deploy.yml # Configuration principale
.kamal/
secrets # Variables d'environnement secrètes
.env # Variables locales (ajouté au .gitignore)
4.2 Configurer deploy.yml
Remplacez le contenu de config/deploy.yml par :
service: mon-app-nextjs
image: votre-docker-hub-user/mon-app-nextjs
servers:
web:
hosts:
- votre-serveur.com
labels:
traefik.http.routers.mon-app-nextjs.rule: Host(`app.votre-domaine.com`)
traefik.http.routers.mon-app-nextjs.entrypoints: websecure
traefik.http.routers.mon-app-nextjs.tls.certresolver: letsencrypt
options:
network: kamal
proxy:
ssl: true
host: app.votre-domaine.com
registry:
username: votre-docker-hub-user
password:
- KAMAL_REGISTRY_PASSWORD
env:
clear:
NODE_ENV: production
HOSTNAME: 0.0.0.0
secret:
- DATABASE_URL
builder:
dockerfile: Dockerfile
multiarch: false
healthcheck:
path: /api/health
port: 3000
interval: 10
max_attempts: 30Décortiquons les sections importantes :
- service : le nom de votre application (utilisé pour nommer les conteneurs)
- image : le chemin complet vers votre image Docker sur le registre
- servers : liste des serveurs cibles avec les labels Traefik pour le routage
- proxy : active le SSL automatique via Traefik et kamal-proxy
- registry : vos identifiants pour le registre Docker
- env : variables d'environnement (claires et secrètes séparées)
- healthcheck : Kamal vérifie que votre app répond avant de basculer le trafic
4.3 Configurer les secrets
Éditez le fichier .kamal/secrets :
KAMAL_REGISTRY_PASSWORD=votre-mot-de-passe-docker-hub
DATABASE_URL=postgresql://user:pass@db-host:5432/mydbCe fichier ne doit jamais être commité dans Git. Vérifiez que .kamal/secrets est dans votre .gitignore.
4.4 Créer le endpoint de healthcheck
Kamal utilise un healthcheck pour vérifier que votre application est prête avant de basculer le trafic. Créez un endpoint API simple :
// app/api/health/route.ts
import { NextResponse } from 'next/server'
export async function GET() {
return NextResponse.json({
status: 'ok',
timestamp: new Date().toISOString(),
})
}Étape 5 : Comprendre le déploiement zéro downtime
Avant de lancer le premier déploiement, comprenons comment Kamal 2 assure le zéro downtime :
Le flux de déploiement
1. Build de l'image Docker localement (ou en CI)
2. Push de l'image vers le registre
3. Pull de l'image sur le(s) serveur(s)
4. Lancement du nouveau conteneur
5. Healthcheck : Kamal attend que /api/health réponde 200
6. kamal-proxy bascule le trafic vers le nouveau conteneur
7. Arrêt gracieux de l'ancien conteneur
Le point clé est l'étape 6 : le trafic ne bascule qu'après que le nouveau conteneur a passé le healthcheck. Cela signifie que vos utilisateurs ne verront jamais une erreur 502 ou une page blanche pendant le déploiement.
kamal-proxy vs Traefik
Kamal 2 utilise kamal-proxy, un reverse proxy léger développé spécifiquement pour Kamal. Il remplace la dépendance à Traefik pour le routage de base, bien que Traefik reste disponible comme option pour les configurations avancées (multi-service, middlewares, etc.).
kamal-proxy gère :
- Le basculement de trafic entre ancien et nouveau conteneur
- La terminaison SSL avec Let's Encrypt
- Les healthchecks automatiques
- Le draining des connexions existantes
Étape 6 : Premier déploiement
6.1 Lancer le setup initial
Le premier déploiement nécessite une étape de setup qui configure Docker et kamal-proxy sur le serveur :
kamal setupCette commande effectue les opérations suivantes :
- Connexion SSH au serveur
- Installation de Docker (si non présent)
- Installation de kamal-proxy
- Login au registre Docker
- Build et push de l'image
- Pull et démarrage du conteneur
- Configuration du SSL
Vous verrez une sortie détaillée de chaque étape :
INFO [f97da026] Running docker login ...
INFO [f97da026] Finished in 1.337 seconds
INFO Building image ...
INFO Pushing image ...
INFO [48716498] Running docker pull ...
INFO [48716498] Finished in 8.234 seconds
INFO [48716498] Running docker run ...
INFO Waiting for healthy container ...
INFO Container is healthy!
6.2 Vérifier le déploiement
Une fois terminé, vérifiez que tout fonctionne :
# Vérifier les conteneurs en cours d'exécution
kamal details
# Voir les logs de l'application
kamal app logs
# Vérifier le statut du proxy
kamal proxy detailsVisitez https://app.votre-domaine.com — votre application Next.js devrait être accessible avec un certificat SSL valide.
Étape 7 : Déploiements suivants
Pour les déploiements ultérieurs, une seule commande suffit :
kamal deployC'est tout. Kamal va :
- Construire la nouvelle image
- La pousser vers le registre
- La télécharger sur le serveur
- Lancer le nouveau conteneur
- Vérifier le healthcheck
- Basculer le trafic
- Arrêter l'ancien conteneur
Déployer sans reconstruire l'image
Si vous avez déjà construit et poussé l'image (par exemple en CI) :
kamal deploy --skip-pushÉtape 8 : Rollback
L'un des plus grands avantages de Kamal est la facilité de rollback. Si un déploiement cause des problèmes :
# Revenir à la version précédente
kamal rollback [TAG_DE_VERSION]Pour voir les versions disponibles :
kamal app containersLe rollback est quasi-instantané car l'image précédente est déjà présente sur le serveur. Kamal relance simplement l'ancien conteneur et bascule le trafic.
Étape 9 : Gérer les accessoires (bases de données, Redis, etc.)
Kamal peut gérer des services annexes appelés accessories. Ajoutez-les dans votre config/deploy.yml :
accessories:
db:
image: postgres:16-alpine
host: votre-serveur.com
port: "5432:5432"
env:
clear:
POSTGRES_DB: mon_app
secret:
- POSTGRES_PASSWORD
directories:
- data:/var/lib/postgresql/data
options:
network: kamal
redis:
image: redis:7-alpine
host: votre-serveur.com
port: "6379:6379"
directories:
- data:/data
options:
network: kamalDéployez les accessoires :
# Déployer tous les accessoires
kamal accessory boot all
# Ou un accessoire spécifique
kamal accessory boot dbLes accessoires sont persistants — ils ne sont pas redémarrés lors des déploiements de l'application principale.
Étape 10 : Déploiement multi-serveur
Pour une application à fort trafic, vous pouvez déployer sur plusieurs serveurs :
servers:
web:
hosts:
- serveur-1.votre-domaine.com
- serveur-2.votre-domaine.com
- serveur-3.votre-domaine.comKamal déploie sur tous les serveurs en parallèle. Combiné avec un load balancer (comme un DigitalOcean Load Balancer ou un DNS round-robin), vous obtenez une haute disponibilité sans effort supplémentaire.
Rôles de serveur
Vous pouvez définir des rôles différents pour vos serveurs :
servers:
web:
hosts:
- web-1.exemple.com
- web-2.exemple.com
worker:
hosts:
- worker-1.exemple.com
cmd: npm run workerÉtape 11 : Intégration CI/CD avec GitHub Actions
Automatisez le déploiement avec GitHub Actions :
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
concurrency:
group: deploy
cancel-in-progress: true
env:
DOCKER_BUILDKIT: 1
jobs:
deploy:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: "3.3"
bundler-cache: true
- name: Install Kamal
run: gem install kamal
- name: Set up SSH
uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Deploy with Kamal
env:
KAMAL_REGISTRY_PASSWORD: ${{ secrets.DOCKER_HUB_TOKEN }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
run: kamal deployAjoutez les secrets suivants dans votre dépôt GitHub :
SSH_PRIVATE_KEY: votre clé SSH privée pour le serveurDOCKER_HUB_TOKEN: votre token Docker HubDATABASE_URL: la chaîne de connexion à la base de données
Étape 12 : Commandes utiles au quotidien
Voici les commandes Kamal que vous utiliserez le plus souvent :
Gestion de l'application
# Déployer
kamal deploy
# Voir les logs en temps réel
kamal app logs -f
# Exécuter une commande dans le conteneur
kamal app exec "node -e 'console.log(process.env.NODE_ENV)'"
# Ouvrir une session interactive
kamal app exec -i bash
# Redémarrer l'application
kamal app boot
# Arrêter l'application
kamal app stopGestion du proxy
# Voir la configuration du proxy
kamal proxy details
# Recharger la configuration
kamal proxy rebootGestion des accessoires
# Voir les logs de la base de données
kamal accessory logs db
# Redémarrer Redis
kamal accessory reboot redis
# Exécuter psql
kamal accessory exec db "psql -U postgres mon_app"Maintenance
# Nettoyer les anciennes images Docker
kamal prune all
# Verrouiller les déploiements (maintenance)
kamal lock acquire -m "Maintenance en cours"
# Déverrouiller
kamal lock releaseÉtape 13 : Surveiller votre application
Healthcheck avancé
Améliorez votre endpoint de healthcheck pour vérifier les dépendances :
// app/api/health/route.ts
import { NextResponse } from 'next/server'
interface HealthStatus {
status: string
timestamp: string
uptime: number
checks: {
database?: string
memory?: {
used: number
total: number
}
}
}
export async function GET() {
const health: HealthStatus = {
status: 'ok',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
checks: {},
}
// Vérifier la mémoire
const memUsage = process.memoryUsage()
health.checks.memory = {
used: Math.round(memUsage.heapUsed / 1024 / 1024),
total: Math.round(memUsage.heapTotal / 1024 / 1024),
}
return NextResponse.json(health)
}Monitoring externe
Pour un monitoring complet, vous pouvez ajouter un service de surveillance comme accessoire :
accessories:
uptime-kuma:
image: louislam/uptime-kuma:1
host: votre-serveur.com
port: "3001:3001"
directories:
- data:/app/data
options:
network: kamalDépannage
Le healthcheck échoue
Si le déploiement échoue avec un timeout de healthcheck :
-
Vérifiez les logs du conteneur :
kamal app logs -
Testez le healthcheck localement :
docker build -t test-app . docker run -p 3000:3000 test-app curl http://localhost:3000/api/health -
Augmentez le timeout dans
deploy.yml:healthcheck: max_attempts: 60 interval: 5
Erreur de permission SSH
Si Kamal ne peut pas se connecter au serveur :
# Vérifiez que votre clé est chargée
ssh-add -l
# Ajoutez votre clé si nécessaire
ssh-add ~/.ssh/id_ed25519
# Testez la connexion
ssh root@votre-serveur.com "echo OK"Image trop volumineuse
Si le build ou le push prend trop de temps, optimisez votre Dockerfile :
- Utilisez le multi-stage build (déjà fait dans notre Dockerfile)
- Vérifiez votre
.dockerignore - Utilisez
node:20-alpineau lieu denode:20 - Activez le BuildKit pour le cache de layers :
export DOCKER_BUILDKIT=1
Problème de certificat SSL
Si le SSL ne fonctionne pas :
-
Vérifiez que le DNS pointe bien vers le serveur :
dig app.votre-domaine.com -
Vérifiez les logs du proxy :
kamal proxy logs -
Assurez-vous que les ports 80 et 443 sont ouverts sur le firewall du serveur.
Kamal vs les alternatives
| Fonctionnalité | Kamal 2 | Coolify | Vercel | Kubernetes |
|---|---|---|---|---|
| Zéro downtime | Oui | Oui | Oui | Oui |
| SSL automatique | Oui | Oui | Oui | Complexe |
| Rollback | 1 commande | Interface | Automatique | Complexe |
| Multi-serveur | Oui | Oui | Géré | Oui |
| Coût | VPS seulement | VPS seulement | Par usage | Élevé |
| Complexité | Faible | Faible | Très faible | Très élevée |
| Vendor lock-in | Aucun | Faible | Fort | Faible |
| Interface web | Non | Oui | Oui | Dashboard |
| CI/CD intégré | Non (via GH Actions) | Oui | Oui | Non |
Kamal se distingue par sa simplicité et son absence de vendor lock-in. Un seul fichier YAML, quelques commandes, et votre application est en production. Pas de dashboard web à gérer, pas de service cloud à payer — juste SSH, Docker et votre serveur.
Prochaines étapes
Maintenant que votre application est déployée avec Kamal 2, voici comment aller plus loin :
- Ajoutez un CDN comme Cloudflare devant votre serveur pour améliorer les performances
- Configurez des alertes avec Uptime Kuma ou Better Stack pour être notifié en cas de problème
- Mettez en place un environnement de staging avec un deuxième fichier de configuration (
config/deploy.staging.yml) - Automatisez les backups de votre base de données avec un cron job sur le serveur
- Explorez les hooks Kamal pour exécuter des scripts avant/après le déploiement (migrations de base de données, invalidation de cache, etc.)
Conclusion
Kamal 2 représente une approche rafraîchissante du déploiement : pas de complexité inutile, pas de vendor lock-in, pas de facture surprise. Avec un VPS à 5 euros par mois et quelques minutes de configuration, vous obtenez un pipeline de déploiement professionnel avec zéro downtime.
La philosophie de Kamal s'aligne parfaitement avec le mouvement croissant vers le self-hosting et la souveraineté numérique. Vous gardez le contrôle total de vos données et de votre infrastructure, tout en bénéficiant d'une expérience développeur fluide.
Que vous déployiez un side project, une startup ou une application d'entreprise, Kamal 2 vous offre la puissance nécessaire sans la complexité superflue. Et si demain vous devez changer de fournisseur de serveur, il suffit de modifier une ligne dans votre fichier de configuration.
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

Déployer une application Next.js avec Coolify v4 : guide complet du self-hosting
Apprenez à déployer vos applications Next.js sur votre propre serveur avec Coolify v4, une alternative open source à Vercel et Netlify. Installation, configuration, SSL automatique, CI/CD et monitoring inclus.

Deployer une Application Next.js avec Docker et CI/CD en Production
Apprenez a containeriser votre application Next.js avec Docker, configurer un pipeline CI/CD avec GitHub Actions, et deployer en production sur un VPS. Guide complet du developpement au deploiement automatise.

Docker Compose pour développeurs Full-Stack : Next.js, PostgreSQL et Redis
Apprenez à conteneuriser une application Next.js full-stack avec PostgreSQL et Redis en utilisant Docker Compose. Ce tutoriel pratique couvre l'orchestration multi-services, les workflows de développement, le rechargement à chaud, les health checks et les configurations prêtes pour la production.