Why Use Environment Variables?
Whilst building my Django project I replaced several of the values within settings.py with an option for them to be read from environment variables, rather than storing them in plain text within settings.py. This change allows me to store the settings.py file within a source control repository (e.g. Git) and also allow the file to be inside a container (e.g. inside a Docker container) without me accidentally sharing any secrets (e.g. user credentials, database credentials).
Example Of Environment Variable
Within Django’s settings.py I have replaced the database settings. The first step is to confirm that settings.py is importing the os Python library.
Then change any values that you want reading from a environment variable to utilise os.environ
Django will then look for an environment variable using the same key e.g. if a key of DATABASE_NAME has been set then Django expects to find DATABASE_NAME=the_name_of_the_database within the operating system’s (OS) environment. If the value has not been set within the OS environment then errors will occur.
Setting Environment Variables In A Docker Container
With Django set up to read environment variables I then needed to pass the variables into my Docker container.
Individually Via Docker Run
To set the environment variables individually via the docker run command use the -e flag. For example:
docker run -e DATABASE_NAME=my_db django_container:latest
This runs the image called django_container with the tag of latest and sets the DATABASE_NAME value to my_db.
Multiple values can be passed to the command at the same time:
docker run -e DATABASE_NAME=my_db -e DATABASE_HOST=localhost -e DATABASE_USER=geektechstuff -e DATABASE_PORT=3306 django_container:latest
In the above example the DATABASE_NAME, DATABASE_HOST, DATABASE_USER and DATABASE_PORT values are all set.
Via An Environment File
Setting environment values individually via the Docker Run command can become a little bothersome when there are multiple values, especially if it means typing in each one. With this in mind an environment file, or env file, can be useful. An environment file is a text file with each environment variable key and value set on an individual line.
For example in my example env file I would add values to each of the keys so that each line has key=value. Note: Whitespace (spaces) between the key and the value are read as values, so DATABASE_NAME= geektechstuff and DATABASE_NAME=geektechstuff are two different values, as is a key=value that has a space at the end of the value before the carriage return compared to one that doesn’t. In other words, be careful with spacing.
To load the values into a Docker container the docker run command is used with the –env-file flag. For example:
docker run --env-file ./my_env_file django_container:latest
This would load the environment file called my_env_file from the current working directly into the container of the image django_container:latest as the container starts. Note: Placement of the –env-file is important, if the command is placed after the image name then you will probably get an error similar to:
Error response from daemon: OCI runtime create failed: container_linux.go:367: starting container process caused: exec: “–env-file”: executable file not found in $PATH: unknown.
This is because Docker is trying to run the command within the container, instead of feeding the values into the container.
A Note On Security
Removing the values from settings.py allows for it to be stored with a version control system (e.g. git) and allows for it to be placed within a container (e.g. a Docker container). Please make sure though that any environment files (env files) that contain sensitive information are not placed into version control / containers. This can be accomplished by adding the file name (or a filename pattern) to the appropriate ignore file (e.g. .gitignore, .dockerignore).