Cross-module GC, snapshot LRU eviction, and scheduled cleanup.
cocoon gc performs cross-module garbage collection:
This ensures blobs referenced by running VMs or saved snapshots are never deleted.
Every collected item is logged at INFO level with a structured key=value payload under gc.<module>, and a summary line ends the cycle. Sample:
INFO gc.snapshot collected id=XEOU... name=ubuntu-hot-testing:v1 bytes=3221225472 last_accessed=2026-04-12T10:30:00Z reason=lru-age
INFO gc.snapshot collected id=2GQVEA... name= bytes=0 last_accessed=never reason=orphan
INFO gc.cloudhypervisor collected id=ABC123 runDir=/var/lib/cocoon/run/cloudhypervisor/ABC123 logDir=/var/log/cocoon/cloudhypervisor/ABC123 reason=orphan-runDir
INFO gc.oci collected blob=b40150c1c2717d... reason=unreferenced
INFO gc.cni collected id=JKLMN netns=cocoon-JKLMN nics=2 reason=orphan
INFO gc.bridge collected id=MNOPQ iface=btMNOPQ-0 reason=orphan-tap
INFO gc.Run completed: cloudhypervisor=1 cni=1 oci=4 snapshot=3 (failures: 0, duration: 230ms)
Filter with awk / grep:
journalctl -u cocoon-gc.service --since today | grep "gc.snapshot.*reason=lru-"
journalctl -u cocoon-gc.service --since today | awk '/gc.Run completed/'
Reasons:
orphan (dataDir without DB record), stale-pending (Create crashed >24h ago), lru-all / lru-age / lru-keep / lru-size (multi-criterion uses + joiner)orphan-runDir, orphan-logDir, stale-creatingunreferencedorphan (netns without active VM)orphan-tapBare cocoon gc only reclaims orphans (on-disk data with no DB record) and stale pending records (crashed mid-Create, older than 24h). To also evict healthy snapshots by access recency, pass --snapshot:
| Flag | Effect |
|---|---|
--snapshot |
Enable LRU eviction. Bare flag = evict every non-pending snapshot. |
--snapshot-keep N |
Keep at most N most-recently-accessed snapshots. |
--snapshot-age DUR |
Evict snapshots last accessed before this duration (e.g. 720h for 30d). |
--snapshot-size SZ |
Evict oldest snapshots until total size ≤ this (e.g. 100GB). |
--snapshot-dry-run |
Log which snapshots would be LRU-evicted; act on nothing. Snapshot-only — orphans and other GC modules still execute. |
Sub-flags combine as union of evictions (intersection of kept) — a snapshot is kept only if it passes every active criterion. All sub-flags require --snapshot; negative values are rejected.
LastAccessedAt is updated on Restore, vm clone (via DataDir), snapshot export, and snapshot import (set to creation time). Inspect and list do not count as access.
# Preview what 30-day eviction would remove (snapshot-only — other GC modules still run)
cocoon gc --snapshot --snapshot-age=720h --snapshot-dry-run
# Production: weekly cleanup, keep 50 newest within 7 days
cocoon gc --snapshot --snapshot-age=168h --snapshot-keep=50
# Cap storage at 100GB
cocoon gc --snapshot --snapshot-size=100GB
# Nuke all snapshots (dev / test reset)
cocoon gc --snapshot
cocoon gc is a one-shot, lock-safe operation — drive periodic execution from a systemd timer or cron. Recommended template (systemd):
# /etc/systemd/system/cocoon-gc.service
[Unit]
Description=Cocoon snapshot GC (LRU eviction)
[Service]
Type=oneshot
ExecStart=/usr/local/bin/cocoon gc --snapshot --snapshot-age=168h --snapshot-keep=50
StandardOutput=journal
StandardError=journal
# /etc/systemd/system/cocoon-gc.timer
[Unit]
Description=Run cocoon snapshot GC daily
[Timer]
OnCalendar=daily
RandomizedDelaySec=1h
Persistent=true
Unit=cocoon-gc.service
[Install]
WantedBy=timers.target
Enable: systemctl enable --now cocoon-gc.timer.
For cron, drop a one-liner into /etc/cron.daily/cocoon-gc:
#!/bin/sh
exec /usr/local/bin/cocoon gc --snapshot --snapshot-age=168h --snapshot-keep=50