J'ai eu la chance de présenter Mise à Devoxx France 2026 il y a quelques jours. J'utilise cet outil depuis plusieurs mois, et je trouvais intéressant de partager mon utilisation avec vous. Voici un retour d'expérience sur mon setup.
Si vous souhaitez une introduction plus complète vous pourrez bientôt voir la vidéo youtube, sinon les slides sont dispos ici.
Le problème : jongler entre contextes
Je suis consultant Cloud Native Infra, et je gère au quotidien plus d'une dizaine de projets avec chacun leurs clusters Kubernetes en dev et en prod, hébergés sur des zones différentes. Chaque projet a ses propres versions d'outils, ses variables d'environnement, ses secrets, ses scripts...
Au quotidien, ça donne grosso modo ça :
1## Projet A - Dev - Helium ☕
2$ java --version # openjdk 25.x
3$ python --version # 3.14.x
4$ kubectl version # v1.35.x
5$ export KUBECONFIG=~/.kube/helium
6$ export ANSIBLE_INVENTORY=inventories/helium/dev
7
8## Projet B - Prod - Lithium ⚓
9$ java --version # openjdk 17.x
10$ python --version # 3.11.x
11$ kubectl version # v1.33.x
12$ export KUBECONFIG=~/.kube/lithium
13$ export ANSIBLE_INVENTORY=inventories/lithium/prod
Concrètement :
- 🔄 Versions d'outils différentes selon les projets
- 🌍 Variables d'environnement spécifiques à chaque contexte
- 📜 Scripts custom à maintenir
- ⚠️ Oublier de switcher = bugs, erreurs, frustration (😱 le
terraform plansur le mauvais projet qui demande de redéployer un cluster, c'est arrivé ^^)
Avant, je faisais tout cela avec un mix d'outils :
| Outil | Ce qu'il fait |
|---|---|
| asdf | Gestion de versions d'outils |
| direnv | Variables d'env par répertoire |
| sdkman / venv / nvm | Version manager (1 langage) |
| Scripts shell | Tout le reste (notamment source de secrets) |
Ça marchait, mais c'était perfectible. Et puis j'ai (re)découvert Mise, un outil créé par @jdx en 2023, écrit en Rust (donc vachement rapide), qui agrège dans un seul binaire asdf + direnv + un task runner, avec une activation automatique par répertoire, le tout configurable simplement avec des fichiers au format TOML.
Pour l'installation, c'est un simple curl https://mise.run | sh puis eval "$(mise activate zsh)" dans le shell. Je ne vais pas refaire la doc ici, vous trouverez tout sur mise.jdx.dev, je préfère vous montrer mon vrai setup.
Mon setup : Un dizaine de projets & clusters Kubernetes
J'ai une arborescence du genre :
1projects-kubernetes-clusters/
2├── calcium/ # Projet A
3│ ├── calcium-dev/
4│ └── calcium-prod/
5├── lithium/ # Projet B
6│ ├── lithium-dev/
7│ └── lithium-prod/
8├── helium/ # Projet C
9│ ├── helium-dev/
10│ │ └── ansible/
11│ └── helium-prod/
12└── ... # 10+ projets identiques
Et la magie de Mise, c'est que la configuration est hiérarchique sur 3 niveaux :
- Global (
~/.config/mise/config.toml) : ce qui s'applique partout (proxy WireGuard par exemple) - Projet parent (
calcium/mise.toml) : variables OVH, endpoints, project name - Environnement spécifique (
calcium/calcium-dev/mise.toml) : nom du cluster, kubeconfig
Niveau global
Au niveau de mon home, j'ai juste de quoi sourcer mon proxy WireGuard quand je suis chez un client :
1# ~/.config/mise.toml
2[env]
3# WireGuard Proxy
4_.source = ["~/.config/mise/proxy.sh"]
Niveau projet parent
Au niveau du projet parent, je définis les variables communes à tous les environnements (dev + prod), et j'active fnox pour les secrets :
1# calcium/mise.toml
2[env]
3OS_REGION_NAME = "GRA7"
4OVH_ENDPOINT = "ovh-eu"
5OS_AUTH_URL = "https://auth.cloud.ovh.net/v3/"
6
7VAULT_ADDR = "https://vault.gravitek.io"
8VAULT_OVH_PROJECT = "calcium"
9TF_VAR_project_name = "{{env.VAULT_OVH_PROJECT}}"
10
11# fnox pour les secrets
12_.fnox-env = { tools = true }
13
14[plugins]
15fnox-env = "https://github.com/jdx/mise-env-fnox"
Les secrets sont gérés via fnox, l'outil compagnon de Mise. Chez moi, j'utilise principalement HashiCorp Vault pour ce qui est partagé. La conf tient dans un fnox.toml au niveau du projet :
1# calcium/fnox.toml
2[providers.vault-ovh-project-api]
3type = "vault"
4address = "https://vault.gravitek.io"
5path = "secret/myorg/ovh/calcium"
6
7[providers.vault-ovh-project-openstack]
8type = "vault"
9address = "https://vault.gravitek.io"
10path = "secret/myorg/ovh/calcium/openstack"
11
12[secrets]
13OVH_APPLICATION_KEY = { provider = "vault-ovh-project-api", value = "API_CREDENTIALS/OVH_APPLICATION_KEY" }
14OVH_APPLICATION_SECRET = { provider = "vault-ovh-project-api", value = "API_CREDENTIALS/OVH_APPLICATION_SECRET" }
15OS_USERNAME = { provider = "vault-ovh-project-openstack", value = "admin/OS_USERNAME" }
16OS_PASSWORD = { provider = "vault-ovh-project-openstack", value = "admin/OS_PASSWORD" }
Et pour le VAULT_TOKEN lui-même, je le stocke dans le Keychain macOS, ce qui m'évite de l'avoir en clair quelque part :
1[providers.keychain]
2type = "keychain"
3service = "fnox"
4
5[secrets]
6VAULT_TOKEN = { provider = "keychain", value = "VAULT_TOKEN" }
Niveau environnement spécifique
Au niveau de l'environnement spécifique, j'ajoute le nom du cluster, l'URL, et surtout un hook qui auto-switch le contexte kubectl à l'entrée du répertoire :
1# calcium/calcium-dev/mise.toml
2[env]
3CLUSTER_NAME = "calcium-dev"
4CLUSTER_URL = "https://xxx.k8s.ovh.net"
5
6[hooks] # mode expérimental
7enter = """
8if [ -n "$CLUSTER_NAME" ] && kubectl config get-contexts "$CLUSTER_NAME" &>/dev/null; then
9 kubectl config use-context "$CLUSTER_NAME"
10fi
11"""
Plus de kubectx à la main, plus de risque de terraform plan sur le mauvais cluster 🙏.
Bonus : sous-répertoire Ansible avec venv Python
Pour mes répertoires Ansible, j'ajoute encore un niveau de configuration avec un venv Python dédié grâce à uv, et des tâches pour installer les dépendances :
1# helium/helium-dev/ansible/mise.toml
2[tools]
3python = "3.14"
4
5[env]
6_.python.venv = { path = ".venv", create = true, python = "3.14", uv_create_args = ['--seed'] }
7
8[tasks.install]
9description = "Installer les dépendances et les rôles Ansible"
10run = ["uv pip install ansible ansible-lint python-gitlab",
11 "ansible-galaxy install -r roles/requirements.yml -p roles --force"]
Le résultat au quotidien
Concrètement, en switchant de répertoire :
1$ cd projects/calcium/calcium-dev/
2🔑 [MISE] Environnement CALCIUM détecté.
3$ echo $PROJECT_NAME
4calcium
5$ kubectl config current-context
6calcium-dev
7$ echo $OVH_APPLICATION_SECRET
8s3cr3t_p4ssw0rd # résolu depuis Vault automatiquement
9
10$ cd ../../lithium/lithium-prod/
11🔑 [MISE] Environnement LITHIUM détecté.
12$ echo $PROJECT_NAME
13lithium
14$ kubectl config current-context
15lithium-prod
Je cd dans un répertoire, tout est automatiquement chargé : versions d'outils, variables d'env, secrets résolus depuis Vault ou le Keychain, contexte kubectl switché. Zéro friction, zéro oubli.
Au final, Mise a remplacé chez moi : asdf, direnv, mes scripts shell custom, et même les virtualenvs python. C'est devenu le point d'entrée de mes projets.
Ressources
- Documentation : mise.jdx.dev & fnox.jdx.dev
- GitHub : jdx/mise & jdx/fnox
- Slides Devoxx France 2026 : presentations.verchere.fr/Mise_Devoxxfr_2026
- Dépôt de démo : github.com/gravitek-io/mise-demo
- Blog Julien Wittouck : Adieu direnv, bonjour mise
- Blog Stéphane Robert : Mise