Introduction
Fly.io est une plateforme de déploiement serverless qui excelle dans les déploiements globaux à faible latence grâce à son réseau edge étendu sur 35+ régions. Contrairement à Vercel ou Heroku, Fly.io utilise des conteneurs Firecracker pour une scalabilité horizontale native et un pricing au volume CPU/mémoire. En 2026, avec l'essor des apps full-stack et des microservices, maîtriser Fly.io permet de réduire les coûts de 40-60% par rapport aux alternatives cloud traditionnelles tout en offrant des performances mondiales.
Ce tutoriel intermédiaire vous guide pour déployer une API Node.js Express : de l'installation de flyctl à la mise en production scalable. Vous apprendrez à configurer un Dockerfile optimisé, un fly.toml personnalisé, gérer les secrets et scaler automatiquement. À la fin, votre app sera live sur un domaine .fly.dev avec HTTPS gratuit, prête pour la prod. Idéal pour les devs qui veulent bookmarker un guide actionnable et sans fluff.
Prérequis
- Node.js 20+ installé
- Docker Desktop (pour builds locaux)
- Compte GitHub gratuit (optionnel pour CI/CD)
- Connaissances de base en Docker et CLI
- Terminal Unix-like (WSL sur Windows)
Installer flyctl
curl -L https://fly.io/install.sh | sh
# Vérifier l'installation
fly version
fly auth signupCe script one-liner installe flyctl, la CLI officielle de Fly.io. La commande fly auth signup crée un compte gratuit (avec 3$ de crédit initial). Évitez les proxies d'entreprise qui bloquent curl ; utilisez brew install flyctl sur macOS comme fallback.
Créer l'application Node.js de base
Avant de déployer, initialisons une API Express minimaliste. Imaginez-la comme une maison : le serveur est la fondation, les routes les pièces. Nous créons un projet complet avec health-check pour les probes Kubernetes-like de Fly.io.
Initialiser le projet Express
mkdir flyio-node-app && cd flyio-node-app
npm init -y
npm install express
npm install -D nodemon typescript @types/express @types/node tsxOn crée un projet Node avec Express pour l'API et TypeScript pour la robustesse. nodemon et tsx facilitent le dev local. Testez localement avec npx tsx server.ts avant de passer au Docker.
Écrire le serveur Express
import express from 'express';
const app = express();
const PORT = process.env.PORT || 8080;
app.use(express.json());
app.get('/health', (req, res) => {
res.status(200).json({ status: 'ok', timestamp: new Date().toISOString() });
});
app.get('/api/users', (req, res) => {
res.json([{ id: 1, name: 'John Doe' }, { id: 2, name: 'Jane Smith' }]);
});
app.post('/api/users', (req, res) => {
const user = req.body;
res.status(201).json({ id: 3, ...user });
});
const server = app.listen(PORT, '0.0.0.0', () => {
console.log(`Server running on port ${PORT}`);
});
process.on('SIGTERM', () => {
console.log('SIGTERM received, shutting down gracefully');
server.close(() => {
console.log('Process terminated');
});
});Ce serveur TypeScript complet expose /health pour les checks Fly.io, gère users CRUD basique et écoute sur 0.0.0.0 (requis pour conteneurs). Le handler SIGTERM assure un shutdown gracieux, évitant les pertes de connexions en scaling.
Containeriser avec Dockerfile
Fly.io build et déploie via Docker. Notre Dockerfile est multi-stage pour un image légère (<100MB), comme un expresso concentré : build rapide, runtime minimal.
Créer le Dockerfile optimisé
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json tsconfig.json ./
RUN npm ci --only=production && npm cache clean --force
COPY . .
RUN npm run build || true # Si build step existe
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/server.ts ./server.ts
EXPOSE 8080
CMD ["npx", "tsx", "server.ts"]Multi-stage réduit la taille : builder installe deps, runner copie seulement prod. Utilisez Alpine pour légèreté. Ajoutez tsconfig.json basique si absent : {"compilerOptions": {"target": "ES2022", "module": "ESNext"}}.
Configurer fly.toml
fly.toml est le manifest de déploiement : ports, régions, scaling. C'est comme le blueprint d'une usine : définit processes, volumes et HTTP.
Générer et personnaliser fly.toml
app = "flyio-node-demo-2026"
primary_region = "cdg" # Paris pour latence EU
[build]
builder = "paketobuildpacks/builder:base"
[env]
PORT = "8080"
[http_service]
internal_port = 8080
force_https = true
auto_stop_machines = true
auto_start_machines = true
min_machines_running = 0
processes = ["app"]
[[http_service.checks]]
path = "/health"
port = 8080
type = "http"
interval = "10s"
grace_period = "5s"
method = "GET"
[[services]]
internal_port = 8080
protocol = "tcp"
[[services.ports]]
port = 80
handlers = ["http"]
[[services.ports]]
port = 443
handlers = ["tls", "http"]Ce fly.toml configure un app nommé, région EU (cdg=Paris), health-checks pour auto-scaling et HTTPS forcé. auto_stop_machines économise ~90% en idle. Lancez fly launch --no-deploy pour générer auto et éditez.
Définir les secrets d'environnement
flyctl apps create flyio-node-demo-2026 --region cdg
flyctl secrets set DATABASE_URL=postgres://user:pass@db.flycast:5432/db
flyctl secrets set API_KEY=sk-1234567890abcdef
flyctl secrets listSecrets sont chiffrés et injectés au runtime, invisibles en logs. Utilisez pour DB creds ou API keys. fly apps create si pas de launch ; list vérifie sans exposer valeurs.
Déployer l'application
flyctl deploy --ha=false
# Vérifier status
flyctl status
flyctl logs
curl https://flyio-node-demo-2026.fly.dev/healthdeploy build/push l'image et rollout zero-downtime. --ha=false pour single region init ; retirez pour global. Logs en temps réel aident debug ; health endpoint confirme readiness.
Scaling et monitoring
Une fois live, scalez horizontalement. Fly.io autoscaler ajuste VMs (machines) par CPU/load, comme un thermostat intelligent.
Scaler et config autoscaler
flyctl scale count 2 --max-per-region 3
flyctl scale vm shared-cpu-1x --memory 512
# Autoscaler
flyctl scale set-autoscaler --min 1 --max 10
# Metrics
flyctl metrics
flyctl dashboardScale à 2 instances min, max 3/region pour HA. Autoscaler gère bursts (min1 idle). metrics montre CPU/RAM live ; dashboard web pour graphs avancés.
Bonnes pratiques
- Régions intelligentes : Choisissez primary_region près de vos users (fly regions list), ajoutez [[services]] pour multi-régions.
- Images légères : <200MB max ; utilisez .dockerignore pour node_modules/src.
- Health checks stricts : Toujours /healthz avec timeout <5s pour restarts rapides.
- CI/CD GitHub : Ajoutez flyctl/deploy dans Actions pour zero-config.
- Volumes persistants :
fly volumes create data --region cdg --size 10pour DB locale.
Erreurs courantes à éviter
- Port binding : Écoutez sur 0.0.0.0:8080, pas localhost ; sinon OOMKilled.
- No Dockerfile : Fly fallback sur buildpacks, mais lent ; toujours Dockerfile custom.
- Secrets exposés : Jamais en fly.toml ou logs ; utilisez
fly secrets setuniquement. - Scaling sans checks : Machines stuck en crashed sans /health ; ajoutez toujours.
Pour aller plus loin
Maîtrisez les Postgres managed (fly postgres create), Workers KV ou intégration Tailscale VPN. Consultez la doc officielle Fly.io et nos formations Learni Dev sur le déploiement cloud-native pour des ateliers pratiques avancés.