Sites Web12 min de lecture

CI/CD GitHub Actions + Hetzner + Kubernetes : pipeline SaaS 2026

Mohamed Bah·Fondateur, Kolonell
20 mai 2026
Partager :
CI/CD GitHub Actions + Hetzner + Kubernetes : pipeline SaaS 2026

CI/CD GitHub Actions + Hetzner + Kubernetes : pipeline SaaS 2026

Sites Web

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

ComposantSpecCoût
Hetzner K8s control planeManaged30 EUR/mois
Worker nodes (3x CX21)4 vCPU 8 GB chacun24 EUR/mois
Postgres (Neon Pro)5 GB19 EUR/mois
Redis (Upstash)Pay-as-you-go5-15 EUR/mois
Cloudflare CDNFree + Pro0-25 EUR/mois
Sentry50K errors26 EUR/mois
GitHub Actions3K minutes0 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étriqueStack
ClusterHetzner K8s 5 nodes
Cost mensuel infra145 EUR
Deploy frequency12-18/jour
Deploy duration4-7 min
Rollback time30 sec (ArgoCD)
Uptime99.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.

Tags :#CI/CD#GitHub Actions#Hetzner#Kubernetes#ArgoCD#DevOps
Partager :

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.