Use dotenvx with Turborepo

Use dotenvx with Turborepo

Initial setup

Create a new monorepo.

npx create-turbo@latest

This will create a handful of files and a couple workspaces (apps).

$ ls -1
README.md
apps/
node_modules/
package-lock.json
package.json
packages/
turbo.json

Edit apps/web/app/page.tsx to include process.env.HELLO – to say 'Hello World'.

apps/web/app/page.tsx

export default function Page() {
  return (
    <>
      Hello {process.env.HELLO}
    </>
  );
}

Declare the apps/web environment variable in turbo.json by adding a web#build configuration under the pipeline configuration key.

turbo.json

  ...
  "pipeline": {
    ...
    "web#build": {
      "dependsOn": [
        "^build"
      ],
      "env": [
        "HELLO"
      ]
    },
    ...

Run dotenvx

Install dotenvx under apps/web

cd apps/web
npm install @dotenvx/dotenvx --save
cd ../..

Create a .env file under apps/web.

apps/web/.env

# apps/web/.env
HELLO="World"

Adjust your apps/web/package.json scripts to inject your env using dotenvx.

apps/web/package.json

...
"scripts": {
  "dotenvx": "dotenvx",
  "build": "dotenvx run -- turbo build",
  "dev": "dotenvx run -- turbo dev",
  "lint": "dotenvx run -- turbo lint",
  "format": "prettier --write \"**/*.{ts,tsx,md}\""
},

Try running it locally.

$ npm run dev
web:dev: [[email protected]] injecting env (1) from .env
docs:dev: - ready started server on 0.0.0.0:3001, url: http://localhost:3001
web:dev: - ready started server on 0.0.0.0:3002, url: http://localhost:3002

Visit localhost:3002.

localhost:3002

Your app will say Hello World. The values from your .env file were successfully injected into your env.

Add production environment

Create a .env.production file under apps/web.

apps/web/.env.production

# apps/web/.env.production
HELLO="production"

Modify your apps/web/package.json scripts to use the .env.production file.

apps/web/package.json

...
"scripts": {
  "dotenvx": "dotenvx",
  "build": "dotenvx run --env-file=.env.production -- turbo build",
  "dev": "dotenvx run --env-file=.env.production -- turbo dev",
  "lint": "dotenvx run --env-file=.env.production -- turbo lint",
  "format": "prettier --write \"**/*.{ts,tsx,md}\""
},
localhost:3002

Your app will say Hello production, simulating production. Solid. Let's encrypt your secrets next.

Encrypt secrets

Use dotenvx to encrypt your apps/web secrets.

dotenvx encrypt apps/web

Also run dotenvx gitignore to make sure your .env.keys file is gitignored

dotenvx gitnore

This generates a .env.vault and .env.keys file.

apps/web/.env.vault

#/-------------------.env.vault---------------------/
#/         cloud-agnostic vaulting standard         /
#/   [how it works](https://dotenv.org/env-vault)   /
#/--------------------------------------------------/

# development
DOTENV_VAULT_DEVELOPMENT="V4NYVn0Pow6Uf2ez2mbHEzTrYURloHL6VDAFRLqnQBppA/OmHI5x5AXoxCMVor7wOg=="

# production
DOTENV_VAULT_PRODUCTION="YZkhtbh1IlzBgIamAAsG5nzGPfH6p8Zbuj9egXoziviVu/eYIyNjJWtIYyhiW/vHhFbqbsvo5+P9b27OC6ZC7qU="

The .env.vault file contains encrypted (AES-256-GCM) versions of your secrets, and the .env.keys file contains the decryption keys.

apps/web/.env.keys

#/!!!!!!!!!!!!!!!!!!!.env.keys!!!!!!!!!!!!!!!!!!!!!!/
#/   DOTENV_KEYs. DO NOT commit to source control   /
#/   [how it works](https://dotenv.org/env-keys)    /
#/--------------------------------------------------/
DOTENV_KEY_DEVELOPMENT="dotenv://:key_e507c60efa8841d8d5bbb85bd701ee92406cf3b06506d1d80f1553c2a72791e4@dotenvx.com/vault/.env.vault?environment=development"
DOTENV_KEY_PRODUCTION="dotenv://:key_10283719af6a30ef49050048617f4fea10c23a38021fbebeb9fd858caa01852e@dotenvx.com/vault/.env.vault?environment=production"

We're ready to simulate production by using the DOTENV_KEY.

Set DOTENV_KEY

Set DOTENV_KEY using the production key in your .env.keys file.

DOTENV_KEY='dotenv://:key_10283719af6a30ef49050048617f4fea10c23a38021fbebeb9fd858caa01852e@dotenvx.com/vault/.env.vault?environment=production' npm run dev

Your script starts and env is successfully injected using the encrypted contents of .env.vault.

web:dev: [[email protected]] injecting env (1) from encrypted .env.vault
docs:dev: - ready started server on 0.0.0.0:3001, url: http://localhost:3001
web:dev: - ready started server on 0.0.0.0:3002, url: http://localhost:3002

Visit your url and it says Hello production.

localhost:3002

Great job! That's pretty much it. See the bonus section(s) below to go a little deeper.


Bonus

Try changing the value of .env.production to your name.

.env.production

# .env.production
HELLO="Mot"

Re-encrypt it.

dotenvx encrypt apps/web

Commit .env.vault safely to code and re-run npm run dev