-->
Last updated: 27 Aug 2024
I’ve been working with Expo since 2018 and as it’s come to be a mature framework it’s been my go-to for production projects. The most cumbersome piece of Expo is managing environment variables.
There are 3 types of environment variables at play:
Typically, all of these are needed for both developing and deploying a project. Each of these has their own set of complexities, and a gripe I’ve always had is that the presented solutions tend to introduce more complexity. That complexity increases if you’re working with a distributed team.
If you’re using Expo and EAS, their solution is to store your secrets in EAS and then work off of development builds from EAS (no local builds).
The ‘hidden’ cost of this, however, can get out of control quickly if you have a distributed team that’s building multiple features that require native libraries. Since 2-3 builds are needed for a single development app (iOS device, iOS Simulator, Android), and each build is between $2-4, you’re looking at $4-10 total for a single developer. Once those features get merged, more development builds need to be built that contain all native changes.
Nobody wants to pay for throwaway stuff. This is when folks move into building the app locally.
Ultimately, many projects end up having to go this route for one way or another. If this is the case, environment variable management has to be done locally. With the .env
file not being committed to the repo, getting the environment variables to the developer is a manual process.
There are several ways to do this:
.env
file in another service like 1Password where developers can download itBoth of these are fine, but introduce complexities and requiring developers to manage and communicate updates to any variables. This isn’t abnormal, and how it works the majority of the time, but still not ideal.
Enter dotenvx . Dotenvx allows you to encrypt the values of your .env
files and decrypts them at runtime using a private decryption key. Using this method:
.env
file gets encrypted and committed to your repoI like this solution for a few reasons:
.env
file changes (i.e. keys get added/removed/changed), it’s visible in a pull request.env
fileI recommend installing dotenvx globally:
brew install dotenvx
As well as locally in your project:
# with npm
npm install --save-dev @dotenvx/dotenvx
# with yarn
yarn add --dev @dotenvx/dotenvx
# with pnpm
pnpm add --save-dev @dotenvx/dotenvx
# with bun
bun add --dev @dotenvx/dotenvx
Create a .env
file at the root of your project (regardless of if you’re in a monorepo).
touch .env
# add a variable to the .env file
echo "PING=pong" > .env
.env
fileWith your .env
file in place, encrypt it:
dotenvx encrypt .env
Open your .env
file and it will look something like this:
#/-------------------[DOTENV_PUBLIC_KEY]--------------------/
#/ public-key encryption for .env files /
#/ [how it works](https://dotenvx.com/encryption) /
#/----------------------------------------------------------/
DOTENV_PUBLIC_KEY="0350420f322054f5ca3d7c28606f27f65a9ea5025349b4c642448e6069e4d4e566"
# .env
PING="encrypted:BER++pQ0xx/aUjajDfNKUMzyLE2mQ+hnjXyvU1qiTCOZGoZaBdVmQiQ0LCrdc8AxUf2PvpviW55oLGV+wGwGIAijYbUlzcfAbRk2BPM0ClBkPar86HHS7bqEDGdXi1BTIdzFKBw="
Additionally, a .env.keys
file will be created. This should not be committed, but rather stored in a secure location. This will also have to be stored in any cloud service that handles deployments (i.e. EAS).
Dotenvx by default will decrypt the .env
file at runtime. This means that you can continue to use process.env
as you normally would. You’ll need to pass dotenvx
to your development commands:
dotenvx run -f .env -- expo start # or whatever your command is
Your .env
file will be decrypted and available to your app.
If you’re using a monorepo, a few other steps are needed. This assumes you’re using Turborepo .
In your turbo.json
file, you’ll need to add the env variables to the appropriate task, as well as add the .env
as an input
to the task.
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": {
"dependsOn": [
"^build"
],
"inputs": [
"$TURBO_DEFAULT$",
".env*"
],
"outputs": [
"build/**",
"dist/**",
"node_modules/.cache/metro/**"
],
"env": [
"PING"
]
},
"dev": {
"cache": false,
"persistent": true,
"env": [
"PING"
]
}
}
}
You can then adjust the scripts in the root package.json
of the monorepo to use dotenvx and have environment variables be made available in all apps and packages:
"scripts": {
"dev": "dotenvx run -- turbo dev",
"build": "dotenvx run -- turbo build",
}
Another benefit of using dotenvx is that you can store encrypted development certificates in the .env
file and have developers decrypt them onto their machines straight from the repository.
You’ll first need to ensure you have the following certificates installed locally on your machine:
.cer
file).p12
file)
a. This file requires a password, which is also required.These get generated automatically when first setting up a project with EAS. If you did this, you can find them in your Expo dashboard under the project settings > credentials.
Be sure you have the password for the .p12
file.
I recommend dropping the certs in the root of your repo where the .env
file is located.
First, we’ll hash the content of each file with base64 so that we have a string hash for the .env
file, and then we’ll set that as the value for the variable.
In the root of your repo, run the following:
touch hash.sh
chmod +x hash.sh
Open hash.sh
and paste the following, ensuring to update the paths to the .cer
and .p12
files:
cert=$(sha256sum /path/to/cert.cer | awk '{print $1}')
key=$(sha256sum /path/to/key.p12 | awk '{print $1}')
echo -n "$cert" | xxd -r -p | base64 | dotenvx set APPLE_CERT
echo -n "$key" | xxd -r -p | base64 | dotenvx set APPLE_KEY
Run the script:
./hash.sh
Your .env
file now should contain the hash values for the certificates. Now, add the password to the .p12
key in the .env
file.
Your env file should have 3 variables now:
APPLE_CERT="" # base64 string
APPLE_KEY="" # base64 string
APPLE_KEY_PASSWORD="" # password
Lastly, run dotenvx encrypt .env
to encrypt the values with the dotenvx public key. You can now commit the .env
file to your repo.
Developers can now decrypt and add the certificates to their machines straight from the .env
file. The developer must have the private key (.env.keys
) file in their local clone of the repository.
First, decyrpt the .env
file:
dotenvx decrypt .env
Then, run the following to decrypt the certificates:
cert=$(dotenvx get APPLE_CERT | base64 -d | xxd -r -p)
key=$(dotenvx get APPLE_KEY | base64 -d | xxd -r -p)
echo -n "$cert" > /path/to/cert.cer
echo -n "$key" > /path/to/key.p12
The developer should first copy the value of the decrypted .p12 password and then double-click on each file to add them to their machine’s keychain.