npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@dotenvx/dotenvx

v1.51.2

Published

a secure dotenv–from the creator of `dotenv`

Downloads

5,161,753

Readme

dotenvx

a secure dotenv–from the creator of dotenv.

  • run anywhere (cross-platform)
  • multi-environment
  • encrypted envs

Read the whitepaper

 

Quickstart npm version downloads test suite

Install and use it in code just like dotenv.

npm install @dotenvx/dotenvx --save
// index.js
require('@dotenvx/dotenvx').config()
// or import '@dotenvx/dotenvx/config' // for esm

console.log(`Hello ${process.env.HELLO}`)

 

or install globally - unlocks dotenv for any language, framework, or platform!

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

curl installs

 

brew install dotenvx/brew/dotenvx
dotenvx help

brew installs

 

docker run -it --rm -v $(pwd):/app dotenv/dotenvx help

docker pulls

 

curl -L -o dotenvx.tar.gz "https://github.com/dotenvx/dotenvx/releases/latest/download/dotenvx-$(uname -s)-$(uname -m).tar.gz"
tar -xzf dotenvx.tar.gz
./dotenvx help

github releases

 

winget install dotenvx
dotenvx help

 

Run Anywhere

$ echo "HELLO=World" > .env
$ echo "console.log('Hello ' + process.env.HELLO)" > index.js

$ node index.js
Hello undefined # without dotenvx

$ dotenvx run -- node index.js
Hello World # with dotenvx
> :-D

see extended quickstart guide

More examples

// package.json
{
  "type": "module",
  "dependencies": {
    "chalk": "^5.3.0"
  }
}
// index.ts
import chalk from 'chalk'
console.log(chalk.blue(`Hello ${process.env.HELLO}`))
$ npm install
$ echo "HELLO=World" > .env

$ dotenvx run -- npx tsx index.ts
Hello World
$ echo "HELLO=World" > .env
$ echo "console.log('Hello ' + Deno.env.get('HELLO'))" > index.ts

$ deno run --allow-env index.ts
Hello undefined

$ dotenvx run -- deno run --allow-env index.ts
Hello World

[!WARNING] Some of you are attempting to use the npm module directly with deno run. Don't, because deno currently has incomplete support for these encryption ciphers.

$ deno run -A npm:@dotenvx/dotenvx encrypt
Unknown cipher

Instead, use dotenvx as designed, by installing the cli as a binary - via curl, brew, etc.

$ echo "HELLO=Test" > .env.test
$ echo "console.log('Hello ' + process.env.HELLO)" > index.js

$ bun index.js
Hello undefined

$ dotenvx run -f .env.test -- bun index.js
Hello Test
$ echo "HELLO=World" > .env
$ echo 'import os;print("Hello " + os.getenv("HELLO", ""))' > index.py

$ dotenvx run -- python3 index.py
Hello World

see extended python guide

$ echo "HELLO=World" > .env
$ echo '<?php echo "Hello {$_SERVER["HELLO"]}\n";' > index.php

$ dotenvx run -- php index.php
Hello World

see extended php guide

$ echo "HELLO=World" > .env
$ echo 'puts "Hello #{ENV["HELLO"]}"' > index.rb

$ dotenvx run -- ruby index.rb
Hello World

see extended ruby guide

$ echo "HELLO=World" > .env
$ echo 'package main; import ("fmt"; "os"); func main() { fmt.Printf("Hello %s\n", os.Getenv("HELLO")) }' > main.go

$ dotenvx run -- go run main.go
Hello World

see extended go guide

$ echo "HELLO=World" > .env
$ echo 'fn main() {let hello = std::env::var("HELLO").unwrap_or("".to_string());println!("Hello {hello}");}' > src/main.rs

$ dotenvx run -- cargo run
Hello World

see extended rust guide

$ echo "HELLO=World" > .env
$ echo 'public class Index { public static void main(String[] args) { System.out.println("Hello " + System.getenv("HELLO")); } }' > index.java

$ dotenvx run -- java index.java
Hello World
$ echo "HELLO=World" > .env
$ echo '(println "Hello" (System/getenv "HELLO"))' > index.clj

$ dotenvx run -- clojure -M index.clj
Hello World
$ echo "HELLO=World" > .env
$ echo 'fun main() { val hello = System.getenv("HELLO") ?: ""; println("Hello $hello") }' > index.kt
$ kotlinc index.kt -include-runtime -d index.jar

$ dotenvx run -- java -jar index.jar
Hello World
$ dotnet new console -n HelloWorld -o HelloWorld
$ cd HelloWorld
$ echo "HELLO=World" | Out-File -FilePath .env -Encoding utf8
$ echo 'Console.WriteLine($"Hello {Environment.GetEnvironmentVariable("HELLO")}");' > Program.cs

$ dotenvx run -- dotnet run
Hello World
$ echo "HELLO=World" > .env

$ dotenvx run --quiet -- sh -c 'echo Hello $HELLO'
Hello World
$ echo "HELLO=World" > .env

$ dotenvx run --quiet -- sh -c 'echo Hello $HELLO'
Hello World
# run every day at 8am
0 8 * * * dotenvx run -- /path/to/myscript.sh
$ dotenvx run -- next dev
$ dotenvx run -- npm start
$ dotenvx run -- bin/rails s
$ dotenvx run -- php artisan serve

see framework guides

$ docker run -it --rm -v $(pwd):/app dotenv/dotenvx run -- node index.js

Or in any image:

FROM node:latest
RUN echo "HELLO=World" > .env && echo "console.log('Hello ' + process.env.HELLO)" > index.js
RUN curl -fsS https://dotenvx.sh/install.sh | sh
CMD ["dotenvx", "run", "--", "echo", "Hello $HELLO"]

see docker guide

name: build
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - uses: actions/setup-node@v3
      with:
        node-version: 16
    - run: curl -fsS https://dotenvx.sh/install.sh | sh
    - run: dotenvx run -- node build.js
      env:
        DOTENV_KEY: ${{ secrets.DOTENV_KEY }}

see github actions guide

# heroku
heroku buildpacks:add https://github.com/dotenvx/heroku-buildpack-dotenvx

# docker
RUN curl -fsS https://dotenvx.sh/install.sh | sh

# vercel
npm install @dotenvx/dotenvx --save

see platform guides

// pm2
"scripts": {
  "start": "dotenvx run -- pm2-runtime start ecosystem.config.js --env production"
},

see process manager guides

# alternatively use npx
$ npx @dotenvx/dotenvx run -- node index.js
$ npx @dotenvx/dotenvx run -- next dev
$ npx @dotenvx/dotenvx run -- npm start
$ npm install @dotenvx/dotenvx --save
{
  "scripts": {
    "start": "./node_modules/.bin/dotenvx run -- node index.js"
  },
  "dependencies": {
    "@dotenvx/dotenvx": "^0.5.0"
  }
}
$ npm run start

> start
> ./node_modules/.bin/dotenvx run -- node index.js

[[email protected]] injecting env (1) from .env.production
Hello World
# use dotenvx with asdf
$ asdf plugin add dotenvx
$ asdf install dotenvx latest

thank you @jgburet of Paris 🇫🇷

# use as a git submodule
$ git dotenvx run -- node index.js
$ git dotenvx run -- next dev
$ git dotenvx run -- npm start

Reference and expand variables already on your machine for use in your .env file.

# .env
USERNAME="username"
DATABASE_URL="postgres://${USERNAME}@localhost/my_database"
// index.js
console.log('DATABASE_URL', process.env.DATABASE_URL)
$ dotenvx run --debug -- node index.js
[[email protected]] injecting env (2) from .env
DATABASE_URL postgres://username@localhost/my_database

Add the output of a command to one of your variables in your .env file.

# .env
DATABASE_URL="postgres://$(whoami)@localhost/my_database"
// index.js
console.log('DATABASE_URL', process.env.DATABASE_URL)
$ dotenvx run --debug -- node index.js
[[email protected]] injecting env (1) from .env
DATABASE_URL postgres://yourusername@localhost/my_database

 

Multiple Environments

Create a .env.production file and use -f to load it. It's straightforward, yet flexible.

$ echo "HELLO=production" > .env.production
$ echo "console.log('Hello ' + process.env.HELLO)" > index.js

$ dotenvx run -f .env.production -- node index.js
[[email protected]] injecting env (1) from .env.production
Hello production
> ^^

More examples

$ echo "HELLO=local" > .env.local

$ echo "HELLO=World" > .env

$ dotenvx run -f .env.local -f .env -- node index.js
[[email protected]] injecting env (1) from .env.local,.env
Hello local

Note subsequent files do NOT override pre-existing variables defined in previous files or env. This follows historic principle. For example, above local wins – from the first file.

$ echo "HELLO=local" > .env.local

$ echo "HELLO=World" > .env

$ dotenvx run -f .env.local -f .env --overload -- node index.js
[[email protected]] injecting env (1) from .env.local,.env
Hello World

Note that with --overload subsequent files DO override pre-existing variables defined in previous files.

$ echo "HELLO=production" > .env.production

$ dotenvx run -f .env.production --verbose -- node index.js
[dotenvx][verbose] injecting env from /path/to/.env.production
[dotenvx][verbose] HELLO set
[[email protected]] injecting env (1) from .env.production
Hello production
$ echo "HELLO=production" > .env.production

$ dotenvx run -f .env.production --debug -- node index.js
[dotenvx][debug] configuring options
[dotenvx][debug] {"envFile":[".env.production"]}
[dotenvx][verbose] injecting env from /path/to/.env.production
[dotenvx][debug] reading env from /path/to/.env.production
[dotenvx][debug] parsing env from /path/to/.env.production
[dotenvx][debug] {"HELLO":"production"}
[dotenvx][debug] writing env from /path/to/.env.production
[dotenvx][verbose] HELLO set
[dotenvx][debug] HELLO set to production
[[email protected]] injecting env (1) from .env.production
Hello production

Use --quiet to suppress all output (except errors).

$ echo "HELLO=production" > .env.production

$ dotenvx run -f .env.production --quiet -- node index.js
Hello production

Set --log-level to whatever you wish. For example, to suppress warnings (risky), set log level to error:

$ echo "HELLO=production" > .env.production

$ dotenvx run -f .env.production --log-level=error -- node index.js
Hello production

Available log levels are error, warn, info, verbose, debug, silly

Load envs using Next.js' convention or dotenv-flow convention. Set --convention to nextjs or flow:

$ echo "HELLO=development local" > .env.development.local
$ echo "HELLO=local" > .env.local
$ echo "HELLO=development" > .env.development
$ echo "HELLO=env" > .env

$ dotenvx run --convention=nextjs -- node index.js
Hello development local

$ dotenvx run --convention=flow -- node index.js
Hello development local

(more conventions available upon request)

 

Encryption

Add encryption to your .env files with a single command. Use dotenvx encrypt.

$ dotenvx encrypt
✔ encrypted (.env)

encrypted .env

A DOTENV_PUBLIC_KEY (encryption key) and a DOTENV_PRIVATE_KEY (decryption key) are generated using the same public-key cryptography as Bitcoin.

More examples

$ echo "HELLO=World" > .env
$ dotenvx encrypt
$ echo "console.log('Hello ' + process.env.HELLO)" > index.js

$ dotenvx run -- node index.js
[[email protected]] injecting env (2) from .env
Hello World
$ echo "HELLO=Production" > .env.production
$ dotenvx encrypt -f .env.production
$ echo "console.log('Hello ' + process.env.HELLO)" > index.js

$ DOTENV_PRIVATE_KEY_PRODUCTION="<.env.production private key>" dotenvx run -- node index.js
[[email protected]] injecting env (2) from .env.production
Hello Production

Note the DOTENV_PRIVATE_KEY_PRODUCTION ends with _PRODUCTION. This instructs dotenvx run to load the .env.production file.

$ echo "HELLO=Ci" > .env.ci
$ dotenvx encrypt -f .env.ci
$ echo "console.log('Hello ' + process.env.HELLO)" > index.js

$ DOTENV_PRIVATE_KEY_CI="<.env.ci private key>" dotenvx run -- node index.js
[[email protected]] injecting env (2) from .env.ci
Hello Ci

Note the DOTENV_PRIVATE_KEY_CI ends with _CI. This instructs dotenvx run to load the .env.ci file. See the pattern?

$ dotenvx set HELLO World -f .env
$ dotenvx set HELLO Production -f .env.production
$ echo "console.log('Hello ' + process.env.HELLO)" > index.js

$ DOTENV_PRIVATE_KEY="<.env private key>" DOTENV_PRIVATE_KEY_PRODUCTION="<.env.production private key>" dotenvx run -- node index.js
[[email protected]] injecting env (3) from .env, .env.production
Hello World

Note the DOTENV_PRIVATE_KEY instructs dotenvx run to load the .env file and the DOTENV_PRIVATE_KEY_PRODUCTION instructs it to load the .env.production file. See the pattern?

$ mkdir app1
$ mkdir app2
$ dotenvx set HELLO app1 -f app1/.env.ci
$ dotenvx set HELLO app2 -f app2/.env.ci
$ echo "console.log('Hello ' + process.env.HELLO)" > index.js

$ DOTENV_PRIVATE_KEY_CI="<app1/privat ci key>,<app2/private ci key>" dotenvx run -f app1/.env.ci -f app2/.env.ci -- node index.js
[[email protected]] injecting env (2) from app1/.env.ci,app2/.env.ci
Hello app1

$ DOTENV_PRIVATE_KEY_CI="<app1/privat ci key>,<app2/private ci key>" dotenvx run -f app1/.env.ci -f app2/.env.ci --overload -- node index.js
[[email protected]] injecting env (2) from app1/.env.ci,app2/.env.ci
Hello app2

Note the DOTENV_PRIVATE_KEY_CI (and any DOTENV_PRIVATE_KEY*) can take multiple private keys by simply comma separating them.

$ echo "HELLO=World" > .env
$ dotenvx encrypt --stdout
$ dotenvx encrypt --stdout > .env.encrypted

secp256k1 is a well-known and battle tested curve, in use with Bitcoin and other cryptocurrencies, but we are open to adding support for more curves.

If your organization's compliance department requires NIST approved curves or other curves like curve25519, please reach out at [email protected].

 

Advanced

Become a dotenvx power user.

CLI 📟

Advanced CLI commands.

Reference and expand variables already on your machine for use in your .env file.

# .env
USERNAME="username"
DATABASE_URL="postgres://${USERNAME}@localhost/my_database"
// index.js
console.log('DATABASE_URL', process.env.DATABASE_URL)
$ dotenvx run --debug -- node index.js
[[email protected]] injecting env (2) from .env
DATABASE_URL postgres://username@localhost/my_database

Use default values when environment variables are unset or empty.

# .env
# Default value syntax: use value if set, otherwise use default
DATABASE_HOST=${DB_HOST:-localhost}
DATABASE_PORT=${DB_PORT:-5432}

# Alternative syntax (no colon): use value if set, otherwise use default
API_URL=${API_BASE_URL-https://api.example.com}
// index.js
console.log('DATABASE_HOST', process.env.DATABASE_HOST)
console.log('DATABASE_PORT', process.env.DATABASE_PORT)
console.log('API_URL', process.env.API_URL)
$ dotenvx run --debug -- node index.js
[[email protected]] injecting env (3) from .env
DATABASE_HOST localhost
DATABASE_PORT 5432
API_URL https://api.example.com

Use alternate values when environment variables are set and non-empty.

# .env
NODE_ENV=production

# Alternate value syntax: use alternate if set and non-empty, otherwise empty
DEBUG_MODE=${NODE_ENV:+false}
LOG_LEVEL=${NODE_ENV:+error}

# Alternative syntax (no colon): use alternate if set, otherwise empty  
CACHE_ENABLED=${NODE_ENV+true}
// index.js
console.log('NODE_ENV', process.env.NODE_ENV)
console.log('DEBUG_MODE', process.env.DEBUG_MODE)
console.log('LOG_LEVEL', process.env.LOG_LEVEL)
console.log('CACHE_ENABLED', process.env.CACHE_ENABLED)
$ dotenvx run --debug -- node index.js
[[email protected]] injecting env (4) from .env
NODE_ENV production
DEBUG_MODE false
LOG_LEVEL error
CACHE_ENABLED true

Complete reference for variable interpolation patterns supported by dotenvx:

# .env
DEFINED_VAR=hello
EMPTY_VAR=
# UNDEFINED_VAR is not set

# Default value syntax - use variable if set/non-empty, otherwise use default
TEST1=${DEFINED_VAR:-fallback}     # Result: "hello"
TEST2=${EMPTY_VAR:-fallback}       # Result: "fallback"  
TEST3=${UNDEFINED_VAR:-fallback}   # Result: "fallback"

# Default value syntax (no colon) - use variable if set, otherwise use default
TEST4=${DEFINED_VAR-fallback}      # Result: "hello"
TEST5=${EMPTY_VAR-fallback}        # Result: "" (empty, but set)
TEST6=${UNDEFINED_VAR-fallback}    # Result: "fallback"

# Alternate value syntax - use alternate if variable is set/non-empty, otherwise empty
TEST7=${DEFINED_VAR:+alternate}    # Result: "alternate"
TEST8=${EMPTY_VAR:+alternate}      # Result: "" (empty)
TEST9=${UNDEFINED_VAR:+alternate}  # Result: "" (empty)

# Alternate value syntax (no colon) - use alternate if variable is set, otherwise empty  
TEST10=${DEFINED_VAR+alternate}    # Result: "alternate"
TEST11=${EMPTY_VAR+alternate}      # Result: "alternate" (empty but set)
TEST12=${UNDEFINED_VAR+alternate}  # Result: "" (empty)

Key differences:

  • :- vs -: The colon makes empty values trigger the fallback
  • :+ vs +: The colon makes empty values not trigger the alternate
  • Default syntax (-): Use variable value or fallback
  • Alternate syntax (+): Use alternate value or empty string

Add the output of a command to one of your variables in your .env file.

# .env
DATABASE_URL="postgres://$(whoami)@localhost/my_database"
// index.js
console.log('DATABASE_URL', process.env.DATABASE_URL)
$ dotenvx run --debug -- node index.js
[[email protected]] injecting env (1) from .env
DATABASE_URL postgres://yourusername@localhost/my_database

Prevent your shell from expanding inline $VARIABLES before dotenvx has a chance to inject it. Use a subshell.

$ dotenvx run --env="HELLO=World" -- sh -c 'echo Hello $HELLO'
Hello World

Dotenvx supports multiline values. This is particularly useful in conjunction with Docker - which does not support multiline values.

# .env
MULTILINE_PEM="-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnNl1tL3QjKp3DZWM0T3u
LgGJQwu9WqyzHKZ6WIA5T+7zPjO1L8l3S8k8YzBrfH4mqWOD1GBI8Yjq2L1ac3Y/
bTdfHN8CmQr2iDJC0C6zY8YV93oZB3x0zC/LPbRYpF8f6OqX1lZj5vo2zJZy4fI/
kKcI5jHYc8VJq+KCuRZrvn+3V+KuL9tF9v8ZgjF2PZbU+LsCy5Yqg1M8f5Jp5f6V
u4QuUoobAgMBAAE=
-----END PUBLIC KEY-----"
// index.js
console.log('MULTILINE_PEM', process.env.MULTILINE_PEM)
$ dotenvx run -- node index.js
MULTILINE_PEM -----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnNl1tL3QjKp3DZWM0T3u
LgGJQwu9WqyzHKZ6WIA5T+7zPjO1L8l3S8k8YzBrfH4mqWOD1GBI8Yjq2L1ac3Y/
bTdfHN8CmQr2iDJC0C6zY8YV93oZB3x0zC/LPbRYpF8f6OqX1lZj5vo2zJZy4fI/
kKcI5jHYc8VJq+KCuRZrvn+3V+KuL9tF9v8ZgjF2PZbU+LsCy5Yqg1M8f5Jp5f6V
u4QuUoobAgMBAAE=
-----END PUBLIC KEY-----

Unlike other dotenv libraries, dotenvx attempts to unblock you with contextual help.

For example, when missing a custom .env file:

$ dotenvx run -f .env.missing -- echo $HELLO
[MISSING_ENV_FILE] missing .env.missing file (/Users/scottmotte/Code/dotenvx/playground/apr-16/.env.missing)
[MISSING_ENV_FILE] https://github.com/dotenvx/dotenvx/issues/484 and re-run [dotenvx run -- echo]

or when missing a KEY:

$ echo "HELLO=World" > .env
$ dotenvx get GOODBYE
[MISSING_KEY] missing GOODBYE key

Compose multiple .env files for environment variables loading, as you need.

$ echo "HELLO=local" > .env.local
$ echo "HELLO=World" > .env
$ echo "console.log('Hello ' + process.env.HELLO)" > index.js

$ dotenvx run -f .env.local -f .env -- node index.js
[[email protected]] injecting env (1) from .env.local, .env
Hello local

Note subsequent files do NOT override pre-existing variables defined in previous files or env. This follows historic principle. For example, above local wins – from the first file.

Set environment variables as a simple KEY=value string pair.

$ echo "HELLO=World" > .env
$ echo "console.log('Hello ' + process.env.HELLO)" > index.js

$ dotenvx run --env HELLO=String -f .env -- node index.js
[[email protected]] injecting env (1) from .env, and --env flag
Hello String

Override existing env variables. These can be variables already on your machine or variables loaded as files consecutively. The last variable seen will 'win'.

$ echo "HELLO=local" > .env.local
$ echo "HELLO=World" > .env
$ echo "console.log('Hello ' + process.env.HELLO)" > index.js

$ dotenvx run -f .env.local -f .env --overload -- node index.js
[[email protected]] injecting env (1) from .env.local, .env
Hello World

Note that with --overload subsequent files DO override pre-existing variables defined in previous files.

When deploying applications in containers or cloud environments, you often need to override specific environment variables at runtime without modifying committed .env files. By default, dotenvx follows the historic dotenv principle: environment variables already present take precedence over .env files.

# .env.prod contains: MODEL_REGISTRY=registry.company.com/models/v1
$ echo "MODEL_REGISTRY=registry.company.com/models/v1" > .env.prod
$ echo "console.log('MODEL_REGISTRY:', process.env.MODEL_REGISTRY)" > app.js

# Without environment variable set - uses .env.prod value
$ dotenvx run -f .env.prod -- node app.js
MODEL_REGISTRY: registry.company.com/models/v1

# With environment variable set (e.g., via Azure Container Service) - environment variable takes precedence
$ MODEL_REGISTRY=registry.azure.com/models/v2 dotenvx run -f .env.prod -- node app.js
MODEL_REGISTRY: registry.azure.com/models/v2

# To force .env.prod to override environment variables, use --overload
$ MODEL_REGISTRY=registry.azure.com/models/v2 dotenvx run -f .env.prod --overload -- node app.js
MODEL_REGISTRY: registry.company.com/models/v1

For container deployments: Set environment variables through your cloud provider's UI/configuration (Azure Container Service, AWS ECS, etc.) to override specific values from committed .env files without rebuilding your application.

Decrypt your encrypted .env by setting DOTENV_PRIVATE_KEY before dotenvx run.

$ touch .env
$ dotenvx set HELLO encrypted
$ echo "console.log('Hello ' + process.env.HELLO)" > index.js

# check your .env.keys files for your privateKey
$ DOTENV_PRIVATE_KEY="122...0b8" dotenvx run -- node index.js
[[email protected]] injecting env (2) from .env
Hello encrypted

Decrypt your encrypted .env.production by setting DOTENV_PRIVATE_KEY_PRODUCTION before dotenvx run. Alternatively, this can be already set on your server or cloud provider.

$ touch .env.production
$ dotenvx set HELLO "production encrypted" -f .env.production
$ echo "console.log('Hello ' + process.env.HELLO)" > index.js

# check .env.keys for your privateKey
$ DOTENV_PRIVATE_KEY_PRODUCTION="122...0b8" dotenvx run -- node index.js
[[email protected]] injecting env (2) from .env.production
Hello production encrypted

Note the DOTENV_PRIVATE_KEY_PRODUCTION ends with _PRODUCTION. This instructs dotenvx run to load the .env.production file.

Decrypt your encrypted .env.ci by setting DOTENV_PRIVATE_KEY_CI before dotenvx run. Alternatively, this can be already set on your server or cloud provider.

$ touch .env.ci
$ dotenvx set HELLO "ci encrypted" -f .env.ci
$ echo "console.log('Hello ' + process.env.HELLO)" > index.js

# check .env.keys for your privateKey
$ DOTENV_PRIVATE_KEY_CI="122...0b8" dotenvx run -- node index.js
[[email protected]] injecting env (2) from .env.ci
Hello ci encrypted

Note the DOTENV_PRIVATE_KEY_CI ends with _CI. This instructs dotenvx run to load the .env.ci file. See the pattern?

Decrypt your encrypted .env and .env.production files by setting DOTENV_PRIVATE_KEY and DOTENV_PRIVATE_KEY_PRODUCTION before dotenvx run.

$ touch .env
$ touch .env.production
$ dotenvx set HELLO encrypted
$ dotenvx set HELLO "production encrypted" -f .env.production
$ echo "console.log('Hello ' + process.env.HELLO)" > index.js

# check .env.keys for your privateKeys
$ DOTENV_PRIVATE_KEY="122...0b8" DOTENV_PRIVATE_KEY_PRODUCTION="122...0b8" dotenvx run -- node index.js
[[email protected]] injecting env (3) from .env, .env.production
Hello encrypted

$ DOTENV_PRIVATE_KEY_PRODUCTION="122...0b8" DOTENV_PRIVATE_KEY="122...0b8" dotenvx run -- node index.js
[[email protected]] injecting env (3) from .env.production, .env
Hello production encrypted

Compose any encrypted files you want this way. As long as a DOTENV_PRIVATE_KEY_${environment} is set, the values from .env.${environment} will be decrypted at runtime.

Set log level to verbose. (log levels)

$ echo "HELLO=production" > .env.production
$ echo "console.log('Hello ' + process.env.HELLO)" > index.js

$ dotenvx run -f .env.production --verbose -- node index.js
loading env from .env.production (/path/to/.env.production)
HELLO set
[[email protected]] injecting env (1) from .env.production
Hello production

Set log level to debug. (log levels)

$ echo "HELLO=production" > .env.production
$ echo "console.log('Hello ' + process.env.HELLO)" > index.js

$ dotenvx run -f .env.production --debug -- node index.js
process command [node index.js]
options: {"env":[],"envFile":[".env.production"]}
loading env from .env.production (/path/to/.env.production)
{"HELLO":"production"}
HELLO set
HELLO set to production
[[email protected]] injecting env (1) from .env.production
executing process command [node index.js]
expanding process command to [/opt/homebrew/bin/node index.js]
Hello production

Use --quiet to suppress all output (except errors). (log levels)

$ echo "HELLO=production" > .env.production
$ echo "console.log('Hello ' + process.env.HELLO)" > index.js

$ dotenvx run -f .env.production --quiet -- node index.js
Hello production

Set --log-level to whatever you wish. For example, to suppress warnings (risky), set log level to error:

$ echo "HELLO=production" > .env.production
$ echo "console.log('Hello ' + process.env.HELLO)" > index.js

$ dotenvx run -f .env.production --log-level=error -- node index.js
Hello production

Available log levels are error, warn, info, verbose, debug, silly (source)

Exit with code 1 if any errors are encountered - like a missing .env file or decryption failure.

$ echo "console.log('Hello ' + process.env.HELLO)" > index.js

$ dotenvx run -f .env.missing --strict -- node index.js
[MISSING_ENV_FILE] missing .env.missing file (/path/to/.env.missing)
[MISSING_ENV_FILE] ? add one with [echo "HELLO=World" > .env.missing]

This can be useful in ci scripts where you want to fail the ci if your .env file could not be decrypted at runtime.

Ignore errors like MISSING_ENV_FILE.

$ echo "console.log('Hello ' + process.env.HELLO)" > index.js

$ dotenvx run -f .env.missing --ignore=MISSING_ENV_FILE -- node index.js
...

Load envs using Next.js' convention. Set --convention to nextjs:

$ echo "HELLO=development local" > .env.development.local
$ echo "HELLO=local" > .env.local
$ echo "HELLO=development" > .env.development
$ echo "HELLO=env" > .env
$ echo "console.log('Hello ' + process.env.HELLO)" > index.js

$ dotenvx run --convention=nextjs -- node index.js
[[email protected]] injecting env (1) from .env.development.local, .env.local, .env.development, .env
Hello development local

(more conventions available upon request)

Load envs using dotenv-flow's convention. Set --convention to flow:

$ echo "HELLO=development local" > .env.development.local
$ echo "HELLO=development" > .env.development
$ echo "HELLO=local" > .env.local
$ echo "HELLO=env" > .env
$ echo "console.log('Hello ' + process.env.HELLO)" > index.js

$ NODE_ENV=development dotenvx run --convention=flow -- node index.js 
[[email protected]] injecting env (1) from .env.development.local, .env.development, .env.local, .env
Hello development local

Further, we recommend using DOTENV_ENV over NODE_ENV– as dotenvx works everywhere, not just node.

$ DOTENV_ENV=development dotenvx run --convention=flow -- node index.js 
[[email protected]] injecting env (1) from .env.development.local, .env.development, .env.local, .env
Hello development local

Specify path to .env.keys. This is useful with monorepos.

$ mkdir -p apps/app1
$ touch apps/app1/.env
$ dotenvx set HELLO world -fk .env.keys -f apps/app1/.env

$ dotenvx run -fk .env.keys -f apps/app1/.env -- yourcommand

Turn off Dotenvx Ops features.

$ dotenvx run --ops-off -- yourcommand

Return a single environment variable's value.

$ echo "HELLO=World" > .env

$ dotenvx get HELLO
World

Return a single environment variable's value from a specific .env file.

$ echo "HELLO=World" > .env
$ echo "HELLO=production" > .env.production

$ dotenvx get HELLO -f .env.production
production

Specify path to .env.keys. This is useful with monorepos.

$ mkdir -p apps/app1
$ touch apps/app1/.env
$ dotenvx set HELLO world -fk .env.keys -f apps/app1/.env

$ dotenvx get HELLO -fk .env.keys -f apps/app1/.env
world

Return a single environment variable's value from a --env string.

$ dotenvx get HELLO --env HELLO=String -f .env.production
String

Return a single environment variable's value where each found value is overloaded.

$ echo "HELLO=World" > .env
$ echo "HELLO=production" > .env.production

$ dotenvx get HELLO -f .env.production --env HELLO=String -f .env --overload
World

Exit with code 1 if any errors are encountered - like a missing key, missing .env file, or decryption failure.

$ dotenvx get DOES_NOT_EXIST --strict
[MISSING_KEY] missing DOES_NOT_EXIST key

Return a single environment variable's value using Next.js' convention. Set --convention to nextjs:

$ echo "HELLO=development local" > .env.development.local
$ echo "HELLO=local" > .env.local
$ echo "HELLO=development" > .env.development
$ echo "HELLO=env" > .env
$ echo "console.log('Hello ' + process.env.HELLO)" > index.js

$ dotenvx get HELLO --convention=nextjs
development local

Return a single environment variable's value using dotenv-flow's convention. Set --convention to flow:

$ echo "HELLO=development local" > .env.development.local
$ echo "HELLO=development" > .env.development
$ echo "HELLO=local" > .env.local
$ echo "HELLO=env" > .env
$ echo "console.log('Hello ' + process.env.HELLO)" > index.js

$ NODE_ENV=development dotenvx get HELLO --convention=flow
development local

Further, we recommend using DOTENV_ENV over NODE_ENV– as dotenvx works everywhere, not just node.

$ DOTENV_ENV=development dotenvx get HELLO --convention=flow
development local

Return a json response of all key/value pairs in a .env file.

$ echo "HELLO=World" > .env

$ dotenvx get
{"HELLO":"World"}

Return a shell formatted response of all key/value pairs in a .env file.

$ echo "HELLO=World" > .env
$ echo "KEY=value" >> .env

$ dotenvx get --format shell
HELLO=World KEY=value

This can be useful when combined with env on the command line.

$ echo "console.log('Hello ' + process.env.KEY + ' ' + process.env.HELLO)" > index.js
$ env $(dotenvx get --format=shell) node index.js
Hello value World

or with export.

$ echo "console.log('Hello ' + process.env.KEY + ' ' + process.env.HELLO)" > index.js
$ export $(dotenvx get --format=shell)
$ node index.js
Hello value World

Return an eval-ready shell formatted response of all key/value pairs in a .env file.

$ echo "HELLO=World" > .env
$ echo "KEY=value" >> .env

$ dotenvx get --format eval
HELLO="World"
KEY="value"

Note that this exports newlines and quoted strings.

This can be useful for more complex .env values (spaces, escaped characters, quotes, etc) combined with eval on the command line.

$ echo "console.log('Hello ' + process.env.KEY + ' ' + process.env.HELLO)" > index.js
$ eval $(dotenvx get --format=eval) node index.js
Hello value World

Be careful with eval as it allows for arbitrary execution of commands. Prefer dotenvx run -- but in some cases eval is a sharp knife that is useful to have.

Return preset machine envs as well.

$ echo "HELLO=World" > .env

$ dotenvx get --all
{"PWD":"/some/file/path","USER":"username","LIBRARY_PATH":"/usr/local/lib", ..., "HELLO":"World"}

Make the output more readable - pretty print it.

$ echo "HELLO=World" > .env

$ dotenvx get --all --pretty-print
{
  "PWD": "/some/filepath",
  "USER": "username",
  "LIBRARY_PATH": "/usr/local/lib",
  ...,
  "HELLO": "World"
}

Set an encrypted key/value (on by default).

$ touch .env

$ dotenvx set HELLO World
set HELLO with encryption (.env)

Set an (encrypted) key/value for another .env file.

$ touch .env.production

$ dotenvx set HELLO production -f .env.production
set HELLO with encryption (.env.production)

Specify path to .env.keys. This is useful with monorepos.

$ mkdir -p apps/app1
$ touch apps/app1/.env

$ dotenvx set HELLO world -fk .env.keys -f apps/app1/.env
set HELLO with encryption (.env)

Put it to use.

$ dotenvx get -fk .env.keys -f apps/app1/.env

Use it with a relative path.

$ cd apps/app1
$ dotenvx get -fk ../../.env.keys -f .env

Set a value containing spaces.

$ touch .env.ci

$ dotenvx set HELLO "my ci" -f .env.ci
set HELLO with encryption (.env.ci)

If your value starts with a dash (-), then place two dashes instructing the cli that there are no more flag arguments.

$ touch .env.ci

$ dotenvx set HELLO -f .env.ci -- "- + * ÷"
set HELLO with encryption (.env.ci)

Set a plaintext key/value.

$ touch .env

$ dotenvx set HELLO World --plain
set HELLO (.env)

Encrypt the contents of a .env file to an encrypted .env file.

$ echo "HELLO=World" > .env

$ dotenvx encrypt
✔ encrypted (.env)
✔ key added to .env.keys (DOTENV_PRIVATE_KEY)
⮕  next run [dotenvx ext gitignore --pattern .env.keys] to gitignore .env.keys
⮕  next run [DOTENV_PRIVATE_KEY='122...0b8' dotenvx run -- yourcommand] to test decryption locally

Encrypt the contents of a specified .env file to an encrypted .env file.

$ echo "HELLO=World" > .env
$ echo "HELLO=Production" > .env.production

$ dotenvx encrypt -f .env.production
✔ encrypted (.env.production)
✔ key added to .env.keys (DOTENV_PRIVATE_KEY_PRODUCTION)
⮕  next run [dotenvx ext gitignore --pattern .env.keys] to gitignore .env.keys
⮕  next run [DOTENV_PRIVATE_KEY='bff...bc4' dotenvx run -- yourcommand] to test decryption locally

Specify path to .env.keys. This is useful with monorepos.

$ mkdir -p apps/app1
$ echo "HELLO=World" > apps/app1/.env

$ dotenvx encrypt -fk .env.keys -f apps/app1/.env
✔ encrypted (apps/app1/.env)

Put it to use.

$ dotenvx run -fk .env.keys -f apps/app1/.env

Use with a relative path.

$ cd apps/app1
$ dotenvx run -fk ../../.env.keys -f .env

Specify the key(s) to encrypt by passing --key.

$ echo "HELLO=World\nHELLO2=Universe" > .env

$ dotenvx encrypt -k HELLO2
✔ encrypted (.env)

Even specify a glob pattern.

$ echo "HELLO=World\nHOLA=Mundo" > .env

$ dotenvx encrypt -k "HE*"
✔ encrypted (.env)

Specify the key(s) to NOT encrypt by passing --exclude-key.

$ echo "HELLO=World\nHELLO2=Universe" > .env

$ dotenvx encrypt -ek HELLO
✔ encrypted (.env)

Even specify a glob pattern.

$ echo "HELLO=World\nHOLA=Mundo" > .env

$ dotenvx encrypt -ek "HO*"
✔ encrypted (.env)

Encrypt the contents of a .env file and send to stdout.

$ echo "HELLO=World" > .env
$ dotenvx encrypt --stdout
#/-------------------[DOTENV_PUBLIC_KEY]--------------------/
#/            public-key encryption for .env files          /
#/       [how it works](https://dotenvx.com/encryption)     /
#/----------------------------------------------------------/
DOTENV_PUBLIC_KEY="034af93e93708b994c10f236c96ef88e47291066946cce2e8d98c9e02c741ced45"
# .env
HELLO="encrypted:BDqDBibm4wsYqMpCjTQ6BsDHmMadg9K3dAt+Z9HPMfLEIRVz50hmLXPXRuDBXaJi/LwWYEVUNiq0HISrslzQPaoyS8Lotg3gFWJTsNCdOWnqpjF2xNUX2RQiP05kAbEXM6MWVjDr"

or send to a file:

$ echo "HELLO=World" > .env
$ dotenvx encrypt --stdout > somefile.txt

Decrypt the contents of an encrypted .env file to an unencrypted .env file.

$ echo "HELLO=World" > .env
$ dotenvx encrypt
✔ encrypted (.env)
$ dotenvx decrypt
✔ decrypted (.env)

Decrypt the contents of a specified encrypted .env file to an unencrypted .env file.

$ echo "HELLO=World" > .env
$ echo "HELLO=Production" > .env.production

$ dotenvx encrypt -f .env.production
✔ encrypted (.env.production)
$ dotenvx decrypt -f .env.production
✔ decrypted (.env.production)

Specify path to .env.keys. This is useful with monorepos.

$ mkdir -p apps/app1
$ echo "HELLO=World" > apps/app1/.env

$ dotenvx encrypt -fk .env.keys -f apps/app1/.env
✔ encrypted (apps/app1/.env)
$ dotenvx decrypt -fk .env.keys -f apps/app1/.env
✔ decrypted (apps/app1/.env)

Decrypt the contents of a specified key inside an encrypted .env file.

$ echo "HELLO=World\nHOLA=Mundo" > .env
$ dotenvx encrypt
✔ encrypted (.env)
$ dotenvx decrypt -k HELLO
✔ decrypted (.env)

Even specify a glob pattern.

$ echo "HELLO=World\nHOLA=Mundo" > .env
$ dotenvx encrypt
✔ encrypted (.env)
$ dotenvx decrypt -k "HE*"
✔ encrypted (.env)

Decrypt the contents inside an encrypted .env file except for an excluded key.

$ echo "HELLO=World\nHOLA=Mundo" > .env
$ dotenvx encrypt
✔ encrypted (.env)
$ dotenvx decrypt -ek HOLA
✔ decrypted (.env)

Even specify a glob pattern.

$ echo "HELLO=World\nHOLA=Mundo" > .env
$ dotenvx encrypt
✔ encrypted (.env)
$ dotenvx decrypt -ek "HO*"
✔ encrypted (.env)

Decrypt the contents of an encrypted .env file and send to stdout.

$ dotenvx decrypt --stdout
#/-------------------[DOTENV_PUBLIC_KEY]--------------------/
#/            public-key encryption for .env files          /
#/       [how it works](https://dotenvx.com/encryption)     /
#/----------------------------------------------------------/
DOTENV_PUBLIC_KEY="034af93e93708b994c10f236c96ef88e47291066946cce2e8d98c9e02c741ced45"
# .env
HELLO="World"

or send to a file:

$ dotenvx decrypt --stdout > somefile.txt

Print public/private keys for .env file.

$ echo "HELLO=World" > .env
$ dotenvx encrypt

$ dotenvx keypair
{"DOTENV_PUBLIC_KEY":"<publicKey>","DOTENV_PRIVATE_KEY":"<privateKey>"}

Print public/private keys for .env.production file.

$ echo "HELLO=Production" > .env.production
$ dotenvx encrypt -f .env.production

$ dotenvx keypair -f .env.production
{"DOTENV_PUBLIC_KEY_PRODUCTION":"<publicKey>","DOTENV_PRIVATE_KEY_PRODUCTION":"<privateKey>"}

Specify path to .env.keys. This is useful for printing public/private keys for monorepos.

$ mkdir -p apps/app1
$ echo "HELLO=World" > apps/app1/.env
$ dotenvx encrypt -fk .env.keys -f apps/app1/.env

$ dotenvx keypair -fk .env.keys -f apps/app1/.env
{"DOTENV_PUBLIC_KEY":"<publicKey>","DOTENV_PRIVATE_KEY":"<privateKey>"}

Print specific keypair for .env file.

$ echo "HELLO=World" > .env
$ dotenvx encrypt

$ dotenvx keypair DOTENV_PRIVATE_KEY
<privateKey>

Print a shell formatted response of public/private keys.

$ echo "HELLO=World" > .env
$ dotenx encrypt

$ dotenvx keypair --format shell
DOTENV_PUBLIC_KEY=<publicKey> DOTENV_PRIVATE_KEY=<privateKey>

Print all .env files in a tree structure.

$ touch .env
$ touch .env.production
$ mkdir -p apps/backend
$ touch apps/backend/.env

$ dotenvx ls
├─ .env.production
├─ .env
└─ apps
   └─ backend
      └─ .env

Print all .env files inside a specified path to a directory.

$ touch .env
$ touch .env.production
$ mkdir -p apps/backend
$ touch apps/backend/.env

$ dotenvx ls apps/backend
└─ .env

Glob .env filenames matching a wildcard.

$ touch .env
$ touch .env.production
$ mkdir -p apps/backend
$ touch apps/backend/.env
$ touch apps/backend/.env.prod

$ dotenvx ls -f **/.env.prod*
├─ .env.production
└─ apps
   └─ backend
      └─ .env.prod

Glob .env filenames excluding a wildcard.

$ touch .env
$ touch .env.production
$ mkdir -p apps/backend
$ touch apps/backend/.env
$ touch apps/backend/.env.prod

$ dotenvx ls -ef '**/.env.prod*'
├─ .env
└─ apps
   └─ backend
      └─ .env

Rotate public/private keys for .env file and re-encrypt all encrypted values.

$ echo "HELLO=World" > .env
$ dotenvx encrypt
✔ encrypted (.env)
$ dotenvx rotate
✔ rotated (.env)

Rotate public/private keys for a specified encrypted .env file and re-encrypt all encrypted values.

$ echo "HELLO=World" > .env
$ echo "HELLO=Production" > .env.production

$ dotenvx encrypt -f .env.production
✔ encrypted (.env.production)
$ dotenvx rotate -f .env.production
✔ rotated (.env.production)

Specify path to .env.keys. This is useful with monorepos.

$ mkdir -p apps/app1
$ echo "HELLO=World" > apps/app1/.env

$ dotenvx encrypt -fk .env.keys -f apps/app1/.env
✔ encrypted (apps/app1/.env)
$ dotenvx rotate -fk .env.keys -f apps/app1/.env
✔ rotated (apps/app1/.env)

Rotate the contents of a specified key inside an encrypted .env file.

$ echo "HELLO=World\nHOLA=Mundo" > .env
$ dotenvx encrypt
✔ encrypted (.env)
$ dotenvx rotate -k HELLO
✔ rotated (.env)

Even specify a glob pattern.

$ echo "HELLO=World\nHOLA=Mundo" > .env
$ dotenvx encrypt
✔ encrypted (.env)
$ dotenvx rotate -k "HE*"
✔ rotated (.env)

Rotate the encrypted contents inside an encrypted .env file except for an excluded key.

$ echo "HELLO=World\nHOLA=Mundo" > .env
$ dotenvx encrypt
✔ encrypted (.env)
$ dotenvx rotate -ek HOLA
✔ rotated (.env)

Even specify a glob pattern.

$ echo "HELLO=World\nHOLA=Mundo" > .env
$ dotenvx encrypt
✔ encrypted (.env)
$ dotenvx rotate -ek "HO*"
✔ rotated (.env)

Rotate the contents of an encrypted .env file and send to stdout.

$ dotenvx rotate --stdout
#/-------------------[DOTENV_PUBLIC_KEY]--------------------/
#/            public-key encryption for .env files          /
#/       [how it works](https://dotenvx.com/encryption)     /
#/----------------------------------------------------------/
DOTENV_PUBLIC_KEY="034af93e93708b994c10f236c96ef88e47291066946cce2e8d98c9e02c741ced45"
# .env
HELLO="encrypted:12345"

or send to a file:

$ dotenvx rotate --stdout > somefile.txt

Output help for dotenvx.

$ dotenvx help
Usage: dotenvx run -- yourcommand

a secure dotenv–from the creator of `dotenv`

Options:
  -l, --log-level <level>      set log level (default: "info")
  -q, --quiet                  sets log level to error
  -v, --verbose                sets log level to verbose
  -d, --debug                  sets log level to debug
  -V, --version                output the version number
  -h, --help                   display help for command

Commands:
  run                inject env at runtime [dotenvx run -- yourcommand]
  get [KEY]          return a single environment variable
  set <KEY> <value>  set a single environment variable
  encrypt            convert .env file(s) to encrypted .env file(s)
  decrypt            convert encrypted .env file(s) to plain .env file(s)
  keypair [KEY]      print public/private keys for .env file(s)
  ls [directory]     print all .env files in a tree structure
 
Advanced: 
  pro                          🏆 pro
  ext                          🔌 extensions

You can get more detailed help per command with dotenvx help COMMAND.

$ dotenvx help run
Usage: @dotenvx/dotenvx run [options]

inject env at runtime [dotenvx run -- yourcommand]

Options:
  -e, --env <strings...>            environment variable(s) set as string (example: "HELLO=World") (default: [])
  -f, --env-file <paths...>         path(s) to your env file(s) (default: [])
  -fv, --env-vault-file <paths...>  path(s) to your .env.vault file(s) (default: [])
  -o, --overload                    override existing env variables
  --convention <name>               load a .env convention (available conventions: ['nextjs'])
  -h, --help                        display help for command

Examples:

  $ dotenvx run -- npm run dev
  $ dotenvx run -- flask --app index run
  $ dotenvx run -- php artisan serve
  $ dotenvx run -- bin/rails s

Try it:

  $ echo "HELLO=World" > .env
  $ echo "console.log('Hello ' + process.env.HELLO)" > index.js

  $ dotenvx run -- node index.js
  [[email protected]] injecting env (1) from .env
  Hello World

Check current version of dotenvx.

$ dotenvx --version
X.X.X

Extensions 🔌

CLI extensions.

In one command, generate a .env.example file from your current .env file contents.

$ echo "HELLO=World" > .env

$ dotenvx ext genexample
✔ updated .env.example (1)
# .env.example
HELLO=""

Pass multiple .env files to generate your .env.example file from the combination of their contents.

$ echo "HELLO=World" > .env
$ echo "DB_HOST=example.com" > .env.production

$ dotenvx ext genexample -f .env -f .env.production
✔ updated .env.example (2)
# .env.example
HELLO=""
DB_HOST=""

Generate a .env.example file inside the specified directory. Useful for monorepos.

$ echo "HELLO=World" > .env
$ mkdir -p apps/backend
$ echo "HELLO=Backend" > apps/backend/.env

$ dotenvx ext genexample apps/backend
✔ updated .env.example (1)
# apps/backend/.env.example
HELLO=""

Gitignore your .env files.

$ dotenvx ext gitignore
✔ ignored .env* (.gitignore)

Gitignore specific pattern(s) of .env files.

$ dotenvx ext gitignore --pattern .env.keys
✔ ignored .env.keys (.gitignore)

Prevent .env files from being committed to code.

$ dotenvx ext precommit
[dotenvx][precommit] .env files (1) protected (encrypted or gitignored)

Install a shell script to .git/hooks/pre-commit to prevent accidentally committing any .env files to source control.

$ dotenvx ext precommit --install
[dotenvx][precommit] dotenvx ext precommit installed [.git/hooks/pre-commit]

Prevent .env files from being committed to code inside a specified path to a directory.

$ echo "HELLO=World" > .env
$ mkdir -p apps/backend
$ echo "HELLO=Backend" > apps/backend/.env

$ dotenvx ext precommit apps/backend
[dotenvx][precommit] apps/backend/.env not protected (encrypted or gitignored)

Prevent .env files from being built into your docker containers.

Add it to your Dockerfile.

# Dockerfile
RUN curl -fsS https://dotenvx.sh | sh

...

RUN dotenvx ext prebuild
CMD ["dotenvx", "run", "--", "node", "index.js"]

Prevent .env files from being built into your docker containers inside a specified path to a directory.

Add it to your Dockerfile.

# Dockerfile
RUN curl -fsS https://dotenvx.sh | sh

...

RUN dotenvx ext prebuild apps/backend
CMD ["dotenvx", "run", "--", "node", "apps/backend/index.js"]

Scan for leaked secrets.

$ dotenvx ext scan
100 commits scanned.
no leaks found

Uses gitleaks under the hood.

Library 📦

Use dotenvx directly in code.

Use directly in node.js code.

# .env
HELLO="World"
// index.js
require('@dotenvx/dotenvx').config()

console.log(`Hello ${process.env.HELLO}`)
$ node index.js
[[email protected]] injecting env (1) from .env
Hello World

It defaults to looking for a .env file.

Specify path(s) to multiple .env files.

# .env.local
HELLO="Me"
# .env
HELLO="World"
// index.js
require('@dotenvx/dotenvx').config({path: ['.env.local', '.env']})

// esm
// import dotenvx from "@dotenvx/dotenvx";
// dotenvx.config({path: ['.env.local', '.env']});

console.log(`Hello ${process.env.HELLO}`)
$ node index.js
[[email protected]] injecting env (1) from .env.local, .env
Hello Me

Use overload to overwrite the prior set value.

# .env.local
HELLO="Me"
# .env
HELLO="World"
// index.js
require('@dotenvx/dotenvx').config({path: ['.env.local', '.env'], overload: true})

// esm
// import dotenvx from "@dotenvx/dotenvx";
// dotenvx.config({path: ['.env.local', '.env'], overload: true});

console.log(`Hello ${process.env.HELLO}`)
$ node index.js
[[email protected]] injecting env (1) from .env.local, .env
Hello World

Suppress all output (except errors).

# .env
HELLO="World"
// index.js
require('@dotenvx/dotenvx').config({path: ['.env.missing', '.env'], quiet: true})

// esm
// import dotenvx from "@dotenvx/dotenvx";
// dotenvx.config({path: ['.env.missing', '.env'], quiet: true});

console.log(`Hello ${process.env.HELLO}`)
$ node index.js
Error: [MISSING_ENV_FILE] missing .env.missing file (/path/to/.env.missing)
Hello World

Exit with code 1 if any errors are encountered - like a missing .env file or decryption failure.

# .env
HELLO="World"
// index.js
require('@dotenvx/dotenvx').config({path: ['.env.missing', '.env'], strict: true})

// esm
// import dotenvx from "@dotenvx/dotenvx";
// dotenvx.config({path: ['.env.missing', '.env'], strict: true});

console.log(`Hello ${process.env.HELLO}`)
$ node index.js
Error: [MISSING_ENV_FILE] missing .env.missing file (/path/to/.env.missing)

Use ignore to suppress specific errors like MISSING_ENV_FILE.

# .env
HELLO="World"
// index.js
require('@dotenvx/dotenvx').config({path: ['.env.missing', '.env'], ignore: ['MISSING_ENV_FILE']})

// esm
// import dotenvx from "@dotenvx/dotenvx";
// dotenvx.config({path: ['.env.missing', '.env'], ignore: ['MISSING_ENV_FILE']});

console.log(`Hello ${process.env.HELLO}`)
$ node index.js
[[email protected]] injecting env (1) from .env
Hello World

Use envKeysFile to customize the path to your .env.keys file. This is useful with monorepos.

# .env
HELLO="World"
// index.js
require('@dotenvx/dotenvx').config({path: ['.env'], envKeysFile: '../../.env.keys'})

Set a convention when using dotenvx.config(). This allows you to use the same file loading order as the CLI without needing to specify each file individually.

# Setup environment files
$ echo "HELLO=development local" > .env.development.local
$ echo "HELLO=local" > .env.local
$ echo "HELLO=development" > .env.development
$ echo "HELLO=env" > .env
// index.js
require('@dotenvx/dotenvx').config({ convention: 'nextjs' })

console.log(`Hello ${process.env.HELLO}`)
$ NODE_ENV=development node index.js
[[email protected]] injecting env (1) from .env.development.local, .env.local, .env.development, .env
Hello development local

This is equivalent to using --convention=nextjs with the CLI:

$ dotenvx run --convention=nextjs -- node index.js

Turn off Dotenvx Ops features.

// index.js
require('@dotenvx/dotenvx').config({opsOff: true})

Parse a .env string directly in node.js code.

// index.js
const dotenvx = require('@dotenvx/dotenvx')
const src = 'HELLO=World'
const parsed = dotenvx.parse(src)
console.log(`Hello ${parsed.HELLO}`)
$ node index.js
Hello World

Sometimes, you want to run parse without it accessing process.env. (You can pass a fake processEnv this way as well - sometimes useful.)

// index.js
const dotenvx = require('@dotenvx/dotenvx')
const src = 'USER=Me'
const parsed = dotenvx.parse(src, { processEnv: {} })
console.log(`Hello ${parsed.USER}`)
$ node index.js
Hello Me

Decrypt an encrypted .env string with privateKey.

// index.js
const dotenvx = require('@dotenvx/dotenvx')
const src = 'HELLO="encrypted:BE9Y7LKANx77X1pv1HnEoil93fPa5c9rpL/1ps48uaRT9zM8VR6mHx9yM+HktKdsPGIZELuZ7rr2mn1gScsmWitppAgE/1lVprNYBCqiYeaTcKXjDUXU5LfsEsflnAsDhT/kWG1l"'
const parsed = dotenvx.parse(src, { privateKey: 'a4547dcd9d3429615a3649bb79e87edb62ee6a74b007075e9141ae44f5fb412c' })
console.log(`Hello ${parsed.HELLO}`)
$ node index.js
Hello World

Programmatically set an environment variable.

// index.js
const dotenvx = require('@dotenvx/dotenvx')
dotenvx.set('HELLO', 'World', { path: '.env' })

Programmatically get an environment variable at access/runtime.

// index.js
const dotenvx = require('@dotenvx/dotenvx')
const decryptedValue = dotenvx.get('HELLO')
console.log(decryptedValue)

This is known as Decryption at Access and is written about in the whitepaper.

 

Ops 🏰

dotenvx-ops

Dotenvx Ops is optional commercial tooling for dotenvx.

Use dotenvx across your team, infrastructure, agents, and more.

Usage

  1. Install dotenvx-ops
$ curl -sfS https://dotenvx.sh/ops | sh
  1. Log in
$ dotenvx-ops login
✔ logged in [username]
  1. Run dotenvx
$ dotenvx run -- yourcommand
[[email protected]] 📡 radar active
[[email protected]] injecting env (1) from .env

That's it! Your environment variables are auto-observed and backed up by Dotenvx Ops radar feature.

UI

dotenvx-ops

CLI

Log in to Ops.

$ dotenvx-ops login
press Enter to open [https://ops.dotenvx.com/login/device] and enter code [D9C1-03BC]... (Y/n)
⠹ waiting on browser authorization
✔ logged in [username] to this device and activated token [dxo_6kjPifI…]

Log out of Ops.

$ dotenvx-ops logout
✔ logged out [username] from this device and revoked token [dxo_5ZrwRXV…]

Check current status of Ops - on or off (logged in or out).

$ dotenvx-ops status
on