Complete setup instructions for the RPI Camera Plugin.
- Raspberry Pi 5 (recommended) or Pi 4
- Raspberry Pi Camera Module 3 (recommended) or v2
- MicroSD card (8GB or larger)
- Power supply (wall adapter or power bank)
- Network connection (Ethernet or WiFi)
- Camera mount (tripod, clamp, or custom)
- Raspberry Pi OS (64-bit recommended)
- Python 3.13+
- Network connectivity to RELab platform
-
Install Raspberry Pi OS
Follow the official installation guide. -
Connect camera module
Attach the camera to the Pi's camera port. See the camera module guide for alignment. -
Test camera
rpicam-hello
-
Clone repository
git clone https://github.com/CMLPlatform/relab-rpi-cam-plugin.git cd relab-rpi-cam-plugin
The plugin connects to the RELab backend via WebSocket relay. The Pi initiates an outbound connection — no public IP or port forwarding is needed.
Configuration precedence at startup:
- environment variables from
.env - credentials persisted at
~/.config/relab/relay_credentials.json - generated defaults for local-only secrets (e.g.
local_api_key)
The simplest approach. No credential exchange required.
-
Create
.envfilecp .env.example .env
-
Set the pairing backend URL
PAIRING_BACKEND_URL=https://api.cml-relab.org
-
Start the plugin (see Step 3).
-
Read the pairing code Use either of these supported setup paths:
- Browser UI: visit
http://your-pi-ip:8018/setup - Headless over SSH/logs: watch for the
PAIRING READYlog line
If the code needs to be replaced, use the
Generate a new pairing codebutton on/setup. It rotates the current code without removing relay credentials. - Browser UI: visit
-
Pair in RELab app
Go to Cameras > Add Camera and enter the pairing code. -
Done
The Pi automatically receives credentials, saves them to~/.config/relab/relay_credentials.json, and connects to the backend.This automatic pairing flow is for the native RELab app. If you open the setup page from the RELab web frontend over HTTPS, the browser will block any attempt to probe the Pi's HTTP API as mixed content, so the web UI cannot auto-switch to direct mode.
When you run the plugin via Docker Compose, compose.yml persists this directory with a named
volume at /home/rpicam/.config/relab, so paired relay credentials survive container restarts.
-
Generate device mapping
./scripts/generate_compose_override.py > compose.override.ymlThe generated override targets the existing
appservice fromcompose.yml, so Compose can merge the device mappings into the plugin container. -
Start the stack
docker compose build docker compose up -d
View logs with:
docker compose logs -f app
To ship logs from a Pi to an external Loki-compatible collector, add to
.env:COMPOSE_PROFILES=observability-ship OBSERVABILITY_INSTANCE=pi-01 LOKI_PUSH_URL=http://your-observability-host:3100/loki/api/v1/push
If you do not enable
observability-ship, the app still writes bounded Docker logs and structured 7-day rotating logs to the mountedapp_logsvolume. Local Loki/Grafana is not bundled with this plugin; use a central observability stack when you need fleet log browsing.
-
Prepare environment
./scripts/local_setup.sh
-
Start server
uv run fastapi run app/main.py --host 0.0.0.0 --port 8018
Useful local commands from the repo root:
just dev
just lint
just typecheck
just test
just show-keyWhen pairing mode is active, the terminal prints a line like:
══════════════════════════════════════════════════════
PAIRING READY
PAIRING CODE: ABC123
Setup : /setup
Backend : https://api.cml-relab.org
Claim in : RELab app > Cameras > Add Camera
══════════════════════════════════════════════════════
Once running, verify at:
- Setup & Status:
http://your-pi-ip:8018/setup - API Docs:
http://your-pi-ip:8018/docs
For headless operators, you can also read the pairing code from logs:
- Docker Compose:
docker compose logs app - Systemd/journald:
journalctl -u relab-rpi-cam -f - Direct shell run: read the boxed
PAIRING READYbanner in the terminal output
- structured JSON logs always exist
- OTLP tracing is opt-in via
OTEL_ENABLED=trueandOTEL_EXPORTER_OTLP_ENDPOINT - the repo does not bundle a full OTLP collector/pipeline configuration
rpicam-hello --list-camerasVerify camera is properly connected to the CSI port.
- Check port 8018 availability:
sudo netstat -tlnp | grep :8018 - Try dev mode:
just dev - Check logs for Python errors
- Verify
~/.config/relab/relay_credentials.jsonexists (created automatically after pairing) - Check Pi has outbound internet access
- Check plugin logs:
docker compose logs app - If
observability-shipis enabled, inspect the external Loki/Grafana collector configured byLOKI_PUSH_URL.
-
Ensure
PAIRING_BACKEND_URLis set in.env -
If you're using the RELab web frontend in a browser over HTTPS, direct-mode auto-detection will not work because the browser blocks HTTP requests to the Pi as mixed content. Use the native RELab app or read the pairing code from
/setupor logs. -
If the plugin runs in Docker,
http://localhost:8011points at the plugin container itself, not your host machine. Usehttp://host.docker.internal:8011, your host's LAN IP, or the real HTTPS API URL instead. -
403 on pairing or WebSocket connect? If the backend sits behind Cloudflare, the machine-facing paths may be challenged. Add a WAF bypass rule for
/plugins/rpi-cam/pairing/*and/plugins/rpi-cam/ws/connecton the relevant hosts. -
Remove
~/.config/relab/relay_credentials.jsonif it exists (pairing is skipped when credentials present) -
Check
/setupor look for thePAIRING MODEandPAIRING READYlog lines
- Clean camera lens gently
- Improve lighting at capture location
- Check camera module connection