In diesem Beitrag zeige ich euch, wie man eine lokale NVMe-SSD per NVMe-over-TCP im Netzwerk bereitstellt. Dazu nutze ich ein kleines Linux Bash-Script, dass die komplette Konfiguration in nur wenigen Sekunden automatisch übernimmt.
Der Ablauf ist relativ simpel: Erstmal die NVMe Kernel-Module laden, ein NVMe-Subsystem anlegen, einen Namespace definieren und das Ganze auf einem TCP-Port freigeben. In der Regel nimmt man den Port 4420/TCP.

Ich empfehle jedem der aktuell mit iSCSI unterwegs das hier einfach mal auszuprobieren, denn meiner Erfahrung nach ist NVMe-over-TCP gnadenlos ca. 3x so schnell und das einfach nur so beim Wechsel des Protokolls.

Hier erkläre ich euch jetzt Schritt für Schritt, was mein Script genau macht und warum.

1. Laden der benötigten Kernel-Module

Zu Beginn stelle ich sicher, dass die Kernel-Module nvmet und nvmet_tcp geladen worden sind:

modprobe nvmet
modprobe nvmet_tcp

Diese Module ermöglichen es dem Kernel, als NVMe-Target zu arbeiten – also selbst Storage über NVMe-over-TCP anzubieten.

2. configfs mounten

Damit ich ein NVMe-Target dynamisch konfigurieren kann, nutze ich das configfs des Kernels.
Falls es noch nicht eingehängt ist, hole ich das nach:

mount -t configfs none /sys/kernel/config

3. Subsystem erstellen

Nun lege ich ein NVMe-Subsystem an. Dieses ist eine Art logische Einheit, die später einen oder mehrere Namespaces (also Speichergeräte) enthalten kann. Also ja, man kann natürlich mehrere anlegen und mehrere Blockdevices präsentieren.

SUBNQN="nqn.2025-11.de.mynvme:target1"
mkdir -p "/sys/kernel/config/nvmet/subsystems/${SUBNQN}"

Der NQN (NVMe Qualified Name) dient zur eindeutigen Identifikation meines Targets.

4. Zugriff erlauben

Für meine Testumgebung lasse ich erstmal jeden Host zu:

echo 1 > "${SUBSYS_DIR}/attr_allow_any_host"

Das ist praktisch zum Experimentieren, sollte aber in produktiven Umgebungen durch explizite Host-Einträge ersetzt werden. Denkt wirklich daran, in sicheren Umgebungen das hier nicht zu setzen.

5. Namespace anlegen und NVMe-Gerät zuordnen

Jetzt definiere ich den eigentlichen Speicherbereich, den ich freigeben möchte.
In meinem Fall ist das die physische SSD unter /dev/nvme1n1.

echo -n "${DEVICE}" > "${NS_DIR}/device_path"
echo 1 > "${NS_DIR}/enable"

Damit ist der Namespace aktiv und mit dem echten NVMe-Device verbunden.

6. Port konfigurieren

Als Nächstes richte ich den TCP Port ein. Über diesen ist dann das NVMe Target erreichbar:

echo tcp   > addr_trtype
echo ipv4  > addr_adrfam
echo 172.16.5.1 > addr_traddr
echo 4420       > addr_trsvcid

Der Port 4420 ist der Standardport für NVMe-over-TCP.

7. Subsystem an den Port binden

Nun verknüpfe ich das Subsystem mit dem Port, damit Clients überhaupt darauf zugreifen können:

ln -s "${SUBSYS_DIR}" "${PORT_DIR}/subsystems/${SUBNQN}"

Damit ist das Target offiziell „online“ und erreichbar.

8. Log-Ausgabe zur Kontrolle

Zum Schluss werfe ich noch einen Blick in die letzten Kernel-Meldungen:

dmesg | tail -20

So sehe ich sofort, ob das Target korrekt registriert wurde.

Das vollständige Bash Script für die Einrichtung des NVMe Targets.

#!/bin/bash
# nvmet-tcp target setup script
# Anpassung erforderlich: TARGET_IP, PORT, SUBNQN, DEVICE

TARGET_IP="172.16.5.1"
PORT="4420"
SUBNQN="nqn.2025-11.de.mynvme:target1"
DEVICE="/dev/nvme1n1"

# 1. Module laden
modprobe nvmet
modprobe nvmet_tcp

# 2. configfs mounten falls nicht vorhanden
if ! mountpoint -q /sys/kernel/config; then
  mount -t configfs none /sys/kernel/config
fi

# 3. Subsystem erstellen
SUBSYS_DIR="/sys/kernel/config/nvmet/subsystems/${SUBNQN}"
mkdir -p "${SUBSYS_DIR}"

# 4. Zugriff erlauben (vorläufig jeder Host)
echo 1 > "${SUBSYS_DIR}/attr_allow_any_host"

# 5. Namespace erstellen (nsid = 1)
NS_DIR="${SUBSYS_DIR}/namespaces/1"
mkdir -p "${NS_DIR}"
echo -n "${DEVICE}" > "${NS_DIR}/device_path"
echo 1 > "${NS_DIR}/enable"

# 6. Port anlegen
PORT_DIR="/sys/kernel/config/nvmet/ports/1"
mkdir -p "${PORT_DIR}"
echo tcp   > "${PORT_DIR}/addr_trtype"
echo ipv4  > "${PORT_DIR}/addr_adrfam"
echo "${TARGET_IP}" > "${PORT_DIR}/addr_traddr"
echo "${PORT}"       > "${PORT_DIR}/addr_trsvcid"

# 7. Subsystem mit Port verbinden
ln -s "${SUBSYS_DIR}" "${PORT_DIR}/subsystems/${SUBNQN}"

# 8. Anzeige dmesg zur Kontrolle
dmesg | tail -20