How to share a .env file securely

A new teammate needs the project's .env, or a CI job needs a few keys once. The quick fix is to paste the file into Slack or commit it “just for now”. Both leave copies of every key in places you don't control. Here is a handoff that leaves no copy behind.

What goes wrong with the usual ways

  • Chat: the file stays in history and search, and in every client that synced it.
  • Git: even after you delete it, the file is in the history and in every clone.
  • Email or a shared drive: no expiry, no record of who opened it.

Send it to a person

  1. Create a secret from the file with one allowed view and a short expiry. jq reads the file and builds the JSON, so quotes, $ and line breaks arrive as they are. Don't type the passphrase into the command itself: shells save commands in history. read -rs PASSPHRASE asks for it without showing it and keeps it out of history:
    read -rs PASSPHRASE   # typed, not echoed, not saved in shell history
    curl -fsS -X POST https://secretbolt.io/api/v1/secrets \
      -H "Authorization: Bearer $SECRETBOLT_API_KEY" \
      -H "Content-Type: application/json" \
      -d "$(jq -n --rawfile content .env --arg passphrase "$PASSPHRASE" '{
        name: "project .env", content: $content, passphrase: $passphrase,
        max_views: 1, expires_in_hours: 1}')"
    # → {"url": ".../s/…", ...}
  2. Send the link in chat and the passphrase through a different channel.
  3. Your teammate opens the link, copies the values, and the secret is erased.

Load it straight into a shell or a CI job

The recipient can skip the browser and load the values with a read-only API key. The export endpoint returns safe export KEY='value' lines and never exports PATH or LD_PRELOAD. The file is created readable only by you, checked, loaded and removed:

# Recommended: write to a private file, read it, then load it
(umask 077 && curl -fsS -o .env.secretbolt \
  -H "Authorization: Bearer $READ_ONLY_KEY" \
  https://secretbolt.io/api/v1/secrets/$TOKEN/export \
  -H "X-Passphrase: $PASSPHRASE")   # leave $PASSPHRASE empty if the secret has none
cat .env.secretbolt          # check what you're about to load
set -a; . ./.env.secretbolt; set +a
rm .env.secretbolt

Each fetch uses one view, so this suits one-off handoffs, not a job that runs on every build. For that, keep the values in your CI's own secret store.

Using Docker? The same secret can become an env file for docker run --env-file or a swarm secret. See the docs.

Good habits

  • Send only the keys the person needs, not the whole production file.
  • Use a read-only API key on the receiving side.
  • Check the event log: if the secret was viewed before your teammate opened it, rotate the keys.

Setting up a new machine? See How to move your secrets to a new laptop.