Deploiement du site
Déployer un portfolio SvelteKit + SQLite sur VPS IONOS — le guide complet (et honnête)
Date : juillet 2026
Stack : SvelteKit · SQLite · Node.js · PM2 · Nginx · Let's Encrypt
Infra : IONOS VPS S+ · Ubuntu 26.04 LTS
Le contexte
Mon portfolio était prêt — SvelteKit côté front, SQLite pour la persistance, adapter-node pour le build. Il me fallait juste un hébergement pour le mettre en ligne. Et c'est là que j'ai fait mon premier faux pas.
L'erreur de départ : l'hébergement mutualisé
J'ai souscrit à l'offre Hébergement Web Plus d'IONOS. Quelques euros par mois, domaine inclus, simple à configurer. Sauf que… l'hébergement mutualisé, c'est du PHP/MySQL pour WordPress. Pas de process Node.js persistant, pas de SQLite, pas d'accès shell digne de ce nom. Aucun mutualisé grand public ne fait tourner Node correctement, peu importe le vendeur.
Résultat : résiliation du mutualisé (dans la fenêtre des 30 jours satisfait-ou-remboursé d'IONOS), et pivot vers un VPS Linux.
Piège à éviter : si ton domaine est inclus dans le pack mutualisé, détache-le avant de résilier. Sinon il part avec le contrat. La fenêtre de récupération est d'environ 30 jours, mais ça passe par le support IONOS, et certains TLD ont leurs propres règles.
Le choix du VPS
J'ai pris le VPS S+ d'IONOS : 2 vCores, 2 Go RAM, 90 Go NVMe. Pour un portfolio SvelteKit + SQLite avec du trafic recruteurs, c'est largement suffisant. Le M+ (4 Go RAM) n'a de sens que si tu prévois de faire tourner plusieurs projets dessus en parallèle.
OS choisi : Ubuntu 26.04 LTS. Meilleure doc pour la stack Node/PM2/Nginx, repos NodeSource testés en priorité dessus, communauté plus large que Debian sur ce type de déploiement.
Étape 0 : le firewall réseau IONOS (souvent oublié)
C'est le piège numéro un sur les VPS IONOS : il existe une couche de pare-feu au niveau du panel, en amont de la machine. Même si tu configures ufw dans Ubuntu, si le firewall réseau IONOS bloque le port 22, tu ne peux tout simplement pas te connecter en SSH.
Dans My IONOS → Serveurs & Cloud → ton VPS → Stratégies de pare-feu, ajoute trois règles entrantes TCP, source 0.0.0.0/0 :
- Port 22 (SSH)
- Port 80 (HTTP)
- Port 443 (HTTPS)
Fais ça avant de te connecter la première fois. Ça évite les timeouts inexpliqués.
Étape 1 : premier accès et création du user
ssh root@TON_IP
adduser cedric && usermod -aG sudo cedric && exit
Tu remplis le mot de passe pour cedric, Entrée pour tout le reste.
Étape 2 : clé SSH (depuis ta machine locale)
ssh-keygen -t ed25519 -C "cedric-vps"
ssh-copy-id cedric@TON_IP
Teste dans un nouveau terminal :
ssh cedric@TON_IP
Doit se connecter sans mot de passe. Si c'est le cas, on peut verrouiller le serveur.
Étape 3 : verrouillage SSH
sudo sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/; s/^#\?PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config && sudo systemctl restart ssh
Règle d'or : toujours confirmer la connexion dans un nouveau terminal avant de fermer la session en cours. Si tu casses SSH sans avoir une session de secours ouverte, tu es dehors.
Étape 4 : firewall OS + fail2ban + Nginx
sudo ufw allow OpenSSH && sudo ufw allow 80/tcp && sudo ufw allow 443/tcp && sudo ufw --force enable
sudo apt update && sudo apt install -y fail2ban nginx certbot python3-certbot-nginx && sudo systemctl enable --now fail2ban nginx
fail2ban bannit automatiquement les IPs qui tentent du brute-force SSH. À laisser tourner en fond sans y toucher.
Étape 5 : Node.js via NodeSource + PM2
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo bash - && sudo apt install -y nodejs && sudo npm install -g pm2
On installe Node via NodeSource plutôt que les repos Ubuntu officiels — les versions y sont beaucoup plus récentes et mieux maintenues.
PM2 est le process manager qui garde l'app Node en vie, la relance en cas de crash, et la redémarre au boot du serveur.
Étape 6 : deploy key GitHub pour le repo privé
On ne met jamais sa clé SSH personnelle sur un serveur. On génère une deploy key dédiée, en lecture seule sur le repo concerné.
ssh-keygen -t ed25519 -C "vps-deploy" -f ~/.ssh/github_deploy -N "" && cat ~/.ssh/github_deploy.pub
La clé publique affichée va dans GitHub → repo → Settings → Deploy keys → Add deploy key, sans write access.
On indique ensuite à SSH d'utiliser cette clé pour GitHub :
printf 'Host github.com\n IdentityFile ~/.ssh/github_deploy\n IdentitiesOnly yes\n' >> ~/.ssh/config && chmod 600 ~/.ssh/config
Test de connexion :
ssh -T git@github.com
# Hi CedricCT/portefolio! You've successfully authenticated...
Étape 7 : clone, variables d'environnement et build
cd ~ && git clone git@github.com:CedricCT/portefolio.git app && cd app
Le .env n'est jamais commité sur GitHub (normal). Il faut le créer sur le serveur :
echo "ADMIN_PASSWORD=un_mot_de_passe_solide" > .env
Une particularité de npm 11.x : les scripts d'installation des dépendances sont bloqués par défaut pour des raisons de sécurité supply-chain. Il faut les approuver explicitement avant d'installer :
npm approve-scripts --all && npm install && npm run build
Le build génère un dossier build/ avec un index.js que Node peut exécuter directement.
Étape 8 : lancement avec PM2
cat > ~/app/ecosystem.config.cjs << 'EOF'
module.exports = { apps: [{ name: 'portfolio', script: './build/index.js', env: { PORT: 3000, ORIGIN: 'https://cedriccubat.dev', NODE_ENV: 'production' } }] };
EOF
pm2 start ~/app/ecosystem.config.cjs && pm2 save && pm2 startup
pm2 startup affiche une ligne à copier-coller (commence par sudo env PATH=...) — exécute-la pour que PM2 redémarre automatiquement au reboot.
Pourquoi
ORIGINest important : SvelteKit l'utilise pour valider les form actions et la protection CSRF en production. Sans ça, tes formulaires peuvent se comporter bizarrement.
Étape 9 : Nginx en reverse proxy
sudo bash -c 'cat > /etc/nginx/sites-available/portfolio << EOF
server {
listen 80;
server_name cedriccubat.dev www.cedriccubat.dev;
location / {
proxy_pass http://localhost:3000;
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;
}
}
EOF'
sudo ln -s /etc/nginx/sites-available/portfolio /etc/nginx/sites-enabled/ && sudo rm -f /etc/nginx/sites-enabled/default && sudo nginx -t && sudo systemctl restart nginx
Nginx reçoit les requêtes publiques sur le port 80/443 et les transfère à l'app Node qui écoute en local sur le port 3000. L'app n'est jamais exposée directement à internet.
Étape 10 : HTTPS avec Let's Encrypt
D'abord, vérifier que le DNS a bien propagé (le TTL était à 1h chez IONOS) :
curl -I http://cedriccubat.dev
# HTTP/1.1 200 OK — c'est bon
Puis Certbot s'occupe de tout — génération du certificat, modification de la config Nginx, redirection HTTP → HTTPS automatique :
sudo certbot --nginx -d cedriccubat.dev -d www.cedriccubat.dev --non-interactive --agree-tos -m ton@email.fr
Le renouvellement du certificat est automatique (timer systemd installé par défaut).
Étape 11 : script de déploiement
Pour les mises à jour futures, un script simple à la racine du home :
cat > ~/deploy.sh << 'EOF'
#!/bin/bash
cd ~/app
git pull
npm install
npm run build
pm2 restart portfolio
EOF
chmod +x ~/deploy.sh
À chaque push sur le repo, un simple ./deploy.sh suffit.
Ce que j'ai appris (les vraies leçons)
Le mutualisé c'est pas fait pour Node. C'est évident en théorie, moins en pratique quand t'as juste envie de payer 2€/mois et de passer à autre chose.
Le firewall réseau IONOS est séparé de ufw. Si tu n'ouvres pas les ports dans le panel avant de configurer l'OS, tu vas passer une heure à chercher pourquoi SSH timeout.
Ne jamais désactiver l'auth par mot de passe SSH avant d'avoir confirmé que la clé fonctionne. Deux terminaux ouverts, pas de précipitation.
Les versions dans package.json ça compte. Un typo ^0.0.30 au lieu de ^2.x.x sur @sveltejs/kit fait un build silencieusement cassé — les erreurs rollup/vite qui suivent sont trompeuses.
npm 11.x bloque les install scripts par défaut. npm approve-scripts --all avant le premier npm install sur un projet frais avec esbuild/Vite dedans.
Stack finale
| Couche | Outil |
|---|---|
| Framework | SvelteKit 2.x avec adapter-node |
| Base de données | SQLite (fichier sur disque NVMe du VPS) |
| Runtime | Node.js 24 LTS |
| Process manager | PM2 7.x |
| Reverse proxy | Nginx 1.28 |
| SSL | Let's Encrypt via Certbot |
| OS | Ubuntu 26.04 LTS |
| Hébergement | IONOS VPS S+ (2 vCores / 2 Go RAM / 90 Go NVMe) |
Article publié sur Dans ton terminal — le blog où je documente mon parcours vers la cybersécurité, un terminal à la fois.