Virtiofs ist eine moderne und performante Methode, um Verzeichnisse direkt vom Proxmox-Host in virtuelle Maschinen einzubinden. Im Gegensatz zu klassischen Netzwerkdateisystemen wie NFS oder SSHFS erfolgt der Zugriff lokal über den Hypervisor, ohne Netzwerk-Stack und ohne zusätzliche Dienste. Dadurch eignet sich virtiofs besonders für High-Performance-Dateifreigaben zwischen Host und VM, in denen hoher Datendurchsatz, geringe Latenz und eine klare Rechteverwaltung erforderlich sind, etwa bei Videoaufzeichnungen, Datenbanken oder Medienarchiven.
1. Grundprinzip von virtiofs
Virtiofs arbeitet strikt POSIX-konform:
- Benutzer- und Gruppen-IDs (UID/GID) werden numerisch 1:1 vom Host in die VM durchgereicht
- Es findet kein User- oder ID-Mapping statt
- Dateirechte und Benutzer werden ausschließlich auf dem Host definiert und von der VM übernommen
- Mount-Optionen wie
uid=,gid=oderallow_otherwerden ignoriert
Das bedeutet: Stimmen UID/GID zwischen Host und VM nicht exakt überein, ist Schreiben im Gast nicht möglich.
2. Host vorbereiten
2.1 Host-Verzeichnis
Für die Nutzung von Virtio-FS ist eine eindeutige Trennung zwischen dem Verzeichnis auf dem Proxmox-Host und dem Mountpunkt innerhalb der virtuellen Maschine erforderlich. Das Verzeichnis befindet sich physisch auf dem Host, entweder auf einem lokalen Datenträger oder auf einem eingebundenen Storage, und dient als Quelle der Virtio-FS-Freigabe. Sämtliche Zugriffsrechte, Eigentümer und Gruppen werden ausschließlich an diesem Ort definiert und unverändert an die virtuelle Maschine weitergereicht.
|
1 |
/mnt/proxmox-host-dir |
2.2 Benutzer und Gruppen
Der Benutzer, der innerhalb der virtuellen Maschine Schreibzugriff auf das über virtiofs eingebundene Verzeichnis erhalten soll, muss auf dem Proxmox-Host mit identischer Benutzerkennung (UID) und Gruppenzugehörigkeit (GID) vorhanden sein. virtiofs führt keine Übersetzung oder Abbildung von Benutzer- und Gruppenkennungen durch, sondern reicht diese unverändert vom Gast an den Host weiter. Abweichende UIDs oder GIDs führen dazu, dass Zugriffe aus der VM auf Host-Ebene als fremder oder nicht existenter Benutzer interpretiert werden, was in der Praxis zu fehlenden Schreibrechten oder unerwarteten Zugriffsverweigerungen führt.
Aus diesem Grund ist sicherzustellen, dass die relevanten Benutzer und Gruppen auf Host und virtueller Maschine identische numerische IDs besitzen und dass das freigegebene Host-Verzeichnis die entsprechenden Zugriffsrechte entsprechend dieser IDs aufweist.
- Vorhandene Benutzer und Gruppen können überprüft werden:
|
1 2 |
getent passwd vuser getent group vuser |
- Falls der Benutzer noch nicht existiert, kann er mit den entsprechenden IDs angelegt werden (ohne Login Shell und Home Verzeichnis):
|
1 2 |
useradd -u 200 -g 200 -s /usr/sbin/nologin -M vuser groupadd -g 200 vuser |
- Falls der Benutzer bereits existiert, können UID und GID bei Bedarf angepasst werden:
|
1 2 3 4 5 6 |
usermod -u 200 vuser # UID ändern groupmod -g 200 vuser # GID ändern chown -R vuser:vuser /home/vuser # Besitzrechte aktualisieren # Berechtigungen korrigieren. Alte UID und GID (33) an neue IDs (301) systemweit anpassen. sudo find / \( -user 33 -o -group 33 \) -not -path "/proc/*" -not -path "/sys/*" -not -path "/dev/*" -exec chown -h 301:301 {} + 2>/dev/null |
- Die UID/GID müssen mit dem Host übereinstimmen, damit Virtio-FS die Zugriffsrechte korrekt durchreicht.
- Das Home-Verzeichnis und die Shell können nach Bedarf angepasst werden; für reine Virtio-FS-Nutzung ist ein Login oder Home Verzeichnis nicht erforderlich.
Script zum Anlegen eines Systemusers:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
#!/usr/bin/env bash # # Creates a lightweight system user + matching group # - No home directory (-M) # - Shell: /usr/sbin/nologin (no login possible) # - UID and GID are identical # - Username: vuser + Unix timestamp (seconds) # - UID range: 300–999 (typical system user range) # - Scriptname: add-system-user.sh # - Suppresses useradd warning about UID_MIN/MAX # set -euo pipefail # ==================== CONFIGURATION ==================== MIN_ID=300 MAX_ID=999 DEFAULT_SHELL="/usr/sbin/nologin" USER_PREFIX="vuser" COMMENT="System service user" # ========================================================= # ──────────────────────────────────────────────── # Check if running as root # ──────────────────────────────────────────────── if [ "$EUID" -ne 0 ]; then echo "ERROR: This script must be run as root!" echo "" echo "Please run the script with sudo:" echo " sudo bash $0" echo "" exit 1 fi # ──────────────────────────────────────────────── # Functions # ──────────────────────────────────────────────── find_next_free_id() { local start_id=$1 local id=$start_id while [ $id -le $MAX_ID ]; do if ! getent passwd "$id" >/dev/null 2>&1 && \ ! getent group "$id" >/dev/null 2>&1; then echo "$id" return 0 fi ((id++)) done echo "Error: No free UID/GID found between $MIN_ID and $MAX_ID!" >&2 exit 1 } generate_timestamp_suffix() { date +%s } # ──────────────────────────────────────────────── # Main program # ──────────────────────────────────────────────── echo "→ Searching for next free UID/GID starting from ${MIN_ID} ..." FREE_ID=$(find_next_free_id "$MIN_ID") TIMESTAMP_SUFFIX=$(generate_timestamp_suffix) USERNAME="${USER_PREFIX}${TIMESTAMP_SUFFIX}" # ──────────────────────────────────────────────── # Confirmation & edit loop # ──────────────────────────────────────────────── while true; do echo "" echo "Suggested values:" echo " Username : ${USERNAME}" echo " UID / GID : ${FREE_ID}" echo " Group : ${USERNAME}" echo " Shell : ${DEFAULT_SHELL}" echo " Comment : ${COMMENT}" echo "" read -p "Proceed with these values? [y/n/c] " -n 1 -r choice echo case "$choice" in [Yy]) echo "→ Creating user and group ..." break ;; [Nn]) echo "" echo "Edit values (press Enter to keep current value):" echo "" read -e -i "${USERNAME}" -p "Username [${USERNAME}]: " input [ -n "$input" ] && USERNAME="$input" read -e -i "${FREE_ID}" -p "UID / GID [${FREE_ID}]: " input [ -n "$input" ] && FREE_ID="$input" read -e -i "${DEFAULT_SHELL}" -p "Shell [${DEFAULT_SHELL}]: " input [ -n "$input" ] && DEFAULT_SHELL="$input" read -e -i "${COMMENT}" -p "Comment [${COMMENT}]: " input [ -n "$input" ] && COMMENT="$input" continue ;; [Cc]) echo "→ Aborted." exit 0 ;; *) echo "Invalid input. Please enter y, n or c." continue ;; esac done # ──────────────────────────────────────────────── # Creation # ──────────────────────────────────────────────── echo "" echo "Creating group ${USERNAME} (GID ${FREE_ID}) ..." if ! groupadd -g "${FREE_ID}" "${USERNAME}"; then echo "Error creating group ${USERNAME} (GID ${FREE_ID})" >&2 exit 1 fi echo "Creating user ${USERNAME} (UID ${FREE_ID}) ..." # Suppress useradd warning about UID range, but detect real errors if ! useradd \ -u "${FREE_ID}" \ -g "${FREE_ID}" \ -s "${DEFAULT_SHELL}" \ -M \ -c "${COMMENT}" \ "${USERNAME}" 2>/dev/null; then echo "" echo "┌──────────────────────────────────────────────────────────────┐" echo "│ ERROR CREATING THE USER │" echo "└──────────────────────────────────────────────────────────────┘" echo "Username: ${USERNAME}" echo "UID/GID: ${FREE_ID}" echo "" echo "Possible causes:" echo " • Username already exists" echo " • UID or GID already in use" echo " • Invalid characters in username" echo "" echo "Try running the command manually for more details:" echo " useradd -u ${FREE_ID} -g ${FREE_ID} -s ${DEFAULT_SHELL} -M -c \"${COMMENT}\" ${USERNAME}" echo "" # Cleanup groupdel "${USERNAME}" 2>/dev/null || true exit 1 fi echo "" echo "========================================" echo " Successfully created! " echo "========================================" echo "Username : ${USERNAME}" echo "Group : ${USERNAME}" echo "UID / GID : ${FREE_ID}" echo "Shell : ${DEFAULT_SHELL}" echo "Comment : ${COMMENT}" echo "========================================" echo "" exit 0 |
Script zum ändern von User und Gruppen IDs und systemweite korrektor der Dateirechte:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
#!/usr/bin/env bash # # - Interactive user selection (UID < 1000) # - UID and GID are set to the same value # - Automatically updates all matching files # # WARNING: # Can break services – backup /etc/passwd and /etc/group first! # Restart affected services after execution. # # Script name: change-uid-gid.sh # set -euo pipefail # ------------------------------------------------------- # 1. Root check # ------------------------------------------------------- if [ "$EUID" -ne 0 ]; then echo "ERROR: This script must be run as root!" echo " sudo bash $0" exit 1 fi echo "" echo "=== UID / GID Change Script ===" echo "WARNING: This operation can break services and applications!" echo " Backup recommended (e.g. cp /etc/passwd /etc/passwd.bak)" echo "" # ------------------------------------------------------- # 2. Select user – all with UID < 1000 # ------------------------------------------------------- echo "Available users (UID < 1000):" echo "------------------------------------------------" PS3="Select a user (enter number): " # List all users with UID < 1000 (excluding some special pseudo-accounts) mapfile -t users < <(awk -F: '$3 < 1000 && $3 >= 0 && $7 !~ /(sync|halt|shutdown)$/ {print $1}' /etc/passwd | sort -u) if [ ${#users[@]} -eq 0 ]; then echo "No users with UID < 1000 found." exit 1 fi select username in "${users[@]}"; do if [[ -n "$username" ]]; then break fi echo "Invalid selection, please try again." done # Get current UID / GID old_uid=$(id -u "$username" 2>/dev/null || echo "not found") old_gid=$(id -g "$username" 2>/dev/null || echo "not found") echo "" echo "Selected user: $username" echo "Current UID: $old_uid" echo "Current GID: $old_gid" echo "" # ------------------------------------------------------- # 3. Ask for new UID # ------------------------------------------------------- while true; do read -p "Enter new UID (GID will be set to the same value): " new_uid # Check if it's a number if ! [[ "$new_uid" =~ ^[0-9]+$ ]]; then echo "→ Only numbers allowed!" continue fi # Check if already in use (except for the current user itself) if getent passwd "$new_uid" >/dev/null 2>&1 && [ "$new_uid" != "$old_uid" ]; then echo "→ UID $new_uid is already in use!" continue fi if getent group "$new_uid" >/dev/null 2>&1 && [ "$new_uid" != "$old_gid" ]; then echo "→ GID $new_uid is already in use!" continue fi # Confirmation read -p "Really change UID and GID of $username to $new_uid? (y/N): " -n 1 confirm echo if [[ "$confirm" =~ ^[Yy]$ ]]; then break else echo "Aborted." exit 0 fi done new_gid="$new_uid" # ------------------------------------------------------- # 4. Perform changes # ------------------------------------------------------- echo "" echo "Performing changes:" echo " User: $username" echo " Old UID: $old_uid → New UID: $new_uid" echo " Old GID: $old_gid → New GID: $new_gid" echo "" # Change UID echo "→ usermod -u $new_uid $username" usermod -u "$new_uid" "$username" || { echo "usermod failed"; exit 1; } # Change GID echo "→ groupmod -g $new_gid $username" groupmod -g "$new_gid" "$username" || { echo "groupmod failed"; exit 1; } # Update user's primary group (if needed) usermod -g "$new_gid" "$username" 2>/dev/null || true # ------------------------------------------------------- # 5. Correct file ownership system-wide # ------------------------------------------------------- echo "" echo "→ Searching and correcting files owned by old UID/GID ($old_uid / $old_gid) ..." echo " (this may take a few minutes depending on your system)" find / \ \( -user "$old_uid" -o -group "$old_gid" \) \ -not -path "/proc/*" \ -not -path "/sys/*" \ -not -path "/dev/*" \ -exec chown -h "$new_uid:$new_gid" {} + 2>/dev/null echo "" echo "Done!" echo "" echo "Verification:" echo " id $username" echo "" echo "IMPORTANT:" echo " • Restart any services that use this user (apache2, nginx, php-fpm, docker, etc.)" echo " • Example: systemctl restart apache2" echo " • Check logs if services fail to start" echo "" exit 0 |
2.3 Benutzer und Rechte setzen
|
1 2 3 |
chown -R vuser:vuser /mnt/proxmox-host-dir chmod -R 775 /mnt/proxmox-host-dir chmod g+s /mnt/proxmox-host-dir |
- 775 erlaubt Schreiben für User und Gruppe
- g+s stellt sicher, dass neu angelegte Dateien automatisch die Gruppe behalten
Alle übergeordneten Verzeichnisse müssen mindestens das Execute-Bit (x) für die jeweilige Gruppe besitzen.
3 Einrichtung eines Virtio-FS-Geräts in Proxmox (Verzeichnis-Mapping)
Bevor in der VM ein virtiofs-Verzeichnis gemountet werden kann, muss in Proxmox zunächst das entsprechende Host-Verzeichnis als virtiofs-Gerät erstellt werden. Dazu in der Weboberfläche zu Rechenzentrum → Verzeichnis-Mapping navigieren, dort auf Erstellen / Hinzufügen klicken und folgende Felder ausfüllen und anschließend mit Erstellen bestätigen.
- Name / Tag: Eindeutiger Bezeichner, z. B.
proxmox-host-dir-name(wird später in der VM als Mount-Name verwendet) - Pfad: Host-Verzeichnis, z. B.
/mnt/proxmox-host-dir - Knoten: Zielknoten auswählen
- Kommentar: optional, z. B. „Freigabe für Ubuntu-VM“
- ACL / Sicherheitsmodell:
none
3.1 Zuweisung des Verzeichnis-Mappings an die VM
- Die gewünschte VM auswählen → Hardware → Hinzufügen → VirtIO-FS-Gerät.
- Im Feld Tag den zuvor definierten Namen des Verzeichnis-Mappings eintragen (
proxmox-host-dir-name). - Optional: Nur Lesen aktivieren, falls Schreibzugriff nicht erforderlich ist.
- Änderungen speichern.
Dadurch wird das Verzeichnis des Proxmox-Hosts (/mnt/proxmox-host-dir) der virtuellen Maschine unter dem Bezeichner proxmox-host-dir-name als Mountquelle bereitgestellt.
4. VM vorbereiten
4.1 Benutzer anlegen / anpassen
In der virtuellen Maschine muss derselbe Benutzer mit identischer UID und GID wie auf dem Proxmox-Host existieren.
- Vorhandene Benutzer und Gruppen können überprüft werden:
|
1 2 |
getent passwd vuser getent group vuser |
- Falls der Benutzer noch nicht existiert, kann er mit den entsprechenden IDs angelegt werden:
|
1 2 |
groupadd -g 200 vuser useradd -u 200 -g 200 -d /home/vuser -s /bin/bash vuser |
- Falls der Benutzer bereits existiert, können UID und GID bei Bedarf angepasst werden:
|
1 2 3 |
usermod -u 200 vuser # UID ändern groupmod -g 200 vuser # GID ändern chown -R vuser:vuser /home/vuser # Besitzrechte aktualisieren |
Hinweise:
- Die UID/GID müssen mit dem Host übereinstimmen, damit Virtio-FS die Zugriffsrechte korrekt durchreicht.
- Das Home-Verzeichnis und die Shell können nach Bedarf angepasst werden; für reine Virtio-FS-Nutzung ist ein Login nicht zwingend erforderlich.
4.2 Mountpunkt in der VM erstellen und über fstab beim starten einbinden
Nachdem der Benutzer mit den entsprechenden UID/GID in der VM eingerichtet wurde, wird ein Verzeichnis angelegt, das als Ziel für die Virtio-FS-Freigabe vom Host dient. Mit mkdir -p wird der Mountpunkt einschließlich aller übergeordneten Verzeichnisse erstellt, falls diese noch nicht existieren
|
1 |
mkdir -p /mnt/host-dir |
Vor dem dauerhaften Eintrag kann der Mount manuell getestet werden, um sicherzustellen, dass die Freigabe funktioniert.
|
1 |
mount -t virtiofs proxmox-host-dir-name /mnt/host-dir |
mount -t virtiofs verbindet das Host-Verzeichnis (über den Tag proxmox-host-dir-name) mit dem Mountpunkt in der VM. Für einen automatischen Mount bei jedem Bootvorgang wird ein Eintrag in /etc/fstab erstellt.
|
1 |
proxmox-host-dir-name /mnt/host-dir virtiofs rw,nofail,x-systemd.automount 0 0 |
rw– Verzeichnis kann gelesen und beschrieben werdennofail– System startet weiter, auch wenn das Mounten fehlschlägtx-systemd.automount– Mount erfolgt erst beim ersten Zugriff, beschleunigt den Bootvorgang
5. Funktionstest
|
1 2 3 |
mount -a sudo -u vuser touch /mnt/host-dir/testfile ls -l /mnt/host-dir |
Erwartetes Ergebnis:
|
1 |
-rw-r--r-- 1 vuser vuser testfile |
6. Typische Fehlerquellen
- UID/GID zwischen Host und VM stimmen nicht überein
- Ownership wurde nur im Gast, nicht auf dem Host gesetzt
- Übergeordnete Verzeichnisse blockieren den Zugriff (
xfehlt) - Mount-Optionen wie
uid=odergid=werden verwendet (wirkungslos)
Debug-Hilfe:
|
1 2 |
sudo namei -l /mnt/host-dir sudo journalctl -b | grep virtiofs |
7. Zusammenfassung
Virtiofs ist eine performante und saubere Lösung, um Host-Verzeichnisse direkt in Proxmox-VMs zu nutzen. Entscheidend ist ein konsistentes Benutzer- und Rechtekonzept:
- UID/GID müssen identisch sein
- Rechte werden ausschließlich auf dem Host gepflegt
- Der fstab-Eintrag entscheidet über Boot-Verhalten und Stabilität
Bei korrekter Einrichtung verhält sich virtiofs wie ein lokales Dateisystem – schnell, zuverlässig und ohne Netzwerkabhängigkeiten.
by Speefak