How to Configure NVMe Storage Target Mode in Ubuntu Using systemd-storagetm

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

Get the best tech tips delivered straight to your inbox.

Join thousands of readers mastering Apple, Google, Microsoft, and Linux.