How to add a mirror to a single ZFS disk

tl;dr: zpool attach data /dev/disk/by-partlabel/zfs-3a1xx /dev/sdx0. Adjust for your own pool and disks.

A mirror is a Very Good Thing

I got a second identical disk to add as a ZFS mirror to my existing setup.

Redundancy FTW. ZFS is king and queen. Needs at least two disks to reap the full benefit (though a single-disk setup is still rad - CoW with snapshots and send/receive are worth it, and NixOS makes it easy to set up).

It’s all about the disk path

The basic command is (as a super user, or using sudo):

zpool attach name-of-the-pool disk-you-already-had disk-you-want-to-add

On Linux, with a new drive, no need for formatting or any other disk preparation. Just plug it in.

But how to specify which disks to use in the command? I saw several guides that just used /dev/sdx, but it didn’t work.

I don’t know what it is about my setup that differs from the guides I found online (if you have a hint, lmk), so YMMV, but below is some of what I tried and what eventually worked, mostly for my own reference.

In my setup, /dev/sdb was my existing drive and /dev/sda is the one I was trying to add (these names are determined at boot time and can vary from boot to boot - ZFS takes care of finding things again on the back end after you’ve added a drive, regardless of the label you used to add it, so using the /dev/sdx name is just fine in many situations).

However, using

zpool attach data /dev/sdb dev/sda

gave me:

cannot attach /dev/sda to /dev/sdb: no such device in pool

I tried a bunch of different things, from just the lsblk name with the partition (“sdb1”), to by-id (“ata-ST1400…”), to full path combinations, etc.

What finally worked was the by-partlabel identifier for the existing disk, combined with the /dev/sdx0 name of the new one.

A little zdb -l on the existing drive gave me the path I wanted:

zdb -l /dev/sdb1
    version: 5000
    name: 'data'
    state: 0
    txg: 170113
    pool_guid: 8237228336559358688
    errata: 0
    hostid: 3236406100
    hostname: 'dell7050'
    top_guid: 11182661003591634341
    guid: 11182661003591634341
    vdev_children: 1
        type: 'disk'
        id: 0
        guid: 11182661003591634341
        path: '/dev/disk/by-partlabel/zfs-3a1e459c75dc9b74'
        whole_disk: 1
        metaslab_array: 128
        metaslab_shift: 34
        ashift: 12
        asize: 14000504438784
        is_log: 0
        DTL: 3865
        create_txg: 4
    labels = 0 1 2 3

So I tried:

zpool attach data /dev/disk/by-partlabel/zfs-3a1e459c75dc9b74 /dev/sda1

Great success

->> zpool status
  pool: data
 state: ONLINE
status: One or more devices is currently being resilvered.  The pool will
        continue to function, possibly in a degraded state.
action: Wait for the resilver to complete.
  scan: resilver in progress since Thu Oct 19 17:05:26 2023
        2.33T scanned at 1.90G/s, 252G issued at 205M/s, 3.77T total
        252G resilvered, 6.53% done, 04:59:42 to go

        NAME                      STATE     READ WRITE CKSUM
        data                      ONLINE       0     0     0
          mirror-0                ONLINE       0     0     0
            zfs-3a1e459c75dc9b74  ONLINE       0     0     0
            sda1                  ONLINE       0     0     0  (resilvering)

errors: No known data errors

…and the next day, after resilvering completed (and a reboot to make sure the disk came online):

->> zpool status
  pool: data
 state: ONLINE
  scan: resilvered 3.77T in 05:13:53 with 0 errors on Thu Oct 19 22:19:19 2023

        NAME                      STATE     READ WRITE CKSUM
        data                      ONLINE       0     0     0
          mirror-0                ONLINE       0     0     0
            zfs-3a1e459c75dc9b74  ONLINE       0     0     0
            zfs-e2937e0fc8ebe95a  ONLINE       0     0     0

errors: No known data errors

(notice that what was sda1 is now referred to by partlabel)