T
Developerdevopssecurityconfiguration

Environment Config Without Secret Leaks

A practical guide to secure environment configuration without exposing secrets.

TechHowAI Team11 min read

Environment Config Without Secret Leaks

id: hook

Avoid accidental secret exposure in config files by treating environment variables as immutable. Never hardcode secrets in .env or config.yaml—use a secrets manager like AWS Secrets Manager or HashiCorp Vault. Always validate that .env files are excluded from version control with .gitignore.

Use JSON or YAML formatters to ensure consistent indentation and syntax, reducing human error. JSON Formatter and YAML Formatter help catch formatting issues before they hit production.

Never store API keys, passwords, or database credentials in .env files. Instead, reference secrets via environment variables injected at runtime. For example, DATABASE_URL should point to a secret stored in Vault, not the actual connection string.

If you must store config in a file, keep it minimal and focused on non-sensitive values like feature flags or log levels. Secrets should never be in the same file as app-specific settings.

Use a CI/CD pipeline to enforce .env file validation. Tools like dotenv or env2config can check for missing or invalid variables before deployment.

Avoid using .env files in monorepos unless you're certain they're not shared across services. Secrets should flow through a dedicated secrets pipeline, not be scattered in multiple .env files.

If you're using Docker, mount secrets as volumes or use docker secrets to keep them out of the image. Never commit Dockerfiles that include secrets.

For teams, enforce a config hygiene policy: all .env files must be in a .gitignore, and any changes to secrets must go through a secrets management workflow.

Never assume that a config file is safe. Always audit your config files for secrets, and use tools like trufflehog or git-secrets to scan for accidental leaks.

DevOps team deploying microservices

id: use-case

You're deploying a microservice with a PostgreSQL backend. The app uses a .env file to read DB credentials. You need to ensure secrets never hit Git. Use a .env.example file with placeholder values, and never commit the real ones. Store secrets in a vault like AWS Secrets Manager or HashiCorp Vault, and inject them at runtime.

If you're using Docker, mount secrets as a volume or use --env-file with a file that's not in Git. For example:

dockerfile
ENV_FILE=/run/secrets/db_credentials.env

In your app, read the .env file with a library like dotenv or python-dotenv. Always validate that the environment variables are set before starting the service. If a secret is missing, the app should fail fast.

Never include .env files in .gitignore unless you're using a CI/CD tool that handles secrets. Instead, use a .env.example to document required variables. For example:

env
DB_USER=your_db_user
DB_PASSWORD=your_db_password
DB_HOST=localhost
DB_PORT=5432

If you're using a CI/CD pipeline, ensure that secrets are injected via the pipeline's secret management, not through the .env file. This avoids leaking secrets during testing or staging.

Secrets flow from vault to app

id: diagram

Common mistakes in config management

id: faq

Q: What's the biggest mistake in config management?
A: Committing .env files to Git. Always use .env.example and keep real secrets in a vault.

Q: How do I handle config in a multi-environment setup?
A: Use environment-specific .env files and ensure they're not committed. Use a CI/CD pipeline to inject secrets during deployment.

12-factor config with .env files

id: walkthrough

Use dotenv to load .env files into process.env. Always store secrets in a vault, never in .env. For example, in a Node.js app, you'd run:

js
require('dotenv').config();
console.log(process.env.DB_PASSWORD);

Keep .env files out of version control. Add them to .gitignore. Never commit .env to GitHub. Use a .env.example to show structure, but never include real secrets.

If you're using Vault, set VAULT_ADDR and VAULT_TOKEN in your shell before running the app. Vault can inject secrets into process.env via environment variables. For example:

bash
export VAULT_ADDR='http://localhost:8200'
export VAULT_TOKEN='s.1234567890abcdef'

Never store API keys, passwords, or tokens in .env. Use Vault or a secrets manager. If you must store something in .env, it should be a configuration value, not a secret.

Use json-formatter or yaml-formatter to keep config files readable. Avoid markdown in config files. Always validate that process.env has the required keys before starting the app.

If you're deploying to a server, ensure the server doesn't expose process.env in logs. Use console.log sparingly. Never print secrets to stdout.

For microservices, use a shared vault for secrets. Each service reads from the same vault. Avoid per-service .env files. Use a single .env file per environment.

If you're using Docker, mount .env files from a volume. Never hardcode secrets in Dockerfiles. Use docker run with -e flags for environment variables.

Never use .env files for production secrets. Always use a secrets manager. If you're in a hurry, use a .env file for dev, but never for prod.

Secrets flow from vault to app

This flowchart illustrates how secrets and configuration data move from secure sources to your application. The Secrets Manager (like AWS Secrets Manager or HashiCorp Vault) is the primary source of sensitive data, such as API keys and database passwords. These secrets are injected into the environment variables at runtime, which are then read by the application.

The .env file is used to store non-sensitive configuration, such as feature flags or logging levels. It should never contain secrets, as it may be accidentally committed to version control. Always use a tool like json-formatter or yaml-formatter to ensure consistent formatting and avoid syntax errors.

In a CI/CD pipeline, environment variables are often set directly in the pipeline configuration, ensuring secrets are never exposed in logs or code. This approach aligns with the 12-factor methodology, where configuration is externalized and managed separately from the codebase.

Avoid storing secrets in .env files or version control. Instead, use a secrets manager and inject secrets at runtime. This minimizes the risk of accidental exposure and ensures your application remains secure across different environments.

Common mistakes in config management

id: pitfalls

Q: What's the biggest mistake when managing environment configs?

A: Committing secrets to version control. Developers often add .env files to git, thinking they're safe, but a single git push can leak API keys, database passwords, or AWS credentials. Always use .gitignore and a secrets manager like AWS Secrets Manager or Vault.

Q: How do I avoid accidental commits of config files?

A: Use .gitignore and a linter like ESLint or pre-commit to catch accidental commits. For example, add this to your .gitignore:

code
.env
.env.local

Q: What if I need to test with different configs?

A: Use environment-specific files like .env.development, .env.production, and .env.staging. Never merge them into a single file. Tools like dotenv can load these conditionally based on the environment.

Q: Should I store all config in a secrets manager?

A: No. Store secrets in a manager, but use .env files for non-sensitive config like API endpoints, log levels, or feature flags. Secrets managers are for keys, not for app-specific settings.

Q: What about when I need to share configs with a team?

A: Use a shared .env.example file to document required variables. Never share .env files directly. Tools like json-formatter or yaml-formatter help keep config files clean and readable.

When to skip environment configs

If you're the sole developer on a single-machine project with no secrets, environment configs are a waste of time. You don't need .env files, JSON, or YAML — just run the app and be done. No need to parse configs, no need to worry about leaks.

Q: What if I'm using a framework that requires config files?
A: Skip it. Use hardcoded values or environment variables directly. If the framework insists on a config file, write it once and never check it into Git. You're not in a team, so you don't need the overhead.

Q: But what about CI/CD?
A: If you're not deploying, you don't need config. If you are, use a CI/CD tool that supports secrets management. Don't store anything in Git. If you're using GitHub Actions, use secrets and env variables. If you're using GitLab, use CI/CD secrets. Don't use .env files in CI.

Q: What about testing?
A: Use in-memory mocks or test-specific config files. Don't check them into Git. If you're writing unit tests, use @mock or @patch to avoid config altogether. If you're doing integration tests, use a test database and a test config file that's not shared.

Q: What if I'm using a framework like Next.js or Flask that expects a config?
A: Use a single config file that's not checked into Git. For example, config.js that exports a single object. You can write it once and use it in all environments. Don't use .env files unless you're in a team.

Q: What about when I'm prototyping?
A: Use hardcoded values. You're not going to deploy this, so no need for config. If you're writing a script, use process.env or a config object. Don't use .env unless you're going to ship it.

If you're not deploying, not sharing code, and not using secrets, environment configs are a distraction. Save your time and skip the config.

Q: How do I avoid leaking secrets in environment config files?
A: Store secrets in a vault like AWS Secrets Manager or Vault, and reference them via environment variables. Never hardcode secrets in .env or config.yaml. Use .env.example for structure, but keep secrets out of version control.

Q: What should never be in a git repo for config?
A: Any secret, API keys, or credentials. Also avoid full config files with sensitive data. Use .env files with placeholders for development, and ensure .env is in .gitignore. Tools like JSON Formatter help structure non-sensitive config safely.

Q: How to format environment config files without errors?
A: Use YAML Formatter to ensure proper indentation and syntax. For .env files, keep lines simple and avoid special characters. Always validate config files before deployment with tools like dotenv or env2config.

Secure config with minimal friction

id: conclusion

You want your .env files to be readable by humans, but not by Git. Always use a .env file with ENV=production and ENV=staging as the only variables in your .env files. Never store secrets in .env files. Use Vault to inject secrets at runtime. If you must store secrets in .env, use a tool like Dotenv to load them from a separate file that's not committed.

For teams, use a JSON or YAML formatter like JSON Formatter or YAML Formatter to ensure consistent formatting of config files. This helps avoid merge conflicts and makes diffs more readable.

Never commit .env files to Git. Always use .env.example as a template and keep the real .env file in .gitignore. If you're using a CI/CD pipeline, make sure it's configured to load secrets from Vault or another secure source.

If you're using Docker, mount your .env file as a volume and ensure it's not exposed to the host. Use envsubst or dotenv in your Dockerfile to inject environment variables from .env into the container.

For microservices, use a centralized secret management system like Vault. It allows you to manage secrets across multiple services and environments. Vault can also be used to inject secrets into your .env files at runtime.

If you're using a framework like Express or Flask, make sure you're not exposing environment variables in logs or error messages. Always sanitize logs and avoid logging sensitive data.

In summary: use .env files for configuration, not secrets. Store secrets in Vault. Keep .env files out of Git. Use formatters to keep config files clean. Always validate that your app isn't leaking secrets through logs or error messages.

Put what you learned into practice — no signup required.