Skip to content

How to use picamera2 and GPIO with uv on Raspberry Pi

picamera2, libcamera, and gpiozero ship through apt on Raspberry Pi OS, not through PyPI. A clean uv venv cannot import them, and pip install picamera2 inside that venv fails on the missing libcamera module. The workaround is to install the system packages with apt, then build a uv venv against /usr/bin/python3 that inherits the system site-packages. This guide walks through that setup and the constraints it brings.

It assumes uv is already installed on the Pi. If not, start with How to run Python scripts on a Raspberry Pi with uv.

Install the apt packages

Install the Pi-specific bindings through the system package manager:

sudo apt update
sudo apt install -y python3-picamera2 python3-libcamera python3-gpiozero

These packages register themselves under /usr/lib/python3/dist-packages/, which is only visible to /usr/bin/python3 and to venvs that explicitly opt in.

Create a uv venv that sees the apt packages

In your project directory, point uv at the system Python and pass --system-site-packages:

uv venv --python /usr/bin/python3 --system-site-packages

Then install pip-installable dependencies into the same venv:

uv pip install adafruit-circuitpython-dht

The venv now has both stacks. Confirm:

uv run python -c "from picamera2 import Picamera2; print(Picamera2.global_camera_info())"

The command prints the connected camera’s info, importing both the apt-installed picamera2 and any pip-installed dependencies from the same interpreter.

Constraints to plan around

A few things behave differently with this setup:

  • uv init and uv add ignore apt packages. They resolve against pyproject.toml and don’t know about python3-picamera2. Add an explicit import picamera2 check at the top of your script so a missing apt package fails loudly.
  • uv sync won’t reinstall the apt packages on another Pi. Document the sudo apt install step in your project’s README, or wrap it in a small setup.sh.
  • uv python install can’t manage the Pi-bound Python. The picamera2 package binds to /usr/bin/python3 specifically; staying on system Python is required for projects that need it.
  • Newer Python versions break this path. If your project needs a Python newer than the one Raspberry Pi OS ships, the apt + system-site-packages approach won’t work. Either downgrade to the system Python or accept that the project can’t use the apt-only libraries.

Run as a systemd service on boot

systemd needs an absolute path to the interpreter. For a project venv, point ExecStart directly at the venv’s Python:

/etc/systemd/system/camera-logger.service
[Unit]
Description=Camera logger
After=network.target

[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi/camera-logger
ExecStart=/home/pi/camera-logger/.venv/bin/python main.py
Restart=on-failure

[Install]
WantedBy=multi-user.target

/home/pi/camera-logger/.venv/bin/python resolves through the system-site-packages venv, so it sees picamera2 and any pip-installed deps. Keep User=pi matched to the user that ran uv venv; ~/.cache/uv and ~/.local/share/uv need to be readable by the service user.

For a single-file script that uses inline metadata, set ExecStart=/home/pi/.local/bin/uv run /home/pi/script.py. The inline-metadata path doesn’t combine with --system-site-packages, though, so picamera2 / GPIO projects should use a venv-based project rather than an inline-metadata script.

Learn More

Last updated on