220

I have a Heroku project that uses environment variables to get its configuration, but I use virtualenv to test my app locally first.

Is there a way to set the environment variables defined on the remote machine inside virtualenv?

10 Answers 10

331

In case you're using virtualenvwrapper (I highly recommend doing so), you can define different hooks (preactivate, postactivate, predeactivate, postdeactivate) using the scripts with the same names in $VIRTUAL_ENV/bin/. You need the postactivate hook.

$ workon myvenv

$ cat $VIRTUAL_ENV/bin/postactivate
#!/bin/bash
# This hook is run after this virtualenv is activated.
export DJANGO_DEBUG=True
export S3_KEY=mykey
export S3_SECRET=mysecret

$ echo $DJANGO_DEBUG
True

If you want to keep this configuration in your project directory, simply create a symlink from your project directory to $VIRTUAL_ENV/bin/postactivate.

$ rm $VIRTUAL_ENV/bin/postactivate
$ ln -s .env/postactivate $VIRTUAL_ENV/bin/postactivate

You could even automate the creation of the symlinks each time you use mkvirtualenv.

Cleaning up on deactivate

Remember that this wont clean up after itself. When you deactivate the virtualenv, the environment variable will persist. To clean up symmetrically you can add to $VIRTUAL_ENV/bin/predeactivate.

$ cat $VIRTUAL_ENV/bin/predeactivate
#!/bin/bash
# This hook is run before this virtualenv is deactivated.
unset DJANGO_DEBUG

$ deactivate

$ echo $DJANGO_DEBUG

Remember that if using this for environment variables that might already be set in your environment then the unset will result in them being completely unset on leaving the virtualenv. So if that is at all probable you could record the previous value somewhere temporary then read it back in on deactivate.

Setup:

$ cat $VIRTUAL_ENV/bin/postactivate
#!/bin/bash
# This hook is run after this virtualenv is activated.
if [[ -n $SOME_VAR ]]
then
    export SOME_VAR_BACKUP=$SOME_VAR
fi
export SOME_VAR=apple

$ cat $VIRTUAL_ENV/bin/predeactivate
#!/bin/bash
# This hook is run before this virtualenv is deactivated.
if [[ -n $SOME_VAR_BACKUP ]]
then
    export SOME_VAR=$SOME_VAR_BACKUP
    unset SOME_VAR_BACKUP
else
    unset SOME_VAR
fi

Test:

$ echo $SOME_VAR
banana

$ workon myenv

$ echo $SOME_VAR
apple

$ deactivate

$ echo $SOME_VAR
banana
8
  • 1
    Just a precision: doing ln -s .env/postactivate $VIRTUAL_ENV/bin/postactivate did not work for me. ln wants a full path, so I had to do ln -s `pwd`/.env/postactivate $VIRTUAL_ENV/bin/postactivate
    – Zoneur
    Commented Aug 15, 2013 at 16:23
  • @Zoneur What OS are you on? Under Linux relative paths work for ln. Commented Aug 16, 2013 at 8:44
  • @DaniloBargen I use LinuxMint 3.2.0. This answer said that ln likes full paths so I tried that and it worked. When I tried to cat the symlink with relative path it said No such file or directory.
    – Zoneur
    Commented Aug 16, 2013 at 14:02
  • @dpwrussel, that almost didn't make it through review, its a good addition, but its so significant it could have been made as its own post ( which would have gotten you some rep ). Lots of Good answers are good :) Commented Nov 15, 2013 at 2:08
  • 2
    And source control? How does this translate to other people cloning and setting up a project that needs the env. var.s?
    – CpILL
    Commented Sep 14, 2016 at 16:49
87

Using only virtualenv (without virtualenvwrapper), setting environment variables is easy through the activate script you're sourcing in order to activate the virtualenv.

On unix, run:

nano YOUR_ENV/bin/activate

or if you're on windows:

nano YOUR_ENV/Scripts/activate.bat

Then, add the environment variables to the end of the file. If you're on unix:

export KEY=VALUE

or if you're on windows:

set KEY=VALUE

You can also set a similar hook to unset the environment variable as suggested by Danilo Bargen in his excellent answer above.

4
  • 17
    a much more sane approach IMO. overriding cd just to have environment variables? shudder Commented May 22, 2014 at 8:25
  • 6
    how about the cleanup after deactive?
    – buncis
    Commented Feb 3, 2020 at 16:38
  • 3
    This works but for me on Windows it was YOUR_ENV/Scripts/activate.bat and set KEY=VALUE. Commented Jan 27, 2022 at 10:40
  • 2
    Thanks for the windows specific info @RiversCuomo! Added this to the answer.
    – Nagasaki45
    Commented Jan 27, 2022 at 12:24
65

While there are a lot of nice answers here, I didn't see a solution posted that both includes unsetting environment variables on deactivate and doesn't require additional libraries beyond virtualenv, so here's my solution that just involves editing /bin/activate, using the variables MY_SERVER_NAME and MY_DATABASE_URL as examples:

There should be a definition for deactivate in the activate script, and you want to unset your variables at the end of it:

deactivate () {
    ...

    # Unset My Server's variables
    unset MY_SERVER_NAME
    unset MY_DATABASE_URL
}

Then at the end of the activate script, set the variables:

# Set My Server's variables
export MY_SERVER_NAME="<domain for My Server>"
export MY_DATABASE_URL="<url for database>"

This way you don't have to install anything else to get it working, and you don't end up with the variables being left over when you deactivate the virtualenv.

5
  • 11
    I like this approach because I don't want external libs or apps but the problem with this is that if you rebuild the environment you will loose all your settings.
    – VStoykov
    Commented Sep 8, 2016 at 15:13
  • 7
    The advantage to this approach is the speed of setup and lack of magic. Keeping environ variables out of source control will always lead you back to the problem of potentially destroying your secrets/settings when rebuilding environments. Commented Sep 9, 2016 at 8:19
  • Does the virtualenv directory end up being checked into the repository for this to work? What if the variables hold secrets that you don't want in the repo? How would you handle this?
    – fraxture
    Commented Jan 24, 2018 at 2:06
  • 6
    I don't really see why it would be a good idea to include a virtualenv in your repository, as they are not very portable, but I imagine you could put your exports in a separate file instead of the activate script and source the file if it's present, and don't add that file to your repository.
    – TheLetterN
    Commented Jan 25, 2018 at 18:48
  • I like this approach a lot, it's way cleaner than any of the other answers. However, my concern is whether there is any possibility/action that could lead to the "activate" script being overwritten/reset. Some of the other answers contain such discussions. A guarantee that the "activate" script cannot be overwritten without the user doing so manually would make this the best and simplest answer IMO.
    – waykiki
    Commented Jan 6, 2021 at 15:47
58

You could try:

export ENVVAR=value

in virtualenv_root/bin/activate. Basically the activate script is what is executed when you start using the virtualenv so you can put all your customization in there.

5
  • 2
    Not sure if that's clean enough but definitively works!
    – chachan
    Commented Nov 4, 2014 at 22:51
  • 4
    Yeah, it's cheap and nasty, but occasionally that's what you need. Commented Dec 5, 2014 at 3:21
  • 2
    I don't recommend this, I did it and sometime later all the activate scripts (activate, activate.csh, activate.fish) were overwritten automatically so I lost my change. Use postactivate and predeactivate.
    – William
    Commented Oct 4, 2018 at 11:50
  • do not use spaces around the = Commented Mar 8, 2019 at 16:47
  • 6
    Could also add 'unset ENVVAR' in the deactivate function defined virtualenv_root/bin/activate to balance setting and unsetting
    – Lou Zell
    Commented Aug 23, 2019 at 19:59
20

Locally within an virtualenv there are two methods you could use to test this. The first is a tool which is installed via the Heroku toolbelt (https://toolbelt.heroku.com/). The tool is foreman. It will export all of your environment variables that are stored in a .env file locally and then run app processes within your Procfile.

The second way if you're looking for a lighter approach is to have a .env file locally then run:

export $(cat .env)
13

To activate virtualenv in env directory and export envinroment variables stored in .env use :

source env/bin/activate && set -a; source .env; set +a

Or just the following line for pyenv:

set -a; source .env; set +a
5
  • save to aliases with echo 'alias e=". env/bin/activate && set -a; source .env; set +a"' >> ~/.bash_aliases
    – Danil
    Commented Jan 13, 2020 at 11:52
  • 1
    I like this most other because 1: reuse present code, 2: do not add extra overstructure tools (like autoenv, env, env...) that add logics and commands... only thing that the sourced vars remain in the user space (aka: are effective also after deactivate) but nice and (quite) clean solution for most of dedicated systems Commented Oct 18, 2020 at 9:41
  • 4
    Can you explain set +a?
    – niid
    Commented Dec 7, 2020 at 14:44
  • @niid - this may help - unix.stackexchange.com/a/79084 Commented Dec 26, 2020 at 6:43
  • Note that set -a/set +a is equivalent to set -o allexport/set +o allexport, which I find more descriptive Commented Jan 15 at 15:50
8

Concerning the standard venv package, here is a solution that:

  • sets or updates an environment variable on activation.
  • restores the previous value of the environment variable on deactivation.

SOLUTION:

  1. Create the MY_ENV environment.
python -m venv MY_ENV
  1. Open the activate script in any text editor (e.g vim).
vim MY_ENV/bin/activate
  1. Update the body of the deactivate function.

These lines are executed when you deactivate the virtual environment. They restore the previous value of the ENV_VARIABLE.

deactivate () {
    ... # Already existing code

    # Restore the previous value of the ENV_VARIABLE on deactivation
    # This code is executed when `deactivate` alias is called.
    if [ ! "${1:-}" = "nondestructive" ] ; then
        if [ -n "${_OLD_ENV_VARIABLE:-}" ] ; then
            ENV_VARIABLE="${_OLD_ENV_VARIABLE:-}"
            export ENV_VARIABLE
            unset _OLD_ENV_VARIABLE
        else
            unset ENV_VARIABLE
        fi
    fi

}
  1. At the end of the script add lines, where you define the "new value of the ENV_VARIABLE".

These lines are executed when you activate the virtual environment.

# Update or set a ENV_VARIABLE on activation
if [ -n "${ENV_VARIABLE:-}" ] ; then
    _OLD_ENV_VARIABLE="${ENV_VARIABLE:-}"
fi
export ENV_VARIABLE="new value of the ENV_VARIABLE"

  1. Save an close the file.

OUTCOME:

Result of setting a new environment variable:

:~$ echo $ENV_VARIABLE

:~$

:~$ source MY_ENV/bin/activate
(MY_ENV):~$

(MY_ENV):~$ echo $ENV_VARIABLE
new value of the ENV_VARIABLE
:~$

(MY_ENV):~$ deactivate
:~$

:~$ echo $ENV_VARIABLE

:~$


Result of updating already existing environment variable:

:~$ export ENV_VARIABLE = "old value of the ENV_VARIABLE"
:~$

:~$ echo $ENV_VARIABLE
old value of the ENV_VARIABLE
:~$

:~$ source MY_ENV/bin/activate
(MY_ENV):~$

(MY_ENV):~$ echo $ENV_VARIABLE
new value of the ENV_VARIABLE
:~$

(MY_ENV):~$ deactivate
:~$

:~$ echo $ENV_VARIABLE
old value of the ENV_VARIABLE
:~$

2
  • 2
    This is the only answer that uses the modern venv, and should be the correct one. The other answers are hopelessly outdated. The postactivate and predeactivate hooks do not exist in venv-created virtual environments. Commented Feb 20, 2022 at 13:16
  • use pyEnv and dirEnv together is better; Commented Feb 3, 2023 at 16:30
5

Install autoenv either by

$ pip install autoenv

(or)

$ brew install autoenv

And then create .env file in your virtualenv project folder

$ echo "source bin/activate" > .env

Now everything works fine.

1
  • 1
    The last line will auto-activate the virtual env? I don't think that was asked in the original question. Also I think autoenv is maybe a security risk if you download repos or code from the internet. Are we sure no code from the .env is executed?
    – niid
    Commented Dec 7, 2020 at 14:43
2

If you're already using Heroku, consider running your server via Foreman. It supports a .env file which is simply a list of lines with KEY=VAL that will be exported to your app before it runs.

2

Another way to do it that's designed for django, but should work in most settings, is to use django-dotenv.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.