Initial documentation of the hubris Proxmox homelab as a cross-linked markdown wiki. Per-node pages, cross-cutting infrastructure pages, an investigation log, and an operations cheatsheet. Each node and topic ends with a Changelog section so changes can be tracked in-place going forward. Refreshed against live state on 2026-04-28 — 14 active LXCs (109 syncthing currently stopped) + 1 VM (108 haos). Reflects post-A/B-test state of the 2026-04-21 hubris crash-loop investigation.
6.8 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
pct set <id> -mp0 /mnt/library,mp=/mnt/library(if not already mounted).- Inside the container:
groupadd -g 10000 media usermod -aG media <service-user> # for every user that needs library access - If the container is unprivileged (check
pct config <id> | grep unprivileged), append this idmap block to/etc/pve/lxc/<id>.conf(back up first):Thenlxc.idmap: u 0 100000 65536 lxc.idmap: g 0 100000 10000 lxc.idmap: g 10000 10000 1 lxc.idmap: g 10001 110001 55535pct stop <id> && pct start <id>. - For systemd services running with
User=root(not typical — but syncthing (109) is one), add a drop-in withSupplementaryGroups=media. Systemd skipsinitgroups()forUser=root. pct execsessions don't get supplementary groups (no initgroups). Usesudo -iorsu - <user>inside the container to verify membership interactively. Real services useinitgroupsand work correctly.
State snapshot
Host
- Group
mediaGID 10000 exists. /etc/subgidhasroot:100000:65536ANDroot:10000:1(second line required for unprivileged LXCs to receive GID 10000).- Shared subtrees owned
:mediamode2775(drwxrwsr-x, setgid):movies,tv,music,anime,podcasts— jellyfin librariesaudiobooks,audiobookshelf-metadata,books,comics— audiobookshelf / bookloredownloads— *arr stack outputimages— photoprism / immich / mulitaroms— emu frontendssyncthing— 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 purposenotes,heaper,sophia(single-container use)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 |
| 116 | heaper | 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 the older snapshot — 100 (arr-yunohost), 107 (marimo), 110 (photoprism), 112 (immich) — 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
mediato the LXC alone is not enough for Docker containers inside. Each Docker container needs its GID passed in explicitly:--group-add 10000oruser: "<uid>:10000"in compose. Booklore, audiobookshelf-in-docker, etc. need this per-container. pct execdoes NOT run initgroups. Sopct exec 109 -- idshows only the primary group. For interactive verification, usepct exec 109 -- sudo -i -u root idorsu - <user> -c id. Real systemd services work fine.- systemd
User=rootskips initgroups — explicitSupplementaryGroups=mediadrop-in needed. pct restoreor template rebuilds wipe in-container group membership and unprivileged-LXC idmap blocks. Re-apply from this page./etc/subgidmust retain bothroot:100000:65536ANDroot: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/homecloudare 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.envbecause s6-setuidgid only honors the primary PGID;group_add:doesn't propagate. See arriman (122).
Related
- Hubris host
- All container pages list whether they're in the standard
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.