env.dev

How to Share .env Files With Your Team Securely

Never commit .env files to Git. Compare secure sharing methods: 1Password CLI, Doppler, AWS Secrets Manager, HashiCorp Vault, and encrypted git with SOPS.

Last updated:

Sharing .env files between team members is one of the most common sources of credential leaks. A single accidental commit exposes API keys, database passwords, and third-party tokens to anyone with repository access — and if the repo is public, to the entire internet. Git history is permanent: even if you delete the file in a follow-up commit, the secret remains in the log. The solution is never to commit .env files at all, use a .env.example file to document required variables, and adopt a secrets sharing tool that matches your team size and security requirements.

Why should you never commit .env files to Git?

A .env file typically contains production database URLs, API keys with billing implications, OAuth client secrets, and encryption keys. Committing it means every contributor, every CI runner, and every fork has access. GitHub automatically scans for known secret patterns and will revoke some tokens, but coverage is incomplete and the damage is often done before detection.

Add .env to your .gitignore before the first commit. If you already have a repository, add the rule and remove the cached file:

.gitignore setup
# Add to .gitignore
echo ".env" >> .gitignore
echo ".env.local" >> .gitignore
echo ".env.*.local" >> .gitignore

# If .env was already tracked, remove it from the index
git rm --cached .env 2>/dev/null
git commit -m "chore: stop tracking .env"

A minimal .gitignore block for environment files:

.gitignore
# Environment files — never commit secrets
.env
.env.local
.env.*.local
.env.production
.env.staging

What is the .env.example pattern?

A .env.example file is a committed template that lists every required variable with placeholder values. It serves as documentation, onboarding aid, and a validation reference. New team members copy it to .env and fill in real values. The rule is simple: include every key, exclude every real value.

.env.example
# Database
DATABASE_URL=postgresql://user:password@localhost:5432/myapp

# Auth provider
AUTH_SECRET=generate-a-random-string-here
GITHUB_CLIENT_ID=your-github-oauth-client-id
GITHUB_CLIENT_SECRET=your-github-oauth-client-secret

# Third-party APIs
STRIPE_SECRET_KEY=sk_test_replace_me
SENDGRID_API_KEY=SG.replace_me

# Feature flags (non-secret, safe defaults)
ENABLE_BETA_FEATURES=false
LOG_LEVEL=info

Validate that your actual .env contains all required keys using the env validator. For a deeper dive into .env syntax and parser behavior, see the .env guide.

What are the best options for sharing secrets with your team?

Pasting secrets in Slack or email is insecure and unauditable. Use a dedicated tool instead. The right choice depends on your team size, budget, and compliance requirements.

1Password / Bitwarden CLI

Password managers with CLI support let you store .env values in a shared vault and inject them at runtime. This is the simplest option for small teams that already use a password manager.

1Password CLI — inject secrets
# Reference secrets in .env with 1Password references
# .env (uses op:// references instead of real values)
# DATABASE_URL=op://Engineering/database/url
# STRIPE_KEY=op://Engineering/stripe/secret-key

# Inject secrets at runtime
op run --env-file=.env -- node server.js

# Or export a single secret
op read "op://Engineering/database/url"
Bitwarden CLI — retrieve secrets
# Unlock the vault
export BW_SESSION=$(bw unlock --raw)

# Retrieve a secret by item name and field
bw get item "Production Database" | jq -r '.fields[] | select(.name=="url") | .value'

# Use bw-cli in a script to build .env
bw get item "App Secrets" | jq -r '.notes' > .env

Doppler / Infisical

Managed secrets platforms provide a centralized dashboard, environment-based scoping (dev/staging/prod), audit logs, and automatic rotation. They replace .env files entirely in most workflows.

Doppler
# Set up Doppler for your project
doppler setup

# Run your app with injected secrets (no .env file needed)
doppler run -- node server.js

# Download secrets as .env for local tools that require a file
doppler secrets download --no-file --format env > .env
Infisical
# Login and initialize
infisical login
infisical init

# Run with injected secrets
infisical run -- node server.js

# Export as .env
infisical export --env=dev --format=dotenv > .env

AWS Secrets Manager / Parameter Store

For teams already on AWS, Secrets Manager provides automatic rotation, fine-grained IAM policies, and cross-account access. Parameter Store is a lower-cost alternative for configuration values that do not require rotation.

AWS Secrets Manager
# Store a secret
aws secretsmanager create-secret \
  --name "myapp/production/database" \
  --secret-string '{"url":"postgresql://...","password":"..."}'

# Retrieve in your app or CI pipeline
aws secretsmanager get-secret-value \
  --secret-id "myapp/production/database" \
  --query SecretString --output text
AWS Systems Manager Parameter Store
# Store a parameter (SecureString encrypts with KMS)
aws ssm put-parameter \
  --name "/myapp/prod/STRIPE_KEY" \
  --value "sk_live_..." \
  --type SecureString

# Retrieve
aws ssm get-parameter \
  --name "/myapp/prod/STRIPE_KEY" \
  --with-decryption \
  --query Parameter.Value --output text

HashiCorp Vault

Vault is the industry standard for organizations with strict compliance requirements. It supports dynamic secrets (short-lived database credentials), leasing, revocation, and detailed audit trails. The trade-off is operational complexity.

HashiCorp Vault
# Store a secret
vault kv put secret/myapp/prod \
  DATABASE_URL="postgresql://..." \
  STRIPE_KEY="sk_live_..."

# Read a secret
vault kv get -field=DATABASE_URL secret/myapp/prod

# Generate a dynamic database credential (auto-expires)
vault read database/creds/myapp-role

Encrypted files in Git (git-crypt, SOPS)

If you need secrets versioned alongside code, encrypt them before committing. git-crypt transparently encrypts files on push and decrypts on pull. Mozilla SOPS encrypts individual values within YAML/JSON files, making diffs readable.

git-crypt
# Initialize git-crypt in your repo
git-crypt init

# Specify which files to encrypt in .gitattributes
echo ".env.production filter=git-crypt diff=git-crypt" >> .gitattributes

# Add a collaborator's GPG key
git-crypt add-gpg-user COLLABORATOR_GPG_KEY_ID

# Files are encrypted in the repo, decrypted on checkout
git-crypt unlock
Mozilla SOPS
# Encrypt a file (uses age, GPG, or cloud KMS)
sops --encrypt --age age1... secrets.env > secrets.enc.env

# Edit encrypted file in-place (decrypts, opens editor, re-encrypts)
sops secrets.enc.env

# Decrypt to stdout for use in scripts
sops --decrypt secrets.enc.env > .env

How do these approaches compare?

ApproachSecurityBest forCostSetup effort
1Password / BitwardenHigh1-20 developers$3-8/user/moLow
Doppler / InfisicalHigh5-200 developersFree tier, then $5-18/user/moLow
AWS Secrets ManagerVery highAWS-native teams$0.40/secret/mo + API callsMedium
AWS Parameter StoreHighAWS-native teamsFree (standard), $0.05/param/mo (advanced)Medium
HashiCorp VaultVery highEnterprise / complianceFree (OSS), $1.58/hr (HCP)High
git-crypt / SOPSMedium-HighSmall teams, monoreposFreeMedium

For most teams under 20 people, a password manager CLI (1Password or Bitwarden) is the fastest path to secure secret sharing. As you grow past that or need audit trails and rotation, move to Doppler, Infisical, or your cloud provider's native secrets service.

What are the best practices for .env.example files?

  • List every variable — if the app reads it, the example file must include it. Missing keys are the number one onboarding friction point.
  • Use obviously fake values — placeholders like your-api-key-here or sk_test_replace_me make it clear the value must be replaced.
  • Group by service — use comments to separate database, auth, third-party, and feature flag sections.
  • Include safe defaults — non-secret values like LOG_LEVEL=info or PORT=3000 should use working defaults so the app starts immediately after copying the file.
  • Add comments for non-obvious variables — a one-line note explaining where to get the value saves significant onboarding time.
  • Keep it in sync — add a CI check or pre-commit hook that verifies .env.example keys match the keys your app actually reads.

How do you set up .gitignore and .env.example in 2 minutes?

Run these commands in your project root to create both files and make your first secure commit:

Quick setup
# 1. Create .gitignore entries for env files
cat >> .gitignore << 'EOF'
.env
.env.local
.env.*.local
EOF

# 2. Create .env.example from your current .env (strips values)
sed 's/=.*/=/' .env > .env.example

# 3. Remove .env from git tracking if it was committed
git rm --cached .env 2>/dev/null

# 4. Commit the safe files
git add .gitignore .env.example
git commit -m "chore: add .env.example and ignore .env files"

The sed command strips everything after the = sign, leaving you with a clean list of keys. Review the output before committing — make sure no real values slipped through.

Validate your environment files with the env validator, or learn more about .env syntax in the .env guide.

Frequently Asked Questions

Should I commit .env files to Git?

No. .env files often contain secrets like API keys, database passwords, and tokens. Always add .env to .gitignore. Instead, commit a .env.example file with placeholder values so team members know which variables are required.

What is the best way to share secrets with a team?

For small teams: use a password manager like 1Password or Bitwarden with CLI integration. For larger teams: use a dedicated secrets manager like Doppler, Infisical, or HashiCorp Vault that provides audit trails, rotation, and environment-specific overrides.

What should go in a .env.example file?

Include every variable name with a descriptive placeholder value or comment. Never include real secrets. Example: DATABASE_URL=postgresql://user:password@localhost:5432/mydb. Group related variables and add comments explaining each.

Was this helpful?

Stay up to date

Get notified about new guides, tools, and cheatsheets.