Skip to content

Instantly share code, notes, and snippets.

@NorkzYT
Last active July 8, 2025 02:58
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."
@rucknapucknavitz
Copy link

This is great - thank you.
I do think you need to revert to the original instructions in the first part of step 1 from the revision on March 7.

10000 in the LXC will map correctly to 110000 in the PVE
(mapping 110000 in the LXC will incorrectly map on the PVE)

When I made this change it’s working great on my tteck Jellyfin container.

echo "Creating group 'lxc_shares' with GID=10000 in LXC..."
pct exec $lxc_id -- groupadd -g 10000 lxc_shares```

@NorkzYT
Copy link
Author

NorkzYT commented Jun 9, 2024

@rucknapucknavitz

No problem, I have revised the script with the change you requested. Thank you.

I will be changing the following line as well to use 10000 instead of 110000:
https://github.com/NorkzYT/Wolflith/blob/b10bc7b4680bf240a1055ea7d7f36435107e6c15/Ansible/playbooks/provision-proxmox-lxc.yml#L145

@campmeharder
Copy link

campmeharder commented Jun 13, 2024

Hi there for some reason not working with me, new to proxmox that is why Im using this, thanks btw, but even though it says complete it doesnt show. I tried it with the *arr that are in a lxc. Also type on line 34 read -sp "Enter SMB password: " smb_password && echo

the message im getting is mount error(16): Device or resource busy. Im trying to connect it to my synology rackstation so not sure what could be giving that error.

@Puntoboy
Copy link

This works great, but I'm having an issue with sub folder permissions. Any existing folder in the share I cannot write to so I see errors in the web GUIs for the LXCs. If I create a folder in the root from the LXC, then it's writeable, so it's existing folders that are the issue.

@Puntoboy
Copy link

Puntoboy commented Jun 22, 2024

The permissions look the same, test3 is a folder I created within the LXC, Movies is one I created inside the NAS server before connecting the LXC.

I can connect to test3 fine, but I cannot write to Movies or any of the other subfolders (root folder is fine).

image

@rucknapucknavitz
Copy link

This isn’t a script issue so not really a topic for this thread, it’s a permissions issue.
Checkout some chmod tutorials online, like this one: https://www.tomshardware.com/how-to/change-file-directory-permissions-linux

@Puntoboy
Copy link

This isn’t a script issue so not really a topic for this thread, it’s a permissions issue. Checkout some chmod tutorials online, like this one: https://www.tomshardware.com/how-to/change-file-directory-permissions-linux

this script is supposed to fix the permissions issue with the LXCs. Hence why I asked.

@rucknapucknavitz
Copy link

Sounds like it could be a share host permission issue, unless you’re sharing directly from the proxmox host?

@Puntoboy
Copy link

I'm sharing from a Cockpit LXC on the same host.

Problem is now, since rebooting, the host won't even mount that share now.

image

@LarsNorgaard
Copy link

There's a bug, if you have snapshots saved of the LXC. The Mp0: isn't getting added to the "running" version, but instead to the saved snapshot.

@souljedi
Copy link

souljedi commented Nov 19, 2024

Thanks for the script. Working great! On my version of proxmox I had to make one tiny modification and change from:
read -sp "Enter SMB password: " smb_password && echo to read -p "Enter SMB password: " smb_password && echo

@anubis-genix
Copy link

anubis-genix commented Dec 26, 2024

Great and useful script. Unfortunately I'm getting the error: Stale file handle when trying to open a txt file on my share.
Any ideas?

Edit:
Found the fix for my problem.

pvesm set --options noserverino

@rikmueller
Copy link

Thank you so much! All my attempts to achieve this on my own have been a waste of time with no success...

@hehh2001
Copy link

hehh2001 commented May 7, 2025

非常感谢,很好用

@b1ackm0x
Copy link

b1ackm0x commented Jun 12, 2025

Manual on Read

Screenshot from 2025-06-12 13-49-07

Fyi, I renamed your file for my convienience, lol.

Line 34: read -sp is erroring out,it was not even prompted, do you have to separate the arguments?

mountcifs.sh: 34: read: Illegal option -s

Try:

read -s -p

Also LXD ID? is this the ID # or the the name of the container?

I have been having problems mounting my truenas share through proxmox to ANYTHING(Plex, Torrent, Youtube Downloader) . This script would be a great help. Thank you!
Hope it gets worked out, I wasnt able to use it.

Some of my input and the errors returned, hope it helps.

Enter SMB username: kodi4pi
mountcifs.sh: 34: read: Illegal option -s
Enter the LXC ID: 501
Enter the username within the LXC that needs access to the share (e.g., jellyfin, plex): admin
Creating group 'lxc_shares' with GID=10000 in LXC...
mountcifs.sh: 40: pct: not found
Adding user admin to group 'lxc_shares'...
mountcifs.sh: 43: pct: not found

@NorkzYT
Copy link
Author

NorkzYT commented Jun 12, 2025

@rucknapucknavitz @campmeharder @Puntoboy @souljedi @anubis-genix @rikmueller @hehh2001 @b1ackm0x

It has been a long while since I have been able to work on this. I have updated the file and have added a README.md file for context.

I hope this fixes any issues anyone may have and makes the setup process more seamless than before.

Have a wonderful rest of your day everyone!

@b1ackm0x
Copy link

b1ackm0x commented Jun 12, 2025 via email

@deku-m
Copy link

deku-m commented Jun 26, 2025

Thanks for the script.

But isnt it better to use
lxc.mount.entry: /mnt/${folder_name} /mnt/${folder_name}none bind 0 0
Instead of
mp0: /mnt/${folder_name},mp=/mnt/${folder_name}

As you disable the snapshotting of the LXC when using MP0 which could sometimes be convenient? I have read that somewhere.

@neil-bh
Copy link

neil-bh commented Jun 26, 2025

Thank you for providing this script/wizard to apply a solution to such a common issue.

I have this working but noticed that my docker containers were having permission issues accessing their data. The docker containers I use typically use UID=1000 and GID=1000 but I've had to adjust these to UID=0 and GID=10000. (which matches root:lxc_shares)

From my unprivileged lxc, I tried updating the permissions on the mounted shares from root:lxc_shares to 1000:10000 (myuser1:lxc_shares), using: chown -Rf 1000:10000 /mnt/nas and although there were no errors, the UID didn't change. Perhaps this is not possible because Proxmox has mounted these from the host as root? I'm not sure exactly how to change that from root (if at all possible?)

Is this acceptable practice using UID=0 (root) or is there a way to change it so that I can use UID=1000 (which is is my first created user)?

@NorkzYT
Copy link
Author

NorkzYT commented Jun 27, 2025

@deku-m

That is true. Nice catch. I have updated the script. Thank you.

Edit:
I am reverting it since I do not want to allow backups or snapshots of the mounted CIFS Share. Especially on a multi-terabyte NAS.

@NorkzYT
Copy link
Author

NorkzYT commented Jun 27, 2025

@neil-bh

chown inside the container fails since the ownership is fixed at mount time. I will update the script today to fix this issue as well.

@NorkzYT
Copy link
Author

NorkzYT commented Jun 27, 2025

Script has been updated.

@neil-bh
Copy link

neil-bh commented Jun 27, 2025

Thanks @NorkzYT - I can confirm I can now configure my docker containers to write to a folder that resides on the share using the UID=10000 and GID=10000 (which I believe is the equivalent of UID 1000 and GID 1000 (myuser1:myuser1) (?)

@NorkzYT
Copy link
Author

NorkzYT commented Jun 28, 2025

@neil-bh

That is exactly what you should be seeing — UID/GID 10000 on the host corresponds to UID/GID 1000 inside your unprivileged LXC.

@rfResearch
Copy link

Hi @NorkzYT - Very useful script, thank you for sharing. I've added a new function to generate a config file for the user inputs and an additional function to take the config file as input. I've also added a fonction to list the LXC IDs and the LXC usernames in the user input section, and a completion check.
I've noticed that the LXC container must be started before the execution of the script in order to work.

#!/usr/bin/env bash
# proxmox-lxc-cifs-share.sh
# https://gist.github.com/NorkzYT/14449b247dae9ac81ba4664564669299
#
# Mount a CIFS/SMB share on a Proxmox VE host and bind-mount it into
# an unprivileged LXC container as any container user, by dynamically
# mapping UIDs/GIDs.
#
# ---------------------------------------------------------------
# Compatible with Proxmox VE 7-8.  Tested on Debian 12 host.
# ---------------------------------------------------------------

set -euo pipefail

#
#----------------------#
#----- Preconditions --#
#----------------------#
#

# Must be root
if [[ $EUID -ne 0 ]]; then
  echo "ERROR: Please run as root." >&2
  exit 1
fi

# Helper for error + exit
error_exit() {
  echo "ERROR: $1" >&2
  exit 1
}

# Function to display help
show_help() {
  echo "Usage: $0 [OPTIONS]"
  echo
  echo "Mount a CIFS/SMB share on a Proxmox VE host and bind-mount it into an unprivileged LXC container."
  echo
  echo "Options:"
  echo "  --config FILE      Specify a configuration file with parameters."
  echo "  --noserverino      Disable serverino option for CIFS mount."
  echo "  --help             Display this help message."
  echo
  echo "Configuration File Format:"
  echo "The configuration file should contain the following parameters:"
  echo "  folder_name=your_folder_name"
  echo "  cifs_host=your_cifs_host_ip_or_dns"
  echo "  share_name=your_share_name"
  echo "  smb_username=your_smb_username"
  echo "  smb_password=your_smb_password"
  echo "  lxc_id=your_lxc_numeric_id"
  echo "  lxc_username=your_container_username"
  echo
  echo "Example configuration file:"
  echo "folder_name=nas_rwx"
  echo "cifs_host=10.0.0.2"
  echo "share_name=media"
  echo "smb_username=your_username"
  echo "smb_password=your_password"
  echo "lxc_id=105"
  echo "lxc_username=ubuntu"
  exit 0
}

# Function to list LXC container IDs
list_lxc_ids() {
  echo "Available LXC container IDs:"
  pct list | awk 'NR>1 {print $1}'  # Ignore the header and print only the IDs
}

# Function to list usernames in the LXC container
list_usernames() {
  local lxc_id="$1"
  echo "Validating container ${lxc_id}..."
  pct status "$lxc_id" &>/dev/null \
    || error_exit "LXC ${lxc_id} does not exist."
  echo "Available usernames in LXC container ${lxc_id}:"
  pct exec "$lxc_id" -- getent passwd | awk -F: '{print $1}'  # Print only the usernames
}

# Ensure required commands exist
command -v bash         >/dev/null 2>&1 || error_exit "Please run with bash: bash $0"
command -v pct          >/dev/null 2>&1 || error_exit "'pct' command not found. Run this on the Proxmox host."
command -v mount.cifs   >/dev/null 2>&1 || error_exit "cifs-utils missing. Install: apt update && apt install cifs-utils"
command -v systemd-escape >/dev/null 2>&1 || error_exit "systemd-escape missing. Required for systemd unit management."

#
#----------------------#
#----- Parse Flags ----#
#----------------------#
#

NOSERVERINO=0
config_file=""

# Parse command line arguments
while [[ $# -gt 0 ]]; do
  case $1 in
    --noserverino)
      NOSERVERINO=1
      shift
      ;;
    --config)
      config_file="$2"
      shift 2
      ;;
    --help)
      show_help
      ;;
    *)
      echo "ERROR: Unknown option $1" >&2
      exit 1
      ;;
  esac
done


#
#----------------------#
#----- User Input -----#
#----------------------#
#
if [[ -n "$config_file" ]]; then
  # Read parameters from the configuration file
  if [[ ! -f "$config_file" ]]; then
    error_exit "Configuration file '$config_file' not found."
  fi
  source "$config_file"
else
  # Ask parameters to the user
  echo "=== Proxmox CIFS Share Mount Wizard ==="
  read -r -p "1) Folder name under /mnt/lxc_shares (e.g. nas_rwx): " folder_name
  read -r -p "2) CIFS host (IP/DNS, e.g. 10.0.0.2): "     cifs_host
  read -r -p "3) Share name (e.g. media): "             share_name
  read -r -p "4) SMB username: "                       smb_username
  read -s -p "5) SMB password: "                       smb_password && echo

  # List available LXC container IDs
  list_lxc_ids
  read -r -p "6) LXC numeric ID (e.g. 105): "           lxc_id

  # List available usernames in the selected LXC container
  list_usernames "$lxc_id" 
 read -r -p "7) Container username (e.g. ubuntu): "   lxc_username
  echo

  # Set default configuration file path
  default_config_file_path="./proxmox-lxc-cifs-share.conf"
  config_file_path="$default_config_file_path"

  # Ask if the user wants to generate a configuration file
  read -r -p "Do you want to generate a configuration file? (y/n): " generate_config

  if [[ "$generate_config" == "y" || "$generate_config" == "Y" ]]; then
    read -r -p "Enter the path for the configuration file [default: $default_config_file_path]: " user_input

    # Use default path if no input is provided
    if [[ -n "$user_input" ]]; then
      config_file_path="$user_input"
    fi

    # Create the configuration file and write parameters to it
    {
      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 "lxc_id=${lxc_id}"
      echo "lxc_username=${lxc_username}"
    } > "$config_file_path"

    echo "Configuration file created at: $config_file_path"
  fi
fi

#
#----------------------#
#----- Validation -----#
#----------------------#
#

echo "Validating container ${lxc_id}..."
pct status "$lxc_id" &>/dev/null \
  || error_exit "LXC ${lxc_id} does not exist."

echo "Retrieving UID/GID for '${lxc_username}' inside LXC ${lxc_id}..."
if ! container_uid=$(pct exec "$lxc_id" -- id -u "$lxc_username" 2>/dev/null); then
  error_exit "User '${lxc_username}' not found in container ${lxc_id}."
fi
container_gid=$(pct exec "$lxc_id" -- id -g "$lxc_username")
echo "  ↳ container UID=${container_uid}, GID=${container_gid}"

echo "Parsing idmap offset from container config..."
idmap_offset=$(pct config "$lxc_id" \
  | awk '/^lxc.idmap: u 0 /{print $4; exit}')
if [[ -z "$idmap_offset" ]]; then
  echo "  ↳ Warning: idmap offset not found; defaulting to 100000"
  idmap_offset=100000
fi
echo "  ↳ host idmap offset=${idmap_offset}"

host_uid=$(( idmap_offset + container_uid ))
host_gid=$(( idmap_offset + container_gid ))
echo "  ↳ will mount with host UID=${host_uid}, GID=${host_gid}"

#
#----------------------#
#--- Stop Container ----#
#----------------------#
#

echo "Stopping LXC ${lxc_id}..."
pct stop "$lxc_id"
while [[ "$(pct status "$lxc_id")" != "status: stopped" ]]; do
  sleep 1
done

#
#----------------------#
#--- Host-side Mount --#
#----------------------#
#

mnt_root="/mnt/lxc_shares/${folder_name}"
echo "Ensuring host mount point exists at ${mnt_root}..."
mkdir -p "$mnt_root"

echo "Building fstab entry..."
options="_netdev,x-systemd.automount,noatime,nobrl"
options+=",uid=${host_uid},gid=${host_gid},dir_mode=0770,file_mode=0770"
options+=",username=${smb_username},password=${smb_password}"
options+=",iocharset=utf8"  # Add option iocharset=utf8
(( NOSERVERINO )) && options+=",noserverino"

fstab_entry="//${cifs_host}/${share_name} ${mnt_root} cifs ${options} 0 0"

# Remove any stale entry
if grep -Eqs "^//${cifs_host}/${share_name}[[:space:]]+${mnt_root}[[:space:]]+cifs" /etc/fstab; then
  echo "  ↳ Removing old /etc/fstab entry..."
  sed -i "\|^//${cifs_host}/${share_name} ${mnt_root} .*|d" /etc/fstab
fi

echo "  ↳ Adding new /etc/fstab entry..."
echo "$fstab_entry" >> /etc/fstab

echo "Reloading systemd daemon..."
systemctl daemon-reload

unit_base=$(systemd-escape --path "$mnt_root")
echo "Stopping potential systemd units ${unit_base}.automount & ${unit_base}.mount..."
systemctl stop "${unit_base}.automount" "${unit_base}.mount" >/dev/null 2>&1 || true

if mountpoint -q "$mnt_root"; then
  echo "  ↳ Unmounting existing mount..."
  umount -l "$mnt_root"
fi

echo "Mounting //${cifs_host}/${share_name} → ${mnt_root}..."
mount "$mnt_root"

#
#----------------------#
#--- Container Bind ----#
#----------------------#
#

echo "Configuring bind-mount into LXC (no backups)..."
pct set "$lxc_id" \
  --mp0 "${mnt_root},mp=/mnt/${folder_name},backup=0"
echo "  ↳ Bind-mount set as mp0 → /mnt/${folder_name}"

echo "Starting LXC ${lxc_id}..."
pct start "$lxc_id"

#
#----------------------#
#--- Access Validation --#
#----------------------#
#

echo "Checking access to the mount from container ${lxc_id}..."
if pct exec "$lxc_id" -- test -d "/mnt/${folder_name}"; then
  echo "  ↳ Access to the mount confirmed."
  
  # Display access rights
  echo "Access rights of the share:"
  pct exec "$lxc_id" -- ls -ld "/mnt/${folder_name}"
else
  error_exit "ERROR: The mount is not accessible from container ${lxc_id}."
fi

#
#----------------------#
#----- Completion ------#
#----------------------#
#

echo -e "\n✅  Configuration complete!"
echo "Inspect inside the container with:"
echo "    ls -ld /mnt/${folder_name}"

@neil-bh
Copy link

neil-bh commented Jul 1, 2025

That suggestion from @rfResearch looks great! If @NorkzYT decides to merge it could I also make a request based on a common use case...

When running the script for the first time, all is great and I end up with CIFS share accessible in my unprivileged LXC container . But then I decide I would like the CIFS share available in another of my unprivileged LXC containers - so I'd run the script again. I am prompted to create the mount again on the host (although it already exists) - I can see from the script that it will remove the existing line in fstab if it already exists, so maybe that's good enough? Or perhaps confirm with the user that the share already exists: "would you like to skip the host mount?"... or... maybe pass in a parameter for executing in this use case to additional LXC containers, e.g. bash ./proxmox-lxc-cifs-share.sh add? Or we could simply even use the config file approach with all the values populated to simplify adding the share to more LXC containers?

Another idea here... Is it possible to provide more than 1 user when prompted at step 7 for the lxc username? (Update: I noticed in @rfResearch's version they are not using lxc_shares as a group, so it's using username:username which I believe is the typical way to do it, and therefore probably negates my idea of being able to add more than one user to the share - after all, I cant think of a situation where i'd need multiple different user accounts to have individual access the share)

@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