Aller au contenu
Administration Linux medium

Cas pratiques DevOps avec Nix : standardiser les environnements et fiabiliser la CI/CD

23 min de lecture

Vous avez des flakes, des devShells, un flake.lock qui verrouille chaque dépendance. Maintenant, comment en faire un usage d’équipe concret ? Ce guide transforme les briques des guides 8 à 10 en six cas pratiques opérationnels : poste de travail standardisé, CI/CD identique au local, checks automatisés, build reproductible d’un outil interne, stratégie de mise à jour et onboarding express. Chaque exemple s’appuie sur la même flake.nix de référence, directement utilisable dans un projet réel.

  • Construire un devShell d’équipe embarquant Terraform, Ansible, kubectl, Helm et les outils de qualité
  • Consommer ce devShell dans GitHub Actions et GitLab CI — sans configuration supplémentaire
  • Définir des checks reproductibles (lint, format) exécutés localement et en CI avec nix flake check
  • Empaqueter un script d’administration comme dérivation Nix reproductible avec writeShellApplication
  • Gérer les mises à jour de flake.lock sans surprises pour l’équipe
  • Ramener l’onboarding d’un nouveau membre à git clone + nix develop

Nix devient particulièrement utile dès qu’une équipe est impliquée :

  • Deux ingénieurs ont des versions différentes de Terraform ou d’Ansible — les plans divergent
  • La CI installe des paquets avec sudo apt install dans le pipeline, créant des builds fragiles et lents
  • Un nouveau contributeur passe une demi-journée à installer l’outillage avant d’écrire la première ligne
  • Une mise à jour silencieuse de helm en CI casse le déploiement — impossible de reproduire localement
  • Les scripts shell sont valides “sur ma machine” mais pas sur le runner

Un flake.nix versionné avec le code résout structurellement chacun de ces problèmes.

Ce guide fait suite aux guides 8 (environnements), 9 (factorisation) et 10 (flakes). Vous devez :

  • avoir Nix installé avec les flakes activés (voir le guide d’installation),
  • comprendre la structure d’un flake.nix (inputs, outputs, devShells),
  • savoir utiliser nix develop et nix flake check.

Créez ce projet dans votre répertoire de lab. Tous les cas pratiques utilisent cette même base :

Fenêtre de terminal
mkdir -p ~/Projets/lab-nix && cd ~/Projets/lab-nix
mkdir -p nix scripts src
git init
  • Répertoirelab-nix/
    • flake.nix (point d’entrée — inputs + délégation aux modules)
    • flake.lock (révisions verrouillées — toujours commité dans Git)
    • Répertoirenix/
      • devshells.nix (devShell d’équipe)
      • checks.nix (vérifications automatisables)
      • packages.nix (builds reproductibles)
    • Répertoirescripts/
      • lint.sh (lint shell)
      • validate.sh (validation Terraform)
    • Répertoiresrc/ (code source)
    • .envrc (activation automatique avec direnv — optionnel)

Le flake.nix racine reste court grâce à la factorisation dans nix/. C’est la convention introduite dans le guide 9.

flake.nix
{
description = "Boîte à outils DevOps — reproductible pour toute l'équipe";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
src = ./.;
in {
devShells.default = import ./nix/devshells.nix { inherit pkgs; };
checks = import ./nix/checks.nix { inherit pkgs src; };
packages = import ./nix/packages.nix { inherit pkgs; };
}
);
}

Cas pratique 1 — Boîte à outils DevOps standardisée

Section intitulée « Cas pratique 1 — Boîte à outils DevOps standardisée »

Un devShell unique qui embarque tout l’outillage d’une équipe DevOps. Chaque membre entre dans cet environnement avec nix develop et obtient les mêmes versions, peu importe sa distribution ou son historique d’installation.

nix/devshells.nix
{ pkgs }:
pkgs.mkShell {
name = "devops-toolkit";
packages = with pkgs; [
# Contrôle de version
git
git-crypt
# Traitement de données structurées
jq
yq-go
# Qualité de code shell
shellcheck
shfmt
# Infrastructure as Code
terraform
opentofu
# Gestion de configuration
ansible
ansible-lint
# Kubernetes
kubectl
helm
kustomize
k9s
# Sécurité
trivy
cosign
# Debug réseau
curl
httpie
];
shellHook = ''
echo "Environnement DevOps actif ($(uname -m))"
echo " Terraform : $(terraform version 2>/dev/null | head -1)"
echo " kubectl : $(kubectl version --client 2>/dev/null | head -1)"
echo " Helm : $(helm version --short 2>/dev/null)"
echo " Ansible : $(ansible --version 2>/dev/null | head -1)"
'';
}
Fenêtre de terminal
cd ~/Projets/lab-nix
git add flake.nix nix/devshells.nix
nix develop
Environnement DevOps actif (x86_64-linux)
Terraform : Terraform v1.11.4
kubectl : Client Version: v1.31.4
Helm : v3.17.2+g4a5b5ed
Ansible : ansible [core 2.18.2]

Votre flake.nix est versionné avec le code. La CI le consomme directement avec la même commande qu’en local :

Fenêtre de terminal
# En local
nix develop --command bash -c "shellcheck scripts/*.sh && terraform -chdir=infra validate"
# En CI — exactement la même commande
nix develop --command bash -c "shellcheck scripts/*.sh && terraform -chdir=infra validate"
.github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v31
with:
github_access_token: ${{ secrets.GITHUB_TOKEN }}
extra_nix_config: |
experimental-features = nix-command flakes
# Cache binaire — réduit drastiquement le temps de téléchargement
- uses: cachix/cachix-action@v16
with:
name: my-org-cache
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
- name: Vérification de la structure du flake
run: nix flake check --no-build
- name: Lint et validation IaC
run: |
nix develop --command bash -c "
shellcheck scripts/*.sh
terraform -chdir=infra validate
ansible-lint playbooks/ || true
"

Cas pratique 3 — Checks reproductibles avec nix flake check

Section intitulée « Cas pratique 3 — Checks reproductibles avec nix flake check »

Définir des vérifications (lint, format) comme des dérivations Nix :

  • exécutables en local avec nix flake check,
  • intégrées automatiquement en CI,
  • mises en cache — un check déjà réussi ne retourne pas.
nix/checks.nix
{ pkgs, src }:
{
# Vérification des scripts shell
shellcheck = pkgs.runCommandLocal "check-shellcheck"
{ buildInputs = [ pkgs.shellcheck ]; }
''
find ${src}/scripts -name "*.sh" -exec shellcheck {} +
touch $out
'';
# Format des fichiers Nix (RFC-style)
nixfmt = pkgs.runCommandLocal "check-nixfmt"
{ buildInputs = [ pkgs.nixfmt-rfc-style ]; }
''
nixfmt --check ${src}/flake.nix
find ${src}/nix -name "*.nix" -exec nixfmt --check {} +
touch $out
'';
}
Fenêtre de terminal
cd ~/Projets/lab-nix
git add nix/checks.nix scripts/
# Tous les checks
nix flake check
# Un seul check, avec trace détaillée si erreur
nix build .#checks.x86_64-linux.shellcheck --show-trace
checking flake output 'checks.x86_64-linux.shellcheck'...
checking flake output 'checks.x86_64-linux.nixfmt'...

Si shellcheck détecte un problème :

checking flake output 'checks.x86_64-linux.shellcheck'...
error: builder for '/nix/store/...-check-shellcheck.drv' failed with exit code 1
scripts/lint.sh:12:5: warning [SC2086]: Double quote to prevent
globbing and word splitting.

En GitLab CI et GitHub Actions, remplacez vos scripts de lint ad hoc par :

Fenêtre de terminal
nix flake check

C’est une seule commande, indépendante du runner, qui exécute exactement les mêmes vérifications qu’en local.


Cas pratique 4 — Build reproductible d’un outil interne

Section intitulée « Cas pratique 4 — Build reproductible d’un outil interne »

Empaqueter un script d’administration comme dérivation Nix. Le binaire résultant est identique bit à bit sur toutes les machines. Il peut être distribué via un cache binaire ou installé directement avec nix profile install.

nix/packages.nix
{ pkgs }:
{
# Outil d'audit Kubernetes avec ses dépendances exactes embarquées
k8s-audit = pkgs.writeShellApplication {
name = "k8s-audit";
runtimeInputs = with pkgs; [ kubectl jq ];
text = ''
echo "=== Noeuds du cluster ==="
kubectl get nodes -o json \
| jq -r '.items[] | "\(.metadata.name)\t\(.status.conditions[]
| select(.type=="Ready") | .status)"'
echo ""
echo "=== Pods non Running ==="
kubectl get pods --all-namespaces \
--field-selector 'status.phase!=Running,status.phase!=Succeeded' \
2>/dev/null || echo "(aucun)"
echo ""
echo "=== Événements Warning récents ==="
kubectl get events --all-namespaces \
--field-selector type=Warning \
--sort-by='.lastTimestamp' \
2>/dev/null | tail -15
'';
};
}
Fenêtre de terminal
cd ~/Projets/lab-nix
git add nix/packages.nix
# Construire
nix build .#k8s-audit
# Tester (le binaire est dans ./result/bin/)
./result/bin/k8s-audit
# Utiliser temporairement sans installer
nix shell .#k8s-audit --command k8s-audit
# Installer dans le profil utilisateur
nix profile install .#k8s-audit

Le binaire k8s-audit embarque ses propres versions de kubectl et jq depuis le store Nix — il fonctionne sur n’importe quelle machine avec Nix installé, sans dépendances système.


Cas pratique 5 — Stratégie de mise à jour de flake.lock

Section intitulée « Cas pratique 5 — Stratégie de mise à jour de flake.lock »

flake.lock fige la révision de nixpkgs. Il doit être mis à jour régulièrement pour obtenir les correctifs de sécurité et les nouvelles versions d’outils — sans casser l’équipe au passage.

  1. Créer une branche dédiée

    Fenêtre de terminal
    git checkout -b chore/nix-update-$(date +%Y-%m)
  2. Mettre à jour toutes les dépendances

    Fenêtre de terminal
    nix flake update

    Cela recrée flake.lock avec les révisions les plus récentes de chaque input déclaré.

  3. Inspecter ce qui a changé

    Fenêtre de terminal
    git diff flake.lock
    "lastModified": 1739000000,
    "lastModified": 1744823519,
    "rev": "d78a5c8a34f5b7e0c4d2e1f3a6b8c9e0f",
    "rev": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",

    La révision de nixpkgs est la seule vraie information. Consultez github.com/NixOS/nixpkgs/compare/<ancienne-rev>...<nouvelle-rev> pour voir les changements inclus.

  4. Valider localement

    Fenêtre de terminal
    nix flake check
    nix develop --command bash -c "terraform version && kubectl version --client"
  5. Ouvrir une PR/MR

    La CI valide les checks automatiquement. La PR est fusionnée si tout est vert.

  6. Rollback si problème

    Fenêtre de terminal
    # Revenir simplement à la révision précédente de nixpkgs
    git revert HEAD
    # Ou épingler une révision spécifique
    nix flake lock --update-input nixpkgs \
    --override-input nixpkgs \
    "github:nixos/nixpkgs?rev=d78a5c8a34f5b7e0c4d2e1f3a6b8c9e0f"
FréquenceCas d’usage
HebdomadaireÉquipes avec forte activité CI/CD, dépendances nombreuses
MensuelleProjets stables, outillage peu changeant
À la demandeCVE critique dans une dépendance détectée par la veille

Un nouveau DevOps rejoint l’équipe. En moins de 15 minutes, il dispose de l’environnement complet.

  1. Installer Nix (une seule fois, 3-5 minutes)

    Fenêtre de terminal
    sh <(curl -L https://nixos.org/nix/install) --no-daemon
    source ~/.nix-profile/etc/profile.d/nix.sh
  2. Activer les flakes

    Fenêtre de terminal
    mkdir -p ~/.config/nix
    echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf
  3. Cloner et entrer dans l’environnement

    Fenêtre de terminal
    git clone https://gitlab.mon-org.example.com/infra/devops-tools.git
    cd devops-tools
    nix develop
  4. Vérifier

    Fenêtre de terminal
    terraform version
    kubectl version --client
    helm version
    ansible --version

    Toutes les versions affichées sont identiques à celles du reste de l’équipe.

  5. (Optionnel) Activation automatique avec direnv

    Fenêtre de terminal
    nix profile install nixpkgs#direnv
    echo 'eval "$(direnv hook bash)"' >> ~/.bashrc
    source ~/.bashrc
    direnv allow .

    À partir de là, nix develop se déclenche automatiquement à chaque cd dans le répertoire.

Le README d’installation se réduit à ces 5 étapes. Plus de liste de paquets à maintenir, plus de “ça marche chez moi”.


Voici comment les six cas pratiques se combinent dans un projet réaliste.

flake.nix (version complète)
{
description = "Infra — Python + Terraform + Ansible";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
src = ./.;
in {
# Cas pratique 1 — plusieurs shells selon le rôle
devShells = {
default = pkgs.mkShell {
name = "infra-full";
packages = with pkgs; [
python312 python312Packages.boto3
terraform ansible ansible-lint
kubectl helm shellcheck shfmt
];
shellHook = ''
echo "Environnement complet actif"
'';
};
ci = pkgs.mkShell {
# Shell allégé pour la CI — sans éléments interactifs
name = "infra-ci";
packages = with pkgs; [
python312 terraform ansible kubectl shellcheck
];
};
};
# Cas pratique 3 — checks automatisés
checks = {
shellcheck = pkgs.runCommandLocal "check-shellcheck"
{ buildInputs = [ pkgs.shellcheck ]; }
''
find ${src}/scripts -name "*.sh" -exec shellcheck {} +
touch $out
'';
nixfmt = pkgs.runCommandLocal "check-nixfmt"
{ buildInputs = [ pkgs.nixfmt-rfc-style ]; }
''
nixfmt --check ${src}/flake.nix
touch $out
'';
};
# Cas pratique 4 — outil interne empaquetable
packages.default = pkgs.writeShellApplication {
name = "infra-status";
runtimeInputs = with pkgs; [ kubectl jq python312 ];
text = ''
echo "=== Cluster status ==="
kubectl get nodes -o json | jq -r '.items[].metadata.name'
'';
};
}
);
}

Pipeline CI/CD correspondant :

.github/workflows/ci.yml (fil rouge)
name: Infra CI
on: [push, pull_request]
jobs:
checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v31
with:
github_access_token: ${{ secrets.GITHUB_TOKEN }}
extra_nix_config: |
experimental-features = nix-command flakes
- run: nix flake check
- run: nix develop .#ci --command terraform -chdir=infra validate
- run: nix develop .#ci --command ansible-lint playbooks/
- run: nix build .#infra-status

PiègeImpactCorrectif
git add oublié avant nix developNix ignore silencieusement les nouveaux fichiersgit add -A systématique avant toute commande Nix
flake.lock absent du dépôtChaque machine ou CI job résout une révision différenteCommiter flake.lock — c’est une fonctionnalité, pas un artefact jetable
Cache Nix absent en CIPremier job lent (5-10 min de téléchargements)Configurer Cachix ou un runner persistant avec /nix monté
Trop d’outils dans un seul devShellTemps d’entrée long, résolution de conflits plus probablePlusieurs devShells nommés selon le rôle (default, sec, ci)
shellHook lourdRalentit chaque nix developGarder le shellHook court, déléguer aux scripts
Ignorer nix flake check en PRErreurs de structure détectées trop tardAjouter nix flake check --no-build comme premier job
Mettre à jour flake.lock sans branche dédiéeImpossible de revenir en arrière proprementToujours une branche + PR dédiée pour les mises à jour

  • La flake devient le contrat entre le poste de développement, la CI et les collègues — une seule source de vérité pour toute l’équipe
  • nix develop --command ... en CI consomme exactement le même environnement qu’en local — plus de divergence
  • flake.lock doit être versionné dans Git — il garantit que tout le monde et la CI travaillent avec les mêmes révisions
  • Les checks Nix sont mis en cache : un check déjà réussi ne tourne pas une deuxième fois
  • writeShellApplication est le moyen propre d’empaqueter des scripts avec leurs dépendances sans polluer le PATH système
  • L’onboarding se résume à : installer Nix une fois + git clone + nix develop
  • Mettez flake.lock à jour régulièrement via une PR dédiée — jamais directement sur la branche principale

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn