Files
oikos/infrastructure/media-permissions.md
root 060eff826c Decommission LXC 116 (heaper)
User destroyed the heaper LXC on 2026-05-14. Removed it from the
container index, README quicktable, hubris host doc, and
media-permissions membership table; moved to the "recently destroyed"
archaeology list. /mnt/library/heaper retained (224 MiB).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 12:55:53 +02:00

7.0 KiB

Media permissions — media GID 10000

Standard for any LXC reading/writing /mnt/library on hubris. 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, syncthing syncs, photoprism / immich / mulita scan, 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 <id> -mp0 /mnt/library,mp=/mnt/library (if not already mounted).
  2. Inside the container:
    groupadd -g 10000 media
    usermod -aG media <service-user>   # for every user that needs library access
    
  3. If the container is unprivileged (check pct config <id> | grep unprivileged), append this idmap block to /etc/pve/lxc/<id>.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 <id> && pct start <id>.
  4. For systemd services running with User=root (not typical — but syncthing (109) is one), 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 - <user> 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 / booklore
    • downloads — *arr stack output
    • images — photoprism / immich / mulita
    • roms — emu frontends
    • syncthing — cross-device sync
  • 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 — don't touch)

LXCs with media-group membership

ID Name Priv Media-group members
101 jellyfin unpriv + idmap jellyfin
103 paperless priv www-data
104 gitea priv www-data, gitea
105 apps priv www-data
109 syncthing unpriv + idmap root (via SupplementaryGroups=media drop-in on syncthing@.service)
114 nextcloud priv www-data
119 sophia priv www-data
120 mule-images priv www-data
122 arriman priv www-data, audiobookshelf, radarr, sonarr, lidarr, prowlarr, qbittorrent, bazarr, jellyseerr, mylar, jackett, overseerr, plex, arr

Some entries from earlier snapshots — 100 (arr-yunohost), 107 (marimo), 110 (photoprism), 112 (immich), 116 (heaper) — referenced LXCs that have since been destroyed. See containers/index.

Config backups: /root/101.conf.bak.*, /root/109.conf.bak.*.

Gotchas

  • apps (105) is a Docker host. 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 or user: "<uid>:10000" in compose. Booklore, audiobookshelf-in-docker, etc. need this per-container.
  • pct exec does NOT run initgroups. So pct exec 109 -- id shows only the primary group. For interactive verification, use pct exec 109 -- sudo -i -u root id or su - <user> -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).
  • Nextcloud files under /mnt/library/homecloud are deliberately NOT in the media group. NC manages its own permission model. See nextcloud (114).
  • *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).
  • Hubris host
  • All container pages list whether they're in the standard

Changelog

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.

2026-04-28 — wiki entry created

Initial documentation.

2026-04-26 — MEDIACENTER_GID fix on arriman (122)

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.