How to share a .env file with a teammate safely
A new teammate needs the project's .env file, and the quickest route is to paste it into a direct message. It is also the route that leaves the most copies of your credentials behind. Here is what to avoid, what to use instead, and how the options trade off.
1. What not to do
- Paste it into Slack, Teams or email. The message is stored on the provider's servers, indexed for search, included in workspace exports and retention archives, and synced to every device the recipient is signed in on. Email adds notification copies and backups. Deleting the message later does not reach those copies.
- Put it in a shared doc or wiki. Google Docs, Notion and Confluence pages get shared further, get version history, and show up in search for people who were never meant to see them.
- Commit it "just for now". Git history keeps it after you delete the file. If that has already happened, see how to remove an API key from git history.
- Send it as a zip with the password in the same message. Anyone who can read the message can open the zip.
2. Before you send anything, shrink what you send
- Commit a
.env.examplewith every variable name and no real values. The teammate then knows exactly which values they need. - Give them their own credentials where you can. Many providers let each developer have their own API key, database user or sandbox account. A personal key can be revoked when they leave without breaking everyone else.
- Send development values, not production. Most people setting up a laptop do not need live keys.
3. Option A: a shared vault in a password manager
If the team already uses 1Password or Bitwarden, store the values there and give the teammate access to the vault. Updates reach everyone, access can be removed, and nothing sits in a chat log.
With 1Password you can go one step further and keep a .env that contains references instead of values, which is safe to commit:
# .env
STRIPE_SECRET_KEY="op://Development/Stripe/secret key"
DATABASE_URL="op://Development/Postgres/connection string"
op run --env-file=.env -- npm run dev
op run resolves each op://vault/item/field reference when the command starts and passes the real values in as environment variables. They never exist in a file on disk.
4. Option B: a self-expiring link
For a one-off handover to someone outside your password manager, send a link that expires:
- 1Password item sharing creates a link to a copy of an item. You choose when the link expires and whether anyone with the link can open it or only specific email addresses. The recipient does not need a 1Password account. The copy does not update if you change the item later.
- Bitwarden Send shares text or a file through an end-to-end encrypted link with a deletion date of up to 31 days, an optional password and an optional maximum access count. Text Sends work on the free plan.
Set the shortest expiry that works and, where offered, a single view. The link itself is now the secret, so it can travel over Slack, but if the recipient opens it and finds it already used or expired, assume someone else got there first and rotate the values.
5. Option C: encrypt the file with age (or gpg)
age is a small file encryption tool with short keys and no configuration. The teammate generates a key pair and sends you the public half, which is safe to paste anywhere:
brew install age
mkdir -p ~/.config/age
age-keygen -o ~/.config/age/key.txt
age-keygen prints Public key: age1.... You encrypt to that public key and send the result over any channel, Slack included:
age -r age1THEIR_PUBLIC_KEY -o .env.age .env
They decrypt with their private key:
age -d -i ~/.config/age/key.txt -o .env .env.age
If they have an ssh-ed25519 or ssh-rsa key on GitHub, you can skip the key exchange and encrypt to the keys GitHub publishes for their account. age skips key types it does not support, such as ECDSA, with a warning.
curl https://github.com/THEIR_USERNAME.keys | age -R - -o .env.age .env
They decrypt with their SSH private key:
age -d -i ~/.ssh/id_ed25519 -o .env .env.age
Add -a for ASCII output you can paste as text. Pass several -r flags to encrypt one file for several people.
If your team already uses GPG, gpg --encrypt --recipient [email protected] .env does the same job. Avoid passphrase-only encryption (gpg -c or age -p) unless the passphrase travels over a different channel from the file.
6. Option D: commit it encrypted with sops
sops encrypts the values in a .env file and leaves the variable names readable, so the encrypted file can live in the repository and show meaningful diffs. List everyone's age public key in .sops.yaml at the repository root:
creation_rules:
- path_regex: \.env$
age: >-
age1YOUR_PUBLIC_KEY,
age1TEAMMATE_PUBLIC_KEY
brew install sops
sops encrypt .env > secrets.enc.env
git add .sops.yaml secrets.enc.env
A teammate on the list decrypts with their age key, or runs a command with the values injected into its environment:
export SOPS_AGE_KEY_FILE=~/.config/age/key.txt
sops decrypt secrets.enc.env > .env
sops exec-env secrets.enc.env 'npm run dev'
To add someone, append their key to .sops.yaml and run sops updatekeys secrets.enc.env. Removing someone from the list does not remove their access to the old encrypted file in git history, so rotate the values when a person leaves. sops also works with AWS KMS, GCP KMS, Azure Key Vault and PGP if you would rather not manage age keys.
7. Option E: a secrets manager
Doppler, Infisical, HashiCorp Vault and AWS Secrets Manager hold the values centrally and hand them to the application at runtime, so there is no .env file to share at all. You get per-person access, audit logs, and rotation in one place. The cost is setup: accounts, a CLI on every machine, and a change to how the app starts. It pays off with a larger team, several environments, or a compliance requirement.
8. Which one to use
| Option | Best for | Main trade-off |
|---|---|---|
| Password manager vault | Teams already on 1Password or Bitwarden | Everyone needs an account in the same team |
| Self-expiring link | A one-off handover | No updates; the link is the secret until opened |
| age or gpg | Two developers, no shared tooling | Manual; re-send on every change |
| sops | Secrets versioned with the code | Key list to maintain; old versions stay decryptable |
| Secrets manager | Larger teams, many environments | Setup and a runtime dependency |
Whichever you pick, the file lands on the teammate's Mac as a plain .env again. Make sure it is gitignored there, see how to prevent committing .env files to git, and delete any decrypted copies left in ~/Downloads.