Mettez en production Angular Universal sur Linux : build SSR, cluster PM2, reverse proxy Nginx, cache statique, logs rotatifs et zero-downtime deploy avec pm2 reload.
Pourquoi deployer Angular en SSR
Une application Angular classique fonctionne en mode SPA (Single Page Application). Le navigateur recoit un fichier HTML quasi vide, telecharge le bundle JavaScript, puis Angular construit l'interface cote client. Ce modele est efficace, mais il a deux limites visibles en production : le First Contentful Paint reste long sur reseau lent et les robots d'indexation interpretent moins bien le contenu genere apres l'execution du JS.
Le rendu cote serveur (SSR), via Angular Universal, change cette equation. Chaque requete est traitee par un processus Node.js qui assemble la page complete, l'envoie au navigateur, puis Angular reprend la main cote client (hydration). Le visiteur voit un contenu structure quasi instantanement et les moteurs de recherche disposent d'un HTML complet a indexer.
Cote infrastructure, cela transforme une simple SPA en application avec runtime permanent. Vous ne servez plus uniquement des fichiers statiques : il faut un serveur Linux capable de faire tourner Node.js de maniere stable, et un reverse proxy comme Nginx pour absorber le trafic HTTPS, gerer les fichiers statiques et faire tampon devant le processus SSR.
Cet article reprend le meme cadre que Nginx en reverse proxy pour APIs et l'automatisation Linux, mais applique au cas concret du rendu serveur Angular.
Preparer le serveur Linux
Avant de toucher Angular, le serveur doit etre propre, a jour et minimal. Sur Ubuntu 24.04 LTS, commencez par mettre a jour les paquets et creer un utilisateur dedie a l'application. Ne deployez jamais une application Node.js sous root.
# Connexion sudo et mise a jour systeme
sudo apt update && sudo apt upgrade -y
# Installation des dependances de base
sudo apt install -y curl git build-essential ufw
# Creation d'un utilisateur dedie a l'app
sudo adduser --disabled-password --gecos "" angularapp
sudo usermod -aG sudo angularapp
Installez ensuite Node.js via le PPA officiel NodeSource. Pour Angular 18+, utilisez Node.js 20 LTS minimum. Evitez le paquet Debian/Ubuntu par defaut, souvent retarde de plusieurs versions majeures.
# Installation Node.js 20 LTS via NodeSource
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
# Verification
node -v # v20.x.x
npm -v # 10.x.x
Activez ensuite le firewall UFW pour limiter les ports exposes. Seuls SSH (22), HTTP (80) et HTTPS (443) doivent etre accessibles depuis l'exterieur. Le port Node (4000) doit rester strictement local.
# Configuration UFW
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
# Verification
sudo ufw status verbose
Construire l'application Angular SSR
Cote projet Angular, activez SSR au moment de la creation ou via la commande dediee. Depuis Angular 17, Universal est integre directement au CLI sous la forme du nouveau builder application.
# Nouveau projet avec SSR active
ng new mon-app --ssr --style=scss
# Sur un projet existant
cd mon-app
ng add @angular/ssr
Cette commande genere un fichier server.ts a la racine, ajoute un script serve:ssr dans package.json et configure le builder approprie. Le build produit deux dossiers dans dist/ : browser/ (fichiers statiques cote client) et server/ (entry point Node).
# Build production
ng build
# Structure typique du resultat
# dist/mon-app/
# ├── browser/ ← assets statiques (servi par Nginx)
# ├── server/ ← bundle Node SSR (servi par PM2)
# └── prerendered-routes.json
Sur le serveur, clonez le projet, installez les dependances et lancez le build. Faites toujours le build sur la machine de production (ou un environnement strictement equivalent) pour eviter les ecarts de versions Node.
# Sur le serveur, en tant qu'utilisateur angularapp
sudo -iu angularapp
cd /home/angularapp
git clone https://github.com/votre-org/mon-app.git
cd mon-app
# Installation des dependances
npm ci
# Build production
npm run build
# Test rapide du serveur SSR
node dist/mon-app/server/server.mjs
Si le serveur ecoute correctement sur le port 4000, vous pouvez quitter et passer a PM2.
Installer et configurer PM2
PM2 est un gestionnaire de processus Node.js qui apporte plusieurs choses essentielles : redemarrage automatique sur crash, mode cluster pour exploiter plusieurs cores, gestion des logs avec rotation, et integration native avec systemd pour survivre aux reboots.
# Installation globale
sudo npm install -g pm2
# Verification
pm2 --version
Plutot que de lancer PM2 a la main, declarez l'application dans un fichier ecosystem.config.js. Ce fichier devient la source unique de verite pour les variables d'environnement, le mode d'execution, le nombre d'instances et les chemins de logs.
// /home/angularapp/mon-app/ecosystem.config.js
module.exports = {
apps: [{
name: 'angular-ssr',
script: './dist/mon-app/server/server.mjs',
instances: 'max', // Une instance par CPU
exec_mode: 'cluster', // Mode cluster pour le load balancing
env: {
NODE_ENV: 'production',
PORT: 4000
},
max_memory_restart: '500M', // Restart si depasse 500 Mo
error_file: '/var/log/angular-ssr/error.log',
out_file: '/var/log/angular-ssr/out.log',
time: true // Timestamp dans les logs
}]
};
Creez le dossier de logs et lancez l'application :
# Creation du dossier de logs
sudo mkdir -p /var/log/angular-ssr
sudo chown angularapp:angularapp /var/log/angular-ssr
# Demarrage
pm2 start ecosystem.config.js
# Verification de l'etat
pm2 status
pm2 logs angular-ssr --lines 50
Pour que PM2 redemarre automatiquement au boot du serveur, generez le script systemd :
# Generation du script de demarrage
pm2 startup systemd -u angularapp --hp /home/angularapp
# La commande affiche une ligne sudo a copier-coller, puis :
pm2 save
Configurer Nginx en reverse proxy
Nginx joue trois roles : terminer le TLS, servir directement les fichiers statiques (plus rapide que Node) et faire passer les requetes dynamiques au cluster PM2. Cette separation est ce qui permet d'absorber un trafic important sans saturer Node.js.
# Installation
sudo apt install -y nginx
# Verification
sudo systemctl status nginx
Creez un virtual host dedie a l'application :
# /etc/nginx/sites-available/angular-ssr
upstream angular_ssr {
server 127.0.0.1:4000;
keepalive 64;
}
server {
listen 80;
server_name votre-domaine.com www.votre-domaine.com;
# Logs dedies
access_log /var/log/nginx/angular-ssr.access.log;
error_log /var/log/nginx/angular-ssr.error.log;
# Fichiers statiques servis directement
root /home/angularapp/mon-app/dist/mon-app/browser;
# Cache long pour les assets versionnes
location ~* \.(js|css|woff2|webp|svg|jpg|png)$ {
expires 1y;
add_header Cache-Control "public, immutable";
try_files $uri @ssr;
}
# Tout le reste passe au SSR
location / {
try_files $uri @ssr;
}
location @ssr {
proxy_pass http://angular_ssr;
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_cache_bypass $http_upgrade;
proxy_read_timeout 30s;
}
}
Activez le site et rechargez Nginx :
# Activation du site
sudo ln -s /etc/nginx/sites-available/angular-ssr /etc/nginx/sites-enabled/
# Test de la configuration
sudo nginx -t
# Reload sans coupure
sudo systemctl reload nginx
Visitez le domaine : Nginx doit servir la page generee par SSR. Pour aller plus loin sur la securisation Nginx, l'article Nginx : configuration et securisation couvre les headers HTTPS et la durcissement.
Optimiser cache et fichiers statiques
Une app Angular SSR genere beaucoup de fichiers statiques avec des hash de contenu (`main-XXXXX.js`, `styles-XXXXX.css`). Ces fichiers ne changent jamais une fois deployes : c'est le cas typique d'usage du header Cache-Control: immutable.
# Bonus : compression Brotli/Gzip dans Nginx
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
gzip_min_length 1000;
# Si module brotli disponible
brotli on;
brotli_types text/plain text/css application/json application/javascript;
Pour le HTML genere par SSR (qui peut contenir des donnees personnalisees), ajoutez un cache court via Nginx pour absorber les pics :
# Dans le bloc location @ssr
proxy_cache_path /var/cache/nginx/ssr levels=1:2 keys_zone=ssr_cache:10m max_size=500m inactive=5m;
location @ssr {
proxy_pass http://angular_ssr;
proxy_cache ssr_cache;
proxy_cache_valid 200 1m;
proxy_cache_use_stale error timeout updating http_500;
proxy_cache_lock on;
add_header X-Cache-Status $upstream_cache_status;
# ... headers proxy_set_header habituels
}
Verifiez le statut cache via curl : la valeur de X-Cache-Status doit passer de MISS a HIT apres la premiere requete.
curl -I https://votre-domaine.com/
# X-Cache-Status: MISS
curl -I https://votre-domaine.com/
# X-Cache-Status: HIT
Monitorer PM2 et les logs
Une fois l'application en production, le monitoring devient prioritaire. PM2 propose plusieurs outils integres tres utiles avant meme d'installer un APM externe.
# Dashboard temps reel (CPU, RAM, requetes)
pm2 monit
# Liste detaillee
pm2 list
# Logs en streaming
pm2 logs angular-ssr
# Logs derniere heure uniquement
pm2 logs angular-ssr --lines 200 --raw
Pour la rotation automatique des logs (sans quoi le disque se remplit en quelques semaines) :
# Installation du module
pm2 install pm2-logrotate
# Configuration
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 14
pm2 set pm2-logrotate:compress true
Cote Nginx, surveillez les codes d'erreur 5xx qui indiquent un probleme cote SSR :
# Nombre de 5xx aujourd'hui
grep "$(date '+%d/%b/%Y')" /var/log/nginx/angular-ssr.access.log | awk '{print $9}' | grep -c "^5"
# Top 10 des URLs en erreur
awk '$9 ~ /^5/ {print $7}' /var/log/nginx/angular-ssr.access.log | sort | uniq -c | sort -rn | head
Pour un suivi durable, l'article monitoring serveur Linux detaille comment combiner journalctl et logs applicatifs.
Deploiement zero-downtime
Avec PM2 en mode cluster, vous pouvez deployer une nouvelle version sans coupure. Le secret est la commande pm2 reload : elle redemarre les workers un par un, ce qui maintient toujours au moins une instance disponible pendant la transition.
#!/usr/bin/env bash
# /home/angularapp/deploy.sh
set -euo pipefail
APP_DIR="/home/angularapp/mon-app"
LOG_FILE="/var/log/angular-ssr/deploy.log"
log() {
printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$1" | tee -a "$LOG_FILE"
}
cd "$APP_DIR"
log "Pull du code"
git pull --ff-only
log "Installation des dependances"
npm ci --omit=dev=false
log "Build production"
npm run build
log "Reload PM2 (zero-downtime)"
pm2 reload angular-ssr
log "Deploiement termine"
Rendez le script executable et associez-le a un alias ou a un webhook GitHub :
chmod +x /home/angularapp/deploy.sh
# Test
/home/angularapp/deploy.sh
Pour aller plus loin, branchez ce script sur un pipeline CI/CD (voir l'article dedie sur GitHub Actions). Le principe : push sur main → action sur GitHub → execution distante via SSH du script `deploy.sh`.
Conclusion
Deployer Angular SSR sur un serveur Linux n'est pas plus complique qu'une SPA classique, a condition d'avoir le bon decoupage : Nginx en frontale pour le TLS et les statiques, PM2 en cluster pour le rendu Node, et une procedure de build/deploy reproductible.
Cette architecture coche toutes les cases d'une production solide : performance grace au cache Nginx et au mode cluster PM2, resilience grace aux redemarrages automatiques, observabilite grace aux logs centralises et zero-downtime grace au `pm2 reload`. Elle reste simple a faire evoluer : passer de 1 a 4 instances PM2 ne demande qu'une ligne de config.
Si vous demarrez aujourd'hui, suivez l'ordre de cet article : preparez le serveur, faites un build local pour valider, puis ajoutez Nginx une fois que PM2 sert correctement la page sur le port 4000. C'est la sequence la plus rapide pour atteindre une production stable.