Skip to content

Instantly share code, notes, and snippets.

@MrDrMcCoy
Last active November 3, 2024 12:54
Show Gist options
  • Save MrDrMcCoy/1c0ccf47b1972d310c8c58be2d453466 to your computer and use it in GitHub Desktop.
Save MrDrMcCoy/1c0ccf47b1972d310c8c58be2d453466 to your computer and use it in GitHub Desktop.
How to handle automatic bind mounting for shared directories with Proxmox/LXC

The Problem

Proxmox has a neat UI for adding extra storage to containers. However, if that storage already exists somewhere or needs to exist for more than one container, you're SOL. Bind mounting is an easy way to take a mount and make it exist in more than one place. However, bind mounting has to be done in a particular order with Proxmox due to how it creates device nodes and pre-populates directories. This is very frustrating, but not unsolvable.

The solution

ZFS needs to come up first

If you are using ZFS for your storage (which you should), you need to ensure that all of ZFS's mounts are fully online before proxmox does anything. You will need to add After=zfs.target to the [Unit] section of the Proxmox Systemd service files. The change needs to be applied to the following files:

/lib/systemd/system/pve-cluster.service
/lib/systemd/system/pve-ha-crm.service
/lib/systemd/system/pve-ha-lrm.service
/lib/systemd/system/[email protected]
/lib/systemd/system/pve-firewall.service
/lib/systemd/system/pvefw-logger.service
/lib/systemd/system/pve-daily-update.service
/lib/systemd/system/pve-guests.service
/lib/systemd/system/pvebanner.service
/lib/systemd/system/pvedaemon.service
/lib/systemd/system/pvenetcommit.service
/lib/systemd/system/pveproxy.service
/lib/systemd/system/pvesr.service
/lib/systemd/system/pvestatd.service

Create mount script

You will need to make a simple script that can be run by Systemd that can do a little cleanup and perform the mounts. An example should be attached to this gist a bit further down. Copy it to /opt/ and be sure to chmod it to be executable.

Create Systemd service file

The Systemd service file that you want is included below. Copy it to /etc/systemd/system/ and do the following commands:

systemctl daemon-reload
systemctl enable bindmounts

The next time you reboot your host[s], this should allow all containers with the shared bind mount to come up properly. You can also do systemctl start bindmounts if you would like to mount the paths for running containers.

Final notes

  • This method, while based on recommendations from Proxmox staff, is unsupported and has a chance of data loss. Use it at your own risk, and under no expectation of warranty.
  • I have not tested this in what one might call a "production" environment. I am using it at home with some success, but that's about it.
  • I plan to replace the bindmounts Bash script with a Python version. This will enable more complex mount definitions, such as more than one mount per container, containers that get different mounts from their neighbors, etc. This will be done as I have time.
[Unit]
Description=Bind mounts for containers
Requires=zfs.target
After=zfs.target
[Service]
#Type=oneshot
ExecStart=/opt/bindmounts.sh up
ExecStop=/opt/bindmounts.sh down
[Install]
WantedBy=multi-user.target
#!/usr/bin/env bash
# Set signal traps
trap "exit 1" INT TERM KILL
trap "exit 0" QUIT HUP
# Set mount source, target, container base, and which container IDs to do the mounts for
MNT_SOURCE=/z/media
MNT_TARGET=/media
CT_BASE=/z/pve
CT_IDS=(1004 1005 1006 1007 1008 1009 1010 1013 1014 1015 1021)
case "$1" in
# Bring up the mounts
up)
for ID in ${CT_IDS[@]} ; do
# Try unmounting the target and, if it succeeds, remove any stray files that may be left behind.
/bin/umount -v ${CT_BASE}/subvol-${ID}-disk-1${MNT_TARGET} && \
/bin/rm -rv ${CT_BASE}/subvol-${ID}-disk-1${MNT_TARGET}/*
# Create target directory if it does not exist
/bin/mkdir -vp ${CT_BASE}/subvol-${ID}-disk-1${MNT_TARGET}
# Mount the paths
/bin/mount -vo bind ${MNT_SOURCE} ${CT_BASE}/subvol-${ID}-disk-1${MNT_TARGET}
done
# The Systemd script won't permit the "down" feature to work if the "service" is not running. To make the script appear to be a running service, we will let it sleep forever.
/bin/sleep INFINITY ;;
# Unmount the bind mounts
down)
for ID in ${CT_IDS[@]} ; do
/bin/umount -v ${CT_BASE}/subvol-${ID}-disk-1${MNT_TARGET}
done ;;
# Exit on invalid or missing choice
*)
echo "Invalid argument: '$1'" ;;
esac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment