fix: run container as host PUID/PGID for workspace mounts

node uid 1000 could not read /home/frank (frank=1002); build and compose now use configurable PUID/PGID.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Frank Schwenk
2026-08-01 11:21:04 +02:00
parent f7d3df128f
commit eb6b0d2fac
6 changed files with 56 additions and 29 deletions
+8 -4
View File
@@ -3,8 +3,12 @@
# OpenRouter (recommended default provider for this stack)
OPENROUTER_API_KEY=sk-or-v1-replace-me
# Match host user that owns the workspace mount (boka: id -u / id -g → 1002)
PUID=1002
PGID=1002
# Host directory mounted at /workspaces inside the containers.
# On boka, point at the projects Pi should see, e.g.:
# WORKSPACE_HOST_PATH=/home/frank
# For local smoke tests, leave unset to use ./workspaces
WORKSPACE_HOST_PATH=./workspaces
# On boka:
WORKSPACE_HOST_PATH=/home/frank
# For local smoke tests without /home/frank projects:
# WORKSPACE_HOST_PATH=./workspaces
+8 -4
View File
@@ -19,15 +19,18 @@ cd /home/frank/pi.schwenk.online
cp .env.example .env
# edit OPENROUTER_API_KEY
# set WORKSPACE_HOST_PATH to the host dirs Pi should edit, e.g. /home/frank
# PUID/PGID = host user (frank on boka: 1002) — must match `id -u` / `id -g`
# WORKSPACE_HOST_PATH=/home/frank
mkdir -p data/pi-web data/pi-agent workspaces
# Container runs as uid 1000 (node); fix ownership if needed:
sudo chown -R 1000:1000 data workspaces
mkdir -p data/pi-web data/pi-agent
chown -R "$(id -u):$(id -g)" data
docker compose build
docker compose up -d
docker compose logs -f --tail=100
# smoke: should list your projects, not Permission denied
docker compose exec web ls /workspaces/eselhoefe.de
```
## Verify
@@ -56,3 +59,4 @@ docker compose build --no-cache && docker compose up -d
- No host port publish; only Traefik on network `traefik` reaches `:8504`
- Middleware is referenced only (`authelia@docker`); ForwardAuth is defined on Authelia
- `pi-coding-agent` pinned to `0.82.1` for current `pi-web` peer range
- Container runs as `PUID`/`PGID` (default 1002) so bind mounts under `/home/frank` are readable
+15 -10
View File
@@ -1,7 +1,14 @@
# Pi Coding Agent + PI WEB (Plain Docker)
# Peer pin: pi-web requires pi-coding-agent >=0.82.1 <0.83
# Run as host UID/GID so bind-mounted /home/frank (etc.) is readable.
ARG PUID=1002
ARG PGID=1002
FROM node:24-bookworm-slim
ARG PUID=1002
ARG PGID=1002
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
bash \
@@ -19,28 +26,26 @@ RUN npm install -g --ignore-scripts @earendil-works/pi-coding-agent@0.82.1 \
&& npm install -g @jmfederico/pi-web@1.202607.3 --allow-scripts=node-pty \
&& npm cache clean --force
# Drop build toolchain from runtime image layer weight is optional; keep for native package installs
# Re-install without g++ would break future pi packages needing compile — leave tools in.
RUN groupadd -g "${PGID}" pi \
&& useradd -u "${PUID}" -g "${PGID}" -m -d /home/pi -s /bin/bash pi \
&& mkdir -p /data/pi-web /workspaces /home/pi/.pi/agent /home/pi/.config/pi-web \
&& chown -R pi:pi /data /workspaces /home/pi
RUN mkdir -p /data/pi-web /workspaces /home/node/.pi/agent /home/node/.config/pi-web \
&& chown -R node:node /data /workspaces /home/node/.pi /home/node/.config
COPY --chown=pi:pi config/pi-web.config.json /etc/pi-web/config.json
COPY --chown=node:node config/pi-web.config.json /etc/pi-web/config.json
ENV HOME=/home/node \
ENV HOME=/home/pi \
PI_WEB_CONFIG=/etc/pi-web/config.json \
PI_WEB_DATA_DIR=/data/pi-web \
PI_CODING_AGENT_DIR=/home/node/.pi/agent \
PI_CODING_AGENT_DIR=/home/pi/.pi/agent \
PI_WEB_SESSIOND_SOCKET=/data/pi-web/sessiond.sock \
PI_WEB_HOST=0.0.0.0 \
PI_WEB_PORT=8504 \
PI_WEB_ALLOWED_HOSTS=pi.schwenk.online \
PATH="/usr/local/bin:${PATH}"
USER node
USER pi
WORKDIR /workspaces
EXPOSE 8504
# Default overridden per-service in compose.yml
CMD ["pi-web-server"]
+3 -1
View File
@@ -13,7 +13,9 @@ Implemented Compose stack for PI WEB + Pi behind Traefik/Authelia. See [DEPLOY.m
- `web` HTTP `200` on `:8504`; `pi --version``0.82.1`
- Full Authelia/DNS/TOTP path: only on boka after DNS + `.env` key
Stack stopped locally after smoke (`docker compose down`).
## UID fix (2026-08-01)
Container must run as host `frank` (1002:1002), not image `node` (1000). Rebuild with `PUID`/`PGID` after pull.
## Authelia handoff (locked)
+2 -1
View File
@@ -46,8 +46,9 @@ Fränky — single trusted user; remote control surface for agent work under `/h
- **Compose:** `compose.yml` — services `sessiond` + `web`, image built from `Dockerfile`
- **Networks:** external `traefik`
- **Auth:** Authelia portal `https://auth.schwenk.online`; middleware `authelia@docker`; ACL `two_factor` for this host
- **Data:** `./data/pi-web` (PI WEB state + sessiond socket), `./data/pi-agent``~/.pi/agent` (sessions, packages, auth)
- **Data:** `./data/pi-web` (PI WEB state + sessiond socket), `./data/pi-agent``/home/pi/.pi/agent` (sessions, packages, auth)
- **Workspaces:** host path via `WORKSPACE_HOST_PATH` → container `/workspaces`
- **UID:** `PUID`/`PGID` in `.env` must match host owner of the mount (boka frank = 1002)
- **Secrets:** `OPENROUTER_API_KEY` in `.env` (see `.env.example`)
### Authelia handoff (consumed)
+20 -9
View File
@@ -1,54 +1,65 @@
# PI WEB + Pi Coding Agent behind Traefik + Authelia (external)
# Deploy: /home/frank/pi.schwenk.online → https://pi.schwenk.online
#
# PUID/PGID must match the host user that owns WORKSPACE_HOST_PATH (boka: frank=1002).
services:
sessiond:
build: .
build:
context: .
args:
PUID: ${PUID:-1002}
PGID: ${PGID:-1002}
image: pidevremote-pi:local
container_name: pi-sessiond
restart: unless-stopped
user: "${PUID:-1002}:${PGID:-1002}"
command: ["pi-web-sessiond"]
env_file:
- .env
environment:
HOME: /home/node
HOME: /home/pi
PI_WEB_CONFIG: /etc/pi-web/config.json
PI_WEB_DATA_DIR: /data/pi-web
PI_WEB_SESSIOND_SOCKET: /data/pi-web/sessiond.sock
PI_CODING_AGENT_DIR: /home/node/.pi/agent
PI_CODING_AGENT_DIR: /home/pi/.pi/agent
OPENROUTER_API_KEY: ${OPENROUTER_API_KEY:-}
volumes:
- ./data/pi-web:/data/pi-web
- ./data/pi-agent:/home/node/.pi/agent
- ./data/pi-agent:/home/pi/.pi/agent
- ${WORKSPACE_HOST_PATH:-./workspaces}:/workspaces
networks:
- internal
web:
build: .
build:
context: .
args:
PUID: ${PUID:-1002}
PGID: ${PGID:-1002}
image: pidevremote-pi:local
container_name: pi-web
restart: unless-stopped
user: "${PUID:-1002}:${PGID:-1002}"
command: ["pi-web-server"]
depends_on:
- sessiond
env_file:
- .env
environment:
HOME: /home/node
HOME: /home/pi
PI_WEB_CONFIG: /etc/pi-web/config.json
PI_WEB_DATA_DIR: /data/pi-web
PI_WEB_SESSIOND_SOCKET: /data/pi-web/sessiond.sock
PI_WEB_HOST: "0.0.0.0"
PI_WEB_PORT: "8504"
PI_WEB_ALLOWED_HOSTS: pi.schwenk.online
PI_CODING_AGENT_DIR: /home/node/.pi/agent
PI_CODING_AGENT_DIR: /home/pi/.pi/agent
OPENROUTER_API_KEY: ${OPENROUTER_API_KEY:-}
volumes:
- ./data/pi-web:/data/pi-web
- ./data/pi-agent:/home/node/.pi/agent
- ./data/pi-agent:/home/pi/.pi/agent
- ${WORKSPACE_HOST_PATH:-./workspaces}:/workspaces
# No host port publish — Traefik reaches the container on the traefik network
expose:
- "8504"
networks: