Deploy an Express.js App with Docker Compose
Deploy an Express.js app with an encrypted .env.vault file on Docker Compose.
Find a complete code example on GitHub for this guide.
Initial setup
Install express.
npm install express --save
Create an index.js
file with the web server code.
index.js
const PORT = process.env.PORT || 3000
const express = require('express')
const app = express()
app.listen(PORT, () => {
console.log(`Running on port ${PORT}.`)
})
app.get('/', function (req, res) {
res.send(`Hello ${process.env.HELLO}`)
})
Add a Dockerfile
.
Dockerfile
# Dockerfile
FROM node:16
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
Create your docker-compose.yml
file.
docker-compose.yml
version: '3.8'
services:
web:
build: .
command: node index.js
ports:
- "3000:3000"
environment:
NODE_ENV: ${NODE_ENV}
DOTENV_KEY: ${DOTENV_KEY}
Create your .dockerignore
file.
.git
.env*
!.env.vault
Commit all that to code and run it with Docker Compose.
docker-compose up --build
Once running, your app will say Hello undefined
at localhost:3000 as it doesn’t have a way to access the environment variable yet. Let’s do that next.
Install dotenv
Install dotenv
.
npm install dotenv --save # Requires dotenv >= 16.1.0
As early as possible in your application, import and configure dotenv.
index.js
require('dotenv').config()
console.log(process.env) // remove this after you've confirmed it is working
const PORT = process.env.PORT || 3000
const express = require('express')
...
Next, tie the .env.vault
file to our docker-compose file.
Build .env.vault
Push your latest .env
file changes and edit your production secrets. Learn more about syncing
npx dotenv-vault@latest push
npx dotenv-vault@latest open production
Use the UI to configure those secrets per environment.
Then build your encrypted .env.vault
file.
npx dotenv-vault@latest build
Its contents should look something like this.
.env.vault
#/-------------------.env.vault---------------------/
#/ cloud-agnostic vaulting standard /
#/ [how it works](https://dotenv.org/env-vault) /
#/--------------------------------------------------/
# development
DOTENV_VAULT_DEVELOPMENT="/HqNgQWsf6Oh6XB9pI/CGkdgCe6d4/vWZHgP50RRoDTzkzPQk/xOaQs="
DOTENV_VAULT_DEVELOPMENT_VERSION=2
# production
DOTENV_VAULT_PRODUCTION="x26PuIKQ/xZ5eKrYomKngM+dO/9v1vxhwslE/zjHdg3l+H6q6PheB5GVDVIbZg=="
DOTENV_VAULT_PRODUCTION_VERSION=2
Set DOTENV_KEY
Fetch your production DOTENV_KEY
.
npx dotenv-vault@latest keys production
# outputs: dotenv://:[email protected]/vault/.env.vault?environment=production
Set DOTENV_KEY
for Docker Compose up.
NODE_ENV=production DOTENV_KEY="dotenv://:[email protected]/vault/.env.vault?environment=production" docker-compose up --build
ProTip: If docker-compose seems cached on old code it probably is. Run docker-compose build --no-cache
to rebuild it.
That's it! On up, your .env.vault
file will be decrypted and its production secrets injected as environment variables – just in time.
You'll know things worked correctly when you see Loading env from encrypted .env.vault
in your logs. If a DOTENV_KEY
is not set (for example when developing on your local machine) it will fall back to standard dotenv functionality.