feat: add PUID/PGID support for Docker bind mount permission compatibility#143
Conversation
…hosts
Docker bind mounts on filesystems like btrfs (common on Synology NAS)
cause two distinct problems:
1. Files created by www-data (UID 33) inside the container appear as
foreign UIDs on the host, making management difficult.
2. btrfs creates directories without write permission bits, even when
ownership is correct. This causes ClickHouse, MariaDB, and the
application itself to fail with "Permission denied" errors.
Changes:
Dockerfile:
- Add PUID/PGID environment variables (default: 33/33) for UID/GID
remapping at container startup
- Add --no-install-recommends to apt-get calls to reduce image size
- Consolidate RUN layers and clean up build caches (pip, composer, apt)
entrypoint.sh:
- Add ensure_dir() helper that always sets mkdir + chown + chmod together,
preventing btrfs from leaving directories without write bits
- Remap www-data UID/GID to match host user when PUID/PGID are set,
with collision detection for existing system users/groups
- Consolidate all directory creation into a single unified block that
runs before any service starts
- Replace deprecated mysqld_safe with mariadbd-safe
- Add --skip-test-db to mysql_install_db
Usage:
services:
bbs:
image: marcpope/borgbackupserver
environment:
- PUID=1000
- PGID=1000
docker-compose.yml:
- Add PUID/PGID environment variables (commented out with usage instructions)
.env.example:
- Add PUID/PGID with explanation of when they're needed
.dockerignore:
- Add docker-compose.yml, .env, .env.example, README.md, LICENSE, docs/
to avoid copying unnecessary files into the build context
|
We are adding some followup code to this. |
Builds on #143. Treats UID/GID changes like a DB migration so users don't have to run chown commands manually when they change PUID/PGID. State file /var/bbs/config/.ownership records what the volume is currently configured for. On startup, the entrypoint compares the env (PUID/PGID/MYSQL_PUID/MYSQL_PGID/CH_PUID/CH_PGID) against the recorded state. If they differ: 1. Preflight guards abort on bad configs (root UID, service collisions, SSH-client UID collisions) with a clear message. 2. The in-container user/group is remapped. Pre-existing system users/groups holding the target UID are shifted by +10000 to free the slot. 3. Files on the volume matching the old UID or GID are chowned, using find -uid/-gid so untouched per-client SSH home dirs are left alone. 4. New state is written atomically (tmp + rename) only after all chowns succeed — so a crash mid-migration retries next start. Every step is logged with a timestamp. File counts are reported per path before chowning, and elapsed seconds after, so users know what the container is doing and why a restart took longer than usual. Also extends PUID support to MariaDB (MYSQL_PUID/MYSQL_PGID) and ClickHouse (CH_PUID/CH_PGID) for users who want every file under the data volume managed as their host user, not just /var/bbs/home. Tightens top-level modes on /var/bbs/mysql and /var/bbs/clickhouse from 755 to 750.
|
Thanks for this — merged and shipped as the foundation. I've layered some follow-up work on top so that users can change UIDs without ever touching the host filesystem manually, treating it like a database migration (commit 4133fb9). What we added1. Declarative state tracking (
|
Test results on Synology btrfs (v2.27.0-beta1)Ran the full migration flow against a real Synology DSM7 NAS, btrfs volume, bind-mounted into a fresh container via Container Manager (Docker 24.0.2). Summary of what we did and what we found: What we testedTest 1 — Fresh container with defaults (no PUID/PGID set).
What we found (and fixed as beta2)The ClickHouse directory on disk was owned by UID 995, GID 996, not the 999/999 we wrote to the state file. Checked inside the container: Debian's If we'd kept the hardcoded Fix (v2.27.0-beta2, just released): detect actual UIDs at startup with Next stepsRetesting with beta2 now. Will post migration runs (changed PUID, no-op restart, preflight rejection) once the beta2 image finishes publishing. What this confirms about the PR
Thanks again @addvanced — the foundation held up well under live testing. The bug we found was in the automation layer I wrote on top, not in your changes. |
Beta2 test results on Synology btrfs — all passing ✅Reran the full test matrix against v2.27.0-beta2 (the fix for the dynamic-UID bug found in beta1). Target: a Synology DSM7 NAS, Container Manager (Docker 24.0.2), bind-mounted btrfs volume. All results below are from a real live run. 1. Fresh container, no PUID/PGID — baseline written correctlyBaseline Host-side ownership on btrfs matches exactly. All directories come up with correct modes (750 for mysql/clickhouse/backups, 1777 for tmp, 755 for the rest). No btrfs missing-write-bit issue. Container reaches 2. Restart with
|
Polish follow-up from the Synology testsAddressed the two cosmetic items I called out in the beta2 report. Fixed (0af279c): Reverted my concern about Not cutting a new beta for this — the state-file cosmetic doesn't warrant a build. Folding it into |
Summary
Related to Discussion: #121
Docker bind mounts cause permission issues when the container's www-data user (UID 33) doesn't match the host user. This is a common problem across all platforms when using bind mounts instead of Docker volumes.
Additionally, some filesystems (notably btrfs on Synology NAS) create directories without write permission bits even when ownership is correct, causing service startup failures.
This PR adds PUID/PGID environment variable support to remap the container's www-data user to match the host user, and introduces an
ensure_dir()helper that always sets ownership and permissions together to ensure correct behavior across all filesystems.Changes
ensure_dir()helper function that sets mkdir + chown + chmod atomicallymysqld_safewithmariadbd-safe--skip-test-dbtomysql_install_db--no-install-recommendsto apt-get calls to reduce image size (~70MB)Testing
Tested on Synology NAS (DS series, btrfs) with Docker bind mount and PUID=1026/PGID=100. Verified clean startup of all services (MariaDB, ClickHouse, Apache, SSH), successful client creation, and correct file ownership on the host volume.