An unprivileged LXC container with a bind mount often shows files and folders owned by nobody:nogroup, even when the permissions on the host look correct. This is not a Proxmox defect: it depends on how the kernel translates user identifiers between container and host. Understanding the mechanism lets you choose knowingly between two solutions, computing permissions on the host and custom mapping with lxc.idmap, each with different security trade-offs.
Privileged and unprivileged containers
In a privileged container the identifiers coincide with the host's: the container's root is the host's root and, in case of a breakout, only the isolation mechanisms (namespaces, AppArmor, capabilities) are left to limit the damage. In an unprivileged container the kernel uses a user namespace: the range of UIDs and GIDs seen by the container is shifted to a different range on the host, so the container's root is an unprivileged user on the host.
On Proxmox VE the default shift starts at 100000 and covers 65536 identifiers, as declared by the root:100000:65536 entry in /etc/subuid and /etc/subgid. The rule is host UID = 100000 + container UID.
| User in the container | UID in the container | UID on the host |
|---|---|---|
| root | 0 | 100000 |
| www-data | 33 | 100033 |
| first user created | 1000 | 101000 |
| nobody | 65534 | 165534 |
The kernel checks permissions using the host's IDs: the container only sees a translation of them. An ID with no match in the namespace appears as 65534 (nobody), and that is what you observe with a bind mount when the folder belongs to a host ID outside the mapped range, for example 1000. Giving up the unprivileged container to work around the problem moves the risk to the host: it is better to fix the permissions, not the isolation.
The bind mount
A bind mount exposes a host directory inside the container. For container 101:
pct set 101 -mp0 /tank/dati,mp=/mnt/datiThe general form is pct set <vmid> -mp0 /host/path,mp=/container/path. The command adds the line mp0: /tank/dati,mp=/mnt/dati to /etc/pve/lxc/101.conf; the mount point becomes active at the next container start. A few rules:
- The directory must exist on the host and, in a cluster, on every node where the container can be started or migrated (see Three-node Proxmox VE cluster).
- Do not mount system directories such as
/,/varor/etc: the documentation flags them as a serious risk for the host. - The contents of bind mounts are not included in
vzdumpbackups: the data must be protected separately, as described in 3-2-1 backup strategy with Proxmox Backup Server.
Computing permissions on the host
With the default mapping, it is enough to give the directory to the host ID that corresponds to the container user. For UID and GID 1000:
chown -R 101000:101000 /tank/datiVerify with numeric IDs, because names depend on the namespace in which they are resolved.
ls -ln /tank/datipct exec 101 -- ls -ln /mnt/dati-rw-r--r-- 1 101000 101000 0 Apr 8 10:00 nota.txt-rw-r--r-- 1 1000 1000 0 Apr 8 10:00 nota.txtThe method requires no changes to the container configuration. The limit is that on the host the data ends up owned by an unusual ID: this is inconvenient if the same directory is shared with other services, for example Samba or NFS, or if the files already exist with a different owner. By default all unprivileged containers use the same range, so a bind mount shared between several containers shows the same IDs everywhere.
Custom mapping with lxc.idmap
When the files already have a real owner on the host, you can let a single ID cross the namespace without translation: the container's UID 1000 becomes the host's UID 1000, while all the others stay shifted. For each type (u for UIDs, g for GIDs) three ranges are needed, which must cover 0 to 65535 with no gaps or overlaps.
lxc.idmap = u 0 100000 1000
lxc.idmap = g 0 100000 1000
lxc.idmap = u 1000 1000 1
lxc.idmap = g 1000 1000 1
lxc.idmap = u 1001 101001 64535
lxc.idmap = g 1001 101001 64535Each line has the form lxc.idmap = <type> <first ID in the container> <first ID on the host> <count>. Here IDs 0 to 999 go to 100000-100999, ID 1000 stays 1000 and IDs 1001 to 65535 go to 101001-165535: 1000 + 1 + 64535 makes 65536.
LXC starts the container as root and applies the mapping with newuidmap and newgidmap, which only accept the IDs delegated to the user in /etc/subuid and /etc/subgid. The default entry covers 100000-165535, so ID 1000 must be delegated to root as well.
echo 'root:1000:1' >> /etc/subuid
echo 'root:1000:1' >> /etc/subgidThe format is user:first-id:count. Without these entries the container does not start and the error mentions newuidmap or newgidmap; to diagnose, it helps to start it in the foreground with lxc-start -F -n 101. The mapping is read at every start, so the container must be restarted.
pct shutdown 101
pct start 101Warning
Mapping an ID without translation reduces isolation: a process running as UID 1000 in the container acts on the host as UID 1000, with access to everything that user can read or write, not just the shared directory. It is better to map an ID reserved for the purpose, for example a host service account with no login shell, and not that of a real user. The host ID does not have to match the container one: just replace the second number in the u 1000 1000 1 and g 1000 1000 1 lines (and the entries in subuid and subgid) with the chosen ID, as long as it is outside the 100000-165535 range.
Important
In a container already in use, the files of its filesystem that belong to the remapped ID (for example the home of user 1000, stored on disk as 101000) show up as nobody after the change: Proxmox does not realign them. It is better to apply the mapping to a new container, or to fix ownership from the host with the container stopped.
Tip
If several containers only need to share a directory, it is often enough to map a GID and work with group permissions (directory with the setgid bit and g+rwX): UIDs stay shifted and no host user is exposed.
Note
The documentation for the most recent Proxmox VE versions (9.x series) also describes an idmap option for a single mount point, which avoids remapping the whole container. Check that the version you use supports it; lxc.idmap remains valid on Proxmox VE 8 and 9.
Verifying the mapping
pct exec 101 -- cat /proc/self/uid_map 0 100000 1000
1000 1000 1
1001 101001 64535The definitive check is to create a file from the container and read its numeric owner on the host.
pct exec 101 -- su -s /bin/sh -c 'id && touch /mnt/dati/prova' utente-esempio
ls -ln /tank/dati/provauid=1000(utente-esempio) gid=1000(utente-esempio) groups=1000(utente-esempio)
-rw-r--r-- 1 1000 1000 0 Apr 8 10:05 provaWith the custom mapping the UID is 1000 on both sides; with the default one it would be 101000 on the host. For a quick diagnosis:
- Owner
nobodyin the container: the host ID is not within the mapped range. - Container that won't start after the change: missing entry in
subuidorsubgid, or ranges that don't add up to 65536. - Permission denied with the correct IDs: check the permissions of the parent directories; with bind mounts in unprivileged containers, ACLs are not usable.