En une soirée, j’ai migré
mon portfolio depuis Scalingo (25€/mois) vers mon VPS Hostinger déjà payé, en utilisant
Kamal — l’outil de déploiement officiel intégré à Rails 8. Voici
comment,
pourquoi, et ce que j’en retiens.
Pourquoi migrer ?
Mon portfolio tournait sur
Scalingo depuis plusieurs mois. Le service est excellent : déploiement par git push, scaling automatique, monitoring intégré. Mais pour une app sans utilisateur, avec quelques visiteurs par jour, le rapport coût/usage devenait disproportionné :
25€ par mois pour héberger un portfolio, soit
300€ par an.
En parallèle, j’avais un VPS KVM 4 chez Hostinger payé deux ans d’avance. La décision était évidente : rapatrier l’app sur ce VPS et économiser le coût Scalingo pendant toute l’année restante.
Restait à choisir comment déployer.
Pourquoi Kamal plutôt qu’autre chose ?
J’ai considéré trois options :
- Docker Compose + Traefik manuel : c’est ce que j’avais bricolé sur le VPS auparavant. Ça marche, mais chaque mise à jour devient pénible — il faut SSH, pull, rebuild, restart, en faisant attention aux certs Let’s Encrypt.
- Coolify : une plateforme PaaS auto-hébergée façon Heroku-like. Très bien pour gérer plusieurs projets via une UI web, mais overkill pour un seul portfolio, et ça consomme 2-3 Go de RAM rien que pour la plateforme elle-même.
- Kamal 2 : l’outil de déploiement officiel intégré à Rails 8 depuis 2024, créé par 37signals (Basecamp, HEY). Pas d’UI, juste une CLI. Build, push, deploy zero-downtime, rollback, certs Let’s Encrypt automatiques.
J’ai choisi Kamal pour trois raisons :
- C’est intégré à Rails 8 nativement
- C’est simple par design (pas de couche superflue).
- C’est la convention de l’écosystème Rails aujourd’hui. Apprendre Kamal sert pour tous mes futurs projets.
Le setup en bref
Kamal fonctionne sur un principe minimaliste : tu décris ton infra dans un fichier config/deploy.yml, tu mets tes secrets dans .kamal/secrets, et tu lances kamal deploy depuis ta machine locale. Kamal SSH sur le serveur, build l’image Docker (en local ou à distance), la push vers un registry, puis orchestre un rolling deploy.
L’architecture finale ressemble à ça :
- VPS Hostinger (Ubuntu 24.04) — l’hôte.
- kamal-proxy — reverse proxy minimaliste qui occupe les ports 80/443 et gère les certificats Let’s Encrypt automatiquement.
- Container Rails 8 — l’app, sur le port interne 3000.
- Container Postgres 17 — la base, accessible uniquement depuis le réseau Docker interne.
- GHCR (GitHub Container Registry) — pour stocker les images Docker, gratuit et illimité avec un compte GitHub.
Le deploy.yml tient en 50 lignes et déclare tout : les serveurs, le builder, le proxy, les variables d’environnement, l’accessoire Postgres.
Les étapes clés de la migration
1. Audit et nettoyage du VPS
2. Sauvegarde du dump Postgres depuis Scalingo
Scalingo permet de télécharger un backup Postgres depuis l’interface web — un fichier .tar.gz contenant un dump au format custom pg_dump -Fc. Je l’ai téléchargé en local avant toute manipulation, et gardé une copie séparée.
Règle d’or : avoir au moins deux copies du dump avant de toucher à quoi que ce soit.
3. Configuration de Kamal
Le deploy.yml définit toute l’infrastructure. Voici une version simplifiée pour mon cas :
text
Copierservice:
image:
servers:
web:
- IP_VPS
proxy:
ssl: true
hosts:
- nomdedomaine.fr
- www.nomdedomaine.fr
app_port: 3000
registry:
server: ghcr.io
username: GitHub profile
password:
- KAMAL_REGISTRY_PASSWORD
builder:
arch: amd64
remote: ssh://root@IP_VPS
env:
secret:
- RAILS_MASTER_KEY
- POSTGRES_PASSWORD
- DATABASE_URL
clear:
DB_HOST: user-db
POSTGRES_USER: user
POSTGRES_DB: user_production
DOMAIN: nomdedomaine.fr
RAILS_LOG_TO_STDOUT: "true"
RAILS_SERVE_STATIC_FILES: "true"
ssh:
user: user
accessories:
db:
image: postgres:17
host: IP_VPS
port: "127.0.0.1:5432:5432"
env:
clear:
POSTGRES_USER: user
POSTGRES_DB: user_production
secret:
- POSTGRES_PASSWORD
directories:
- data:/var/lib/postgresql/data
À côté, le fichier .kamal/secrets contient les valeurs sensibles, hors du repo Git :
text
CopierKAMAL_REGISTRY_PASSWORD=ghp_xxxxx
RAILS_MASTER_KEY=$(cat config/master.key)
POSTGRES_PASSWORD=monMotDePasseGeneré
DATABASE_URL=postgres://user:monMotDePasseGeneré@user-db:5432/user_production
Le builder remote est un détail important : sans Docker Desktop installé localement, Kamal délègue le build au VPS lui-même via SSH. Ça évite d’avoir Docker en permanence sur ma machine, et le build profite des 4 vCPU du serveur.
4. Bascule DNS et activation SSL
Le résultat
Après quelques heures de travail, le portfolio tourne sur le VPS avec :
- HTTPS valide via Let’s Encrypt, renouvellement automatique.
- ~10 ms de temps de réponse Rails, nettement mieux qu’avant.
- Déploiement en une commande : git push puis kamal deploy, ~2 minutes, zero-downtime.
- Rollback en 30 secondes avec kamal app rollback.
- ~300€/an d’économies.
Ce que je retiens
Kamal n’est pas magique. C’est essentiellement une CLI bien pensée autour de Docker et SSH, qui automatise ce qu’on ferait à la main avec Compose. Mais cette automatisation change tout : les déploiements deviennent boring, dans le bon sens. Plus de stress, plus de scripts maison, plus d’oubli.
Pour une app personnelle ou un side-project Rails 8, la combinaison VPS + Kamal est aujourd’hui imbattable en rapport simplicité/contrôle/coût.