Changing the owner of .venv created by uv inside Docker
I have a Django app build by uv running inside Docker. I mount the local filesystem as a volume in the container using Docker Compose so that edits to the source code locally trigger reloading of the app in the container. It almost works.
The issue is that the .venv
directory built by uv is owned by the root user of the Docker container. This means that I cannot edit those files from my local filesystem without root access.
I have gotten around this with pip/pipenv/poetry/pdm in the past by installing the venv as a non-root user who has the same uid and guid as my local user (those values are passed into Docker via a .env
file). But I can't work out how to do that for uv.
Dockerfile
:
FROM python:3.12-slim-trixie
# create non-root user
RUN addgroup --system app && adduser --system --group app
# set work directory
WORKDIR /app
# environment variables
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
UV_LINK_MODE=copy \
UV_PYTHON_DOWNLOADS=never \
UV_PROJECT_ENVIRONMENT=$APP_HOME/.venv
# install uv
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
# install system dependencies
RUN apt-get update
RUN apt-get install -y --no-install-recommends \
build-essential netcat-traditional \
python-is-python3 python3-gdal python3-psycopg2
# switch to app user [THIS MAKES THE NEXT COMMAND FAIL]
# USER app
# synchronise project dependencies
COPY pyproject.toml uv.lock ./
RUN --mount=type=cache,target=/root/.cache \
uv sync --all-groups --frozen --no-install-project
# run entrypoint script
COPY ./entrypoint.sh .
RUN chmod +x entrypoint.sh
ENTRYPOINT ["/app/entrypoint.sh"]
docker-compose.yml
:
services:
server:
build:
context: .
command: uv run manage.py runserver 0.0.0.0:8000
tty: true
environment:
DJANGO_SETTINGS_MODULE: config.settings
volumes:
- ./:/app/
ports:
- "8000:8000"
env_file:
- .env
entrypoint.sh
:
#!/bin/sh
set -euo pipefail
cd /app
# ensure "app" user in the container has same ids as local user outside the container
if [ ! -z ${RUN_AS_UID} ]; then usermod --uid $RUN_AS_UID app; fi
if [ ! -z ${RUN_AS_GID} ]; then groupmod --gid $RUN_AS_GID app; fi
# setup django
uv run ./manage.py migrate
uv run ./manage.py collectstatic --no-input --link
# run whatever command was passed to the container (from docker-compose)
exec "$@"
.env
:
RUN_AS_UID=1001
RUN_AS_GID=1001