# Env Sync in 30 Seconds: Kill Secret Drift Forever

> Kill env drift: one command copies your production secrets to the server over SSH and reloads the app. No vault, no dashboard, no retyping.

Source: https://boringpush.com/blog/env-sync-production-nextjs

## The server runs last month's secrets

It always goes like this. You rotate the payment key, test locally, push. Production keeps failing.

The server's settings file still holds the old value. Or OAuth logins break after a domain change while your laptop works perfectly.

Nobody compares settings files for fun.

So they drift.

Eleven values match.

One does not.

And the one is always the payment key at 11 p.m.

The fix is not a vault product. Not a dashboard.

It is one command, run after every secret change.

## How founders move secrets today

| Method | Speed | Drift risk | Leak risk |
|---|---|---|---|
| Paste in chat or email | Seconds | Total, no source of truth | Keys live in chat history |
| A CI vault UI | Minutes | Medium, two UIs to update | Low, but one more account |
| `scp` by hand | A minute | High, no reload | Low, but forgotten reloads |
| One sync command | 30 seconds | Low, copy and reload together | Low, SSH only |

Hand-syncing is worse than it feels. Three reasons.

**The missing reload.** The file copies fine and nothing changes, because the running app holds the old values in memory. You will forget the reload exactly once, during the incident.

**No parity check.** The example file gains a new email key in March. The server never hears about it. Deploys pass, emails silently never send, and a customer tells you.

**Secrets in shell history.** Export a live payment key on a shared box and it lands in the history file. A sync that reads a file never puts a key on a command line.

## What to do

Keep one local source of truth.

Your production settings file lives on your laptop. It is never committed.

Keep an example file in the repo with empty values, so a new setting has somewhere to be announced.

Add a setting to the example first.

Fill it locally.

Then sync.

Then copy, lock and reload in one motion.

Copy the file to the server over SSH. Tighten its permissions so only the app's user can read it. Reload the running app so the new values take effect with no downtime.

It works from anywhere you can reach the server, including over a private network like Tailscale.

Finally, guard the repo against committed secrets.

Ignore the settings files in git. Add a check that stops a push if one is staged.

Pair it with a quarterly rotation.

Change the key at the provider.

Update the local file.

Sync.

Rotation day becomes half a minute.

## The rotation drill

| Check | Action | Pass looks like |
|---|---|---|
| Copy and reload | Run `bp secret sync` | The app reloads |
| Values live | Trigger something that uses the new key | The feature works |
| Nothing leaked | Search your git history for key prefixes | Empty, forever |
| Phone-capable | Run it from an SSH app | Works over your private network |

## A script is not a vault

Be honest about the gaps.

No audit log of who read what.

No per-key access policies.

No automatic rotation.

And BoringPush does not compare your settings against the example file, so a forgotten new setting is yours to catch.

Past a handful of people with production access, those gaps matter. Buy a vault then.

For one to three founders, the simplicity is the security.

Fewer accounts.

Fewer dashboards.

Fewer places for a secret to rest.

## Know this first

A script is not a vault. There is no audit log of who read what. No per-key access policies.

BoringPush does not compare your settings against the example file for you.

## Why BoringPush is the way to do this

Secrets without the shell history.

Run `bp secret sync`. It copies your settings to the server, locks the file down and reloads the app.

One command. No retyping. No chat messages full of keys.

## FAQ

### Does env sync replace a vault like Doppler or Infisical?

For solo teams, in practice yes. A vault gives you one source of truth, safe transport and a reload on change, and the sync command covers all three.

What vaults add is audit trails and per-person scoping, which matter past about five people with production access. Start with the command, and migrate when headcount says so.

### What if I lose the laptop holding the only copy?

Rebuild from the server, which holds a live copy, plus your password manager, where every secret is also stored. Treat the password manager as the archive, the laptop file as the working copy and the server file as the live copy.

### How do staging and production stay separate?

Use separate files for each, with separate servers or processes. Check which file you are syncing before you run anything. Explicit beats clever when a payment key is involved.

### Should secrets ever appear in logs or error pages?

Never values, sometimes names. A missing-setting error may name the setting but must never print its value. BoringPush does not automatically strip secret-looking values from logs today, so review any new logging that touches settings or request data.

### Can the sync run from my phone?

Yes, from an SSH app on a machine that holds the settings file and can reach the server. Rotating a leaked key from the couch at midnight is a half-minute task, not a drive home.

### What breaks most often?

Forgetting the reload, which the command handles. Editing the server file by hand instead of the local one, which the next sync overwrites. And committing the settings file once, long ago: purge it from history, rotate every key and add a push check.

## Keep reading

- [git push production deploy: Skip the 9-Minute CI Queue](/blog/git-push-production-main-deploy) — the push this sync rides along with
- [Tailscale Deploy Trick: Close Port 22 Forever](/blog/tailscale-wireguard-deploy-private) — the private road secrets travel
- [Deploy Next.js on Hetzner $5 by Dinner Tonight](/blog/deploy-nextjs-hetzner-vps) — the full build, secrets included
