In high-performance data centre environments and automated server deployment workflows, exposing local block storage directly to external network nodes has traditionally required complex manual configuration of kernel iSCSI targets or dedicated userspace storage daemons. Starting with systemd version 254 (standardised in modern Ubuntu releases such as 24.04 LTS), Linux includes systemd-storagetm (Storage Target Mode). This built-in system daemon automates the Linux kernel’s NVMe-over-Fabrics (NVMe-oF) target subsystem, allowing systems administrators to instantly expose internal NVMe solid-state drives, partitions, or loopback block devices over network fabrics such as NVMe/TCP or NVMe/RDMA. Whether reprovisioning bare-metal nodes, extracting forensic disk images, or building distributed storage clusters, configuring systemd-storagetm provides native, high-speed block storage access without external target management utilities.
Understanding NVMe Storage Target Mode in Modern systemd
The systemd-storagetm.service acts as an automated management bridge between systemd’s device discovery engine and the Linux kernel’s nvmet (NVMe Target) infrastructure. In classical enterprise Linux setups, creating an NVMe-oF target requires navigating the virtual ConfigFS filesystem under /sys/kernel/config/nvmet/ or utilizing third-party Python wrappers such as nvmetcli.
With systemd-storagetm, systemd treats storage export as a first-class operating system function. When triggered, the daemon identifies specified block devices, generates standardised NVMe Qualified Names (NQNs), creates NVMe subsystems and namespaces within the kernel, binds them to target network ports, and manages connection listeners over TCP or RDMA transport layers. This allows client nodes running standard NVMe initiator tools (nvme-cli) to mount remote Ubuntu disks with the low latency and high parallelism inherent to the NVMe protocol.
Verifying Kernel Subsystem Support and Prerequisites
Before launching NVMe target services on Ubuntu, verify that the Linux kernel includes necessary NVMe-oF target and transport modules. Load the required kernel modules using modprobe:
sudo modprobe nvmet
sudo modprobe nvmet-tcp
To ensure these modules load automatically upon system boot, create a configuration file inside /etc/modules-load.d/:
echo -e "nvmet\nnvmet-tcp" | sudo tee /etc/modules-load.d/nvmet.conf
Confirm that the NVMe target ConfigFS structure is active on the system:
ls -ld /sys/kernel/config/nvmet
Inspecting and Preparing Target Block Devices
Identify the local storage device you wish to expose over the network. You can export physical NVMe drives (such as /dev/nvme0n1), traditional SATA/SAS disks (/dev/sdb), or raw disk partitions:
lsblk -o NAME,SIZE,TYPE,FSTYPE,MODEL
Alternatively, you can create a sparse file and bind it to a virtual loop device for isolated staging or testing:
sudo truncate -s 50G /var/lib/storage-target.raw
sudo losetup -fP /var/lib/storage-target.raw
losetup -a
Note the block device path (for example, /dev/loop0 or /dev/nvme1n1) that will serve as the NVMe namespace backing store.
Invoking systemd-storagetm Directly for On-Demand Export
You can execute systemd-storagetm directly from the terminal to temporarily expose a block device over NVMe/TCP. This is especially useful during rescue operations or rapid migration tasks:
sudo systemd-storagetm /dev/nvme1n1
By default, systemd-storagetm performs the following operations automatically:
- Constructs an NQN based on the host’s machine identity (
/etc/machine-id). - Creates an NVMe subsystem and assigns the specified device as Namespace 1.
- Binds a TCP listener to port 4420 (the IANA-assigned standard port for NVMe-oF) across all active network interfaces.
- Keeps the export active until interrupted via
Ctrl + C, at which point it automatically unbinds ports and cleans up ConfigFS entries.
Configuring Persistent Storage Target Services
To enable continuous NVMe target export across reboots, configure the system service unit. Inspect the unit definition provided by Ubuntu:
systemctl status systemd-storagetm.service
To specify which disks should be exposed persistently, create a systemd drop-in configuration file:
sudo mkdir -p /etc/systemd/system/systemd-storagetm.service.d
sudo nano /etc/systemd/system/systemd-storagetm.service.d/override.conf
Add the following override syntax to pass the desired block device arguments to the service execution command:
[Service]
ExecStart=
ExecStart=/lib/systemd/systemd-storagetm /dev/disk/by-id/nvme-Samsung_SSD_980_PRO_1TB_S5GXNF0R123456
Reload systemd manager configuration and enable the service:
sudo systemctl daemon-reload
sudo systemctl enable --now systemd-storagetm.service
Connecting from a Remote Linux Client Using nvme-cli
Once systemd-storagetm is running on the Ubuntu host, client machines across the network can discover and connect to the shared NVMe namespace.
On the client machine (running Ubuntu, Debian, or RHEL), install the NVMe management utility:
sudo apt update && sudo apt install -y nvme-cli
Perform an NVMe/TCP discovery query against the target server’s IP address (e.g., 192.168.1.50):
sudo nvme discover -t tcp -a 192.168.1.50 -s 4420
The discovery log returns the exported Subsystem NQN and transport details:
Generation counter: 1
=====Discovery Log Entry 0======
trtype: tcp
adrfam: ipv4
subtype: nvme subsystem
treq: not specified
portid: 1
trsvcid: 4420
subnqn: nqn.2014-08.org.nvmexpress:uuid:a1b2c3d4-e5f6-7890-abcd-ef0123456789
traddr: 192.168.1.50
Connect to the discovered target subsystem using nvme connect:
sudo nvme connect -t tcp -n nqn.2014-08.org.nvmexpress:uuid:a1b2c3d4-e5f6-7890-abcd-ef0123456789 -a 192.168.1.50 -s 4420
Verify that the remote disk now appears in the client’s local block device inventory:
lsblk -o NAME,SIZE,MODEL,TRAN | grep nvme
Auditing and Troubleshooting Target Connections
To inspect active client connections and target operational status on the Ubuntu host, query the systemd journal:
sudo journalctl -u systemd-storagetm.service -f
If client discovery times out, verify that UFW (Uncomplicated Firewall) permits traffic on the NVMe-oF TCP port:
sudo ufw allow 4420/tcp comment "NVMe over TCP target port"
sudo ufw status
You can also directly inspect active kernel target mappings in ConfigFS to ensure namespaces are healthy:
tree /sys/kernel/config/nvmet/
When decommissioning a client connection, execute nvme disconnect on the client node specifying the target NQN before stopping the target service on the Ubuntu server:
sudo nvme disconnect -n nqn.2014-08.org.nvmexpress:uuid:a1b2c3d4-e5f6-7890-abcd-ef0123456789