NixOS pour vos serveurs Linux : usage terrain
En résumé
- NixOS transforme la configuration serveur en code versionné, relisible et reproductible.
- Les générations système permettent de revenir rapidement en arrière après une mise à jour ratée.
- Le vrai gain apparaît sur les serveurs critiques, les VPS clients et les socles Linux répétés.
Un serveur Linux finit rarement dans l’état exact qu’on avait imaginé au départ.
On installe un paquet pour dépanner. On modifie une conf Nginx un vendredi soir. On ajoute une règle firewall après un incident. Puis six mois plus tard, personne ne sait vraiment ce qui est nécessaire, ce qui est historique, et ce qui casse si on le retire.
Sur un VPS qui héberge un petit service interne, ce n’est pas toujours grave. Sur une machine qui porte une API métier, un reverse proxy Traefik, une base PostgreSQL, un serveur HFSQL exposé proprement ou un outil de supervision, ça change vite de nature.
NixOS part d’une idée assez simple à comprendre, moins simple à adopter : le serveur est décrit dans des fichiers. Les paquets installés, les services systemd, le pare-feu, les utilisateurs, les options SSH, parfois même les conteneurs. On ne documente plus le serveur après coup. On écrit son état attendu, puis NixOS le construit.
Au 22 juin 2026, la branche stable courante est NixOS 26.05 " Yarara “, annoncée avec des correctifs et mises à jour de sécurité jusqu’au 31 décembre 2026. La branche précédente 25.11 arrive en fin de support le 30 juin 2026. Ce détail compte en production : NixOS n’échappe pas aux cycles de maintenance. Il les rend juste plus visibles.
Cet article parle d’usage serveur. Pas de poste de développeur, pas de dotfiles, pas de chasse au système parfait. Le sujet ici est plus terre à terre : est-ce que NixOS peut aider à gérer des serveurs Linux d’entreprise avec moins d’écarts, moins de gestes manuels et un vrai retour arrière quand une mise à jour se passe mal ?
Ce que NixOS change dans l’administration serveur
Sur une distribution Linux classique, on obtient un serveur par accumulation.
On installe Ubuntu ou Debian. On ajoute des dépôts. On applique des commandes. On modifie /etc. On crée des services systemd. On durcit SSH. On configure Docker. Une partie est scriptée, une partie reste dans l’historique shell, une autre dans la tête de la personne qui a fait l’intervention.
NixOS inverse le centre de gravité. Le fichier de configuration devient la source principale.
Un exemple minimal donne déjà le ton :
{ config, pkgs, ... }:
{
imports = [
./hardware-configuration.nix
];
system.stateVersion = "26.05";
networking.hostName = "srv-app-01";
time.timeZone = "Europe/Paris";
users.users.admin = {
isNormalUser = true;
extraGroups = [ "wheel" "docker" ];
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAA... cle-admin"
];
};
services.openssh = {
enable = true;
settings = {
PasswordAuthentication = false;
PermitRootLogin = "no";
};
};
networking.firewall = {
enable = true;
allowedTCPPorts = [ 22 80 443 ];
};
environment.systemPackages = with pkgs; [
git
htop
vim
curl
jq
];
}
Ce fichier ne raconte pas tout. Il ne remplace pas l’exploitation, les sauvegardes, la supervision, les tests. Mais il donne une base relisible. Quand un autre administrateur arrive sur le serveur, il ne part pas d’un inventaire flou.
La commande qui applique cette configuration reste volontairement explicite :
sudo nixos-rebuild switch
NixOS construit une nouvelle génération système, puis bascule dessus. Les anciennes générations restent disponibles. C’est une différence très pratique après une mise à jour de kernel, de service réseau ou de reverse proxy.
Pour un serveur, ce point change beaucoup de choses. Un rollback n’est pas un plan écrit dans un wiki. Il est disponible dans le bootloader ou via la commande système.
sudo nixos-rebuild --rollback switch
Je ne présenterais pas ça comme une assurance tous risques. Une migration de base de données reste une migration de base de données. Un volume Docker corrompu ne sera pas réparé par magie. Mais sur la partie système, services, paquets, options de configuration, le retour arrière est franchement confortable.
Définitions utiles avant de juger NixOS
Nix
Nix est le gestionnaire de paquets et l’outil de build. Il peut s’utiliser sur d’autres distributions Linux, ou même macOS. Il construit les paquets dans un magasin dédié, généralement /nix/store, avec des chemins qui intègrent des hash.
Le résultat est moins familier qu’un /usr/bin classique, mais beaucoup plus traçable.
Nixpkgs
Nixpkgs est l’ensemble de paquets et modules utilisés par Nix et NixOS. Quand on fige une version de nixpkgs, on fige une grande partie de l’univers logiciel que le serveur va utiliser.
C’est là que les Flakes deviennent intéressants. Un flake.lock permet de verrouiller exactement les révisions utilisées. On évite le serveur qui marche encore parce qu’il a tiré une version un peu différente de celle du voisin.
NixOS
NixOS est la distribution Linux construite autour de Nix. Elle applique ce modèle déclaratif à l’OS lui-même : réseau, services, utilisateurs, systemd, paquets, sécurité, conteneurs.
C’est ce qui nous intéresse pour les serveurs.
Génération système
Chaque application de configuration produit une génération. On peut démarrer dessus, revenir à la précédente, comparer. Sur un parc serveur, c’est un mécanisme précieux pour garder une trace exploitable des changements.
Les cas où NixOS apporte une vraie valeur
NixOS n’est pas nécessaire pour tous les serveurs Linux.
Sur un VPS unique, qui héberge trois sites vitrines et ne bouge presque jamais, Debian ou Ubuntu restent des choix très solides. L’écosystème est connu, les procédures sont maîtrisées, les prestataires sont faciles à trouver.
NixOS devient plus intéressant quand les serveurs doivent être répétés, reconstruits ou audités.
J’ai en tête plusieurs situations assez courantes dans des PME ou chez des éditeurs métier.
Un premier cas : le serveur reverse proxy. Traefik, certificats TLS, règles d’accès, middlewares, routage vers des conteneurs. Au bout de quelques mois, le serveur devient une petite pièce critique. On ne veut pas le refaire à la main.
Un deuxième cas : l’environnement client. Un PostgreSQL pour un client, un HFSQL C/S pour un autre, une API interne, parfois une application WebDev derrière IIS ou Traefik. Quand l’architecture se répète, la configuration déclarative évite les variantes non assumées.
Un troisième cas : le serveur d’outillage interne. Forge Git, MinIO, monitoring, sauvegardes, jobs planifiés, VPN. Ce sont rarement les machines les plus visibles. Ce sont pourtant celles qui font perdre du temps quand elles dérivent.
Un quatrième cas : le serveur que l’on doit pouvoir reconstruire vite. Pas juste restaurer une image complète. Reconstruire proprement, depuis une configuration versionnée, puis rattacher les volumes et les secrets.
C’est souvent là que NixOS devient crédible. Pas parce qu’il est plus élégant. Parce qu’il réduit le nombre de gestes non documentés.
Exemple de socle NixOS pour un serveur applicatif
Voici une base un peu plus proche d’un serveur réel. Elle active Docker, durcit SSH, limite les ports, installe quelques outils et prépare une convention de répertoires.
{ config, pkgs, ... }:
{
system.stateVersion = "26.05";
networking.hostName = "srv-prod-app";
networking.useDHCP = false;
networking.interfaces.ens3.ipv4.addresses = [
{
address = "10.10.20.15";
prefixLength = 24;
}
];
networking.defaultGateway = "10.10.20.1";
networking.nameservers = [ "1.1.1.1" "9.9.9.9" ];
time.timeZone = "Europe/Paris";
users.users.deploy = {
isNormalUser = true;
extraGroups = [ "wheel" "docker" ];
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAA... poste-admin"
];
};
security.sudo.wheelNeedsPassword = true;
services.openssh = {
enable = true;
openFirewall = false;
settings = {
PasswordAuthentication = false;
KbdInteractiveAuthentication = false;
PermitRootLogin = "no";
AllowUsers = [ "deploy" ];
};
};
networking.firewall = {
enable = true;
allowedTCPPorts = [ 22 80 443 ];
};
virtualisation.docker = {
enable = true;
autoPrune = {
enable = true;
dates = "weekly";
flags = [ "--volumes" ];
};
};
environment.systemPackages = with pkgs; [
docker-compose
git
htop
jq
ripgrep
vim
];
systemd.tmpfiles.rules = [
"d /srv/eloneva 0750 deploy docker -"
"d /var/lib/eloneva/secrets 0700 root root -"
"d /var/log/eloneva 0750 root adm -"
];
}
Ce n’est pas une configuration universelle. L’IP statique, les DNS, les groupes, les règles firewall doivent suivre votre contexte. Mais on voit l’intérêt : les choix sont visibles.
La configuration ne dit pas seulement " Docker est installé “. Elle dit aussi comment il est activé, comment le nettoyage est planifié, quels répertoires existent et avec quels droits.
Sur une machine classique, ces détails se retrouvent souvent répartis entre un script initial, une procédure d’exploitation et quelques commandes passées en SSH. Ça marche. Jusqu’au jour où il faut refaire le serveur.
Piloter une stack Docker Compose depuis NixOS
Beaucoup d’applications métier arrivent encore sous forme de docker-compose.yml. NixOS ne force pas à tout convertir en modules natifs.
On peut garder Docker Compose et demander à systemd de le piloter proprement.
Exemple de service NixOS :
{ pkgs, ... }:
{
systemd.services.eloneva-stack = {
description = "Stack applicative EloNeva";
wantedBy = [ "multi-user.target" ];
after = [ "docker.service" "network-online.target" ];
requires = [ "docker.service" ];
environment = {
COMPOSE_PROJECT_NAME = "eloneva";
};
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
WorkingDirectory = "/srv/eloneva/stack";
LoadCredential = [
"db_password:/var/lib/eloneva/secrets/db_password"
];
};
script = ''
${pkgs.docker-compose}/bin/docker-compose up -d --remove-orphans
'';
preStop = ''
${pkgs.docker-compose}/bin/docker-compose down
'';
};
}
Et côté docker-compose.yml :
services:
app:
image: registry.example.com/eloneva/app:1.8.4
restart: unless-stopped
environment:
DB_HOST: postgres
DB_NAME: app
DB_USER: app
DB_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
depends_on:
- postgres
labels:
- "traefik.enable=true"
- "traefik.http.routers.app.rule=Host(`app.example.com`)"
- "traefik.http.routers.app.entrypoints=websecure"
- "traefik.http.routers.app.tls=true"
postgres:
image: postgres:18
restart: unless-stopped
environment:
POSTGRES_DB: app
POSTGRES_USER: app
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
volumes:
- pgdata:/var/lib/postgresql/data
secrets:
db_password:
file: /run/credentials/eloneva-stack.service/db_password
volumes:
pgdata:
Le secret reste un fichier côté hôte, protégé par les droits système, puis transmis au service via les credentials systemd. Docker Compose l’expose ensuite dans le conteneur sous /run/secrets/db_password.
Ce n’est pas l’unique manière de gérer les secrets avec NixOS. Dans des environnements plus structurés, on regardera plutôt sops-nix, agenix, Vault ou un secret manager déjà en place. Pour une PME qui démarre, ce montage a l’avantage d’être lisible. On sait où est le secret, qui le lit, et à quel moment.
Traefik déclaré dans le système
Traefik peut tourner en conteneur. Il peut aussi être activé comme service NixOS. Le choix dépend de votre manière d’exploiter les certificats, les fichiers dynamiques et la supervision.
Un exemple simplifié :
{ pkgs, ... }:
{
services.traefik = {
enable = true;
staticConfigOptions = {
entryPoints = {
web = {
address = ":80";
http.redirections.entryPoint = {
to = "websecure";
scheme = "https";
};
};
websecure = {
address = ":443";
};
};
providers = {
docker = {
endpoint = "unix:///var/run/docker.sock";
exposedByDefault = false;
};
file = {
directory = "/etc/traefik/dynamic";
watch = true;
};
};
certificatesResolvers.letsencrypt.acme = {
email = "admin@example.com";
storage = "/var/lib/traefik/acme.json";
httpChallenge.entryPoint = "web";
};
api.dashboard = false;
log.level = "INFO";
};
};
systemd.tmpfiles.rules = [
"d /etc/traefik/dynamic 0750 root traefik -"
"d /var/lib/traefik 0750 traefik traefik -"
];
networking.firewall.allowedTCPPorts = [ 80 443 ];
}
Ce type de configuration évite le reverse proxy " installé à côté “. Traefik fait partie du serveur. Ses ports, ses providers, ses répertoires et son activation sont dans le même dépôt que le reste.
Sur un projet où Traefik route vers plusieurs conteneurs clients, c’est agréable à relire. On peut aussi combiner cette approche avec des fichiers dynamiques versionnés, ou générés depuis un dépôt de configuration.
Déploiement depuis un poste Ubuntu 24.02
Dans beaucoup d’équipes, le poste d’administration n’est pas sous NixOS. Il peut être sous Ubuntu. Ce n’est pas bloquant.
Un script Bash minimal peut pousser la configuration puis lancer la reconstruction sur le serveur. Il reste volontairement simple.
#!/usr/bin/env bash
set -euo pipefail
SERVER="deploy@srv-prod-app.example.com"
REMOTE_DIR="/etc/nixos"
LOCAL_DIR="./nixos/srv-prod-app"
rsync -av --delete \
--exclude ".git" \
"${LOCAL_DIR}/" \
"${SERVER}:/tmp/nixos-config/"
ssh "${SERVER}" <<'SSH'
set -euo pipefail
sudo rsync -av --delete /tmp/nixos-config/ /etc/nixos/
sudo nixos-rebuild test
echo "Configuration testée. Bascule en cours..."
sudo nixos-rebuild switch
SSH
La commande nixos-rebuild test applique une configuration sans l’inscrire comme génération de démarrage permanente. C’est utile pour valider un service, un port, un module. Ensuite seulement, on bascule avec switch.
Sur des environnements plus propres, on passera à des Flakes, à une CI, ou à un outil de déploiement Nix. Mais il n’est pas nécessaire de tout industrialiser le premier jour.
Variante avec Flakes pour verrouiller nixpkgs
Les Flakes ajoutent un fichier flake.nix et un flake.lock. Le lock fige les entrées. Dans un contexte serveur, c’est souvent le début d’une vraie reproductibilité.
Exemple court :
{
description = "Configuration NixOS EloNeva";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
};
outputs = { self, nixpkgs, ... }: {
nixosConfigurations.srv-prod-app = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
./hosts/srv-prod-app/configuration.nix
];
};
};
}
Déploiement côté serveur :
sudo nixos-rebuild switch --flake /etc/nixos#srv-prod-app
Mise à jour contrôlée depuis le dépôt :
nix flake update
git diff flake.lock
Ce git diff est important. On voit ce qui change avant de reconstruire. Ce n’est pas parfait, parce qu’une mise à jour de nixpkgs peut embarquer beaucoup de paquets, mais c’est beaucoup moins opaque qu’un serveur mis à jour à la main.
Durcir les services systemd
NixOS expose très bien les options systemd. Pour un service applicatif maison, on peut déclarer un durcissement directement dans le module.
systemd.services.worker-import = {
description = "Import nocturne des données métier";
wantedBy = [ "multi-user.target" ];
serviceConfig = {
User = "worker";
Group = "worker";
ExecStart = "/srv/eloneva/worker/import.sh";
Restart = "on-failure";
NoNewPrivileges = true;
PrivateTmp = true;
ProtectSystem = "strict";
ProtectHome = true;
ReadWritePaths = [
"/var/lib/eloneva/import"
"/var/log/eloneva"
];
CapabilityBoundingSet = "";
LockPersonality = true;
MemoryDenyWriteExecute = true;
};
};
Puis on vérifie avec :
systemd-analyze security worker-import.service
Cette commande ne remplace pas une revue de sécurité. Elle donne un score et des pistes. Pour un service interne, c’est déjà une bonne discipline. On évite le service qui tourne en root parce que " ça a marché comme ça pendant les tests “.
Gestion des mises à jour
NixOS donne de bons outils, mais il ne décide pas de la politique de mise à jour à votre place.
Pour un serveur de production, je préfère une cadence simple :
- branche stable figée ;
- mises à jour testées sur une machine de préproduction ou un clone ;
- lecture du
flake.lockou du canal utilisé ; - bascule avec
nixos-rebuild bootquand un redémarrage est nécessaire ; - fenêtre de maintenance pour les services sensibles.
Le script ci-dessous convient pour un contrôle manuel depuis un dépôt Flake :
#!/usr/bin/env bash
set -euo pipefail
HOST_CONFIG="srv-prod-app"
nix flake update
nix build ".#nixosConfigurations.${HOST_CONFIG}.config.system.build.toplevel"
echo "Build OK. Vérifiez le diff avant commit :"
git diff -- flake.lock
On commit ensuite le lock, on déploie, puis on surveille.
Pour une mise à jour kernel ou libc, je préfère souvent :
sudo nixos-rebuild boot --flake /etc/nixos#srv-prod-app
sudo reboot
switch est pratique, mais tout ne se recharge pas proprement à chaud. Sur un serveur critique, l’absence de redémarrage peut donner une fausse impression de sécurité.
Sauvegardes : NixOS ne sauvegarde pas vos données
C’est un point à poser clairement.
La configuration NixOS permet de reconstruire le système. Elle ne restaure pas vos bases, vos volumes applicatifs, vos fichiers utilisateurs, vos certificats non versionnés, vos secrets, vos buckets MinIO ou vos dépôts Git.
Il faut donc séparer les sujets :
Configuration système : dépôt Git NixOS
Données applicatives : sauvegardes versionnées et testées
Secrets : coffre ou fichiers chiffrés
Volumes Docker : stratégie propre par service
Certificats : stockage et procédure de renouvellement
Un serveur NixOS mal sauvegardé reste un serveur mal sauvegardé.
La différence, c’est que le jour où l’on doit reconstruire, on a déjà une partie fiable du chemin : l’OS, les services et les conventions système.
Comparaison rapide avec Ubuntu, Debian et Ansible
Ubuntu et Debian restent d’excellentes bases serveur. Leur force est évidente : documentation abondante, profils disponibles, prestataires nombreux, compatibilité large. Dans une PME, cet argument pèse lourd.
Ansible répond aussi à beaucoup de besoins. Il sait configurer un parc hétérogène, pousser des changements, orchestrer des tâches. Pour des serveurs déjà existants, il est souvent plus simple à introduire que NixOS.
NixOS devient plus intéressant quand l’équipe veut que l’état système soit reconstruit depuis un modèle unique, avec un historique de générations et un verrouillage fin des paquets.
Le choix ressemble moins à un débat de distribution qu’à une question d’exploitation.
Est-ce que l’on veut administrer des serveurs existants plus proprement ? Ansible peut suffire.
Est-ce que l’on veut pouvoir recréer des machines entières depuis une configuration système versionnée ? NixOS mérite un vrai test.
Est-ce que l’équipe ne maîtrise pas encore Linux, Git et systemd ? NixOS risque d’ajouter une couche de difficulté au mauvais moment.
Les limites que je regarderais avant production
La première limite est la courbe d’apprentissage. NixOS demande de comprendre Nix, le système de modules, les options, la logique du store. Les erreurs de build sont parfois longues à lire. Un administrateur Linux expérimenté peut se sentir débutant pendant quelques semaines.
La deuxième limite concerne les logiciels hors standard. Certains éditeurs fournissent un binaire, une procédure .deb, un script d’installation ou un agent de supervision qui suppose une arborescence Linux classique. On trouve souvent une solution, mais pas toujours en cinq minutes.
La troisième limite est l’équipe. Un serveur NixOS compris par une seule personne devient un risque. La configuration doit être relue, documentée, testée. Sinon on remplace une dette shell par une dette Nix.
La quatrième limite est la gestion des secrets. Mettre des mots de passe en clair dans un dépôt NixOS est une erreur. Il faut choisir une approche : fichiers chiffrés, coffre externe, credentials systemd, secret manager. Même sur un petit serveur.
La cinquième limite est l’écosystème d’exploitation. Beaucoup de procédures fournisseurs commencent par apt install. Sur NixOS, ça ne s’applique pas directement. C’est gérable, mais il faut l’anticiper.
Une méthode d’adoption raisonnable
Je ne commencerais pas par migrer le serveur de production le plus critique.
Un chemin plus sain :
- choisir un serveur simple mais réel ;
- écrire une configuration minimale ;
- versionner le dépôt ;
- reconstruire la machine dans une VM ;
- tester les services, les ports, les sauvegardes ;
- documenter les commandes de rollback ;
- seulement ensuite, migrer un service plus exposé.
Pour un environnement EloNeva, je testerais d’abord sur un serveur d’outillage ou un reverse proxy secondaire. Traefik, Docker, quelques règles firewall, supervision, sauvegardes. C’est assez concret pour apprendre. Pas assez dangereux pour bloquer l’entreprise.
Le dépôt peut rester sobre :
infra-nixos/
├── flake.nix
├── flake.lock
├── modules/
│ ├── docker.nix
│ ├── openssh.nix
│ ├── traefik.nix
│ └── hardening.nix
└── hosts/
├── srv-prod-app/
│ ├── configuration.nix
│ └── hardware-configuration.nix
└── srv-outillage/
├── configuration.nix
└── hardware-configuration.nix
Les modules communs évitent de recopier les mêmes réglages partout. Les fichiers hosts gardent les différences de chaque machine.
Ce que cela change dans un audit serveur
Quand on audite un serveur Linux classique, on passe du temps à reconstituer l’existant.
Quels services sont actifs ? Quels paquets ont été installés à la main ? Qui a accès en SSH ? Quelles règles firewall sont nécessaires ? Quelle partie vient d’un script, d’une procédure ou d’une correction urgente ?
Avec NixOS, une partie de cet audit se déplace dans le dépôt.
On peut lire les options SSH, les services, les ports ouverts, les utilisateurs déclarés. On peut comparer deux machines. On peut relire l’historique Git. Ce n’est pas complet, mais c’est plus propre que de travailler uniquement depuis l’état vivant de la machine.
Pour une entreprise comme EloNeva, c’est probablement l’argument le plus concret. NixOS aide à transformer l’administration Linux en architecture relisible. Pas seulement en exploitation réussie le jour de l’installation.
Conclusion
NixOS n’est pas une réponse automatique à tous les sujets Linux.
Pour un serveur isolé, administré rarement, avec une procédure claire et des sauvegardes testées, le gain peut être modeste. Il faut être honnête là-dessus.
Pour des serveurs qui portent des briques critiques, des reverse proxies, des environnements clients, des conteneurs métier ou des services internes répétés, le modèle devient beaucoup plus intéressant. La configuration est versionnée. Les changements sont visibles. Les retours arrière sont intégrés. La reconstruction n’est plus une opération artisanale.
Dans une entreprise comme EloNeva, qui intervient souvent sur des architectures existantes, NixOS a surtout une vertu : il force à écrire ce que le serveur doit être. C’est moins confortable au départ. Mais sur la durée, un serveur que l’on peut relire, reconstruire et faire évoluer sans deviner son histoire, c’est un vrai gain d’exploitation.