Skip to content

Instantly share code, notes, and snippets.

@NorkzYT
Last active August 22, 2025 20:07
Show Gist options
  • Save NorkzYT/14449b247dae9ac81ba4664564669299 to your computer and use it in GitHub Desktop.
Save NorkzYT/14449b247dae9ac81ba4664564669299 to your computer and use it in GitHub Desktop.
Proxmox CIFS Share Mount Wizard Script

Proxmox LXC ⇆ CIFS Mount Wizard

proxmox-lxc-cifs-share.sh is an interactive helper that mounts a network CIFS/SMB share on a Proxmox VE node and bind-mounts it into one or more unprivileged LXC containers in one pass.
It automates:

  1. Creating a host-side mountpoint under /mnt/lxc_shares/<folder>.
  2. Writing a properly-tuned /etc/fstab entry (systemd.automount, uid/gid mapping, etc.).
  3. Mounting the share immediately.
  4. Injecting the bind-mount (mpX:) into the live LXC configuration with pct set.
  5. Stopping and restarting each container to apply the mount.

Note: target containers must be running when you start the script. It will stop and restart them briefly.


Usage

proxmox-lxc-cifs-share.sh [OPTIONS]

OPTIONS

  • --config FILE Read all parameters from FILE (no prompts).

  • --add Skip host-side mount; only bind the existing share into containers.

  • --containers IDS Comma-separated LXC IDs (e.g. 105,106).

  • --users NAMES Comma-separated container usernames (must match the count and order of --containers).

  • --noserverino Disable the CIFS serverino option (fixes “Stale file handle” on some NAS).

  • --help Show usage and exit.


Quick-start

# 1. Download and make executable
curl -fsSL -o proxmox-lxc-cifs-share.sh \
  https://…/proxmox-lxc-cifs-share.sh
chmod +x proxmox-lxc-cifs-share.sh

# 2a. Interactive setup (prompts for all values)
sudo ./proxmox-lxc-cifs-share.sh

# 2b. Bind into two containers without touching /etc/fstab
sudo ./proxmox-lxc-cifs-share.sh \
  --add \
  --containers 105,106 \
  --users ubuntu,svcuser

# 2c. From a config file, bind into one container
sudo ./proxmox-lxc-cifs-share.sh \
  --config myshare.conf \
  --add

Config-file format

Save your settings in myshare.conf:

folder_name=nas_rwx
cifs_host=10.0.0.2
share_name=media
smb_username=myuser
smb_password=mypass
containers=105,106
users=ubuntu,svcuser

Then run:

sudo ./proxmox-lxc-cifs-share.sh --config myshare.conf --add

Prerequisites

Item Notes
Proxmox VE node Tested on Proxmox VE 7–8 (Debian 12+). Run on the host.
Unprivileged LXC container(s) Must exist and be running.
CIFS/SMB share Hostname/IP, share name, valid credentials.
Root privileges Use sudo or run as root.
cifs-utils Install with apt update && apt install cifs-utils.

Once complete, verify inside each container:

pct exec <ID> -- ls -ld /mnt/<folder_name>

1) Initial interactive run

root@proxmox-node:/opt# chmod +x proxmox-lxc-cifs-share.sh
root@proxmox-node:/opt# ./proxmox-lxc-cifs-share.sh
=== Share Settings ===
Folder under /mnt/lxc_shares (e.g. nas_rwx): appdata
CIFS host (IP/DNS): 10.0.0.2
Share name: appdata
SMB username: xxx
SMB password: xxx

Available LXC IDs:
102
103
104
105
106
107
108
109
110
111
LXC IDs (comma-separated): 103

Usernames in 103:
root
daemon
bin
sys
sync
games
man
lp
mail
news
uucp
proxy
www-data
backup
list
irc
gnats
nobody
messagebus
syslog
postfix
_apt
sshd
systemd-network
systemd-resolve
systemd-timesync
uuidd
tcpdump
systemd-coredump
ubuntu
root
nobody
Usernames (comma-separated): ubuntu

Generate config file? [y/N]: y
Config path [./share.conf]: ./
ERROR: './' is a directory; please specify a file name.
Config path [./share.conf]: ./share.conf
Config saved to ./share.conf
Host mount exists; skip host-side work? [Y/n]: y
Stopping LXC 103…
Binding into 103 → /mnt/appdata
Starting LXC 103…

Verification:
  ↳ 103: OK

✅  All done.

2) Interactive “add” mode

root@proxmox-node:/opt# ./proxmox-lxc-cifs-share.sh \
  --add \
  --containers 103 \
  --users ubuntu
=== Share Settings ===
Folder under /mnt/lxc_shares (e.g. nas_rwx): appdata
CIFS host (IP/DNS): 10.0.0.2
Share name: appdata
SMB username: xxx
SMB password: xxx

Generate config file? [y/N]: n
Stopping LXC 103…
Binding into 103 → /mnt/appdata
Starting LXC 103…

Verification:
  ↳ 103: OK

✅  All done.

3) Config-file + “add” mode

root@proxmox-node:/opt# ./proxmox-lxc-cifs-share.sh \
  --config share.conf \
  --add

Stopping LXC 103…
Binding into 103 → /mnt/appdata
Starting LXC 103…

Verification:
  ↳ 103: OK

✅  All done.
#!/usr/bin/env bash
# proxmox-lxc-cifs-share.sh
#
# Mount a CIFS/SMB share on a Proxmox VE host and bind-mount it into
# one or more unprivileged LXC containers with dynamic UID/GID mapping.
#
# Usage:
# proxmox-lxc-cifs-share.sh [--config FILE] [--add]
# [--containers IDS] [--users NAMES] [--noserverino] [--help]
#
# Compatible with Proxmox VE 7–8 on Debian 12+.
set -euo pipefail
# ──────────────────────────────────────────────────────────────────────────────
# Helpers
# ──────────────────────────────────────────────────────────────────────────────
error_exit() {
echo "ERROR: $1" >&2
exit 1
}
show_help() {
cat <<EOF
Usage: $0 [OPTIONS]
Options:
--config FILE Read all parameters from FILE.
--add Only bind into containers; skip host mount.
--containers IDS Comma-separated LXC IDs (e.g. 105,106).
--users NAMES Comma-separated usernames matching each ID.
--noserverino Disable CIFS “serverino” option.
--help Show this help and exit.
Examples:
# Interactive:
sudo $0
# Bind into two containers:
sudo $0 --add --containers 105,106 --users ubuntu,svcuser
# From config file:
sudo $0 --config myshare.conf --add
EOF
exit 0
}
list_lxc_ids() {
pct list | awk 'NR>1 {print $1}'
}
list_usernames() {
local id=$1
pct status "$id" &>/dev/null || error_exit "LXC $id not found"
pct exec "$id" -- getent passwd | cut -d: -f1
}
validate_prerequisites() {
(( EUID == 0 )) || error_exit "Run as root or via sudo"
command -v pct >/dev/null 2>&1 || error_exit "'pct' not found"
command -v mount.cifs >/dev/null 2>&1 || error_exit "Install cifs-utils"
command -v systemd-escape \
>/dev/null 2>&1 || error_exit "Install systemd"
}
load_config() {
[[ -f $config_file ]] || error_exit "Config file '$config_file' missing"
source "$config_file"
}
prompt_share_settings() {
echo "=== Share Settings ==="
read -rp "Folder under /mnt/lxc_shares (e.g. nas_rwx): " folder_name
read -rp "CIFS host (IP/DNS): " cifs_host
read -rp "Share name: " share_name
read -rp "SMB username: " smb_username
read -rs -p "SMB password: " smb_password
echo
}
prompt_containers_and_users() {
echo "Available LXC IDs:"
list_lxc_ids
read -rp "LXC IDs (comma-separated): " containers
echo
echo "Usernames in ${containers%%,*}:"
list_usernames "${containers%%,*}"
read -rp "Usernames (comma-separated): " users
echo
}
prompt_generate_config() {
read -rp "Generate config file? [y/N]: " gen
if [[ $gen =~ ^[Yy]$ ]]; then
while true; do
read -rp "Config path [./share.conf]: " cfg
cfg=${cfg:-./share.conf}
if [[ -d $cfg ]]; then
echo "ERROR: '$cfg' is a directory; please specify a file name."
continue
fi
break
done
{
echo "folder_name=$folder_name"
echo "cifs_host=$cifs_host"
echo "share_name=$share_name"
echo "smb_username=$smb_username"
echo "smb_password=$smb_password"
echo "containers=$containers"
echo "users=$users"
} >"$cfg"
echo "Config saved to $cfg"
fi
}
parse_flags() {
NOSERVERINO=0
ADD_MODE=0
config_file=""
containers=""
users=""
while [[ $# -gt 0 ]]; do
case $1 in
--noserverino) NOSERVERINO=1; shift ;;
--add) ADD_MODE=1; shift ;;
--config) config_file=$2; shift 2 ;;
--containers) containers=$2; shift 2 ;;
--users) users=$2; shift 2 ;;
--help) show_help ;;
*) error_exit "Unknown option: $1" ;;
esac
done
}
parse_lists() {
IFS=, read -ra CTIDS <<<"$containers"
IFS=, read -ra USERS <<<"$users"
(( ${#CTIDS[@]} == ${#USERS[@]} )) \
|| error_exit "Count of containers != count of users"
}
# ──────────────────────────────────────────────────────────────────────────────
# Main
# ──────────────────────────────────────────────────────────────────────────────
validate_prerequisites
parse_flags "$@"
if [[ -n $config_file ]]; then
load_config
else
prompt_share_settings
[[ -n $containers && -n $users ]] \
|| prompt_containers_and_users
prompt_generate_config
fi
parse_lists
# Primary container for UID/GID mapping
primary_id="${CTIDS[0]}"
primary_user="${USERS[0]}"
pct status "$primary_id" &>/dev/null || error_exit "LXC $primary_id not found"
container_uid=$(pct exec "$primary_id" -- id -u "$primary_user" 2>/dev/null) \
|| error_exit "User $primary_user not in $primary_id"
container_gid=$(pct exec "$primary_id" -- id -g "$primary_user")
idmap_offset=$(pct config "$primary_id" | awk '/^lxc.idmap: u 0 /{print $4; exit}')
idmap_offset=${idmap_offset:-100000}
host_uid=$(( idmap_offset + container_uid ))
host_gid=$(( idmap_offset + container_gid ))
mnt_root="/mnt/lxc_shares/${folder_name}"
if (( ADD_MODE == 0 )); then
ensure_mount() {
mkdir -p "$mnt_root"
opts="_netdev,x-systemd.automount,noatime,nobrl"
opts+=",uid=${host_uid},gid=${host_gid},dir_mode=0770,file_mode=0770"
opts+=",username=${smb_username},password=${smb_password},iocharset=utf8"
(( NOSERVERINO )) && opts+=",noserverino"
entry="//${cifs_host}/${share_name} ${mnt_root} cifs ${opts} 0 0"
grep -q "^//${cifs_host}/${share_name} ${mnt_root} " /etc/fstab \
&& sed -i "\|^//${cifs_host}/${share_name} ${mnt_root} .*|d" /etc/fstab
echo "$entry" >>/etc/fstab
systemctl daemon-reload
base=$(systemd-escape --path "$mnt_root")
systemctl stop "${base}.automount" "${base}.mount" >/dev/null 2>&1 || true
mount "$mnt_root"
}
if grep -q "^//${cifs_host}/${share_name} ${mnt_root} " /etc/fstab; then
read -rp "Host mount exists; skip host-side work? [Y/n]: " yn
[[ $yn =~ ^[Nn]$ ]] && ensure_mount
else
ensure_mount
fi
fi
for i in "${!CTIDS[@]}"; do
id=${CTIDS[i]}
echo "Stopping LXC $id…"
pct stop "$id"
while [[ $(pct status "$id") != "status: stopped" ]]; do sleep 1; done
echo "Binding into $id → /mnt/$folder_name"
pct set "$id" --mp0 "${mnt_root},mp=/mnt/${folder_name},backup=0"
echo "Starting LXC $id…"
pct start "$id"
done
echo
echo "Verification:"
for id in "${CTIDS[@]}"; do
if pct exec "$id" -- test -d "/mnt/${folder_name}"; then
echo " ↳ $id: OK"
else
echo " ↳ $id: FAILED"
fi
done
echo
echo "✅ All done."
@NorkzYT
Copy link
Author

NorkzYT commented Jul 1, 2025

@rfResearch @neil-bh

Thank you for all the information. I will revise the script with your recommendations this weekend.

@NorkzYT
Copy link
Author

NorkzYT commented Jul 6, 2025

All Gist files have been updated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment