Persistent Virtualenv Environment Variables with python-dotenv

Julian, Sat 06 October 2018, Packages

env, howto, learning, packages, Python, tips, virtual-environment, virtualenv

I can't count the amount of times I've followed a tutorial or guide that's said something along the lines of "Store your API Keys in environment variables".

It's easy enough to do with os.getenv but the thing that drives me crazy is having to, 1) hardcode the environment variables as global variables in my OS or 2) redeclare them every time I initiate the terminal session.

In this article I'm going to show you (and to document for myself!) how to declare persistent environment variables in Python Virtual Environments with python-dotenv.

Environment Variables in a Virtual Environment

With a UNIX based OS you'd traditionally declare your environment variables by explicitly defining them at the OS level. Take variables CLIENT_ID and CLIENT_SECRET as an example:

#echo $CLIENT_ID

To do this within your Python Virtual Environment (venv) you'd do the same declarations as above after you've activated your venv. Doing this means your environment variables are within the virtual environment, not the global OS environment.

Read more on Python Virtual Environments here.

This is all well and good except that when you deactivate and reactivate the venv, the environment variables are lost and you'll need to redeclare them.

Enter python-dotenv

python-dotenv is a Python module that allows you to specify environment variables in traditional UNIX-like ".env" (dot-env) file within your Python project directory. This includes in venvs!

Here's the flow:

  1. Populate the .env file with your environment variables.
  2. Import python-dotenv and call it at the start of your script.
  3. Use whatever "getenv" method you use to import the environment variables, eg: os.getenv().

python-dotenv Example

1. Create your .env file

(venv)#vim .env
(venv)#cat .env

2. Import and Call python-dotenv

I'm using a file called as my primary Python script here:

from dotenv import load_dotenv


I begin by importing load_dotenv from the dotenv.

It's then just as simple as running load_dotenv() to make the .env file accessible to your script as your source of environment variables.

3. Access the Environment Variables

I've continued with by writing a function that uses os.getenv to pull and print the environment variables I specified in my .env file:

from dotenv import load_dotenv
import os


client = os.getenv("CLIENT_ID")
secret = os.getenv("CLIENT_SECRET")

def printenvironment():
    print(f'The client id is: {client}.')
    print(f'The secret id is: {secret}.')

if __name__ == "__main__":

Running this script returns the following:

The client id is: 1234509876.
The secret id is: julianandsilentbobstrikeback.


The original problem was that when you'd deactivate and reactivate the venv you'd lose the environment variables.

Watch the persistence in action!

#source venv/bin/activate
The client id is: 1234509876.
The secret id is: julianandsilentbobstrikeback.

Okay... I know. Anticlimactic. No! What am I saying?! Super handy and so time saver-y!

.env Example File

Pro-tip: if you're committing to a public repo, make sure .env files are listed in the .gitignore file. You don't want your environment variables being pushed to a public repo!

That said, you'll want to let people know what environment variables to configure for themselves if they're going to clone your repo or use your script.

The nice way to do this is to create an "empty" .env.example file:

#vim .env.example


This is one of those Python things I'll be taking with me to the grave. With the numerous apps and scripts I've created, managing these env variables has always been a pain.

But no more I say! I'll be using python-dotenv to manage everything.

Keep Calm and Code in Python!

-- Julian

PyBites Python Tips

Do you want to get 250+ concise and applicable Python tips in an ebook that will cost you less than 10 bucks (future updates included), check it out here.

Get our Python Tips Book

"The discussions are succinct yet thorough enough to give you a solid grasp of the particular problem. I just wish I would have had this book when I started learning Python." - Daniel H

"Bob and Julian are the masters at aggregating these small snippets of code that can really make certain aspects of coding easier." - Jesse B

"This is now my favourite first Python go-to reference." - Anthony L

"Do you ever go on one of those cooking websites for a recipe and have to scroll for what feels like an eternity to get to the ingredients and the 4 steps the recipe actually takes? This is the opposite of that." - Sergio S

Get the book