Skip to content

Python pip in containers

Note that this is not a recommended approach. Please read Installing software in containers first, and consider the approach below a last resort.

The Challenge

The software installed in a Singularity image must be installed via the steps in a Singularity definition file when building the container. Once built, the image (SIF file) is immutable, so you cannot install additional software into it at container runtime.
If you have downloaded a pre-defined image from for example NGC or built an image yourself and attempt to run some Python software inside it (for example TensorFlow or PyTorch code), you may discover that your container is missing some dependencies for running the software.

The Temptation

At this point, it may be tempting to simply install the missing packages from PyPI using pip inside the container in order to circumvent the hassle of rebuilding the container image. This is also possible and may work fine at first glance. The potential problem with this approach is that the package(s) and associated libraries installed by pip are not installed inside the container image itself, but instead "bleed through" to your user directory. The files are installed in the hidden directory '.local' in your user directory. The problems described in the following may also apply to other directories such as '.ipython', '.keras', '.jupyter', '.conda', '.config', etc., if present, and can be solved in the same way. Generally, "if it ain't broken, don't fix it."

The Headache

If at a later point you run Python software inside a different container, Python in this container also sees the same '.local' directory in your user directory and may use packages and libraries from here instead of those actually installed in the container image. This can cause incompatibilities between packages/libraries installed in the container image and those found in '.local'. Ultimately, this means your software fails to run.

The Solution

The strict solution to this problem is to avoid using pip to install anything when running inside a Singularity container and instead make sure to build it properly into the container image itself. This can, however, be inconvenient - especially if you are merely using a pre-defined Singularity image that you downloaded from NGC.
So, acknowledging that there are actually benefits to being able to conveniently install things "inside" your container using pip, let us see how to make it work.

When Singularity runs a container, your user directory is by default bind-mounted into your container (effectively, you can also access the contents of your user directory inside the container). We can use this bind-mount feature of Singularity to configure additional directories to mount into the container at specified paths. This allows us to create a separate '.local' directory for each container image we are using, so they do not interfere with each other.

As an example, I have the following two versions of NGC's TensorFlow image in my user directory:

  • tensorflow_20.03-tf2-py3.sif
  • tensorflow_20.12-tf2-py3.sif

I want to be able to use pip inside containers from each of these images without them interfering with each other. I first create two new directories to act as the separate '.local' directories inside my containers:

cd ~
mkdir .local-tf20.03
mkdir .local-tf20.12

Now, when I run a container from the 'tensorflow_20.03-tf2-py3.sif' image, I use the -B parameter to specify where to mount this container's directory inside the container:

srun --pty singularity shell --nv -B .local-tf20.03:$HOME/.local tensorflow_20.03-tf2-py3.sif

This way, inside the container you will be able to access the directory as '~/.local', but files stored in it will really reside in '~/.local-tf20.03'. Similary, I run a container from the 'tensorflow_20.12-tf2-py3.sif' image using this command:

srun --pty singularity shell --nv -B .local-tf20.12:$HOME/.local tensorflow_20.12-tf2-py3.sif

The directory will still appear as '~/.local' inside the container, but this container will really store changes in '~/.local-tf20.12'. You can of course do this for as many different container images and associated directories as you like. Just remember to give the directories different names.

Note that if '.local' already exists in your user directory and you do not wish to delete it, this is still safe to do. The original contents of your '.local' will simply be hidden while the container is running and will again be available unchanged when the container stops running. You can safely leave the original '.local' directory where it is when using the above solution.