This tutorial will teach you how to put ZFS on your external SSD/HDD/flash drive. Note that all the steps below were tested on FreeBSD 13.2-RELEASE. I will briefly introduce ZFS and describe each step in a bit of detail. If you are impatient, you can skip to the summary at the end of this post.
What is ZFS?
ZFS, or the Zettabyte File System, as it was previously known, is a combined file system and logical volume manager. Traditionally, file systems have a 1:1 mapping to disk partitions. For example, if you have a disk with three parititons, then each of those paritions needs its own, separate file system.
A logical volume manager can abstract different disks and/or parititions and present them as logical storage units on which you can put your file system. In other words, a file system can span multiple partitions or disks. ZFS is an all-in-one solution that does both of these functions for you: logical volume management and the actual file system.
Basic ZFS terminology and concepts
This is not meant to be comprehensive (or even accurate), but there are a few things you should know about how ZFS is different from traditional file systems:
- In ZFS terminology, a file system is called a dataset.
- You combine one or more disks to form a zpool. A zpool is a logical container for all of your storage space. You can then create one or more datasets from that zpool.
- zpool management is done with the zpool command, while dataset management is done with the zfs command.
- Unlike traditional partitions, you do not have to set a predefined size for your datasets. A dataset can use as much storage space as is available in your zpool.
Creating the zpool
For this demo, I'm going to use a 128GB USB flash drive. Insert your drive into an available USB port on your machine and check its device name with dmesg:
# dmesg
...
ugen1.5: <SanDisk Ultra> at usbus1
umass0 on uhub1
umass0: <SanDisk Ultra, class 0/0, rev 3.00/1.00, addr 5> on usbus1
umass0: SCSI over Bulk-Only; quirks = 0xc100
umass0:0:0: Attached to scbus0
da0 at umass-sim0 bus 0 scbus0 target 0 lun 0
da0: <SanDisk Ultra 1.00> Removable Direct Access SPC-4 SCSI device
da0: Serial Number 4C530000110623109545
da0: 400.000MB/s transfers
da0: 117312MB (240254976 512 byte sectors)
da0: quirks=0x2<NO_6_BYTE>
Here, da0 is our flash drive. Remember, before we can create a dataset (file system), we must have a zpool to allocate the storage from. We want to use the entire space on /dev/da0 (as opposed to a partition/slice like /dev/da0s1) as part of our zpool, so let's create the zpool and add da0 to it (this will destroy data on the drive, so make sure you back it up first):
# zpool create myexternalpool /dev/da0
I named the zpool myexternalpool, but you can use any other identifier you want. You can confirm the creation of the pool with the list and status sub-commands:
# zpool list
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
myexternalpool 114G 384K 114G - - 0% 0% 1.00x ONLINE -
zroot 472G 115G 357G - - 6% 24% 1.00x ONLINE -
# zpool status myexternalpool
pool: myexternalpool
state: ONLINE
config:
NAME STATE READ WRITE CKSUM
myexternalpool ONLINE 0 0 0
da0 ONLINE 0 0 0
errors: No known data errors
zroot in this case is the name of my machine's main zpool (composed of a single SSD).
Creating a dataset
Now that we have a pool to allocate storage from, let's create a file system (dataset) to store our files on:
# zfs create myexternalpool/myexternaldataset
By default, your new file system will be mounted at /<pool_name>/<dataset_name>/. In our case, that would be /myexternalpool/myexternaldataset/. This can also be confirmed in the output of the zfs list command (MOUNTPOINT column) or the zfs get mountpoint command:
# zfs list
NAME USED AVAIL REFER MOUNTPOINT
myexternalpool 704K 110G 104K /myexternalpool
myexternalpool/myexternaldataset 96K 110G 96K /myexternalpool/myexternaldataset
zroot 115G 343G 96K /zroot
zroot/ROOT 29.9G 343G 96K none
...
# zfs get mountpoint myexternalpool/myexternaldataset
NAME PROPERTY VALUE SOURCE
myexternalpool/myexternaldataset mountpoint /myexternalpool/myexternaldataset default
You can create multiple datasets if you want, but for the purposes of this demo, one will suffice. Go ahead and create some files on your shiny new file system:
# touch /myexternalpool/myexternaldataset/woofwoof.txt
# touch /myexternalpool/myexternaldataset/meow.txt
# ls /myexternalpool/myexternaldataset/
meow.txt woofwoof.txt
zpool export and import
Before you unplug your external drive, it is important, or even crucial, that you export the zpool first. To simplify things, you can think of exporting a pool as making it "ready to be migrated to another machine". Likewise, in order to access your data the next time you plug your drive in, you must import the pool first.
Since we just created a couple of empty text files on our dataset, let's do a zpool export:
# zpool export myexternalpool
# zpool list
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
zroot 472G 115G 357G - - 6% 24% 1.00x ONLINE -
Unplug your drive and plug it back in. Check dmesg to see your device name again (for the purposes of this demo, I first plugged in another USB flash drive so that our external ZFS drive gets a different device name than da0):
# dmesg
...
ugen1.6: <SanDisk Ultra> at usbus1
umass1 on uhub1
umass1: <SanDisk Ultra, class 0/0, rev 3.00/1.00, addr 8> on usbus1
umass1: SCSI over Bulk-Only; quirks = 0xc100
umass1:1:1: Attached to scbus1
da1 at umass-sim1 bus 1 scbus1 target 0 lun 0
da1: <SanDisk Ultra 1.00> Removable Direct Access SPC-4 SCSI device
da1: Serial Number 4C530000110623109545
da1: 400.000MB/s transfers
da1: 117312MB (240254976 512 byte sectors)
da1: quirks=0x2<NO_6_BYTE>
Now import the pool:
# zpool import -d /dev/da1 myexternalpool
# zpool list
NAME SIZE ALLOC FREE CKPOINT EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT
myexternalpool 114G 1.01M 114G - - 0% 0% 1.00x ONLINE -
zroot 472G 115G 357G - - 6% 24% 1.00x ONLINE -
# zfs list
NAME USED AVAIL REFER MOUNTPOINT
myexternalpool 1.01M 110G 104K /myexternalpool
myexternalpool/myexternaldataset 104K 110G 104K /myexternalpool/myexternaldataset
zroot 115G 343G 96K /zroot
zroot/ROOT 29.9G 343G 96K none
...
# ls -l /myexternalpool/myexternaldataset/
total 2
-rw-r--r-- 1 root wheel 0 Oct 29 06:58 meow.txt
-rw-r--r-- 1 root wheel 0 Oct 29 06:58 woofwoof.txt
And voila!
Summary
# echo "Create a zpool from the external drive"
# zpool create <poolname> /dev/<devicename>
# echo "Create a dataset on our pool"
# zfs create <poolname>/<datasetname>
# echo "Your dataset is now mounted at /<poolname>/<datasetname>/"
# echo "Write data to your dataset"
# echo "test" > /<poolname>/<datasetname>/sample.txt
# echo "Read data from your dataset"
# cat /<poolname>/<datasetname>/sample.txt
# echo "Before unplugging your drive, export the pool first"
# zpool export <poolname>
# echo "Next time you plug it in, import it first"
# zpool import -d /dev/<devicename> <poolname>