ITSkillsCenter
Développement Web

Déployer Bun en production : systemd ou PM2 (guide 2026)

6 دقائق للقراءة

Vous avez développé une application avec Bun, elle tourne parfaitement en local, et maintenant il faut la mettre en production sur un VPS Linux. Deux approches dominent en 2026 : systemd (natif Linux, robuste, intégré aux logs et au monitoring système) ou PM2 (process manager Node.js historique, compatible avec Bun). Voici le guide pratique pour les deux, avec une recommandation claire selon le contexte.

Ce tutoriel s’inscrit dans notre série Bun. Pour les bases (installation, performance, écosystème), voir notre guide complet Bun en production 2026. Pour une alternative basée Coolify, voir le guide Coolify self-hosted.

systemd ou PM2 — quelle approche

  • systemd est le système d’init standard de Linux moderne (Ubuntu, Debian, RHEL, Arch). Il gère natifvement les processus, journaux, redémarrage automatique, dépendances entre services, isolation. C’est l’approche la plus « Unix » et la plus légère.
  • PM2 est un process manager écrit en Node.js, conçu initialement pour Node.js, qui apporte clustering, monitoring CLI, log rotation, et un dashboard web optionnel. Compatible avec Bun via l’option --interpreter.

Recommandation : systemd pour les nouveaux projets et les VPS simples ; PM2 si vous gérez déjà une infra avec PM2 ou si vous voulez du clustering automatique sur plusieurs cœurs CPU sans modifier le code.

Prérequis

  • VPS Linux (Ubuntu 22.04+, Debian 12+) avec accès root SSH
  • Bun installé globalement (/usr/local/bin/bun)
  • Une application Bun fonctionnelle (testée avec bun run start)
  • Niveau attendu : intermédiaire
  • Temps : 30 minutes

Approche 1 — systemd

Étape 1 — Préparer le serveur

# Créer un utilisateur dédié (sans shell)
useradd -r -s /bin/false -m -d /opt/myapp myapp

# Cloner / déployer le code
mkdir -p /opt/myapp/app
chown -R myapp:myapp /opt/myapp
cd /opt/myapp/app
git clone https://github.com/votre-org/myapp.git .

# Installer les dépendances en tant que user dédié
sudo -u myapp bun install --production
sudo -u myapp bun run build  # si vous avez une étape de build

# Installer Bun globalement si pas déjà fait
curl -fsSL https://bun.sh/install | bash
mv ~/.bun/bin/bun /usr/local/bin/bun
chmod +x /usr/local/bin/bun

Étape 2 — Créer le service systemd

# /etc/systemd/system/myapp.service

[Unit]
Description=Mon API Bun
After=network.target postgresql.service
Requires=network.target

[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp/app
EnvironmentFile=/opt/myapp/app/.env
ExecStart=/usr/local/bin/bun run start
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapp

# Sécurité - hardening
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/myapp/app/uploads
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
RestrictAddressFamilies=AF_INET AF_INET6
RestrictNamespaces=true
RestrictRealtime=true
LockPersonality=true
MemoryDenyWriteExecute=true

# Limites resources
MemoryMax=512M
CPUQuota=80%

[Install]
WantedBy=multi-user.target

Le bloc « hardening » est important : il restreint ce que le processus peut faire au cas où il serait compromis. Les valeurs ci-dessus conviennent à une API qui n’a besoin que de réseau et d’un répertoire d’uploads.

Étape 3 — Activer et lancer

systemctl daemon-reload
systemctl enable myapp
systemctl start myapp

# Vérifier le statut
systemctl status myapp

# Voir les logs en direct
journalctl -u myapp -f

# Logs des dernières 100 lignes
journalctl -u myapp -n 100 --no-pager

# Redémarrer après une mise à jour
systemctl restart myapp

Étape 4 — Reverse proxy avec Caddy

Votre app Bun écoute typiquement sur 127.0.0.1:3000. Mettez Caddy ou Nginx devant pour HTTPS automatique. Avec Caddy, c’est trois lignes :

# /etc/caddy/Caddyfile
api.exemple.sn {
    reverse_proxy 127.0.0.1:3000
    encode gzip zstd
}

Caddy obtient automatiquement un certificat Let’s Encrypt et redirige HTTP vers HTTPS. systemctl reload caddy.

Étape 5 — Déploiements zero-downtime

Pour mettre à jour sans interruption visible, deux techniques avec systemd :

  • Symlinks atomiques : déployer dans /opt/myapp/releases/v123/, faire un ln -sfn de /opt/myapp/current vers la nouvelle release, puis systemctl restart myapp. Le redémarrage prend ~1 seconde grâce à Bun.
  • Socket activation : systemd peut tenir le socket TCP ouvert pendant qu’un nouvel processus prend le relais. Plus complexe, surtout utile pour des services à très haute disponibilité.

Pour la plupart des PME, un simple restart Bun (300-800 ms) est invisible si Caddy fait du failover. Sinon, deux instances Bun sur ports différents avec un round-robin Caddy fait l’affaire.

Approche 2 — PM2

Étape 1 — Installer PM2

# PM2 lui-même est en Node.js, donc il faut Node OU bun pour l'installer
npm install -g pm2

# Ou via Bun
bun install -g pm2

Étape 2 — Créer le fichier ecosystem

// ecosystem.config.js
module.exports = {
  apps: [{
    name: "myapp",
    script: "./src/index.ts",
    interpreter: "/usr/local/bin/bun",
    interpreter_args: "run",
    instances: 2,                    // ou "max" pour 1 par CPU
    exec_mode: "cluster",            // load balancing automatique
    env: {
      NODE_ENV: "production",
      PORT: 3000,
    },
    max_memory_restart: "500M",
    error_file: "./logs/err.log",
    out_file: "./logs/out.log",
    log_date_format: "YYYY-MM-DD HH:mm:ss",
    merge_logs: true,
    autorestart: true,
    max_restarts: 10,
    min_uptime: "10s",
  }],
};

Étape 3 — Lancer et persister

# Lancer
pm2 start ecosystem.config.js

# Status
pm2 status
pm2 logs myapp

# Persister entre reboots (génère un service systemd PM2)
pm2 save
pm2 startup systemd -u myuser --hp /home/myuser
# Suivre l'instruction sudo qui s'affiche

# Reload zero-downtime
pm2 reload myapp

# Restart classique
pm2 restart myapp

# Stop / delete
pm2 stop myapp
pm2 delete myapp

Étape 4 — Logs et monitoring

PM2 inclut un dashboard CLI via pm2 monit, des métriques par app, et la rotation automatique des logs avec :

pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 30

Comparaison concrète

CritèresystemdPM2
Setup initial5-15 min (fichier .service)5 min (npm install + ecosystem)
Logsjournalctl natif, intégréFichiers + pm2 logs
Cluster modeManuel (multiples services)Natif via instances + cluster
Hardening sécuritéTrès avancé (NoNewPrivileges, ProtectSystem…)Limité
Reload zero-downtimeManuel (symlinks ou socket activation)pm2 reload
Dépendance externeAucune (natif Linux)Node.js ou Bun pour PM2 lui-même
Empreinte mémoire~0 (kernel)~50 Mo pour PM2 daemon

Quel choix pour quel cas

VPS petit (1-2 Go RAM), une seule app → systemd. Plus léger, zero overhead.

Plusieurs apps Bun sur le même serveur, besoin de monitoring CLI → PM2. La vue d’ensemble pm2 status est pratique.

Besoin de cluster mode pour saturer un CPU multi-core → PM2 cluster, ou systemd avec plusieurs instances numérotées (myapp@1, myapp@2…) derrière un load balancer.

Sécurité critique, hardening fort → systemd avec les directives ProtectSystem, NoNewPrivileges, etc.

Adaptation Afrique de l’Ouest

Pour les VPS modestes des freelances et PME ouest-africaines (Hetzner CX22 4 Go RAM), systemd est le choix gagnant : il économise 50 Mo de RAM par rapport à PM2, ce qui permet d’héberger une app supplémentaire. La courbe d’apprentissage est légèrement plus haute mais ça vaut le coup pour le contrôle fin.

Erreurs fréquentes

ErreurCauseSolution
systemd: status=203/EXECChemin Bun incorrect dans ExecStartwhich bun et adapter le chemin absolu
Permission denied sur uploadsProtectSystem trop strictAjouter ReadWritePaths=/path/to/uploads
PM2 OOMmemory_restart trop basAugmenter max_memory_restart selon profil app
App ne démarre plus après reboot (PM2)pm2 save oubliéRefaire pm2 save + pm2 startup
Logs PM2 saturent le disquePas de rotationInstaller pm2-logrotate

Pour aller plus loin

Besoin d'un site web ?

Confiez-nous la Création de Votre Site Web

Site vitrine, e-commerce ou application web — nous transformons votre vision en réalité digitale. Accompagnement personnalisé de A à Z.

À partir de 250.000 FCFA
Parlons de Votre Projet
Publicité