How to manage secrets and configs using dotenv in Node.js and Docker
Tags: node, docker, javascript, webdev
Published: 2019-09-07 16:16:15

# What is `dotenv`?

It's a javascript package that reads `KEY=VALUE` from a `.env` file (example below) and sets each pair as env variables.

```javascript
// 1. Create a .env file:
DB_ADMIN_USERNAME=techbos
DB_ADMIN_PASSWORD=Pa$$w0rd

// 2. In your node app, load dotenv to read .env and set env variables when app starts
require('dotenv').config();

// 3. You can now use the env variables in your app
connectDatabase({
  username: process.env.DB_ADMIN_USERNAME, //techbos
  password: process.env.DB_ADMIN_PASSWORD, // Pa$$w0rd
});
```

# Why should I use `dotenv`?

`dotenv` allows you to separate secrets from your source code. This is useful in a collaborative environment (e.g., work, or open source) where you may not want to share your database login credentials with other people. Instead, you can share the source code while allowing other people to create their own `.env` file.

It's also useful for dynamically configure your app without changing the source code. For example, you can set `DB_URL` to a dev database for local development. Or, if you want to print logs to console for local development, but not in prod, you can do:

```javascript
// .env file
ENV=dev

// in your app
if (process.env.ENV === 'dev') console.log(...);
```

For real-world applications that are deployed to hundreds or even thousands of instances, using `dotenv` (or use other similar tech) allows all instances to share the same source code, while each having a different `.env` file so they can use different configurations, e.g., connecting to different databases or writing logs to different endpoints.

# How to setup `dotenv` in my project?

## 1. Preload `dotenv` in node.js

Start your node app with `dotenv` preloaded so you don't even need to require dotenv in your source code.

```javascript
// use this
node -r dotenv/config index.js
// instead of
require('dotenv').config();
```

## 2. Add `.env` to `.gitignore`

You should never share `.env` file in source control. Instead, create a separate private repo to store your own `.env` files, while sharing the rest of the source code with others.

```javascript
//.gitignore
.env
```

## 3. Create as many `.env` as you like

It's typical to have multiple `.env` files. E.g., you may have one for dev, one for stage and one for prod. After you checkout the source code, copy over the `.env` file for your environment and start the app.

## 3. Start your app
```javascript
// E.g., use with babel-node for local development
nodemon --exec babel-node -r node_modules/dotenv/config src/index.js
// E.g., run with node in production
node -r dotenv/config src/index.js
```

## 4. Run in docker

```javascript
// Dockerfile
CMD node -r dotenv/config ./build/index.js
```

# Use `dotenv-safe`

A good alternative to `dotenv` is [`dotenv-safe`](https://www.npmjs.com/package/dotenv-safe), which is identical to `dotenv` except that it allows you to create a `.env.example` file. This serves two purposes:
1. It allows you to specify all required env variables in the example file. At runtime the package checks if a `.env` file has all the required variables defined, and throws if not.
2. You can share the `.env.example` file with others so everyone knows what env variables are required.

# Use in Docker

In some cases, building the `.env` file into a docker image is considered a bad practice because if the image is shared with others, the secrets can be leaked. 

To fix that, you can map a `.env` file into docker at runtime:
```javascript
volumes:
  - /local/file/path/to/.env:/app/.env
```

Another approach is to use the `env_file` docker option. You can read more [here](https://docs.docker.com/compose/compose-file/#env_file)