Variables d’environnement expliquées : fichiers .env, secrets et configuration correctement effectués

  1. Accueil
  2. Blog
  3. DevOps

DevOps

Variables d’environnement expliquées : .Fichiers env, secrets et configuration correctement effectués

Les variables d’environnement permettent au même code de s’exécuter sur un ordinateur portable, en CI et en production avec des paramètres différents. Voici comment ils se comportent réellement, avec les surprises que nous avons eues en les testant.

8 min de lecture
Variables d'environnement expliquées : fichiers .env, secrets et configuration correctement effectués

À retenir

  • Les variables d’environnement sont des paramètres de valeur-clé qu’un processus reçoit de son environnement.
  • Ils gardent la configuration, comme les ports et les URL de base de données, hors de votre code.
  • Les valeurs sont toujours des chaînes, donc “false” est véridique à moins que vous ne l’analysiez.
  • Ne validez jamais .env fichiers avec de vrais secrets et validez les paramètres requis au démarrage de l’application.

Votre application a besoin d’une URL de base de données. Sur votre ordinateur portable, il s’agit d’une base de données locale, en CI une base de données jetable et en production un cluster géré. Coder en dur l’un d’entre eux signifie changer le code pour chaque environnement. Les variables d’environnement résolvent ce problème. Le code se lit DATABASE_URL et chaque environnement fournit sa propre valeur.

Cette idée est au cœur de la méthode Twelve-Factor App, qui stipule que la configuration qui varie entre les déploiements appartient à l’environnement. Nous avons testé le comportement ci-dessous avec Python 3.13 sous Windows. Chaque sortie affichée provient de cette exécution.

Que sont les variables d’environnement ?

Chaque processus en cours d’exécution possède un ensemble de valeurs de texte nommées appelé son environnement. Des exemples familiers sont PATH, qui indique au shell où trouver les programmes, et HOME ou USERPROFILE. Votre propre application peut utiliser le même mécanisme pour des paramètres tels que APP_PORT, LOG_LEVEL ou DATABASE_URL.

Définition des variables d’environnement

Coquille Pour cette séance Pour une commande
Bash/zsh exporter APP_PORT=8080 APP_PORT=8080 python app.py
PowerShell $env:APP_PORT = "8080" Définissez-le, exécutez, puis supprimez-le
Commande Windows définir APP_PORT=8080 Non intégré

Celles-ci ne durent que pour la session de terminal en cours. Pour les rendre permanents, ajoutez-les à un profil shell tel que ~/.bashrc ou, sous Windows, utilisez les paramètres d’environnement système ou setx. Pour les applications, il est généralement préférable de les fournir au moment du déploiement plutôt que de les définir à l’échelle du système.

Les lire en code

import os

port = os.environ.get("APP_PORT", "8000")   # default if missing
print(port)                                  # 8000

db = os.environ["DATABASE_URL_MISSING_DEMO"]
# KeyError: 'DATABASE_URL_MISSING_DEMO'

Utilisez .get() avec une valeur par défaut pour les paramètres facultatifs. Utilisez des crochets, qui élèvent KeyError, pour les paramètres sans lesquels l’application ne peut pas fonctionner. D’autres langages sont similaires : process.env.APP_PORT dans Node.js, System.getenv("APP_PORT") en Java et os.Getenv("APP_PORT") dans Go.

Tout est une chaîne

Cela attire les gens dans toutes les langues. Nous avons défini un port puis y avons ajouté « 1 » :

os.environ["APP_PORT"] = "8080"
port = os.environ["APP_PORT"]
print(type(port).__name__, repr(port), port + "1")
# str '8080' 80801

os.environ["APP_PORT"] = 8080
# TypeError: str expected, not int

La valeur est revenue sous forme de texte et l’ajout a produit 80801, et non 8081. Convertissez explicitement, comme dans int(os.environ["APP_PORT"]). Les booléens sont pires :

os.environ["DEBUG"] = "false"
print(bool(os.environ["DEBUG"]))   # True

Toute chaîne non vide est véridique en Python, donc "false" est devenu True. Le mode débogage aurait été activé en production. Analyser délibérément les booléens :

def env_bool(name, default=False):
    value = os.environ.get(name)
    if value is None:
        return default
    return value.strip().lower() in {"1", "true", "yes", "on"}

env_bool("DEBUG")                   # False
env_bool("NOT_SET", default=True)   # True

Comment les processus en héritent

Un processus enfant reçoit une copie de l’environnement de son parent. Nous avons démarré les processus Python enfants pour voir ceci :

child sees: 8080 inherited     # default: full copy of the parent's environment
child sees: 9000 None          # given a custom env: only what we passed
parent sees child var: None    # a child's changes never flow back

Trois leçons pratiques suivent. Les variables que vous exportez sont visibles par chaque programme que vous lancez, y compris les scripts que vous n’avez pas écrits, ce qui est important pour les secrets. Passer un environnement personnalisé remplace le tout, alors incluez tout ce dont l’enfant a besoin. Et un script ne peut pas modifier les variables de son shell parent, c’est pourquoi les outils vous demandent de source un fichier au lieu de l’exécuter.

Fichiers .env

Taper de nombreuses variables à la main devient fastidieux, donc la plupart des projets conservent les valeurs de développement local dans un fichier .env :

# local development only
APP_PORT=8080
DATABASE_URL="postgres://dev:devonly@localhost:5432/shop"
FEATURE_SEARCH=true

Les bibliothèques telles que python-dotenv et dotenv pour Node.js chargent ce fichier au démarrage. Node.js 20.6 et versions ultérieures peuvent également en charger un de manière native avec --env-file. Pour voir l’idée, nous avons écrit un petit analyseur, qui a transformé le fichier ci-dessus en :

{'APP_PORT': '8080',
 'DATABASE_URL': 'postgres://dev:devonly@localhost:5432/shop',
 'FEATURE_SEARCH': 'true'}

Utilisez une vraie bibliothèque en pratique. Il gère les guillemets, les échappements, les commentaires et les valeurs multilignes que notre version 10 lignes ignore. Par défaut, la plupart des chargeurs dotenv ne remplacent pas les variables déjà définies, de sorte que les valeurs d’environnement réelles l’emportent sur le fichier. C’est généralement ce que vous voulez.

Astuce : Ajoutez .env à .gitignore avant votre premier commit et validez plutôt un .env.example avec chaque nom de variable et les valeurs d’espace réservé sécurisées. Les nouveaux développeurs le copient et remplissent leurs propres valeurs.

Valider la configuration au démarrage

Une variable manquante devrait arrêter l’application immédiatement avec un message clair, sans provoquer d’erreur déroutante une heure plus tard. Nous avons vérifié les paramètres requis lors du chargement :

REQUIRED = ["DATABASE_URL", "SECRET_KEY"]

def check_required(env):
    missing = [k for k in REQUIRED if not env.get(k)]
    if missing:
        raise SystemExit(f"Missing required settings: {', '.join(missing)}")

# Missing required settings: SECRET_KEY

Les bibliothèques de configuration typées vont plus loin. Les paramètres Pydantic en Python, zod ou envalid dans Node.js et les propriétés de configuration de Spring en Java convertissent tous les types et signalent chaque problème à la fois.

Variables d’environnement et secrets

Les variables d’environnement sont le moyen courant de transmettre des secrets tels que des clés API à une application, et elles sont bien meilleures que de les coder en dur. Ils ont des faiblesses à connaître :

  • Les processus enfants en héritent, comme notre test l’a montré.
  • Ils peuvent apparaître dans les rapports d’erreur, les pages de débogage et les journaux qui vident l’environnement.
  • Sur certains systèmes, d’autres processus exécutés par le même utilisateur peuvent les lire.

Pour la production, de nombreuses équipes conservent les secrets dans un gestionnaire dédié, tel que HashiCorp Vault, AWS Secrets Manager, Azure Key Vault ou Google Secret Manager, et les injectent au moment de l’exécution. Certains préfèrent monter les secrets sous forme de fichiers plutôt que de variables. Quoi qu’il en soit, n’imprimez jamais la configuration dans les journaux et alternez tout secret jamais validé dans Git, même après l’avoir supprimé de l’historique.

Dans Docker et CI

# Docker (not run for this article)
docker run -e APP_PORT=8080 --env-file .env.production myapp

# GitHub Actions (not run for this article)
env:
  APP_PORT: 8080
  API_KEY: ${{ secrets.API_KEY }}

Les plateformes CI stockent les secrets cryptés et les masquent dans les journaux. Évitez d’intégrer des secrets dans les images Docker avec ENV dans le Dockerfile, car ils restent lisibles dans les calques d’image. Consultez notre didacticiel Docker Compose et guide GitHub Actions CI/CD pour les configurations complètes.

Meilleures pratiques

  • Utilisez des noms clairs en majuscules avec un préfixe, tel que SHOP_DB_URL.
  • Lisez toute la configuration dans un module, convertissez-y les types et validez au démarrage.
  • Fournissez des valeurs par défaut sûres uniquement pour les paramètres non sensibles.
  • Gardez .env hors de Git et maintenez un .env.example.
  • Utilisez un gestionnaire de secrets en production pour les valeurs sensibles.
  • Documentez chaque variable, son type et un exemple de valeur.

Problèmes de variables d’environnement de débogage

Lorsqu’une application se comporte comme si un paramètre manquait, effectuez ces vérifications dans l’ordre :

  1. Est-elle placée au bon endroit ? Une variable exportée dans un onglet du terminal n’existe pas dans un autre, ou dans un service démarré par le système.
  2. L’application a-t-elle été redémarrée ? Un processus en cours d’exécution conserve l’environnement avec lequel il a démarré. La modification ultérieure d’une variable n’a aucun effet jusqu’au redémarrage.
  3. Le nom est-il exact ? Les noms sont sensibles à la casse sous Linux et macOS, donc App_Port et APP_PORT sont des variables différentes. Windows les traite de la même manière.
  4. Un fichier .env le remplace-t-il ou est-il ignoré ? Vérifiez si votre chargeur remplace les valeurs existantes et quel fichier il lit réellement.
  5. Y a-t-il des guillemets ou des espaces parasites ? Une valeur copiée avec un espace de fin ou des guillemets supplémentaires peut ne pas être analysée ou ne pas correspondre, d’une manière difficile à repérer.

Pour voir ce qu’un processus a réellement reçu, affichez les noms des variables, jamais les valeurs des secrets. Sous Linux et macOS, printenv répertorie l’environnement du shell actuel, et dans PowerShell Get-ChildItem Env: fait de même. À l’intérieur d’un conteneur, l’exécution de env à docker exec affiche la vue du conteneur, qui est souvent différente de celle de votre propre shell.

Un dernier conseil : enregistrez une ligne au démarrage qui répertorie les paramètres facultatifs qui utilisent leurs valeurs par défaut. Cela ne coûte rien et répond surtout à « pourquoi fait-il cela ? » des questions tout de suite.

Pour conclure

Les variables d’environnement sont simples, mais elles cachent quelques pièges : tout est une chaîne, les enfants en héritent et des secrets peuvent s’y échapper. Traitez-les comme une entrée dans votre application. Lisez-les en un seul endroit, convertissez-les et validez-les rapidement, et gardez les vrais secrets des fichiers qui sont validés.

Questions fréquentes

Qu’est-ce qu’une variable d’environnement ?

Il s’agit d’une valeur de texte nommée qu’un processus reçoit de son environnement, telle que APP_PORT=8080. Les applications les utilisent pour les paramètres qui changent entre les environnements.

Qu’est-ce qu’un fichier .env ?

Un fichier texte de lignes KEY=value, chargé au démarrage par une bibliothèque telle que python-dotenv. Il est principalement utilisé pour le développement local et ne doit pas être confié à de véritables secrets.

Pourquoi ma variable d’environnement est-elle une chaîne ?

Les systèmes d’exploitation stockent les valeurs d’environnement sous forme de texte. Convertissez les nombres avec int() et analysez explicitement les booléens, car la chaîne « false » compte comme vraie.

Les variables d’environnement sont-elles sécurisées pour les secrets ?

Ils sont meilleurs que le codage en dur, mais les processus enfants en héritent et les journaux peuvent les exposer. Les systèmes de production utilisent souvent un gestionnaire de secrets dédié.

Un script peut-il modifier les variables de son shell parent ?

Non. Un processus enfant obtient une copie de l’environnement, ses modifications ne sont donc pas répercutées. Pour définir des variables dans le shell actuel, sourcez plutôt le fichier.

Continuez à apprendre

Travailler avec Stack Quora

Besoin d’aide pour les tests, WordPress ou le référencement ?

Nous aidons les équipes à proposer de meilleurs logiciels et à développer leurs sites. Dites-nous sur quoi vous travaillez et nous vous proposerons une prochaine étape claire.

Continuez à lire

Plus de guides sur tests, IA et développement moderne

Vous avez apprécié cet article ? Choisissez un sujet et accédez directement à nos nouveaux didacticiels.

Intelligence artificielle

Les hallucinations de l'IA expliquées : pourquoi les modèles de langage inventent les choses et comment les réduire

Les hallucinations de l'IA ont expliqué : ce qu'elles sont, pourquoi les modèles de langage les produisent, les types courants, comment les réduire …

Lire l'article →
DevOps

Variables d'environnement expliquées : fichiers .env, secrets et configuration correctement effectués

Variables d'environnement expliquées avec des exemples Python que nous avons exécutés : lecture et définition, pourquoi chaque valeur est une chaîne…

Lire l'article →
Développement logiciel

Limitation du débit de l'API expliquée : algorithmes, en-têtes et gestion des erreurs 429

Limitation du débit de l'API expliquée avec une simulation que nous avons exécutée : compartiment de jetons, fenêtre fixe, fenêtre glissante, 429 ré…

Lire l'article →
Automatisation des tests

Se moquer en Python : unittest.mock, patch et les erreurs qui font mentir les tests

Moquerie en Python avec unittest.mock, testée : Mock et return_value, side_effect, correctifs où un nom est utilisé, spec et autospec, dates moqueus…

Lire l'article →
Tests logiciels

Couverture du code expliquée : couverture des lignes par rapport aux branches et ce que signifie réellement 100 %

Couverture du code expliquée avec une véritable exécution de cover.py : couverture des lignes, des branches et des conditions, lecture d'un rapport,…

Lire l'article →
Bases de données et SQL

DELETE vs TRUNCATE vs DROP dans SQL : différences, restauration et quand les utiliser

DELETE vs TRUNCATE vs DROP expliqué avec SQL testé : ce que chacun supprime, restauration, déclencheurs, réinitialisation d'identité, vitesse et en …

Lire l'article →
Bases de données et SQL

Vues SQL expliquées : créez, utilisez et évitez les pièges, avec des exemples

Vues SQL expliquées avec les requêtes que nous avons exécutées : création d'une vue, résultats en direct, masquage des colonnes, performances, vues …

Lire l'article →
Développement logiciel

Interface TypeScript vs Type : différences réelles, testées avec le compilateur

Interface TypeScript vs type, vérifiée avec le vrai compilateur : ce que les deux peuvent faire, ce qu'un seul peut faire, la fusion des déclaration…

Lire l'article →
Développement logiciel

Python *args et **kwargs expliqués : fonctions flexibles avec des exemples clairs

Python *args et **kwargs expliqués avec le code que nous avons exécuté : emballage et déballage, ordre des paramètres, arguments de mot-clé uniqueme…

Lire l'article →
Développement logiciel

Fonctions Python Lambda : syntaxe, utilisations réelles et quand ne pas les utiliser

Fonctions Python lambda expliquées avec le code que nous avons exécuté : syntaxe, tri avec clé, carte et filtre, fermetures, piège de liaison tardiv…

Lire l'article →
Intelligence artificielle

Que sont les agents IA ? Comment ils fonctionnent, avec une simple boucle d'agent que vous pouvez exécuter

Que sont les agents IA ? Un guide simple sur la boucle d'agent, les outils, la mémoire et les garde-fous, avec une petite boucle Python que nous avo…

Lire l'article →
DevOps

Surveillance vs observabilité : la vraie différence, avec les journaux, les métriques et les traces

Surveillance et observabilité expliquées : échecs connus et inconnus, journaux, métriques et traces, présentation pas à pas des incidents de paiemen…

Lire l'article →
DevOps

Volumes Docker vs montages Bind : conserver les données du conteneur de la bonne manière

Volumes Docker et montages de liaison expliqués : pourquoi les données du conteneur disparaissent, volumes nommés, montages de liaison, tmpfs, exemp…

Lire l'article →
Automatisation des tests

TDD vs BDD : principales différences, une fonctionnalité conçue dans les deux sens et quand les utiliser

TDD vs BDD expliqué avec une fonctionnalité de remise construite dans les deux sens : refactor rouge-vert avec le code que nous avons exécuté, scéna…

Lire l'article →
Développement logiciel

Git merge vs rebase : différences, exemples et quand utiliser chacun

Git merge vs rebase expliqué avec de vraies sorties de log, un tableau comparatif, la règle d'or du rebase et un flux de travail sûr pour les équipes.

Lire l'article →
Développement logiciel

List comprehension Python : syntaxe, exemples et cas à éviter

La list comprehension Python expliquée : syntaxe, filtres, if-else, boucles imbriquées, temps mesurés et cas où une boucle for classique est préféra…

Lire l'article →
Bases de données et SQL

Jointures SQL expliquées avec exemples (Inner, Left, Right, Full)

Comprenez les jointures SQL pas à pas avec des tables et requêtes d'exemple : INNER, LEFT, RIGHT, FULL, CROSS et SELF, plus erreurs courantes et con…

Lire l'article →