How to sync environment variables with Render

Render attaches environment variables to a service, with env groups for values that several services share. It is a clean model for deployment and an awkward one for everything that happens before deployment — starting with the copy of those values that has to exist on your own machine.

Updated

The gap Render leaves

Render's environment variables and env groups do their job: services get their configuration, shared values are defined once, and secret files cover the cases a key-value pair cannot. What sits outside that scope is the team's relationship to those values.

  • Changing a variable replaces it. There is no previous version to read.
  • There is no per-value record of who set it, or when.
  • Access is to the service, not to a subset of its configuration — you cannot let someone read staging's variables and nothing else.
  • Getting the same values onto a laptop means copying them by hand, after which the two copies begin drifting apart with no way to compare them.

Import a Render service's environment

Create a Render API key in your account settings. For Render, the service is the unit — the CLI takes it as --service, and --project also refers to the service, which is why you will usually pass just the one:

sink login
sink init                        # writes .sink.json — ids only, safe to commit

sink import render staging \
  --service my-api \
  --dry-run

Leave any of it out and the CLI prompts rather than guessing. Read the dry-run output, then apply it:

sink import render staging --service my-api
sink import render production --service my-api-prod

Existing keys are skipped by default; --mode overwrite lets Render's value win, minting a new version rather than discarding the previous one. If a service draws on an env group, import it into the same Sink environment — the values arrive flattened, which is what your local process needs anyway.

To keep the API key out of your shell history:

export SINK_RENDER_TOKEN=...
sink import render staging --service my-api

Either way the token is used for that request and never stored.

Then: pull, diff, push

sink pull staging       # writes .env with 0600 permissions
sink diff staging       # what differs, values masked unless --show-values
sink push staging       # send local changes back up

The habit worth building is sink diff before you start debugging anything environment-shaped. A surprising share of "it works on staging but not locally" is a single variable that moved three weeks ago, and the diff finds it in a second rather than an afternoon.

Secret files

Render's secret files — a service account JSON, a certificate — are not key-value pairs and do not import as variables. Two workable options:

  • Keep the file's contents as a single secret and write it out at start-up. Fine for small files, and it gets you versioning and an audit trail.
  • Leave genuinely large files in Render and store only the values that point at them.

The first is usually right for a credentials blob and usually wrong for anything approaching a megabyte.

New machines and new people

git clone [email protected]:you/my-api.git
cd my-api
sink login
sink pull development

.sink.json is committed and contains ids only — no values — so a fresh clone already knows which environment it belongs to. Nothing has to be sent to anyone, which quietly removes the most common way credentials end up somewhere permanent. If some already have, see sharing a .env file securely.

CI

# CI secret store
SINK_API_KEY=sk-...

# job step
sink pull production --stdout > .env

Render still injects its own environment at deploy time; Sink is not in that path. It is where the values are versioned, audited and shared between people. Rotate in the provider first, update Sink, then let everyone re-pull — doing it the other way round leaves a window where the stored value is the wrong one.

Keeping it true

  • Re-run the import with --dry-run periodically; differences mean an undocumented dashboard edit.
  • sink diff production --exit-code in CI turns drift into a failed build instead of a surprise.
  • sink pull --version N reads each secret at an earlier version.
  • Give Viewer to anyone who only needs to read a development environment.
Try it on your own project

Sink keeps every environment under AES-256-GCM envelope encryption, with roles, versions and an audit trail — and puts it back in your .env with one command. The free tier does not ask for a card.