Target vs initiator vocabulary

  • Target — the server, the side exporting storage. Identified by an IQN (iSCSI Qualified Name): iqn.2024-01.com.example.storage:vm-disks.
  • Initiator — the client, the side mounting storage. Also has an IQN: typically iqn.1993-08.org.debian:01:<hostname>.
  • LUN — Logical Unit Number; one exposed block device within a target. A target can have multiple LUNs.
  • Portal — the IP:port the target listens on (typically 3260).

Install the target server

On the box that holds the underlying storage, install targetcli (Linux-IO / LIO is the modern in-kernel target):

# Debian / Ubuntu
sudo apt install targetcli-fb

# Persistent state across reboots
sudo systemctl enable --now rtslib-fb-targetctl

# Or on Rocky / AlmaLinux / Fedora
sudo dnf install targetcli

Decide on the backing storage. Options:

  • A whole disk: /dev/sdb
  • A LVM logical volume (see mdadm + LVM tutorial)
  • A regular file (sparse or dense) on an existing filesystem — flexible, but adds a filesystem layer between the LUN and the underlying disk
  • A ZFS zvol (block device exposed by ZFS)

Configure the target

sudo targetcli

# Inside the interactive shell:

# Define a backstore (the underlying block device)
/> /backstores/block create vm-disks-1 /dev/vg-storage/vm-disks

# Or a file-based backstore (a sparse file as the backing)
/> /backstores/fileio create vm-files-1 /srv/iscsi/vm-1.img 50G sparse=true

# Create the target
/> /iscsi create iqn.2024-01.com.example.storage:vm-disks

# Map a LUN to the backstore
/> /iscsi/iqn.2024-01.com.example.storage:vm-disks/tpg1/luns \
       create /backstores/block/vm-disks-1

# Set ACL: which initiator IQN can connect
/> /iscsi/iqn.2024-01.com.example.storage:vm-disks/tpg1/acls \
       create iqn.1993-08.org.debian:01:vm-host-01

# Set CHAP auth on the ACL
/> /iscsi/iqn.2024-01.com.example.storage:vm-disks/tpg1/acls/iqn.1993-08.org.debian:01:vm-host-01 \
       set auth userid=initiator-1 password=<long-random>

# Save and exit
/> saveconfig
/> exit

That state persists to /etc/rtslib-fb-target/saveconfig.json; on reboot the rtslib-fb-targetctl service restores it.

Open port 3260 in the firewall (allow only the initiator's IP).

Install the initiator

sudo apt install open-iscsi

# Set the initiator IQN (must match what's in the target's ACL)
sudo nano /etc/iscsi/initiatorname.iscsi
# InitiatorName=iqn.1993-08.org.debian:01:vm-host-01

sudo systemctl restart iscsid open-iscsi

Configure CHAP auth on the initiator

Edit /etc/iscsi/iscsid.conf:

node.session.auth.authmethod = CHAP
node.session.auth.username = initiator-1
node.session.auth.password = <long-random>

Discover + login

# Discover targets at the portal
sudo iscsiadm -m discovery -t st -p 192.168.1.10
# 192.168.1.10:3260,1 iqn.2024-01.com.example.storage:vm-disks

# Log in
sudo iscsiadm -m node \
    -T iqn.2024-01.com.example.storage:vm-disks \
    -p 192.168.1.10 --login

# Verify
sudo iscsiadm -m session
lsblk
# New /dev/sdc (or similar) appears, the size of the exported LUN

Use the LUN like a local disk

# Format
sudo mkfs.xfs /dev/sdc

# Or set up LUKS encryption on the iSCSI side (so the data is encrypted at rest on the target)
sudo cryptsetup luksFormat /dev/sdc
sudo cryptsetup luksOpen /dev/sdc luks-vm-disks
sudo mkfs.xfs /dev/mapper/luks-vm-disks

# Mount
sudo mkdir /mnt/iscsi
sudo mount /dev/sdc /mnt/iscsi

# Make it boot-time-persistent in /etc/fstab with _netdev option
UUID=<blkid-uuid> /mnt/iscsi xfs _netdev,defaults 0 0

_netdev matters — tells systemd to wait for the network and the iSCSI session before trying to mount.

Multipath: redundant network paths

For HA, expose the target on two network interfaces; the initiator connects to both and load-balances / fails over. multipath-tools on the initiator side aggregates them into one logical device.

sudo apt install multipath-tools

# /etc/multipath.conf
defaults {
    user_friendly_names yes
    find_multipaths yes
}

# Then login to both portals
sudo iscsiadm -m discovery -t st -p 192.168.1.10
sudo iscsiadm -m discovery -t st -p 192.168.2.10
sudo iscsiadm -m node --login

multipath -ll
# Shows both paths grouped under one logical /dev/mapper/mpath0

The big caveat: concurrent mounts

iSCSI exports a block device, not a filesystem. Most filesystems (ext4, xfs, btrfs) assume a single host owns the device. If two initiators connect to the same LUN and both mount it as ext4, they will corrupt each other within seconds.

To share a LUN between multiple hosts:

  • Use a cluster filesystem (GFS2, OCFS2) that coordinates writes via a distributed lock manager.
  • Or, more commonly, use NFS on top of the LUN-mounted-on-one-host (NFS handles concurrent access; iSCSI just gives you the underlying block storage).
  • Or, give each host its own LUN.

For the canonical "VMs on one hypervisor pool, storage on the NAS" pattern: one LUN per VM, the hypervisor (Proxmox, ESXi, libvirt) handles the locking.

iSCSI for Kubernetes

The kubernetes.io/iscsi in-tree volume plugin is deprecated; use the democratic-csi or longhorn CSI drivers instead, both of which provide K8s StorageClasses backed by iSCSI LUNs from a Linux target.

iSCSI vs NFS vs Ceph

  • iSCSI — block-level, fast (small overhead), single-writer per LUN. Best for VM disks, single-mount databases, dedicated workloads.
  • NFS — file-level, multi-writer (with proper locking), simpler config. Best for shared file storage; slower than iSCSI per request.
  • Ceph RBD (see Ceph tutorial) — block + file + object from a distributed cluster; survives node failures. Heavier ops but production-grade redundancy.

For homelab "one NAS exporting disks to two hypervisors," iSCSI is the simplest right answer. For "shared file storage across a fleet," NFS. For "real production HA," Ceph.

Worth knowing

  • Network MTU. For 10 GbE+, configure jumbo frames (MTU 9000) end-to-end (NIC + switch); iSCSI throughput on 10 GbE typically doubles or triples vs default 1500 MTU.
  • Dedicated VLAN. Isolate iSCSI traffic on its own VLAN or even physical network — security (CHAP is weak; isolation matters) and performance (no competition with other traffic).
  • iSCSI vs NVMe-oF. NVMe-over-Fabrics is the modern successor; lower per-IO overhead, especially on fast SSDs. For new deployments where both ends support it, NVMe-oF is the future. iSCSI's universality (every OS supports it) keeps it relevant.