GitHub Actions + Hetzner Cloud + Kubernetes = combo gagnant pour SaaS B2B 2026 à coût maîtrisé. AWS EKS coûte 70 USD/mois minimum pour rien. Hetzner K8s : 8-30 EUR/mois cluster réel. Voici l'architecture complète.
TL;DR
- GitHub Actions : CI tests + build + deploy auto.
- Hetzner K8s ou Talos : cluster managé ou self-managed.
- ArgoCD : GitOps pour deploys.
- Stack total : 80-200 EUR/mois pour SaaS scale moyen.
Architecture complète
`
[GitHub Repo]
↓
[GitHub Actions]
├── Test (Vitest, Playwright)
├── Lint (ESLint, Prettier)
├── Build (Next.js, Docker)
├── Push image (GitHub Container Registry)
└── Update GitOps repo
↓
[ArgoCD watches GitOps repo]
↓
[Hetzner K8s cluster]
├── Frontend Next.js
├── Backend API
├── Postgres (Neon ou self-host)
└── Redis
`
Étape 1 — pipeline CI
`yaml
# .github/workflows/ci.yml
name: CI
on:
pull_request:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- name: Type-check
run: pnpm tsc --noEmit
- name: Lint
run: pnpm lint
- name: Unit tests
run: pnpm test
- name: E2E tests
run: pnpm playwright test
- name: Cross-tenant security tests
run: pnpm test:tenant-scoping
build-and-push:
if: github.ref == 'refs/heads/main'
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Docker
uses: docker/build-push-action@v5
with:
push: true
tags: |
ghcr.io/kolonell/app:${{ github.sha }}
ghcr.io/kolonell/app:latest
- name: Update GitOps manifest
run: |
git clone https://github.com/kolonell/k8s-manifests
cd k8s-manifests
sed -i "s|image: ghcr.io/kolonell/app:.*|image: ghcr.io/kolonell/app:${{ github.sha }}|" prod/deployment.yaml
git commit -am "deploy: ${{ github.sha }}"
git push
`
Étape 2 — Dockerfile multi-stage
`dockerfile
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
# Stage 2: Production
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
EXPOSE 3000
USER node
CMD ["node", "server.js"]
`
Image finale : ~150 MB (vs 800 MB sans multi-stage).
Étape 3 — Hetzner K8s cluster
Option A — Hetzner managed K8s (recommandé débutants)
`bash
# Créer via console hetzner.com (managed Kubernetes)
# Coût : 30 EUR/mois pour control plane + 8-15 EUR/node
`
Option B — Self-managed avec k3s
Plus économique :
`bash
# Sur master node Hetzner CX21 (4 vCPU, 8 GB)
curl -sfL https://get.k3s.io | sh -
# Récupérer kubeconfig
sudo cat /etc/rancher/k3s/k3s.yaml
# Joindre worker nodes
curl -sfL https://get.k3s.io | K3S_URL=https://MASTER_IP:6443 K3S_TOKEN=TOKEN sh -
`
3-node cluster : 24-45 EUR/mois total.
Étape 4 — Manifestes K8s
`yaml
# prod/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
namespace: prod
spec:
replicas: 3
selector:
matchLabels:
app: app
template:
metadata:
labels:
app: app
Besoin d'un site web professionnel ?
Kolonell crée des sites web qui attirent des clients, optimisés pour le marché sénégalais. Devis gratuit en 2 minutes.
spec:
containers:
- name: app
image: ghcr.io/kolonell/app:abc123
ports:
- containerPort: 3000
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: app-secrets
key: database-url
resources:
requests:
memory: "256Mi"
cpu: "200m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /api/health
port: 3000
initialDelaySeconds: 30
periodSeconds: 30
readinessProbe:
httpGet:
path: /api/health
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: app
namespace: prod
spec:
selector:
app: app
ports:
- port: 80
targetPort: 3000
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app
namespace: prod
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: traefik
tls:
- hosts: [app.kolonell.com]
secretName: app-tls
rules:
- host: app.kolonell.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app
port: { number: 80 }
`
Étape 5 — ArgoCD GitOps
`bash
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
`
`yaml
# argocd/application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: app-prod
spec:
project: default
source:
repoURL: https://github.com/kolonell/k8s-manifests
targetRevision: HEAD
path: prod
destination:
server: https://kubernetes.default.svc
namespace: prod
syncPolicy:
automated:
prune: true
selfHeal: true
`
ArgoCD watch le repo K8s manifests + sync automatiquement à chaque push.
Étape 6 — secrets management (Sealed Secrets)
`bash
# Installer
kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.24.0/controller.yaml
# Créer secret normal
kubectl create secret generic app-secrets \
--from-literal=database-url='postgres://...' \
--dry-run=client -o yaml > secret.yaml
# Sealer
kubeseal -o yaml < secret.yaml > sealed-secret.yaml
# Commit sealed-secret.yaml dans Git (chiffré)
`
Secrets chiffrés en Git, déchiffrés uniquement par le cluster.
Étape 7 — observabilité K8s
`bash
# Prometheus + Grafana via kube-prometheus-stack
helm install prometheus prometheus-community/kube-prometheus-stack \
--namespace monitoring --create-namespace
`
Dashboards inclus : pods CPU/RAM, latence, error rate, requests volume.
Coûts mensuels typiques
| Composant | Spec | Coût |
|---|---|---|
| Hetzner K8s control plane | Managed | 30 EUR/mois |
| Worker nodes (3x CX21) | 4 vCPU 8 GB chacun | 24 EUR/mois |
| Postgres (Neon Pro) | 5 GB | 19 EUR/mois |
| Redis (Upstash) | Pay-as-you-go | 5-15 EUR/mois |
| Cloudflare CDN | Free + Pro | 0-25 EUR/mois |
| Sentry | 50K errors | 26 EUR/mois |
| GitHub Actions | 3K minutes | 0 EUR (free tier) |
| Total mensuel | ~110-160 EUR |
À comparer AWS EKS : ~$300-500/mois pour équivalent. Économie 60-70 %.
Cas réel — SaaS Dakar (350 tenants, 1.2M req/jour)
| Métrique | Stack |
|---|---|
| Cluster | Hetzner K8s 5 nodes |
| Cost mensuel infra | 145 EUR |
| Deploy frequency | 12-18/jour |
| Deploy duration | 4-7 min |
| Rollback time | 30 sec (ArgoCD) |
| Uptime | 99.96 % |
Pièges fréquents
- Pas de readiness probe — pods reçoivent traffic avant d'être prêts.
- Resources requests/limits manquants — OOM kills aléatoires.
- Secrets en clair dans Git — leak catastrophique. Sealed Secrets obligatoire.
- Pas de rolling update strategy — downtime à chaque deploy.
- Pas de backup K8s state — Velero ou similaire pour DR.
FAQ
Q : K8s vs Vercel pour Next.js ?
R : Vercel = simple mais cher à scale + lock-in. K8s = complexe mais scaling économique illimité + portable.
Q : k3s vs k0s vs RKE ?
R : k3s = le plus simple + battle-tested. k0s = alternative moderne. RKE = SUSE entreprise.
Q : Multi-region multi-cluster ?
R : Pour 99 % SaaS B2B Africa : single cluster Frankfurt suffit. Multi-region utile au-delà 100M ARR.
Conclusion
CI/CD GitHub Actions + Hetzner K8s + ArgoCD = stack DevOps 2026 pro pour SaaS B2B sérieux à coût raisonnable. Setup initial 2-4 semaines. ROI : agilité dev + scaling indépendant + zéro lock-in cloud.
Mohamed Bah
Fondateur, Kolonell
Passionné par le digital et l'entrepreneuriat en Afrique, Mohamed accompagne les entreprises sénégalaises dans leur transformation digitale depuis 2020. Fondateur de Kolonell, il croit que chaque PME mérite une présence en ligne professionnelle et accessible.