# Media permissions — `media` GID 10000 Standard for any LXC reading/writing `/mnt/library` on [hubris](../hosts/hubris.md). Applied 2026-04-20. ## Standard Every LXC that mounts `/mnt/library` participates in a shared `media` group with **GID 10000**. Shared subtrees are owned by that group with the setgid bit (`drwxrwsr-x`, mode `2775`), so new files auto-inherit the right group regardless of which container wrote them. ## Why `/mnt/library` is a cross-container storage pool. \*arr writes, jellyfin reads, mulita scans, paperless ingests. Without a shared group, each container sees files as `nobody:nogroup` (unprivileged) or `www-data` (privileged 1:1) and the permission web collapses into one-off chmods. GID 10000 bridges privileged and unprivileged containers. ## Onboarding a new LXC 1. `pct set -mp0 /mnt/library,mp=/mnt/library` (if not already mounted). 2. Inside the container: ``` groupadd -g 10000 media usermod -aG media # for every user that needs library access ``` 3. If the container is **unprivileged** (check `pct config | grep unprivileged`), append this idmap block to `/etc/pve/lxc/.conf` (back up first): ``` lxc.idmap: u 0 100000 65536 lxc.idmap: g 0 100000 10000 lxc.idmap: g 10000 10000 1 lxc.idmap: g 10001 110001 55535 ``` Then `pct stop && pct start `. 4. For systemd services running with `User=root` (not typical), add a drop-in with `SupplementaryGroups=media`. Systemd skips `initgroups()` for `User=root`. 5. `pct exec` sessions don't get supplementary groups (no initgroups). Use `sudo -i` or `su - ` inside the container to verify membership interactively. Real services use `initgroups` and work correctly. ## State snapshot ### Host - Group `media` GID 10000 exists. - `/etc/subgid` has `root:100000:65536` AND `root:10000:1` (second line required for unprivileged LXCs to receive GID 10000). - Shared subtrees owned `:media` mode `2775` (drwxrwsr-x, setgid): - `movies`, `tv`, `music`, `anime`, `podcasts` — jellyfin libraries - `audiobooks`, `audiobookshelf-metadata`, `books`, `comics` — audiobookshelf / grimmory - `downloads` — \*arr stack output - `images` — photoprism / immich / mulita - `roms` — emu frontends - `syncthing` — empty subtree, retained for archaeology (LXC 109 destroyed 2026-05-14) - Container-specific subtrees intentionally **not** migrated (keep their own owner:group): - `documents` (paperless, `www-data:www-data 750`) - `homecloud` (nextcloud — its own permission model, easy to break) - `marimo` (marimo venv) — *LXC since destroyed; review whether subtree still serves a purpose* - `notes`, `sophia` (single-container use); `heaper` — orphaned data subtree (LXC since destroyed 2026-05-14, 224 MiB retained) - `repos` (owner UID 102 GID 105 from inside [gitea](../containers/104-gitea.md) — don't touch) ### LXCs with media-group membership | ID | Name | Priv | Media-group members | | --- | --------------------------------------------- | ---- | --------------------------------------------- | | 101 | [jellyfin](../containers/101-jellyfin.md) | **unpriv + idmap** | jellyfin | | 103 | [paperless](../containers/103-paperless.md) | priv | www-data | | 104 | [gitea](../containers/104-gitea.md) | priv | www-data, gitea | | 105 | [apps](../containers/105-apps.md) | priv | www-data | | 114 | [nextcloud](../containers/114-nextcloud.md) | priv | www-data | | 119 | [sophia](../containers/119-sophia.md) | priv | www-data | | 120 | [mule-images](../containers/120-mule-images.md) | priv | www-data | | 122 | [arriman](../containers/122-arriman.md) | priv | www-data, audiobookshelf, radarr, sonarr, lidarr, prowlarr, qbittorrent, bazarr, jellyseerr, mylar, jackett, overseerr, plex, arr | | 130 | [grimmory](../containers/130-grimmory.md) | priv | Docker container uses `GROUP_ID=10000` env var (linuxserver pattern) — no in-LXC group needed | | 132 | [rclone](../containers/132-rclone.md) | priv | **read-only** mount; runs as root → reads all subtrees. No media group needed | > Some entries from earlier snapshots — 100 (arr-yunohost), 107 (marimo), 109 (syncthing), 110 (photoprism), 112 (immich), 116 (heaper) — referenced LXCs that have since been destroyed. See [containers/index](../containers/index.md#recently-destroyed-kept-for-archaeology). Config backups: `/root/101.conf.bak.*`, `/root/109.conf.bak.*` (109 destroyed 2026-05-14). ## Gotchas - **[apps (105)](../containers/105-apps.md) and [grimmory (130)](../containers/130-grimmory.md) are Docker hosts.** Adding `media` to the LXC alone is *not* enough for Docker containers inside. Each Docker container needs its GID passed in explicitly: `--group-add 10000`, `user: ":10000"`, or `GROUP_ID=10000` (linuxserver images) in compose. Grimmory, audiobookshelf-in-docker, etc. need this per-container. - **`pct exec` does NOT run initgroups.** So `pct exec -- id` shows only the primary group. For interactive verification, use `pct exec -- sudo -i -u root id` or `su - -c id`. Real systemd services work fine. - **systemd `User=root`** skips initgroups — explicit `SupplementaryGroups=media` drop-in needed. - **`pct restore`** or template rebuilds wipe in-container group membership and unprivileged-LXC idmap blocks. Re-apply from this page. - **`/etc/subgid`** must retain both `root:100000:65536` AND `root:10000:1`. Dropping the second breaks startup of any unprivileged LXC with the idmap block. - **\*arr "Set Permissions" options** can override the setgid inheritance by explicitly chown'ing files. Leave those off, or set the group to `media`. Relevant to Sonarr/Radarr/qBittorrent on [arriman (122)](../containers/122-arriman.md). - **Nextcloud** files under `/mnt/library/homecloud` are deliberately NOT in the media group. NC manages its own permission model. See [nextcloud (114)](../containers/114-nextcloud.md). - **\*arr stack on arriman** required `MEDIACENTER_GID=10000` (not 13000) in `.env` because s6-setuidgid only honors the primary PGID; `group_add:` doesn't propagate. See [arriman (122)](../containers/122-arriman.md#changelog). ## Related - [Hubris host](../hosts/hubris.md) - All container pages list whether they're in the standard ## Changelog ### 2026-05-14 — LXC 109 (syncthing) destroyed Removed the syncthing row from the membership table and the syncthing-as-`User=root` example from the onboarding section. `/mnt/library/syncthing` subtree was already empty and retained as an empty dir. ### 2026-05-14 — LXC 116 (heaper) destroyed Removed the heaper row from the LXC membership table and noted the orphaned `/mnt/library/heaper` subtree (224 MiB retained). See [host changelog](../hosts/hubris.md#changelog). ### 2026-04-28 — wiki entry created Initial documentation. ### 2026-04-26 — `MEDIACENTER_GID` fix on [arriman (122)](../containers/122-arriman.md) qBit was erroring every torrent with "Permission denied" because `MEDIACENTER_GID=13000` was set as a supplementary GID via `group_add:`. Changed to 10000 (primary GID); fix described above is now standard. ### 2026-04-20 — standard rolled out GID 10000 hostgroup, idmap blocks for unprivileged LXCs, setgid 2775 on shared subtrees, `media` membership for service users in every participating LXC.