Samba + NFS File Server on Debian/Ubuntu (LXC or VM)
Stand up a central file server that shares the same storage over Samba (for Windows, macOS, and phones) and NFS (for other Linux hosts). Single user, consistent UID 1000 ownership.
bash#!/usr/bin/env bash
# Samba + NFS file server setup — Debian/Ubuntu
# Run as your regular sudo user inside the container or VM.
set -euo pipefail
SHARE_ROOT="/tank" # bind-mount your storage here; adjust to taste
SHARE_USER="media" # Samba/NFS owner — change to match your setup
SHARE_UID=1000 # all services should use this UID too
LAN_SUBNET="192.168.1.0/24" # restrict NFS exports to this subnet
# ── 1. Install Samba and NFS ──────────────────────────────────
sudo apt-get update
sudo apt-get install -y samba nfs-kernel-server
# ── 2. Create share directories ──────────────────────────────
sudo mkdir -p "${SHARE_ROOT}/media" "${SHARE_ROOT}/photos"
sudo chown -R ${SHARE_UID}:${SHARE_UID} "${SHARE_ROOT}"
# ── 3. Create a Linux user for Samba (no login shell) ────────
if ! id "${SHARE_USER}" &>/dev/null; then
sudo useradd -M -u ${SHARE_UID} -s /usr/sbin/nologin "${SHARE_USER}"
fi
# Set Samba password interactively (separate from Linux password)
echo "--- Set a Samba password for '${SHARE_USER}' ---"
sudo smbpasswd -a "${SHARE_USER}"
# ── 4. Configure Samba ──────────────────────────────────────
sudo tee -a /etc/samba/smb.conf > /dev/null <<EOF
[media]
path = ${SHARE_ROOT}/media
browseable = yes
read only = no
valid users = ${SHARE_USER}
create mask = 0664
directory mask = 0775
force user = ${SHARE_USER}
[photos]
path = ${SHARE_ROOT}/photos
browseable = yes
read only = no
valid users = ${SHARE_USER}
create mask = 0664
directory mask = 0775
force user = ${SHARE_USER}
EOF
sudo systemctl enable --now smbd nmbd
sudo systemctl restart smbd
# ── 5. Configure NFS ─────────────────────────────────────────
# all_squash + anonuid maps every client to UID 1000 — consistent ownership
sudo tee -a /etc/exports > /dev/null <<EOF
${SHARE_ROOT}/media ${LAN_SUBNET}(rw,sync,no_subtree_check,all_squash,anonuid=${SHARE_UID},anongid=${SHARE_UID})
${SHARE_ROOT}/photos ${LAN_SUBNET}(rw,sync,no_subtree_check,all_squash,anonuid=${SHARE_UID},anongid=${SHARE_UID})
EOF
sudo exportfs -ra
sudo systemctl enable --now nfs-kernel-server
echo ""
echo "Samba shares:"
echo " Windows: \\\\$(hostname -I | awk '{print $1}')\\media"
echo " macOS: smb://$(hostname -I | awk '{print $1}')/media"
echo ""
echo "NFS exports:"
sudo exportfs -v
What this does
Installs Samba and the NFS kernel server on a Debian/Ubuntu host, creates two shares (media and photos), and exposes them over both protocols from the same storage root. Samba serves Windows, macOS, phones, and TVs; NFS serves other Linux containers and VMs on the same LAN.
Consistent UID 1000 ownership (force user in Samba, all_squash + anonuid in NFS) means files written by either protocol look the same to every service that reads them — which is the prerequisite for Plex/Jellyfin hardlink imports and Immich’s upload directory.
Why this approach
A dedicated file server container is how you make one storage volume available to everything else in the lab. Your NAS has the disks; your Plex container, Immich stack, and Radarr/Sonarr containers need to read and write to them. Without a shared protocol, you’d need to give every container access to the physical storage directly, which means complicated pass-through configuration and tight coupling between services and storage. A file server container decouples the storage from the consumers.
Samba and NFS together cover every use case. Samba (the SMB protocol) is what Windows, macOS, iOS, Android, smart TVs, and most NAS management UIs speak natively — it’s the right choice for anything with a graphical file browser. NFS is the right choice for Linux-to-Linux communication, especially for other LXC containers and VMs on the same Proxmox cluster: it’s lower overhead, more performant for sequential reads, and integrates better with Linux file permissions. Running both from the same container over the same storage root means you configure the storage once and serve it to everything.
The UID 1000 consistency that this playbook enforces (force user = data in Samba, anonuid=1000 in NFS) is the detail that makes hardlink imports work correctly across the stack. When qBittorrent writes a file, Radarr moves it, and Plex reads it, they all need to agree on who owns the file. If the UIDs don’t match, you get permission errors or silent read failures that are frustrating to diagnose. Mapping everything to UID 1000 in both protocols creates a single consistent owner for all files regardless of which service last touched them.
Prerequisites
- Debian 11+ or Ubuntu 22.04+ (LXC container or VM)
- A storage volume mounted at your chosen path (e.g.
/tank) — either a Proxmox bind mount from a ZFS dataset or a dedicated disk sudoaccess inside the container
Proxmox bind-mount setup (before running this script)
Bind-mount your host ZFS dataset or directory into the LXC:
# On the Proxmox host — add a bind-mount point to the container config
# Replace 101 with your LXC ID and the path with your actual dataset
pct set 101 --mp0 /rpool/tank,mp=/tank
For unprivileged LXC containers, UID mapping means the host path should be owned by 100000 (host) which maps to 0 inside the container, and 101000 maps to 1000 inside. Adjust with:
# On the Proxmox host:
chown -R 101000:101000 /rpool/tank
Mount an NFS share on another Linux container
# On the client (e.g. your Plex or Jellyfin container):
sudo apt-get install -y nfs-common
sudo mkdir -p /mnt/media
sudo mount -t nfs SERVER-IP:/tank/media /mnt/media
# Make permanent — add to /etc/fstab:
# SERVER-IP:/tank/media /mnt/media nfs defaults,_netdev,nofail 0 0
Connect from Windows
Open File Explorer and enter \\SERVER-IP\media in the address bar. Sign in with the Samba username and password set during the script.
Connect from macOS
Finder → Go → Connect to Server → smb://SERVER-IP/media → connect with the Samba credentials.
Connect from iOS / Android
Any SMB-capable app works (iOS Files app, VLC, Infuse). Use smb://SERVER-IP/media and the Samba credentials. Restrict access to LAN or Tailscale — never forward SMB ports to the internet.
OpenSUSE Tumbleweed notes
# Install on openSUSE instead:
sudo zypper install -y samba samba-client nfs-kernel-server
# firewall-cmd rules if firewalld is active:
sudo firewall-cmd --permanent --add-service=samba
sudo firewall-cmd --permanent --add-service=nfs
sudo firewall-cmd --reload
Key variables to adjust
| Variable | Default | What to change |
|---|---|---|
SHARE_ROOT |
/tank |
Root of your storage — where media/photos subdirs live |
SHARE_USER |
media |
The Samba user and file owner |
SHARE_UID |
1000 |
Must match the UID used by Jellyfin, Immich, *arr, etc. |
LAN_SUBNET |
192.168.1.0/24 |
Your actual LAN subnet (NFS access restriction) |
Security notes
- Samba should only be accessible on your LAN. If you use Proxmox’s built-in firewall, allow TCP 445 from your LAN subnet only.
- NFS v4 (the default on modern distros) supports Kerberos auth; for a homelab, subnet restriction is adequate.
- Never forward port 445 to the internet — SMB is a top ransomware target.