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.