Docker reste en 2026 (informations vérifiées en avril 2026, susceptibles d’évoluer) le standard de fait pour conteneuriser des applications, et Podman son challenger sans daemon avec une compatibilité 95 %. Pour les développeurs et sysadmins ouest-africains, maîtriser les patterns avancés Docker (multi-stage builds, volumes nommés, networking custom, healthchecks, security context) fait la différence entre une infra fragile et une infra production-ready. Voici le guide pratique 2026.
Ce guide général couvre les patterns avancés. Les articles connexes détaillent : multi-stage builds optimisés, Docker vs Podman, Docker Compose en production, Docker rootless et sécurité.
Docker en 2026
- Docker Desktop : avec license payante pour entreprises > 250 employés ou > 10M USD CA
- Docker Engine : open-source Apache 2.0, gratuit illimité
- BuildKit : builder moderne avec cache distribué et builds parallèles
- Docker Compose v2 (intégré au docker CLI)
- Docker Hub : registry public, plan gratuit limité (200 pulls par 6h pour anonymes)
Podman
- Compatibilité Docker à 95 % : alias
docker=podmanfonctionne souvent - Sans daemon : pas de processus root permanent
- Rootless par défaut : conteneurs sous user normal
- Pods (groupes de conteneurs partageant le réseau) — concept Kubernetes
- Génération de YAML Kubernetes depuis pods
Multi-stage build optimisé
# Dockerfile multi-stage pour app Node.js
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && \
cp -R node_modules /tmp/prod_node_modules
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
USER node
COPY --from=builder --chown=node:node /tmp/prod_node_modules ./node_modules
COPY --from=builder --chown=node:node /app/dist ./dist
COPY --from=builder --chown=node:node /app/package.json ./
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD node -e "require('http').get('http://localhost:3000/health',(r)=>process.exit(r.statusCode===200?0:1))"
CMD ["node", "dist/server.js"]
Image finale ~150 Mo au lieu de 1 Go avec naïve approach. Voir notre tutoriel multi-stage.
Volumes nommés vs bind mounts
- Bind mount :
-v /host/path:/container/path— partage un dossier hôte. Pratique en dev. - Named volume :
-v mydata:/data— Docker gère le storage. Backupable, déplaçable, ne pollue pas le host. - tmpfs :
--tmpfs /tmp— RAM-only, parfait pour caches éphémères
Networking custom
# Créer un réseau privé
docker network create app-net
# Lancer plusieurs services dessus
docker run -d --network app-net --name api -p 3000:3000 myapp:latest
docker run -d --network app-net --name db postgres:16
# api peut résoudre 'db' via DNS interne
Healthchecks
Critique pour zero-downtime deployments. Le orchestrateur (Coolify, Swarm, K8s) attend que le healthcheck pass avant de couper l’ancien container.
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
Security context
USER non-root: ne JAMAIS run en root dans le conteneur--read-only: container avec FS lecture seule, sauf volumes mountés--cap-drop=ALL --cap-add=NET_BIND_SERVICE: retirer toutes les capabilities--security-opt=no-new-privileges: empêcher escalade privilèges- Scan des images avec
trivyou Snyk avant push en prod
BuildKit cache
DOCKER_BUILDKIT=1 docker build --cache-from type=registry,ref=registry/myapp:cache -t myapp:latest .
Cache distribué via registry : builds CI plus rapides, équipe partage le cache.
Adaptation Afrique de l’Ouest
Sur connexion lente, optimisez la taille des images : multi-stage + alpine + slim variants. Une image 150 Mo se transfère en 30s sur 4G, vs 5 min pour 1 Go. Important pour les déploiements fréquents.
Pour approfondir
Où héberger votre projet web ?
Hostinger propose des plans dimensionnés pour les freelances et PME. Lien d’affiliation — pas de surcoût pour vous.
Lien d affiliation. Si vous achetez via ce lien, le blog reçoit une petite commission sans surcoût pour vous.
Pourquoi pousser Docker / Podman au-dela du basique en 2026
En 2026, conteneuriser une app n est plus une competence rare : tout dev junior a Dakar, Abidjan ou Lome a deja ecrit un Dockerfile. Ce qui distingue le profil senior, c est la maitrise des optimisations qui reduisent l image de 1 Go a 80 Mo, durcissent le runtime et evitent les fuites de secrets. Ce tutoriel cible ce niveau intermediaire-avance : multi-stage builds, networking custom, securite (rootless, capabilities, seccomp), et bascule Docker vers Podman pour les contextes ou le daemon root pose probleme.
Concretement, vous repartirez avec : un Dockerfile multi-stage qui produit une image finale de 80 Mo pour une app Node.js, un reseau bridge custom pour isoler vos services, un conteneur rootless avec Podman, et trois techniques de scan de securite (Trivy, Docker Scout, Syft). De quoi presenter un bilan technique credible en entretien ou en mission a 350 000 FCFA / jour pour un mandat DevOps senior a Dakar.
Etape 1 : Multi-stage build pour une image Node.js minimaliste
Une image Node naive base sur node:20 pese 1,1 Go. Avec un multi-stage build et l image distroless de Google, on tombe a 80 Mo. Le principe : un stage build complet (compile TypeScript, install dev dependencies), puis un stage runtime ultra-minimal qui ne copie que les artefacts necessaires.
# Stage 1 : build
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2 : runtime
FROM gcr.io/distroless/nodejs22-debian12
WORKDIR /app
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
CMD ["dist/index.js"]
Buildez avec docker build -t myapp:slim . puis docker images myapp. La sortie attendue affiche une taille autour de 80-120 Mo selon le projet. Si vous voyez encore 500+ Mo, verifiez que le stage runtime n hereite pas accidentellement de node:22 au lieu du distroless.
Etape 2 : Optimiser le cache des layers Docker
Le cache Docker se base sur l ordre des instructions. Une regle d or : copier d abord les fichiers qui changent rarement (package.json, requirements.txt), puis le code source. Sinon chaque modification de code invalide tout le cache des dependances et le build prend 5 minutes au lieu de 5 secondes.
# MAUVAIS : COPY . . avant npm ci
FROM node:22-alpine
WORKDIR /app
COPY . .
RUN npm ci
# Chaque modif de src/ retelecharge node_modules
# BON : package.json en premier
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
# node_modules en cache tant que package.json ne bouge pas
La sortie attendue lors du second build : la ligne RUN npm ci doit afficher CACHED. Si elle re-execute, le cache est casse — verifiez l ordre des COPY.
Etape 3 : Networking avec un bridge custom
Le reseau bridge par defaut de Docker est partage par tous les conteneurs sans isolation et sans resolution DNS par nom. Pour un projet multi-services (app + db + cache), un bridge custom est indispensable. Les conteneurs s y resolvent par leur nom de service.
docker network create app-net
docker run -d --name postgres --network app-net -e POSTGRES_PASSWORD=secret postgres:16
docker run -d --name myapp --network app-net -e DATABASE_URL=postgresql://postgres:secret@postgres:5432/db myapp:slim
Verifiez la connectivite avec docker exec myapp ping -c 1 postgres. La sortie attendue : 1 packets transmitted, 1 received, 0% packet loss. Si le ping echoue, les conteneurs ne sont pas sur le meme reseau ou le DNS interne Docker est desactive.
Etape 4 : Securite — runtime non-root et capabilities
Par defaut, un conteneur Docker tourne en root a l interieur. Une vulnerabilite dans l app peut alors etre amplifiee si une CVE permet l evasion. La parade : un user non-root et la suppression des capabilities Linux inutiles.
FROM node:22-alpine
RUN addgroup -S app && adduser -S app -G app
USER app
WORKDIR /home/app
COPY --chown=app:app package*.json ./
RUN npm ci --omit=dev
COPY --chown=app:app . .
CMD ["node", "index.js"]
Lancez avec docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE --security-opt=no-new-privileges myapp. Verifiez avec docker exec myapp id : la sortie doit montrer uid=100(app) gid=101(app), jamais uid=0(root).
Etape 5 : Scanner les vulnerabilites avec Trivy
Trivy d Aqua Security scanne une image en quelques secondes et liste les CVE par severite. C est devenu le standard de fait en 2026, integre dans la plupart des pipelines GitHub Actions, GitLab CI et Forgejo Actions.
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy:latest image myapp:slim
La sortie liste les CVE par categorie (CRITICAL, HIGH, MEDIUM, LOW). Pour un build de production, refusez le merge si Trivy remonte la moindre CVE CRITICAL ou HIGH non patchable. Astuce : utilisez --severity HIGH,CRITICAL --exit-code 1 dans la pipeline pour faire echouer le build automatiquement.
Etape 6 : Passer a Podman pour le rootless natif
Podman est l alternative compatible Docker promue par Red Hat. Sa difference cle : pas de daemon, et tous les conteneurs tournent en rootless par defaut. Pour les serveurs partages (lab Dakar, prod multi-tenant), c est un argument securite majeur. La CLI est compatible : alias docker=podman et la majorite des commandes fonctionnent sans changement.
sudo apt install -y podman
podman run --rm hello-world
podman build -t myapp:slim .
podman run -d --name myapp -p 8080:8080 myapp:slim
Sortie attendue : hello-world affiche le message classique, puis le build et le run de votre app fonctionnent sans sudo. Aucun daemon Docker ne tourne — verifiez avec systemctl status docker qui doit retourner could not be found.
Etape 7 : Generer un SBOM avec Syft
Le SBOM (Software Bill of Materials) liste tous les composants d une image. C est demande par les auditeurs de securite (ISO 27001, certifications PCI-DSS) et par certains clients enterprise en Cote d Ivoire ou au Senegal qui exigent la tracabilite. Syft d Anchore le genere en JSON, SPDX ou CycloneDX.
syft myapp:slim -o cyclonedx-json > sbom.json
cat sbom.json | jq '.components | length'
Sortie attendue : un nombre representant le total de composants (paquets npm, libs systeme). Stockez ce SBOM avec chaque release : en cas de CVE future sur une lib transitive, vous pouvez tracer instantanement quelles versions de votre app sont impactees.
Etape 8 : Pod multi-conteneurs avec Podman
Podman natif gere les pods (groupes de conteneurs partageant le meme namespace reseau), comme Kubernetes mais en local. Pratique pour deployer un trio app + db + cache sur un VPS modeste sans installer K8s.
podman pod create --name webpod -p 8080:8080
podman run -d --pod webpod --name redis redis:7-alpine
podman run -d --pod webpod --name app myapp:slim
Verifiez avec podman pod ps : le pod doit afficher 3 conteneurs (le pod lui-meme + redis + app). L app voit redis sur localhost:6379 car ils partagent le meme namespace reseau. Pour exporter le pod en manifest Kubernetes prêt a deployer : podman generate kube webpod > webpod.yaml.
Annexe : retours d experience sur la migration Docker vers Podman
Trois pieges courants quand on migre une stack production de Docker vers Podman. Piege 1 : les conteneurs rootless ne peuvent pas binder les ports privileges (< 1024) sans capability speciale. Solution : binder sur 8080 et reverse-proxer via Caddy ou Nginx. Piege 2 : les volumes nommes Docker ne sont pas portables vers Podman. Solution : utiliser des bind mounts explicites sur des chemins absolus.
Piege 3 : les images privees (registry GitLab, Forgejo) demandent une authentification differente. podman login stocke le token dans $XDG_RUNTIME_DIR/containers/auth.json au lieu de ~/.docker/config.json. Verifiez ce chemin si vos pulls echouent en 401 Unauthorized.
Sur un angle proche, consultez notre tutoriel Forgejo Actions CI/CD pour automatiser le build et scan de vos images, et PocketBase vs Firebase vs Supabase pour choisir un backend a conteneuriser.
Annexe : checklist de production pour vos images
Avant de pousser une image sur un registry et de la deployer en prod, validez systematiquement ces 8 points : (1) Dockerfile multi-stage avec image distroless ou Alpine en runtime ; (2) USER non-root explicite ; (3) --cap-drop=ALL et --security-opt=no-new-privileges au runtime ; (4) variables sensibles via secrets Docker ou variables d env injectees au runtime, jamais ecrites dans le Dockerfile ; (5) HEALTHCHECK declare pour permettre l auto-recovery ; (6) labels OCI standard (org.opencontainers.image.source, .version, .created) ; (7) image scannee avec Trivy, zero CVE CRITICAL ou HIGH ; (8) SBOM Syft attache a la release.
Cette checklist est ce que les recruteurs DevOps senior attendent en 2026. La maitriser ouvre des missions remunerees entre 250 000 et 400 000 FCFA / jour selon l experience et la zone (Dakar, Abidjan, remote Europe).
Annexe : seccomp et profils AppArmor
Un cran au-dessus du --cap-drop=ALL, on restreint les appels systeme avec seccomp. Docker fournit un profil par defaut qui bloque deja 44 syscalls dangereux. Pour approfondir, on ecrit un profil custom qui ne whitelist que les syscalls effectivement utilises par l app. Outil pratique : strace -c node index.js liste les syscalls reels, qu on traduit en JSON seccomp.
Pour AppArmor (cote Ubuntu / Debian), Docker charge docker-default automatiquement. Verifiez avec aa-status | grep docker-default. Pour un profil custom plus strict, generez-le avec aa-genprof en mode learning, puis chargez-le via --security-opt=apparmor=mon-profil. Combiner seccomp + AppArmor + capabilities donne un sandbox proche de gVisor sans la perte de performance.