Environment Variable not visible to python

I have been trying to get environment variables working for a while now, to no avail. I have set service variables and device variables via both the CLI and the Python SDK. The variable is set, and it shows up when I type export after sshing into the main service (I am running a single-container app)

However, it doesn’t work in python. I have tried both os.environ.get() and os.getenv() but both times I get a NoneType returned. It seems most of the environment variables are not carrying over to python.

Does anyone know what could be causing this?

After running export in the container:

declare -x BALENA="1"
declare -x BALENA_APP_ID="1"
declare -x BALENA_APP_LOCK_PATH="/tmp/balena/updates.lock"
declare -x BALENA_APP_NAME="lederbord"
declare -x BALENA_DEVICE_NAME_AT_INIT="sparkling-meadow"
declare -x BALENA_DEVICE_TYPE="raspberrypi3"
declare -x BALENA_DEVICE_UUID="70f1a90afba9848352dba96b19b89ef7"
declare -x BALENA_HOST_OS_VERSION="balenaOS 2.47.0+rev1"
declare -x BALENA_SERVICE_HANDOVER_COMPLETE_PATH="/tmp/balena/handover-complete"
declare -x BALENA_SERVICE_NAME="main"
declare -x BALENA_SUPERVISOR_PORT="48484"
declare -x BALENA_SUPERVISOR_VERSION="10.6.27"
declare -x DEBIAN_FRONTEND="noninteractive"
declare -x HOME="/root"
declare -x HOSTNAME="70f1a90"
declare -x LANG="C.UTF-8"
declare -x LC_ALL="C.UTF-8"
declare -x OLDPWD
declare -x PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
declare -x PWD="/usr/src/app"
declare -x PYTHONPATH="/usr/lib/python3/dist-packages:"
declare -x PYTHON_DBUS_VERSION="1.2.8"
declare -x PYTHON_PIP_VERSION="20.1.1"
declare -x PYTHON_VERSION="3.5.7"
declare -x RESIN="1"
declare -x RESIN_APP_ID="1"
declare -x RESIN_APP_LOCK_PATH="/tmp/balena/updates.lock"
declare -x RESIN_APP_NAME="lederbord"
declare -x RESIN_DEVICE_NAME_AT_INIT="sparkling-meadow"
declare -x RESIN_DEVICE_TYPE="raspberrypi3"
declare -x RESIN_DEVICE_UUID="70f1a90afba9848352dba96b19b89ef7"
declare -x RESIN_HOST_OS_VERSION="balenaOS 2.47.0+rev1"
declare -x RESIN_SERVICE_KILL_ME_PATH="/tmp/balena/handover-complete"
declare -x RESIN_SERVICE_NAME="main"
declare -x RESIN_SUPERVISOR_PORT="48484"
declare -x RESIN_SUPERVISOR_VERSION="10.6.27"
declare -x SETUPTOOLS_VERSION="49.1.0"
declare -x SHLVL="1"
declare -x TERM="xterm"
declare -x UDEV="1"
declare -x USER="root"

Results of print(os.environ) in application:

{‘HOME’: ‘/root’, ‘LC_ALL’: ‘C.UTF-8’, ‘SUDO_COMMAND’: ‘/usr/local/bin/python -u src/core/FlaskRPC.py’, ‘USERNAME’: ‘root’, ‘TERM’: ‘xterm’, ‘HOSTNAME’: ‘70f1a90’, ‘SHELL’: ‘/bin/bash’, ‘MAIL’: ‘/var/mail/root’, ‘SUDO_GID’: ‘0’, ‘PATH’: ‘/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin’, ‘USER’: ‘root’, ‘SUDO_UID’: ‘0’, ‘DBUS_SYSTEM_BUS_ADDRESS’: ‘unix:path=/host/run/dbus/system_bus_socket’, ‘LANG’: ‘C.UTF-8’, ‘LOGNAME’: ‘root’, ‘WERKZEUG_SERVER_FD’: ‘4’, ‘SUDO_USER’: ‘root’, ‘WERKZEUG_RUN_MAIN’: ‘true’}`

Hi Caleb,

When you say the environment variables doesn’t work in Python, can you expand on your use-case? If you are running a Python script, can you send the Dockerfile that you used? From what I understand, I feel you need to make sure the script works in the same shell as when the environment variables are being defined.

The last line of the Dockerfile could be CMD ["source", "config.sh", "&&", "python", "script.py"] where config.sh would be a bash script exporting the variables needed. Also, can you confirm if the output of env inside the container same of what you see here?

Hi Vipul,

Thanks for the response, that last line of the Dockerfile makes a lot of sense, I’m sure that’s it. I’ll try and verify tonight!

I think that something concerning these changes in the Dockerfile would make an awesome addition to your documentation on the subject, maybe somewhere here? https://www.balena.io/docs/learn/more/masterclasses/fleet-management/#32-environment-variables

Hi Caleb, hope that will do the job. Don’t hesitate to contact us if you experience further problems…

I am struggling more, is there any way you could provide an example of what the config.sh script might look like?

Hi Caleb,

Running the same print(os.environ) line in a Python file on one of my devices gives me access to all of the BALENA_ environment variables, whereas your output above lacks them. This is aside from the variables you have set through the SDK. Could you please paste in the contents of your dockerfile, for us to take a look at please?


Ok, here it is!

# base-image for python on any machine using a template variable,
# see more about dockerfile templates here: https://www.balena.io/docs/learn/develop/dockerfile/
FROM balenalib/raspberrypi3-python:3-stretch-build

# use `install_packages` if you need to install dependencies,
# for instance if you need git, just uncomment the line below.

RUN install_packages git

# Set our working directory
WORKDIR /usr/src/app

# Copy requirements.txt first for better cache on later pushes
COPY requirements.txt requirements.txt

# pip install python deps from requirements.txt on the resin.io build server
RUN pip install -r requirements.txt

# Get rpi-rgb-led-library
RUN git clone https://github.com/hzeller/rpi-rgb-led-matrix.git

# build hzeller
WORKDIR /usr/src/app/rpi-rgb-led-matrix/
#before dumb change that breaks Makefile
RUN git checkout 1ee46d96fcf9cfb3b4c9ca1d70a9b09f2abd84ad
# copy in custom Makefile (for fixed frame microseconds)
COPY extras/Makefile /usr/src/app/rpi-rgb-led-matrix/lib/Makefile
RUN ls
RUN make build-python
RUN sudo make install-python

#reset working directory
WORKDIR /usr/src/app

# This will copy all files in our root to the working  directory in the container
COPY . ./

#Add sym link so FlaskRPC can find connection manager
# RUN ln -s /usr/src/app/src/wifi/connectionManager.py /usr/src/app/src/core/connectionManager.py

# Enable udevd so that plugged dynamic hardware devices show up in our container.

CMD ["sudo", "python","-u","src/core/FlaskRPC.py"]

#experimental CMDs
#CMD ["sh", "-c", "source", "config.sh", "&&", "sudo", "python", "src/core/FlaskRPC.py"]\
#CMD "/bin/bash source config.sh; sudo python -u src/core/FlaskRPC.py"
#CMD ["sleep", "100000"]

What’s weird is if I run python in the shell in the container, and then do os.environ I see the expected variables… Maybe the fact that I am running the python script with sudo has something to do with it?

Hello there,
yes running some commands on sudo and declaring some variables not on sudo can affect that.

Also, are you declaring your variables in your Makefile?
from what I see of your Dockerfile you are running the following commands :

RUN make build-python
RUN sudo make install-python

if in those commands you are setting variables they are not going to be in the same scope, some are going to be seen by sudoers and the other ones not.

let us know if this helps!

I removed sudo from the CMD line and it works now! Thanks for all of your help!