.md

Neon

Pull each Neon branch's connection strings into an encrypted .env file and inject them at runtime with dotenvx.

Prerequisites

To get the most out of this guide, you'll need to:

Install

Get the Dotenvx CLI.

$ curl -sfS https://dotenvx.sh | sh

Link your directory to a Neon project. --no-env-pull keeps the Neon CLI from writing plaintext credentials to .env.local.

$ neon link --no-env-pull

Encrypt production

Pull your production branch's variables into .env.production.

$ neon env pull --branch main --file .env.production

The Neon CLI pulls variables for every service enabled on the branch. Only some of them are secrets. Add an Envfile to tell Dotenvx which ones to encrypt. Remove the lines for services you don't use.

# Envfile
strict true

# Postgres
env "DATABASE_URL", type: "url"
env "DATABASE_URL_UNPOOLED", type: "url"
env "NEON_BRANCH", encrypted: false, redacted: false

# Neon Auth
env "NEON_AUTH_BASE_URL", type: "url", encrypted: false, redacted: false
env "NEON_AUTH_JWKS_URL", type: "url", encrypted: false, redacted: false

# Object Storage
env "AWS_ACCESS_KEY_ID"
env "AWS_SECRET_ACCESS_KEY"
env "AWS_ENDPOINT_URL_S3", type: "url", encrypted: false, redacted: false
env "AWS_REGION", encrypted: false, redacted: false

# AI Gateway
env "NEON_AI_GATEWAY_TOKEN"
env "NEON_AI_GATEWAY_BASE_URL", type: "url", encrypted: false, redacted: false

Encrypt it.

$ dotenvx encrypt -f .env.production

The connection strings and keys are now encrypted, and you have a .env.keys file. The branch name and service URLs stay readable, so pull requests show which branch a file points to.

#/-------------------[DOTENV_PUBLIC_KEY]--------------------/
#/            public-key encryption for .env files          /
#/       [how it works](https://dotenvx.com/encryption)     /
#/----------------------------------------------------------/
DOTENV_PUBLIC_KEY_PRODUCTION="03a13c..."

# .env.production
DATABASE_URL="encrypted:BJWTQRqLZbLh..."
DATABASE_URL_UNPOOLED="encrypted:BKgN9Vn5IM1I..."
NEON_BRANCH=main
NEON_AUTH_BASE_URL=https://ep-floral-morning-b4ilaws5.neonauth.c-6.us-east-2.aws.neon.tech/neondb/auth
NEON_AUTH_JWKS_URL=https://ep-floral-morning-b4ilaws5.neonauth.c-6.us-east-2.aws.neon.tech/neondb/auth/.well-known/jwks.json
AWS_ACCESS_KEY_ID=encrypted:BNLOduQp2JJu...
AWS_SECRET_ACCESS_KEY=encrypted:BH6o+QcR5u8c...
AWS_ENDPOINT_URL_S3=https://br-bold-rain-b45zwi29.storage.c-6.us-east-2.aws.neon.tech
AWS_REGION=us-east-2
NEON_AI_GATEWAY_TOKEN=encrypted:BBmg6Ej5B7ud...
NEON_AI_GATEWAY_BASE_URL=https://br-bold-rain-b45zwi29-api.ai.c-6.us-east-2.aws.neon.tech

Encrypt a preview branch

Every Neon branch has its own connection strings, Auth URLs and storage keys. Create a preview branch and give it its own encrypted file. The same Envfile applies.

$ neon branches create --name preview
$ neon env pull --branch preview --file .env.preview
$ dotenvx encrypt -f .env.preview

Each file gets its own key pair, so a preview key cannot decrypt production.

Run

Install the Neon serverless driver.

$ npm install @neondatabase/serverless

Create index.js.

// index.js
import { neon } from '@neondatabase/serverless'

const sql = neon(process.env.DATABASE_URL)
const [{ now }] = await sql`select now()`

console.log(`Hello ${process.env.NEON_BRANCH} at ${now.toISOString()}`)

Run it against either branch.

$ dotenvx run -f .env.preview -- node index.js
⟐ injected env (12) from .env.preview (028 28A) via .env.keys ⛉
Hello preview at 2026-10-26T20:48:09.360Z

$ dotenvx run -f .env.production -- node index.js
⟐ injected env (12) from .env.production (03A 13C) via .env.keys ⛉
Hello main at 2026-10-26T20:48:11.968Z

Commit

Commit both encrypted files and the Envfile. Keep .env.keys out of git. The -f flag adds the encrypted files even though neon env pull listed them in .gitignore.

$ echo ".env.keys" >> .gitignore
$ git add .gitignore Envfile
$ git add -f .env.production .env.preview
$ git commit -m "encrypt neon branch env"

Store .env.keys somewhere safe like 1Password or Armor.

Deploy

Set DOTENV_PRIVATE_KEY_PRODUCTION on your host and start your app with --strict, to stop the boot if a key is missing:

$ dotenvx run -f .env.production --strict -- node index.js

See the platform guides for where each host keeps the private key.

Refresh credentials

neon env pull writes plaintext values back into the file. Encrypt again right after every pull, for example after you reset a role password.

{
  "scripts": {
    "env:production": "neon env pull --branch main --file .env.production && dotenvx encrypt -f .env.production",
    "env:preview": "neon env pull --branch preview --file .env.preview && dotenvx encrypt -f .env.preview"
  }
}

dotenvx encrypt reuses the existing key in .env.keys, so your deployed private keys keep working.

Open page ↗

Side path