Freeze a volume in an instant by sharing blocks — and only copy one the moment someone writes to it.
A copy-on-write snapshot captures a volume at a point in time without copying any data. Taking it is instant: the snapshot simply records pointers to the same blocks the live volume already uses. Nothing moves, nothing duplicates — the two views share every block.
The copy happens lazily. When a write lands on a block that’s shared with a snapshot, the system allocates a fresh block for the new value and leaves the old one pinned for the snapshot to keep reading. Only blocks that actually change ever get copied, so a snapshot’s extra space grows with churn, not with volume size.
Creating a snapshot just shares references — it never touches the underlying data. A write checks whether the target block is still shared; if so, it copies the block first, then writes into the private copy. Untouched blocks stay shared forever.
# Take a snapshot: share refs, copy nothing
def take_snapshot(volume):
snap = Snapshot()
for i, block in enumerate(volume.blocks):
snap.ptr[i] = block # point at the SAME block
block.shared = True # mark it shared
return snap # 0 blocks copied — instant
# Write a block: copy only if shared (copy-on-write)
def write(volume, i, value):
block = volume.blocks[i]
if block.shared: # snapshot still needs the old value
new = allocate_block() # one extra block consumed
new.data = value
volume.blocks[i] = new # live moves on…
block.shared = False # …old block stays pinned for the snapshot
else:
block.data = value # private already — write in place
The snapshot keeps reading the old, pinned blocks; the live volume reads its fresh copies. Space used by the snapshot equals the number of blocks the live volume has overwritten since — the delta, nothing more.
| Dimension | What to know |
|---|---|
| Create cost | Constant time — share pointers, copy zero data. Snapshots are effectively instant. |
| Signal | Snapshot size grows only with the delta (changed blocks), not with the volume’s total size |
| Signal | The copy-on-write counter ticks up on the first write to each shared block, then stops |
| Write amplification | The first write to a shared block costs an allocate + copy; later writes to it are in place |
| Reclaim | Pinned old blocks free up only when the snapshot is deleted — space returns to the pool then |
A database volume holds 6 blocks. At 02:00 a nightly snapshot is taken for backup — it completes in milliseconds because all it does is record 6 pointers at the existing blocks; 0 blocks copied. Through the day two rows change: a write to block 2 triggers copy-on-write (allocate a new block for the live value, keep the old one pinned for the snapshot), and later a write to block 4 does the same. The snapshot now uses exactly 2 blocks of extra space and still reads the morning’s consistent values, while the live volume shows the new ones. When the backup finishes and the snapshot is deleted, those 2 pinned blocks are reclaimed and space returns to the pool.
A 500 GB volume has a fresh snapshot taken with no writes since. Roughly how much extra space does the snapshot use?
Why might deleting an old, unused snapshot suddenly free a lot of space?