Environment Variables
April 03, 2020
Inspiration
I was inspired to write the post by an acquaintance who is new to the software industry. They couldn’t find any good resources on environment variables that covered everything in one place. They wanted to know things like:
- What are environment variables?
- What kind of things should be in environment variables?
- How can I set environment variables?
- How can I read environment variables?
- Do only secret things go in them?
- How do I securely share them?
Hopefully this post can become a kind of one stop shop for an introduction into environment variables.
What are Environment Variables?
Environment variables, commonly referred to as env vars, are variables available to a process that are a part of the environment where the process runs. Lazy to define the word with the word, so let’s break it down a little bit more.
A variable represents a value that can change. An environment variable typically represents a value set from outside the application, and exists as a part of system that is running the application. Even if you’re not aware of it, every time you use your computer, environment variables are impacting the applications you run. The browser that you are reading this blog post on probably uses hundreds of them. Some common examples are:
PATH
(When you open a terminal and type a command, where should it look for the executable you are trying to run)HOME
(The home directory for the current user)ENVIRONMENT
(commonly used to say either “development” or “production” to specify where the app is running.)
Env vars keys by convention are written in all capital snake case, meaning multiple words in the key are separated by underscores. The value is a string. E.g.
THIS_IS_A_VAR_NAME=foobar
Environment variables exist within your system whether or not you use them in your applications. If you open a terminal and type the following, you’ll see the content of the environment variable PATH
.
Mac OS/Linux
echo $PATH
Windows
echo %PATH%
What kind of things should be in environment variables?
There are many environment variables that will be set automatically by the system, but you can also set environment variables yourself specific to your application. Commonly you would set things that change based on where the app is running. These might be things such as:
- Database credentials
- Whether your app is running in Dev/Prod
- What port your web application should run on
Some environment variables are secret and sensitive, like your database credentials. Others, like the port or dev/prod flag are not.
How can I set environment variables?
Ah the tried and true answer: “It depends”
In Unix based systems, like Linux or Mac OS, environment variables can be set for every shell session, the current shell session only, or exclusively for a single command:
- Can be set for the shell session automatically (.bashrc/.zshrc etc)
- Can be set temporarily manually
export MY_VAR=foo
- Can be set for a single command
MY_VAR=foo ls
On Windows things are a little bit different. You can set in the environment variables settings for either the whole system or just current user, or in the terminal, use the command SET MY_VAR=foo
. It’s different again if you’re using Powershell.
It is important to note that processes inherit the environment variables of their parent processes. However if all you need is to set some environment variables for your application, a very common convention is to put key value pairs in a a file called .env
(pronounced: “dot ee en vee”. Or at least that’s how I say it) that lives alongside your project files, but is not committed to git. Many frameworks will automatically read from this file if it exists and set the environment variables for your process. If you aren’t using a framework that does this, many languages have libraries (usually called ”dotenv” or something similar) that will do the same. Some teams will have multiple .env
files with different content, and might prefix them with their intended use, like sample.env
, test.env
, or dev.env
.
A common question is how to properly set environment variables when running in CI systems like GitHub Actions or CircleCI. These systems run your build/test/deploy steps in their own isolated, ephemeral (temporary) container, and therefore they might lack some of the same environment variables that you have on your local development environment, and have many extras that you don’t. You should read into the docs of your CI system to see how to properly set environment variables and figure out which ones are already made available to you.
How can I read environment variables?
Here’s a smattering of examples of how to read environment variables in a few programming languages:
JavaScript:
process.env.MY_VAR
Using JavaScript fun, to provide a default you can use
process.env.MY_VAR || "some default value"
Python:
import os
os.environ["MY_VAR"]
os.environ
returns a dict. Python dicts raise a KeyError
if you try to access a key that doesn’t exist, so it tends to be better to actually do something like os.environ.get("MY_VAR", "some default value")
to prevent that situation.
Go
import "os"
os.Getenv("MY_VAR")
Unlike other languages, Go returns an empty string if the key doesn’t exist. Make sure to take that into consideration!
Java
System.getenv().get("MY_VAR")
getenv()
returns a Map<String, String>
, and therefore a get for a key that doesn’t exist returns null
Rust
env::var("MY_VAR")
Because it’s Rust, env::var
returns Result that could contain an Ok
variant or an Err
variant. You’ll need to handle that with either an expect
, unwrap
, or is_err
on the Result.
If the value doesn’t exist, languages do different things. Some return undefined (JavaScript). Others throw an error. Whatever language you are using, make sure you properly handle the case where the value does not exist!
Environment Variables and Secrets
Not all environment variables are secrets! Many of them are totally fine to share, post publicly etc. However, some of them inevitably will be sensitive. As soon as you introduce a database, the credentials to that database will likely be set as environment variables and need to be kept secret. However secrets don’t have to be environment variables. There are other options too:
- Encrypted files (fun fact: Kubernetes “secrets” aren’t actually encrypted by default. Just base64 encoded. You have to add the encryption at rest by yourself.)
- Fetched from some external secret store (Hashicorp Vault, AWS Secrets Manager, etc)
You will need to find the option that best suits you and your organization while still maintaining security best practices.
Don’t share secrets in Slack. Please. Just don’t. Use one of the myriad of options for sharing things securely:
- GPG
- Password Managers (1Password, LastPass, BitWarden, etc)
- Firefox Send
- Send Safely
- Signal
- Use one of the above secret stores (Vault, Secrets Manager)
I’m sure there are a million other options I didn’t include. This is just a few one of the ones I’m familiar with.
What if I really want to commit my secrets to git?
I really don’t recommend it, but if you absolutely must, you should at least use git-crypt or something similar so the file is encrypted!
What if I really want to just put the secret in a Slack message?
It’s just so easy to quickly send it over right? And you’ll definitely delete the message right after the person has a chance to copy it, right? There’s no way anybody might somehow get a hold of that secret, and you totally trust Slack that your messages will never be lost or read by someone else!
BAD!! STOP IT!
Seriously people, don’t do this!! There are so many good ways to send things securely, so stop posting plain text secrets in Slack, no matter how badly you want to. No matter how much of a rush you’re in. No matter how lazy you are.
Right now go download Signal and make an account. Go generate a GPG key pair and post your public key so people can send you encrypted messages. Do something encrypted. Anything! Just please don’t post it in plain text in Slack.
Written by James Quigley, an SRE/DevOps Engineer, and general tech nerd. Views and opinions are my own. Check out my YouTube Channel or follow me on Twitter!