How To Use pip on read-only System

Many of the Kubernetes cluster and Docker on production, have a policy to run on read-only system for all the directory except certain explicit folder that customizable by user. In read-only file system, not all of the application can run smooth by default, but trivial to fix as well.

For this tutorial lets take pip as the example, and run everything from docker for easiness

Run the normal docker image

$ docker run -it python:3.11.9-slim-bookworm touch temp.txt

the command touch temp.txt will executed successfully, hence no error

lets try with read-only file system, we’ll get the error

$ docker run -it --read-only  python:3.11.9-slim-bookworm touch temp.txt
touch: cannot touch 'temp.txt': Read-only file system

Run pip Inside Docker

Let’s install boto3 using pip

root@9155d3e213cc:/# pip install boto3
Defaulting to user installation because normal site-packages is not writeable
ERROR: Exception:
Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/pip/_internal/cli/base_command.py", line 180, in exc_logging_wrapper
    status = run_func(*args)
             ^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/pip/_internal/cli/req_command.py", line 245, in wrapper
    return func(self, options, args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/pip/_internal/commands/install.py", line 333, in run
    build_tracker = self.enter_context(get_build_tracker())
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/pip/_internal/cli/command_context.py", line 27, in enter_context
    return self._main_context.enter_context(context_provider)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/contextlib.py", line 517, in enter_context
    result = _enter(cm)
             ^^^^^^^^^^
  File "/usr/local/lib/python3.11/contextlib.py", line 137, in __enter__
    return next(self.gen)
           ^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/pip/_internal/operations/build/build_tracker.py", line 46, in get_build_tracker
    root = ctx.enter_context(TempDirectory(kind="build-tracker")).path
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/pip/_internal/utils/temp_dir.py", line 137, in __init__
    path = self._create(kind)
           ^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/pip/_internal/utils/temp_dir.py", line 177, in _create
    path = os.path.realpath(tempfile.mkdtemp(prefix=f"pip-{kind}-"))
                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/tempfile.py", line 374, in mkdtemp
    prefix, suffix, dir, output_type = _sanitize_params(prefix, suffix, dir)
                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/tempfile.py", line 127, in _sanitize_params
    dir = gettempdir()
          ^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/tempfile.py", line 316, in gettempdir
    return _os.fsdecode(_gettempdir())
                        ^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/tempfile.py", line 309, in _gettempdir
    tempdir = _get_default_tempdir()
              ^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/tempfile.py", line 224, in _get_default_tempdir
    raise FileNotFoundError(_errno.ENOENT,
FileNotFoundError: [Errno 2] No usable temporary directory found in ['/tmp', '/var/tmp', '/usr/tmp', '/']
WARNING: There was an error checking the latest version of pip.

this happens, because the folder ‘/tmp’, ‘/var/tmp’, ‘/usr/tmp’, ‘/’ is read-only, so the pip cannot store the data there. This issue fixable by define the TMPDIR variable environment to directory that had access read-write. The solution will provided on the bottom.

tmpfs for the rescue

Modify the command by add the --tmpfs DIR

$ docker run -it --read-only --tmpfs /app python:3.11.9-slim-bookworm bash
root@11d6469dd010:/# touch /app/temp.txt; ls -lah /app/temp.txt
-rw-r--r-- 1 root root 0 Apr  5 14:42 /app/temp.txt
root@11d6469dd010:/#

from testing above we can write file to directory /app, set the TMPDIR to /app and try install boto3 again

$ docker run -it --read-only --tmpfs /app python:3.11.9-slim-bookworm bash
root@11d6469dd010:/# export TMPDIR=/app
root@11d6469dd010:/# pip install boto3
Defaulting to user installation because normal site-packages is not writeable
<snip>
Installing collected packages: urllib3, six, jmespath, python-dateutil, botocore, s3transfer, boto3
ERROR: Could not install packages due to an OSError: [Errno 30] Read-only file system: '/root/.local'
 
WARNING: There was an error checking the latest version of pip.

pip DATADIR

The solution here is to combine TMPDIR and --datadir

export TMPDIR=/app
# set the PYTHONPATH to /app
export PYTHONPATH=/app
pip install --target /app boto3

pip boto3 success

For production environment create a non-privileges user to run the app inside the docker

Leave a Comment