Add PUID/PGID support to fix internal valkey UID/GID handling#3105
Add PUID/PGID support to fix internal valkey UID/GID handling#3105
Conversation
Co-authored-by: gantoine <3247106+gantoine@users.noreply.github.com>
Greptile SummaryThis PR adds Key observations:
Confidence Score: 3/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant Docker as Docker Runtime
participant EP as docker-entrypoint.sh (root)
participant SE as su-exec
participant Init as /init (PUID:PGID)
participant VK as valkey-server
participant GU as gunicorn
participant NX as nginx
participant RQ as rq worker/scheduler
Docker->>EP: exec /docker-entrypoint.sh /init
EP->>EP: Load _FILE env vars
EP->>EP: Set ROMM_BASE_PATH / ROMM_PORT defaults
EP->>EP: Run 20-envsubst-on-templates.sh (as root)
EP->>EP: Fix nginx symlinks (as root)
EP->>EP: EUID == 0 → read PUID/PGID (default 1000)
EP->>EP: chown PUID:PGID /romm (non-recursive)
EP->>EP: chown -R PUID:PGID /redis-data
EP->>SE: exec su-exec PUID:PGID /init
SE->>Init: setuid/setgid → exec /init
Init->>VK: start valkey-server --dir /redis-data
Init->>GU: start gunicorn (EUID=PUID)
Init->>NX: nginx (EUID≠0, no user directive)
Init->>RQ: start rq worker + scheduler
Last reviewed commit: 8539f48 |
| PUID=${PUID:-1000} | ||
| PGID=${PGID:-1000} |
There was a problem hiding this comment.
Missing integer validation for PUID/PGID
PUID and PGID are used directly in chown and su-exec without being validated as integers. If a user accidentally sets PUID=my-user or PGID="", chown will fail with an unhelpful error and the container will exit without a clear diagnostic. Adding a numeric check up front gives the operator an actionable error message:
| PUID=${PUID:-1000} | |
| PGID=${PGID:-1000} | |
| PUID=${PUID:-1000} | |
| PGID=${PGID:-1000} | |
| if ! [[ "${PUID}" =~ ^[0-9]+$ ]] || ! [[ "${PGID}" =~ ^[0-9]+$ ]]; then | |
| echo "ERROR: PUID and PGID must be numeric (got PUID='${PUID}' PGID='${PGID}')" >&2 | |
| exit 1 | |
| fi |
| chown "${PUID}:${PGID}" /romm | ||
| chown -R "${PUID}:${PGID}" /redis-data |
There was a problem hiding this comment.
Migrating from default UID may leave volumes inaccessible
chown /romm only updates the top-level directory. Named Docker volumes previously initialised under uid 1000 (e.g. romm_resources mounted at /romm/resources) will still be owned by 1000 after a migration to a custom PUID. Any attempt by the application to write new resources or assets into those volumes will fail silently for the non-root user.
While the intentional non-recursive approach is reasonable for large user libraries, it would be worth emitting a warning when PUID differs from the default 1000 so operators know they may need to chown their existing volumes on the host:
| chown "${PUID}:${PGID}" /romm | |
| chown -R "${PUID}:${PGID}" /redis-data | |
| chown "${PUID}:${PGID}" /romm | |
| chown -R "${PUID}:${PGID}" /redis-data | |
| if [[ "${PUID}" != "1000" || "${PGID}" != "1000" ]]; then | |
| echo "WARNING: PUID/PGID differ from the default 1000. Existing named volumes" \ | |
| "(resources, assets, config) may still be owned by uid 1000 and could be" \ | |
| "unwritable. Re-chown those volumes on the host if you encounter permission errors." >&2 | |
| fi |
| chown -R "${PUID}:${PGID}" /redis-data | ||
|
|
||
| # Drop privileges to the target user and re-execute | ||
| exec su-exec "${PUID}:${PGID}" "$@" |
There was a problem hiding this comment.
Supplementary groups are not preserved by su-exec
su-exec uid:gid cmd calls setuid/setgid and sets the supplementary group list to exactly [gid]. If the host user identified by PUID belongs to additional groups (e.g. a shared media group needed to access the game library bind-mount), those supplementary groups will not be present in the child process, and file-access errors can follow.
The lighter-weight su-exec has no --groups flag; if supplementary group support becomes a requirement the container would need to switch to gosu or create the proper /etc/group entry dynamically (e.g. addgroup -g "${PGID}" appgroup && adduser -u "${PUID}" -G appgroup -D appuser) before dropping privileges. Worth noting this limitation in the compose-file comment.
| # accessible. Avoid -R for /romm to prevent touching user-mounted volumes | ||
| # (library, assets, resources, etc.) which may contain large collections. | ||
| chown "${PUID}:${PGID}" /romm | ||
| chown -R "${PUID}:${PGID}" /redis-data |
There was a problem hiding this comment.
Recursive chown of /redis-data may cause slow startup with large RDB files
chown -R /redis-data re-owners every file recursively at every container start. With a large dump.rdb on spinning-disk storage this can add noticeable latency to each restart. A common mitigation is to skip the chown when ownership is already correct:
| chown -R "${PUID}:${PGID}" /redis-data | |
| if [[ "$(stat -c '%u:%g' /redis-data)" != "${PUID}:${PGID}" ]]; then | |
| chown -R "${PUID}:${PGID}" /redis-data | |
| fi |
This still handles the "prior root-run left files owned by 1000" migration case while being a no-op on subsequent starts.
The container has no mechanism to honor
PUID/PGIDenvironment variables. When run with a non-default UID (e.g., 568 on TrueNAS),/redis-data(owned byromm:romm1000:1000, mode 755) is unwritable by the process, causing internal valkey to fail.Changes
docker/Dockerfile: Addsu-execto the production-stageapk add— needed to drop privileges from root to the target UID/GID.docker/init_scripts/docker-entrypoint.sh: When the entrypoint runs as root (Docker default), readPUID/PGID(default: 1000), fix directory ownership, thenexec su-execto the target user before the init script runs. All root-required setup (nginx template rendering, symlink fixup) runs first./redis-dataownership is fixed recursively (-R) to handle existing data files (e.g.dump.rdbfrom prior root runs)./rommownership is fixed non-recursively to avoid touching potentially large user-mounted volumes (library, assets, etc.).examples/docker-compose.example.yml: DocumentPUID/PGIDas commented-out env vars.Usage
All child processes (valkey, gunicorn, nginx, rq workers) inherit the dropped UID/GID. The existing nginx root-vs-non-root logic in
initalready handles this path correctly (EUID != 0branch).Original prompt
🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.