Securing persistent environment variables using ZEIT Now

Nicholas C. Zakas
Human Who Codes
Published in
4 min readSep 21, 2019

I’m a big fan of ZEIT Now[¹] as an application hosting provider. The way the service abstracts all of the cloud computing details and allows teams to focus on building and deploying web applications is fantastic. That said, I had a lot of trouble setting up secure environment variables for my first application to use. I was used to other services like Netlify[²] and AwS Lambda[³] exposing environment variables in the web interface to allow secure transmission of important information. When ZEIT Now didn’t provide the same option in its web interface, I had to spend some time researching how to securely set persistent environment variables on my application.

For the purposes of this post, assume that you need to set two environment variables, CLIENT_ID and CLIENT_SECRET. These values won't change between deployments (presumably because they are used to authenticate the application with OAuth). As such, you don't want to manually set these environment variables during every deployment but would rather have them stored and used each time the application is deployed.

Setting environment variables in ZEIT Now

According to the documentation[⁴], there are two ways to set environment variables for your ZEIT Now project. The first is to use the now command line tool with the -e option, such as:

now -e CLIENT_ID="abcdefg" -e CLIENT_SECRET="123456789abcdefg"

This approach not only sets the environment variables but also triggers a new deploy. The environment variables set here are valid only for the triggered deploy and will not automatically be available for any future deploys. You need to include the environment variables any time you deploy, which isn’t ideal when the information doesn’t need to change between deploys.

The second way to set environment variables is to include them in the now.json file. There are actually two keys that can contain environment variables in now.json:

  1. env is used for environment variables needed only during application runtime.
  2. build.env is used for environment variables needed only during the build process.

Whether you need the environment variables in one or both modes is up to how your application is built.

Be particularly careful if your build process uses the same JavaScript configuration file as your runtime, as you may find both the build and runtime will require the same environment variables even if it’s not immediately obvious (this happened to me). This is common with universal frameworks such as Next.js and Nuxt.js.

Both the env and build.env keys are objects where the property names are the environment variables to set and the property values are the environment variable values. For example, the following sets CLIENT_ID and CLIENT_SECRET in both the build and runtime environments:

{
"env": {
"CLIENT_ID": "abcdefg",
"CLIENT_SECRET": "123456789abcdefg"
},
"build": {
"env": {
"CLIENT_ID": "abcdefg",
"CLIENT_SECRET": "123456789abcdefg"
}
}
}

The environment variables in now.json are set for each deploy automatically, so this is the easiest way to persist important information for your application. Of course, if your environment variables contain sensitive information then you wouldn't want to check now.json into your source code repository. That's not a great solution because now.json contains more than just environment variables. The solution is to use now.json with project secrets.

Using ZEIT Now secrets

ZEIT Now has the ability to store secrets associated with each project. You can set a secret using the now CLI. You can name these secrets whatever you want, but the documentation[^4] suggests using lower dash case, Here's an example:

now secret add client-id abcdefg
now secret add client-secret 123456890abcdefg

These commands create two secrets: client-id and client-secret. These are automatically synced to my ZEIT Now project and only available within that one project.

The next step is to reference these secrets inside of the now.json file. To specify that the value is a secret, prefix it with the @ symbol. For example, the following sets CLIENT_ID and CLIENT_SECRET in both the build and runtime environments:

{
"env": {
"CLIENT_ID": "@client-id",
"CLIENT_SECRET": "@client-secret"
},
"build": {
"env": {
"CLIENT_ID": "@client-id",
"CLIENT_SECRET": "@client-secret"
}
}
}

This now.json configuration specifies that the environment variables should be filled with secret values. Each time your application is deployed, ZEIT Now will read the client-id and client-secret secrets and expose them as the environment variables CLIENT_ID and CLIENT_SECRET. It's now safe to check now.json into your source code repository because it's not exposing any secure information. You can just use the now command to deploy your application knowing that all of the important environment variables will be added automatically.

Summary

The way ZEIT Now handles environment variables takes a little getting used to. Whereas other services allow you to specify secret environment variables directly in their web interface, ZEIT Now requires using the now command line tool to do so.

The easiest way to securely persist environment variables in your ZEIT Now project is to store the information in secrets and then specify the environment variables in your now.json file. Doing so allows you to check now.json into your source code repository without exposing sensitive information. Given the many configuration options available in now.json, it's helpful to have that file in source control so you can make changes when necessary.

References

[¹]: ZEIT Now

[²]: Netlify

[³]: AWS Lambda

[⁴]: ZEIT Now — Environment Variables and Secrets

This post originally appeared on the Human Who Codes Blog on September 3, 2019. If you enjoyed this post, please consider donating to support my work.

--

--

Nicholas C. Zakas
Human Who Codes

Creator of @geteslint. Author. Speaker. Philosopher. Boston ex-pat. Currently fighting Lyme disease.