Use dotenvx with Fly
Use dotenvx with Fly
Find code examples for this guide on GitHub.
Initial setup
Install the necessary web server libraries in the language of your choice.
npm install express --save
Create a simple Hello World program.
// index.js
const express = require('express')
const app = express()
const PORT = process.env.PORT || 3000
app.get('/', (req, res) => {
res.send(`Hello ${process.env.HELLO || ''}`)
})
app.listen(PORT, () => {
console.log(`Server running on port:${PORT}`)
})
Create Dockerfile
.
# Dockerfile
FROM node:20
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "index.js"]
Create .dockerignore
.
.dockerignore
# .dockerignore
.env*
!.env.vault
Create fly.toml
.
[http_service]
internal_port = 3000
Commit that to code and deploy it to Fly.
flyctl launch
flyctl deploy
Once deployed, your app will say 'Hello [blank]'
as it doesn't have a way to access the environment variable yet. Let's do that next.
Run dotenvx
Install dotenvx
.
# install with Homebrew and then use the dotenvx command
brew install dotenvx/brew/dotenvx
dotenvx help
Create a .env
file in the root of your project.
.env
# .env
HELLO="World"
Inject your env using dotenvx
.
dotenvx run -- node index.js
Your app will say Hello World
. The values from your .env
file were successfully injected into your env. That covers local development. Let's solve for production next.
Add production environment
Create a .env.production
file in the root of your project.
.env.production
# .env.production
HELLO="production"
Use dotenvx
to load your .env.production
file.
dotenvx run --env-file=.env.production -- node index.js
Your app will say Hello production
, simulating production. Solid. Let's encrypt your secrets next.
Encrypt secrets
Use dotenvx
to encrypt your secrets.
dotenvx encrypt
This generates a .env.vault
and .env.keys
file.
.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.
.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"
You SHOULD commit .env.vault
to code. It is safe and recommended to do so. But DO NOT commit .env.keys
to code. Keep them somewhere safe like 1Password or dotenvx hub.
We're ready to add dotenvx
to Fly.
Add dotenvx to Dockerfile
Install dotenvx
to your Dockerfile and prepend your app command with dotenvx run --
.
# Dockerfile
FROM node:20
WORKDIR /app
# Install dotenvx
RUN curl -fsS https://dotenvx.sh/ | sh
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
# Prepend dotenvx run
CMD ["dotenvx", "run", "--", "node", "index.js"]
Redeploy to Fly.
flyctl deploy
$ flyctl logs
[info] INFO Preparing to run: `docker-entrypoint.sh dotenvx run -- node index.js` as root
[info][[email protected]] missing .env file (/app/.env)
[info]? in development: add one with [echo "HELLO=World" > .env] and re-run [dotenvx run -- node index.js]
[info]? for production: set [DOTENV_KEY] on your server and re-deploy
The logs tell us missing .env file
. This is expected, as we don't want to commit .env
to code. It also tells us, for production, that we should set DOTENV_KEY
. That is what we want to do. Let's do that next.
Set DOTENV_KEY
Set DOTENV_KEY
on Fly using the production key in your .env.keys
file.
flyctl secrets set DOTENV_KEY='dotenv://:key_10283719af6a30ef49050048617f4fea10c23a38021fbebeb9fd858caa01852e@dotenvx.com/vault/.env.vault?environment=production'
Your app restarts and env
is successfully injected using the encrypted contents of .env.vault
.
$ flyctl logs
[info] INFO Preparing to run: `docker-entrypoint.sh dotenvx run -- node index.js` as root
[info][[email protected]] injecting env (1) from encrypted .env.vault
[info]Server running on port:3000
Visit your url and it says Hello production
.
You succesfully used the new .env.vault
standard to encrypt and deploy your secrets. This is much safer than scattering your secrets across multiple third-party platforms and tools. Whenever you need to add or change a secret, just re-encrypt .env.vault
and redeploy.
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
Commit .env.vault
safely to code and redeploy.
git commit -am "update production secret"
flyctl deploy