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>
98 lines
7.0 KiB
Markdown
98 lines
7.0 KiB
Markdown
# 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, 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)](../containers/109-syncthing.md) 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](../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 |
|
|
| 109 | [syncthing](../containers/109-syncthing.md) | **unpriv + idmap** | root (via `SupplementaryGroups=media` drop-in on `syncthing@.service`) |
|
|
| 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 |
|
|
|
|
> 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](../containers/index.md#recently-destroyed-kept-for-archaeology).
|
|
|
|
Config backups: `/root/101.conf.bak.*`, `/root/109.conf.bak.*`.
|
|
|
|
## Gotchas
|
|
|
|
- **[apps (105)](../containers/105-apps.md) 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)](../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 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.
|