Configuring iSCSI Target & Inititator on CentOS
— ny_wk
Configuring an iSCSI target and initiator on CentOS lets one Linux server export raw block storage over an ordinary TCP/IP network so that another machine sees and uses it as if it were a locally attached disk. This guide walks through the full workflow with the modern targetcli toolset on the server and iscsiadm on the client, including persistence, security, and the mistakes that trip people up.
What iSCSI Is and Why It Matters
iSCSI (Internet Small Computer System Interface) is an IP-based storage networking standard that carries SCSI commands inside TCP/IP packets. It is the budget-friendly cousin of Fibre Channel: instead of dedicated fibre fabric and special host bus adapters, you build a SAN (Storage Area Network) on the same Ethernet hardware you already own. The traffic is block-level, not file-level, which is the key difference from NFS or Samba. The client receives a real block device it can partition, format with any filesystem, encrypt, or hand to a database.
Because the storage is presented as a raw device, an iSCSI volume behaves exactly like a SATA or SAS disk to the operating system. That makes it ideal for VM datastores, clustered filesystems, diskless boot, and consolidating storage onto one well-provisioned box.
Core Concepts You Must Know
- Target — the server that owns the physical storage and exports it. On CentOS the target is provided by the kernel LIO subsystem and managed by
targetcli(thetarget.servicedaemon). - Initiator — the client that requests and consumes the disk. CentOS uses the
iscsi-initiator-utilspackage and itsiscsiadmcommand. - Backstore — the actual storage object the target hands out: a block device (
/dev/...), an LVM logical volume, a raw file (fileio), or a ramdisk. - LUN (Logical Unit Number) — the addressable unit the initiator mounts. A single target can expose several LUNs, each backed by a different backstore.
- IQN (iSCSI Qualified Name) — the globally unique identifier for every target and initiator. Format:
iqn.YYYY-MM.reverse.domain:identifier, for exampleiqn.2026-06.com.example:server.target1. - ACL (Access Control List) — the rule that says which initiator IQN is permitted to log into a target. ACLs are how you keep two clients from clobbering each other's data.
- Portal — the IP address and TCP port the target listens on. The default iSCSI port is 3260.
The lab in this walkthrough uses two CentOS machines:
| Role | Hostname | IP address |
| Target (server) | server.example.com | 192.168.1.100 |
| Initiator (client) | client.example.com | 192.168.1.101 |
Configuring the iSCSI Target on CentOS with targetcli
The targetcli shell is the supported way to configure an iSCSI target on CentOS 7 and later. It drives the in-kernel LIO target through a familiar filesystem-like tree, and it saves the running configuration so it survives reboots. The legacy scsi-target-utils / tgtd stack still appears in old guides — that approach is covered at the end so you can recognise it, but new builds should use targetcli.
Step 1 — Install the target software and enable the service
- Install the package:
yum install targetcli -y - Enable and start the LIO target service so it comes up after a reboot:
systemctl enable --now target.service - Confirm it is running:
systemctl status target.service
On older CentOS 6 systems that use SysV instead of systemd, the equivalent would be service target start && chkconfig target on, but CentOS 7/8/Stream all use the systemctl form above.
Step 2 — Prepare the storage to export (LVM recommended)
You can export any block device, but backing the LUN with an LVM logical volume is the smart choice: you can grow the volume online later without disrupting the client. The example below carves an LV out of a spare disk.
- Create a partition on the spare disk and re-read the table:
fdisk /dev/sdbpartprobe /dev/sdb - Verify the kernel sees the new partition:
cat /proc/partitions - Initialise it as an LVM physical volume:
pvcreate /dev/sdb1 - Create a volume group and a logical volume (here, a 2 GB LV):
vgcreate myvolgrp /dev/sdb1lvcreate -L 2G -n mylogvol myvolgrp - Confirm the device node exists:
lvdisplay /dev/myvolgrp/mylogvol
Important: do not format the LV with a filesystem on the target. iSCSI exports the raw block device; the initiator owns the filesystem. Formatting it here would just be overwritten by the client.
Step 3 — Build the target in the targetcli shell
Launch the interactive shell as root:
targetcli
You are now at the /> prompt. Each command below builds one piece of the export. The order is deliberate: backstore first, then the target IQN, then the LUN, ACL, and portal.
- Create the backstore pointing at your logical volume:
/> /backstores/block create name=disk1 dev=/dev/myvolgrp/mylogvol - Create the target IQN. Supply your own IQN with the year-month and reverse-DNS naming convention:
/> /iscsi create iqn.2026-06.com.example:server.target1 - Map the backstore to a LUN under that target's TPG (target portal group):
/> /iscsi/iqn.2026-06.com.example:server.target1/tpg1/luns create /backstores/block/disk1 - Add an ACL for the initiator's IQN. You will read the client's IQN from
/etc/iscsi/initiatorname.iscsiin the next section; it must match exactly:/> /iscsi/iqn.2026-06.com.example:server.target1/tpg1/acls create iqn.2026-06.com.example:client01 - Create the network portal. By default targetcli binds to
0.0.0.0:3260(all interfaces). To bind to a specific address:/> /iscsi/iqn.2026-06.com.example:server.target1/tpg1/portals create 192.168.1.100 3260If a portal on
0.0.0.0already exists and you want only the specific one, delete the wildcard portal first withportals delete 0.0.0.0 3260. - Save and exit. Writing the config persists it to
/etc/target/saveconfig.jsonso it reloads on boot:/> saveconfig/> exit
You can review the whole tree at any time by running targetcli and typing ls at the root. A healthy export shows the block backstore, the target IQN, one LUN, one ACL, and the portal on port 3260.
Step 4 (optional) — Require CHAP authentication
ACLs limit access by IQN, but an IQN is easy to spoof. For real security add CHAP credentials on the ACL inside targetcli:
- Enter the ACL node:
/> cd /iscsi/iqn.2026-06.com.example:server.target1/tpg1/acls/iqn.2026-06.com.example:client01 - Set the username and password the initiator must present:
set auth userid=isuserset auth password=S3cretPass123
The matching credentials are then configured on the client in /etc/iscsi/iscsid.conf or per-node.
Opening the Firewall for iSCSI
The target listens on TCP 3260. If firewalld is active, the client's discovery and login will silently hang or time out until you open that port. On CentOS 7/8 use the predefined service:
- Allow the iSCSI target service permanently:
firewall-cmd --permanent --add-service=iscsi-target - Or open the raw port if the named service is unavailable:
firewall-cmd --permanent --add-port=3260/tcp - Reload the ruleset:
firewall-cmd --reload - Confirm the port is actually being listened on:
ss -tlnp | grep 3260
If SELinux is enforcing, the target daemon normally works out of the box, but if you back a LUN with an unusual file path you may need setsebool -P or correct file contexts. Check ausearch -m avc when something is denied for no obvious reason.
Configuring the iSCSI Initiator (Client)
With the target exporting a LUN, the initiator can now discover and log into it. Everything below runs on client.example.com (192.168.1.101).
Step 1 — Install the initiator utilities
- Install the package (it brings in
iscsiadmand theiscsiddaemon):yum install iscsi-initiator-utils -y - Note your initiator's IQN — this is the value you must place in the target's ACL:
cat /etc/iscsi/initiatorname.iscsi
If you set a custom IQN in the ACL (as in the target steps above), edit this file so it reads InitiatorName=iqn.2026-06.com.example:client01, then restart the service. The ACL on the target and this name must be identical, or login is refused.
- Enable and start the iSCSI services:
systemctl enable --now iscsid.service iscsi.service
Step 2 — Discover targets on the portal
Ask the portal what targets it offers using the SendTargets discovery method:
iscsiadm --mode discovery --type sendtargets --portal 192.168.1.100
A successful discovery prints lines such as:
192.168.1.100:3260,1 iqn.2026-06.com.example:server.target1
If nothing returns, the firewall, the portal binding, or basic IP reachability is the culprit — test with ping and telnet 192.168.1.100 3260 before going further.
Step 3 — Log in to the target
- Log into the discovered target. Watch the spelling of
iscsiadm— older copy-paste guides contain a typo (iscsiaadm) that will simply error out:iscsiadm --mode node --targetname iqn.2026-06.com.example:server.target1 --portal 192.168.1.100:3260 --login - Confirm an active session:
iscsiadm --mode session - Identify the new block device the kernel created (it appears as the next free
/dev/sdX):lsblkdmesg | tail
Assume the new device shows up as /dev/sdb of roughly 2 GB — that is your imported iSCSI disk.
Step 4 — Partition, format, and mount on the client
Now the filesystem lives entirely on the initiator. Create a partition, lay down a filesystem, and mount it.
- Partition the imported disk and re-read the table:
fdisk /dev/sdbpartprobe /dev/sdb - Create a filesystem.
xfsis the CentOS 7/8 default;ext4is also fine:mkfs.xfs /dev/sdb1 - Create a mount point and mount it (note the correct order: device first, then directory):
mkdir /datamount /dev/sdb1 /data
Step 5 — Make the mount persistent and reboot-safe
An iSCSI device is a network disk, so its /etc/fstab entry needs the _netdev option. That tells systemd to wait for the network and the iSCSI session before trying to mount — without it the boot can drop to emergency mode. Use a UUID rather than /dev/sdb1, because device letters can shuffle between boots.
- Get the filesystem UUID:
blkid /dev/sdb1 - Add a line to
/etc/fstabusing that UUID and the_netdevflag:UUID=<your-uuid> /data xfs _netdev,defaults 0 0 - Mark the login as automatic so the session is re-established on boot (this is the default after a successful login, but verify it):
iscsiadm --mode node --targetname iqn.2026-06.com.example:server.target1 --portal 192.168.1.100:3260 --op update -n node.startup -v automatic - Test the fstab entry without rebooting:
mount -a
Common Pitfalls and How to Fix Them
- Firewall blocking port 3260. The single most common failure. Discovery hangs or login times out. Open
iscsi-target/3260/tcpon the server and verify withss -tlnp | grep 3260. - ACL or IQN mismatch. If login is refused with an authorization error, the initiator's
/etc/iscsi/initiatorname.iscsidoes not match the ACL you created in targetcli. They must be character-for-character identical, including case. - Formatting on the wrong end. Never
mkfsthe backstore on the target. The filesystem belongs on the initiator side of the LUN. - Forgetting
_netdevin fstab. Without it, boot tries to mount the network disk before the network is up and can wedge the system. Always add_netdevfor iSCSI mounts. - Stale device letters.
/dev/sdbcan become/dev/sdcafter a reboot or when more LUNs appear. Mount by UUID, not by device name. - No multipath where it is needed. If the target and initiator each have two network paths for redundancy, the client will see the same LUN twice (e.g.
/dev/sdband/dev/sdc). Install and configuredevice-mapper-multipath(mpathconf --enable --with_multipathd y) so both paths fold into one/dev/mapper/mpathXdevice — mounting the baresdXin a multipath setup risks corruption. - Editing targetcli but not saving. Changes are live in the kernel immediately, but unless you run
saveconfig, they vanish on the next reboot.
Verifying the Setup End to End
Run these checks to confirm the storage chain is healthy from target to mounted filesystem:
- On the target, list the configuration tree: run
targetclithenls— you should see the backstore, IQN, LUN, ACL, and the 3260 portal. - On the initiator, confirm an active session:
iscsiadm --mode session(or the more detailediscsiadm -m session -P 3to see the mapped device). - Confirm the block device appears:
lsblkshould list the new disk and its partition mounted at/data. - Test read/write:
echo hello > /data/test.txt && cat /data/test.txtproves the LUN is writable. - Check capacity:
df -h /datashould report roughly the size of the LUN you exported.
To cleanly disconnect later, log out of the target with iscsiadm --mode node --targetname iqn.2026-06.com.example:server.target1 --portal 192.168.1.100:3260 --logout after unmounting /data.
The Legacy tgtd / scsi-target-utils Approach
Older CentOS 6 tutorials configure the target with scsi-target-utils and the tgtd daemon instead of targetcli. In that model you install scsi-target-utils, start the daemon with service tgtd start && chkconfig tgtd on, and define exports by editing /etc/tgt/targets.conf with a block such as:
<target iqn.2026-06.com.example:server.target1> backing-store /dev/myvolgrp/mylogvol </target>
then reloading with service tgtd reload. This still works on legacy systems, but tgtd is deprecated on modern CentOS in favour of the kernel LIO target driven by targetcli. For any current build, use the targetcli workflow above — it is the supported, persistent, and better-performing path.
Key Takeaways
- iSCSI exports raw block storage over TCP/IP, giving the client a real disk it fully owns and formats.
- Use
targetcli+target.serviceon the server andiscsiadm+iscsi-initiator-utilson the client; the oldtgtdstack is legacy. - Back LUNs with LVM so you can grow storage online, and never format the backstore on the target.
- The IQN in the target's ACL must match the initiator's
initiatorname.iscsiexactly, and TCP port 3260 must be open in the firewall. - Mount iSCSI disks by UUID with the
_netdevoption and runsaveconfigso the export survives reboots.
Frequently Asked Questions
What is the difference between an iSCSI target and an initiator?
The target is the server that owns the physical storage and exports it as one or more LUNs. The initiator is the client that discovers, logs into, and consumes that storage as a local block device. On CentOS you manage the target with targetcli and the initiator with iscsiadm.
Why can't my initiator discover or log into the target?
Almost always a firewall or ACL problem. Confirm TCP port 3260 is open on the target (firewall-cmd --add-service=iscsi-target --permanent && firewall-cmd --reload), confirm the target is listening with ss -tlnp | grep 3260, and verify the initiator's IQN in /etc/iscsi/initiatorname.iscsi exactly matches the ACL you created in targetcli.
Should I put a filesystem on the storage at the target or the initiator?
At the initiator. iSCSI hands the client a raw block device, so the client runs mkfs, partitions, and mounts it. Formatting the backstore on the target is pointless — the initiator treats the LUN as raw and would overwrite it.
How do I make an iSCSI mount survive a reboot?
Set the node startup to automatic with iscsiadm ... --op update -n node.startup -v automatic, add the mount to /etc/fstab using the filesystem UUID plus the _netdev option, and run saveconfig in targetcli on the server so the export itself persists.
If this walkthrough helped you build your first iSCSI SAN, subscribe to @explorenystream on YouTube for more hands-on Linux and system administration guides.