{"id":2196,"date":"2025-02-16T21:14:02","date_gmt":"2025-02-17T02:14:02","guid":{"rendered":"https:\/\/enterpriseadmins.org\/blog\/?p=2196"},"modified":"2025-02-16T21:14:02","modified_gmt":"2025-02-17T02:14:02","slug":"how-to-set-up-a-minimal-nfs-and-iscsi-storage-solution-using-ubuntu-24-04","status":"publish","type":"post","link":"https:\/\/enterpriseadmins.org\/blog\/virtualization\/how-to-set-up-a-minimal-nfs-and-iscsi-storage-solution-using-ubuntu-24-04\/","title":{"rendered":"How to Set Up a Minimal NFS and iSCSI Storage Solution Using Ubuntu 24.04"},"content":{"rendered":"\n<p>In my lab, I often need different types of storage to test various scenarios.  For example, just last week someone asked about using <code>New-Datastore<\/code> with a specific version of VMFS and I needed to quickly perform a quick syntax check.  I&#8217;ve found that having a nested storage appliance, like a Open Filer or Free NAS available is helpful.  However, these appliances offer way more features than I need and typically have higher resource requirements.  Sometimes, setting up specific storage protocols like NFS or iSCSI is crucial for testing and development, but existing solutions can be overly complex or resource-heavy for lab environments.  In this post I&#8217;ll outline how I solved this problem with a few utilities added to an existing Ubuntu 24.04 template.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Storage Protocols<\/h2>\n\n\n\n<p>With this project I wanted to have a single storage target that could provide both NFS and iSCSI storage protocols. For purposes of my lab, nearly all of my storage testing the &#8216;client&#8217; system will be ESXi.  This post will provide some output\/examples in that context.  For example, ESXi supports block storage (such as iSCSI) and file storage, specifically NFS3 and NFS4.1.  Ideally, I want to provide all three of these options with this single appliance, so we&#8217;ll show examples of using the appliance in all three of those ways.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Setting Up the Test Appliance<\/h2>\n\n\n\n<p>I deployed an Ubuntu 24.04 VM, using the image\/customization spec described here: <a href=\"https:\/\/enterpriseadmins.org\/blog\/scripting\/ubuntu-24-04-packer-and-vcenter-server-customization-specifications\/\">https:\/\/enterpriseadmins.org\/blog\/scripting\/ubuntu-24-04-packer-and-vcenter-server-customization-specifications\/<\/a><\/p>\n\n\n\n<p>The template VM has a single 50GB disk, so I added an additional 50GB disk to use as the backing for the storage server.  We&#8217;ll format this disk as <code>btrfs<\/code> and mount it as <code>\/data<\/code>.  This will be covered in the following code block:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo mkdir \/data\nsudo mkfs.btrfs \/dev\/sdb\necho \"\/dev\/sdb \/data btrfs defaults 0 0\" | sudo tee -a \/etc\/fstab\nsudo systemctl daemon-reload\nsudo mount \/data<\/code><\/pre>\n\n\n\n<p>The above code block is creating a folder, formatting our second disk in the system, adding an entry to the <code>fstab<\/code> file so the filesystem mounts when the system boots, and finally mounts our new disk.  After the above is complete, running <code>df -h \/data<\/code> should return the mounted disk and its size, just as a confirmation that everything worked successfully.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Configuring NFS on Ubuntu 24.04<\/h2>\n\n\n\n<p>I&#8217;ll start with NFS as this is a problem I&#8217;ve previously solved for using a Photon OS (<a href=\"https:\/\/enterpriseadmins.org\/blog\/virtualization\/vmware-workstation-lab-photon-os-container-host-and-nfs-server\/\">https:\/\/enterpriseadmins.org\/blog\/virtualization\/vmware-workstation-lab-photon-os-container-host-and-nfs-server\/<\/a>).  The only difference this time is that I planned to use Ubuntu 24.04, which has a slightly different package name for the NFS Server components.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo apt install nfs-kernel-server -y\nsudo mkdir \/data\/nfs\necho \"\/data\/nfs *(rw,async,no_root_squash,insecure_locks,sec=sys,no_subtree_check)\" | sudo tee -a \/etc\/exports\nsudo systemctl daemon-reload\nsudo systemctl reload nfs-server<\/code><\/pre>\n\n\n\n<p>The above code block installs our NFS server package, creates a subfolder to export as NFS, adds an entry to the NFS exports configuration file, then reloads the configuration to take effect.  On our client system (ESXi), we can confirm that our work was successful by creating a datastore.  I&#8217;ll complete that task in PowerCLI below:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>New-Datastore -VMHost h316-vesx-64* -Name 'nfs326' -Nfs -NfsHost 192.168.10.26 -Path \/data\/nfs -Confirm:$false\n\nName                               FreeSpaceGB      CapacityGB\n----                               -----------      ----------\nnfs326                                  14.994          15.000<\/code><\/pre>\n\n\n\n<p>As we can see, the test was a success and returned our mount point size in the command results.  The above example resulted in an NFS 3 mount of the NFS folder.  I created a subfolder (<code>test41<\/code>) and executed a similar test to confirm this could work for NFS 4.1 as well.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>New-Datastore -VMHost h316-vesx-64* -Name 'nfs326-41' -Nfs -FileSystemVersion 4.1 -NfsHost 192.168.10.26 -Path \/data\/nfs\/test41 -Confirm:$false\n\nName                               FreeSpaceGB      CapacityGB\n----                               -----------      ----------\nnfs326-41                               14.994          15.000<\/code><\/pre>\n\n\n\n<p>As we can see in the vCenter web interface, one of these datastores is NFS 3 and the other is NFS 4.1, both showing the same capacity and free space.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/02\/image.png\"><img loading=\"lazy\" decoding=\"async\" width=\"726\" height=\"97\" src=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/02\/image.png\" alt=\"\" class=\"wp-image-2199\" srcset=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/02\/image.png 726w, https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/02\/image-300x40.png 300w\" sizes=\"auto, (max-width: 726px) 100vw, 726px\" \/><\/a><\/figure>\n\n\n\n<p>This confirms that we were able to successfully connect to our NFS Server service using ESXi with both NFS 3 and NFS 4.1 connections.  Next, we&#8217;ll look at setting up iSCSI storage, which requires a slightly different approach.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">iSCSI<\/h2>\n\n\n\n<p>The iSCSI testing was a bit more interesting.  Looking around, I found a couple of ways to create an iSCSI target.  I ended up using <code>targetcli<\/code> to create the iSCSI target.  There are plenty of tutorials around for this, including a video: <a href=\"https:\/\/www.youtube.com\/watch?v=OIpxwX6pTIU\">https:\/\/www.youtube.com\/watch?v=OIpxwX6pTIU<\/a> and Ubuntu 24.04 specific article: <a href=\"https:\/\/www.server-world.info\/en\/note?os=Ubuntu_24.04&amp;p=iscsi&amp;f=1\">https:\/\/www.server-world.info\/en\/note?os=Ubuntu_24.04&amp;p=iscsi&amp;f=1<\/a> that were very helpful.  I&#8217;ll document the steps below for completeness.  <\/p>\n\n\n\n<p>In this first code block we&#8217;ll install the service and create the folder where we&#8217;ll storage some images.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo apt install targetcli-fb -y\nsudo mkdir \/data\/iscsi<\/code><\/pre>\n\n\n\n<p>The targetcli command can create image files to use as backing for our disks, but in my testing the <code>sparse=true<\/code> switch does not create sparse files, so we&#8217;ll do this as two steps.  You&#8217;ll note that I&#8217;m specifying one image has having a 2T file size&#8230; but as you may have noticed in our NFS example, we only have 15GB of disk for our <code>\/data<\/code> mount.  This doesn&#8217;t result in some &#8216;magic beans&#8217; sort of free storage&#8230; once we write 15GB of stuff to this disk we&#8217;ll be out of capacity and run into problems.  This is only being done for illustration\/simulation purposes&#8230;. sometimes you&#8217;ll want the UI to show &#8216;normal&#8217; sizes that you&#8217;d see with actual datastores.  One reason that the <code>\/data<\/code> mount was configured with <code>btrfs<\/code> instead of something like <code>ext4<\/code> is so we can support image files larger than 16T.  This <code>btrfs<\/code> filesystem will allow files over 62TB in size (62TB being the maximum supported size for VMFS 6).  Also in the code block output below, we&#8217;ll use <code>du<\/code> to show that these disks are using 0 bytes on the file system, but have larger apparent sizes.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo truncate -s 10G \/data\/iscsi\/disk0.img\nsudo truncate -s 10G \/data\/iscsi\/disk1.img\nsudo truncate -s 2T \/data\/iscsi\/disk2.img\n\ndu -h \/data\/iscsi\/*.img\n0       \/data\/iscsi\/disk0.img\n0       \/data\/iscsi\/disk1.img\n0       \/data\/iscsi\/disk2.img\n\ndu -h \/data\/iscsi\/*.img --apparent-size\n10G     \/data\/iscsi\/disk0.img\n10G     \/data\/iscsi\/disk1.img\n2.0T    \/data\/iscsi\/disk2.img<\/code><\/pre>\n\n\n\n<p>Once we have our files pre-staged, we can start working with <code>targetcli<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo targetcli\n\n# this should enter the targetcli shell\n\ncd \/backstores\/fileio\ncreate disk0 \/data\/iscsi\/disk0.img sparse=true write_back=false\ncd disk0\nset attribute is_nonrot=1\ncd ..\ncreate disk1 \/data\/iscsi\/disk1.img sparse=true write_back=false\ncd disk1\nset attribute is_nonrot=1\ncd ..\ncreate disk2 \/data\/iscsi\/disk2.img sparse=true write_back=false\ncd disk2\nset attribute is_nonrot=1<\/code><\/pre>\n\n\n\n<p>The above code block creates the fileio references to each of our disks, and also set the <code>is_nonrot<\/code> flag to tell the system that these are non-rotational (ie Flash) devices.<\/p>\n\n\n\n<p>Still in the <code>targetcli<\/code> shell, we&#8217;ll start our iSCSI configuration.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cd \/iscsi\ncreate iqn.2025-02.com.example.iscsi:target01 \ncd iqn.2025-02.com.example.iscsi:target01\/tpg1\/luns\ncreate \/backstores\/fileio\/disk0\ncreate \/backstores\/fileio\/disk1\ncreate \/backstores\/fileio\/disk2<\/code><\/pre>\n\n\n\n<p>This will create LUNs for each of our disks.  Finally, still in the <code>targetcli<\/code> shell we&#8217;ll create an ACL to allow a specific host to access the target.  We&#8217;ll then delete it.  This puts the correct syntax in our command history, so we can refer back to it in the future (I plan to use this as a template for future tests).  <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cd \/iscsi\/iqn.2025-02.com.example.iscsi:target01\/tpg1\/acls\ncreate iqn.1998-01.com.vmware:host.lab.enterpriseadmins.org:3:65\ndelete iqn.1998-01.com.vmware:host.lab.enterpriseadmins.org:3:65\nexit<\/code><\/pre>\n\n\n\n<p>The exit will cause <code>targetcli<\/code> to save our changes so they&#8217;ll persist a reboot.  For testing, we&#8217;ll go back into <code>targetcli<\/code> and add a specific reference to allow our test host to the iSCSI target.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo targetcli\ncreate iqn.1998-01.com.vmware:h316-vesx-64.lab.enterpriseadmins.org:394284478:65\nexit<\/code><\/pre>\n\n\n\n<p>On our test client system, we can add a dynamic target for the IP\/name &amp; port 3260 of our storage appliance and then rescan for storage.  We should see the three disks that we created, with the sizes specified.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/02\/image-1.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"218\" src=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/02\/image-1-1024x218.png\" alt=\"\" class=\"wp-image-2202\" srcset=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/02\/image-1-1024x218.png 1024w, https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/02\/image-1-300x64.png 300w, https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/02\/image-1-768x163.png 768w, https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/02\/image-1.png 1034w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>As another confirmation, we may want to make each of these disks a VMFS volume.  We can do that using syntax similar to the below code block:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Get-ScsiLun -VmHost h316* |?{$_.Vendor -eq 'LIO-ORG'} | %{\n  New-Datastore -Host h316* -Name \"ds-vmfs-$($_.model)\" -Path $_.CanonicalName -Vmfs -FileSystemVersion 6 \n}\n\nName                               FreeSpaceGB      CapacityGB\n----                               -----------      ----------\nds-vmfs-disk1                            8.345           9.750\nds-vmfs-disk2                        2,046.312       2,047.750\nds-vmfs-disk0                            8.345           9.750<\/code><\/pre>\n\n\n\n<p>Looking in the vCenter web interface, we can see that all our storage has been presented.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/02\/image-2.png\"><img loading=\"lazy\" decoding=\"async\" width=\"852\" height=\"193\" src=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/02\/image-2.png\" alt=\"\" class=\"wp-image-2204\" srcset=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/02\/image-2.png 852w, https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/02\/image-2-300x68.png 300w, https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/02\/image-2-768x174.png 768w\" sizes=\"auto, (max-width: 852px) 100vw, 852px\" \/><\/a><\/figure>\n\n\n\n<p>Once we&#8217;ve placed filesystems on these disks, we can go back to the shell and see how much space is being used on disk.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>du -h \/data\/iscsi\/*.img\n29M     \/data\/iscsi\/disk0.img\n29M     \/data\/iscsi\/disk1.img\n62M     \/data\/iscsi\/disk2.img<\/code><\/pre>\n\n\n\n<p>We can see that the creation of a filesystem on these disks does consume some of the blocks (we are using several MB of disk, instead of the previous 0 bytes).  <\/p>\n\n\n\n<p>Adding extra LUNs to the iSCSI target is a straightforward process, requiring just a handful of commands.  An example can be found in the code block below:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo truncate -s 10T \/data\/iscsi\/disk3.img\nsudo targetcli\n# this should enter the targetcli shell\n\ncd \/backstores\/fileio\ncreate disk3 \/data\/iscsi\/disk3.img sparse=true write_back=false\n\ncd \/iscsi\/iqn.2025-02.com.example.iscsi:target01\/tpg1\/luns\ncreate \/backstores\/fileio\/disk3\n\nexit<\/code><\/pre>\n\n\n\n<p>The above code block shows the creation of a 10TB disk image, entering the <code>targetcli<\/code> shell, adding the newly created disk as a &#8216;fileio&#8217; option, and mapping that disk to our iSCSI target.  Finally we exit, which by default will save the configuration and make it persistent.  Refreshing storage on ESXi should cause the new LUN to appear.  Since we didn&#8217;t set the <code>is_nonrot<\/code> attribute, this device will appear as an HDD instead of a Flash device.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Growing the <code>\/data<\/code> btrfs filesystem<\/h2>\n\n\n\n<p>Our filesystem is currently backed by a 15GB disk.  We&#8217;ve allocated about 12TB of that, so it is grossly oversubscribed.  For a production system this would be a terrible idea, but for our lab\/illustration purposes this is probably fine.  At some point we may need to extend this filesystem to accommodate some growth.   I&#8217;ve grown ext3 and ext4 filesystems before but wanted to document how to do the extension for the <code>btrfs<\/code> filesystem used in this example.  I choose <code>btrfs<\/code> because it supports larger files, allowing us to create images as large as supported by ESXi (62TB).   The following code block will show how to extend this filesystem in the guest OS.  This assumes we have already increased the size of the disk in the vCenter web client, for illustration purposes we&#8217;ve extended the disk from 15GB to 20GB.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>df -h\n# shows that the filesystem did not autogrow\n\necho 1 | sudo tee \/sys\/class\/block\/sdb\/device\/rescan\n# rescans for disk changes\n\nsudo lsblk  \n# confirms disk is now seen as 20gb\n\nsudo btrfs device usage \/data\n# shows that device size is 20gb\n\nsudo btrfs filesystem resize max \/data\n# results in:\n# Resize device id 1 (\/dev\/sdb) from 15.00GiB to max\n\ndf -h \/data\n# confirm filesystem is now 15gb<\/code><\/pre>\n\n\n\n<p>The above commands rescanned our disk to be aware of the new size, then resized the filesystem to the size we defined in the hypervisor (20GB).<\/p>\n\n\n\n<p>To confirm this works as expected, we can refresh storage information for one of our NFS mounts.  The capacity should increase from 15GB to 20GB, as seen in the following screenshot.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/02\/image-3.png\"><img loading=\"lazy\" decoding=\"async\" width=\"735\" height=\"190\" src=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/02\/image-3.png\" alt=\"\" class=\"wp-image-2206\" srcset=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/02\/image-3.png 735w, https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/02\/image-3-300x78.png 300w\" sizes=\"auto, (max-width: 735px) 100vw, 735px\" \/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>Creating this storage server to support NFS 3, NFS 4.1, and iSCSI targets is relatively straightforward.  Having this pre-configured storage appliance can greatly streamline the process of testing various storage protocols, especially in virtual environments where quick deployment is key.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In my lab, I often need different types of storage to test various scenarios. For example, just last week someone asked about using New-Datastore with a specific version of VMFS and I needed to quickly perform a quick syntax check. &hellip; <a href=\"https:\/\/enterpriseadmins.org\/blog\/virtualization\/how-to-set-up-a-minimal-nfs-and-iscsi-storage-solution-using-ubuntu-24-04\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":6,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[9,4],"tags":[],"class_list":["post-2196","post","type-post","status-publish","format-standard","hentry","category-lab-infrastructure","category-virtualization"],"_links":{"self":[{"href":"https:\/\/enterpriseadmins.org\/blog\/wp-json\/wp\/v2\/posts\/2196","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/enterpriseadmins.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/enterpriseadmins.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/enterpriseadmins.org\/blog\/wp-json\/wp\/v2\/users\/6"}],"replies":[{"embeddable":true,"href":"https:\/\/enterpriseadmins.org\/blog\/wp-json\/wp\/v2\/comments?post=2196"}],"version-history":[{"count":8,"href":"https:\/\/enterpriseadmins.org\/blog\/wp-json\/wp\/v2\/posts\/2196\/revisions"}],"predecessor-version":[{"id":2209,"href":"https:\/\/enterpriseadmins.org\/blog\/wp-json\/wp\/v2\/posts\/2196\/revisions\/2209"}],"wp:attachment":[{"href":"https:\/\/enterpriseadmins.org\/blog\/wp-json\/wp\/v2\/media?parent=2196"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/enterpriseadmins.org\/blog\/wp-json\/wp\/v2\/categories?post=2196"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/enterpriseadmins.org\/blog\/wp-json\/wp\/v2\/tags?post=2196"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}