Proxmox: fix "cannot migrate local bind mount point"
In short.
pct migraterefuses a container with a bind mount because it has no way to know that the host directory behindmp0exists on the other node. The fix is not a flag. Mount the same CephFS path on every node with a systemd mount unit, verify it is there, and only then mark the mount pointshared=1so Proxmox stops asking. The flag is a promise; the mount units are what make it true.
"ERROR: migration aborted: cannot migrate local bind mount point 'mp0'"
You run pct migrate 101 pve02 or the HA manager tries to move a container during node maintenance, and the task log ends with:
2026-07-23 14:02:11 ERROR: migration aborted (duration 00:00:00): cannot migrate local bind mount point 'mp0'
For a disk on shared storage, Proxmox knows the target can see it because the storage definition says so. A bind mount is an arbitrary host path. Proxmox cannot check it, so it refuses, unless the mount point entry carries shared=1, which tells it you have taken responsibility for that path existing on every node. If you set the flag without doing the work, the container starts on the target with an empty directory where its data should be, and the application writes happily into the root disk.
What you need
- Proxmox VE 9.2, a three-node cluster, Ceph Squid 19.2 with one CephFS.
- A CephFS subvolume for the data and a path-scoped cephx client for it (how to create those is in the bind-mount post linked below).
- Root on every node, and a configuration management tool that can put the same unit file on all three. Hand-copying works for one container and fails on the fourth.
Steps
- Find the mount point and the host path in the container config.
pct config 101 | grep '^mp'
# mp0: /mnt/cephfs/files/tenant7,mp=/data
- On every node, create an empty mount point and make it immutable, so a failed mount refuses writes instead of silently accepting them on the root filesystem.
mkdir -p /mnt/cephfs/files/tenant7
chattr +i /mnt/cephfs/files/tenant7
- On every node, create the systemd mount unit. The unit name must match the path with slashes turned into dashes. It is ordered before
pve-guests.service, andRequiredBymakes guest startup depend on it.
# /etc/systemd/system/mnt-cephfs-files-tenant7.mount
[Unit]
Description=CephFS subvolume files/tenant7
After=network-online.target
Wants=network-online.target
Before=pve-guests.service
[Mount]
What=10.1.5.51,10.1.5.52,10.1.5.53:/volumes/files/tenant7/9a1c2d3e-0000-4000-8000-000000000001
Where=/mnt/cephfs/files/tenant7
Type=ceph
Options=name=files-tenant7,secretfile=/etc/ceph/files-tenant7.secret,fs=cephfs,_netdev
[Install]
RequiredBy=pve-guests.service
- Enable it on every node and confirm it mounted.
systemctl daemon-reload
systemctl enable --now mnt-cephfs-files-tenant7.mount
findmnt /mnt/cephfs/files/tenant7
- Only now set
shared=1.pct set --mp0replaces the whole entry, so repeat every option you want to keep.
pct set 101 --mp0 /mnt/cephfs/files/tenant7,mp=/data,shared=1
- Migrate. Containers cannot live-migrate, so
pct migrateneeds the container stopped or--restart. If the container is HA-enrolled, ask the HA manager instead, which stops, moves and starts it for you.
pct migrate 101 pve02 --restart
# or, for an HA resource
ha-manager migrate ct:101 pve02
flowchart LR
CFS["CephFS subvolume<br/>/volumes/files/tenant7"]
CFS --> N1["pve01<br/>mount unit, chattr +i"]
CFS --> N2["pve02<br/>mount unit, chattr +i"]
CFS --> N3["pve03<br/>mount unit, chattr +i"]
N1 -- "mp0 shared=1" --> CT["CT 101<br/>/data"]
CT -. "pct migrate" .-> N2Verify it worked
Check from both sides. On the target node, confirm the container is running and that /data inside it reports the subvolume quota, not the size of the node's root disk:
pct status 101
pct exec 101 -- df -h /data
Then write a file from inside the container and read it on the host path of a third node. If it is visible there, every node really is looking at the same directory.
pct exec 101 -- sh -c 'date > /data/.migrate-check'
ssh pve03 cat /mnt/cephfs/files/tenant7/.migrate-check
Finally, check the target was ready before the move, not after: systemctl is-active mnt-cephfs-files-tenant7.mount on the node you intend to move to is a two-second habit that saves a restore.
Gotchas
shared=1is a declaration, not a check. Proxmox never verifies the path on the target. The immutable mount point is what turns a lie into a start failure rather than data on the wrong disk.- The mount unit must be part of the node baseline. A node reinstalled from the answer file has no unit until the configuration run, and the HA manager will happily place the container there. Run the baseline before clearing maintenance.
/etc/fstabwith_netdevis not a substitute. It gives no ordering againstpve-guests.service, so a container can start before the mount lands.pct set --mp0overwrites the entry. If you hadacl=1orro=1on it, put them back in the same command.chattr +imust go on the empty directory before the first mount. If the directory already has data on the root disk, that data is now hidden behind the mount, and you have a second copy nobody knows about.
FAQ
Can I just add shared=1 and skip the mount units?
You can, and the migration will succeed, and the container will start on the target with an empty /data. The application will treat it as a fresh install. The option exists because Proxmox cannot know what your host path is; it is not a way to move data.
Why not mount CephFS inside the container instead? An unprivileged container holding a Ceph key can see the monitors and whatever the key allows. The point of the bind mount is that the container has no Ceph identity at all, and the key on the node is scoped to one subvolume path. The decision post below goes into why.
Does vzdump back up the bind mount?
No. vzdump skips bind mounts by design, which is why the container backup stays small. The data is backed up separately from a CephFS snapshot with the backup client, on its own schedule.