How to Safely Delete ZFS Snapshots With Complex Clone Dependencies While Preserving Filesystems


2 views

When working with ZFS snapshots and clones, we often encounter complex dependency chains where destroying a snapshot requires understanding its relationships with both filesystems and other snapshots. Let's analyze the given scenario:

# zfs list -t all -o name,origin,clones
tank/containers/five         tank/containers/two@B        -
tank/containers/two          tank/containers/six@A        -
tank/containers/six          tank/containers/three@F      -
tank/containers/three        tank/containers/one@E        -
tank/containers/one@E        -                            tank/containers/three
tank/containers/two@B        -                            tank/containers/five
tank/containers/six@A        -                            tank/containers/two
tank/containers/six@C        -                            tank/containers/four
tank/containers/three@F      -                            tank/containers/six

The fundamental problem is that ZFS prevents snapshot deletion when clones exist, even if those clones are themselves snapshots. This creates a catch-22 situation where we want to:

  • Delete all snapshots (A, B, C, E, F)
  • Preserve all active filesystems (one, two, three, four, five, six)
  • Handle the interdependencies gracefully

The promotion strategy needs to be applied systematically. Here's a step-by-step method that works with this dependency chain:

# First promote all cloned filesystems to break dependencies
zfs promote tank/containers/five
zfs promote tank/containers/two
zfs promote tank/containers/six
zfs promote tank/containers/three

# Verify the new relationships
zfs list -t all -o name,origin,clones

# Now attempt to destroy snapshots in reverse dependency order
zfs destroy tank/containers/three@F
zfs destroy tank/containers/six@A
zfs destroy tank/containers/six@C
zfs destroy tank/containers/two@B
zfs destroy tank/containers/one@E

Some scenarios require additional care. For example, when dealing with snapshot clones of snapshots:

# When encountering snapshot clones like tank/containers/two@B
# You'll need to first promote its clone (tank/containers/five)
# Then destroy the intermediate snapshot:
zfs promote tank/containers/five
zfs destroy tank/containers/two@B

For more complex scenarios, consider using bookmarks as an intermediate step:

# Create bookmarks for critical points
zfs bookmark tank/containers/three@F tank/containers/three#F
zfs bookmark tank/containers/six@A tank/containers/six#A

# Then promote and destroy as needed
zfs promote tank/containers/six
zfs destroy tank/containers/three@F

After completing the operations, verify the filesystem integrity:

# Check remaining snapshots
zfs list -t snapshot

# Verify filesystems are intact
zfs list -t filesystem

# Optional: Recreate any needed snapshots
zfs snapshot tank/containers/one@clean
zfs snapshot tank/containers/two@clean

For environments with frequent snapshot rotations, consider scripting this process:

#!/bin/bash
# Sample automation script for snapshot cleanup
for fs in $(zfs list -H -o name -t filesystem | grep "^tank/containers/"); do
    zfs promote "$fs" 2>/dev/null
done

for snap in $(zfs list -H -o name -t snapshot | grep "^tank/containers/"); do
    zfs destroy -v "$snap"
done

When working with ZFS snapshots and clones, you'll often encounter complex dependency chains that prevent straightforward deletion. Let's analyze the specific case:

tank/containers/one@E → tank/containers/three → tank/containers/three@F → tank/containers/six
tank/containers/six@A → tank/containers/two → tank/containers/two@B → tank/containers/five
tank/containers/six@C → tank/containers/four

The main issue arises when trying to delete snapshots that have active clones. ZFS rightfully prevents this to maintain data integrity. The error message:

# zfs destroy tank/containers/six@A
cannot destroy 'tank/containers/six@A': snapshot has dependent clones
use '-R' to destroy the following datasets:
tank/containers/five
tank/containers/two@B
tank/containers/two

Here's how to safely remove all snapshots while preserving the filesystems:

# First, promote all clones to break dependencies
zfs promote tank/containers/five
zfs promote tank/containers/two
zfs promote tank/containers/four
zfs promote tank/containers/six
zfs promote tank/containers/three

# Verify the promotion worked
zfs list -t all -o name,origin,clones

# Now safely delete all snapshots
zfs destroy tank/containers/one@E
zfs destroy tank/containers/three@F
zfs destroy tank/containers/six@A
zfs destroy tank/containers/six@C
zfs destroy tank/containers/two@B

1. Promotion Order Matters: Always promote from the most dependent clone upward

2. Verify Filesystem Integrity: After promotion, check that all data remains accessible

3. Space Implications: Promoting clones may temporarily increase storage usage

If you're comfortable with a more aggressive approach (and have verified backups):

# Destroy all snapshots recursively while preserving filesystems
for snap in $(zfs list -H -t snapshot -o name | grep 'tank/containers'); do
    zfs destroy -R $snap 2>/dev/null
done

# Then clean up any remaining dependencies
zfs list -t all -o name,origin | grep -v "-" | awk '{print $1}' | while read fs; do
    zfs promote $fs
done

For systems with frequent snapshot cleanup needs, consider this bash function:

function zfs_clean_snapshots() {
    local pool=$1
    zfs list -H -t snapshot -o name $pool | while read snap; do
        if ! zfs destroy -vn $snap | grep -q "would destroy"; then
            zfs list -H -o name,origin | grep "$snap" | cut -f1 | while read clone; do
                zfs promote $clone
            done
            zfs destroy $snap
        fi
    done
}