New to Kestra?
Use blueprints to kickstart your first workflows.
List DigitalOcean snapshots on a schedule and alert Slack naming the real stale ones still billing by the GB.
A droplet or volume snapshot taken before a risky change, a migration, or just as a habit does not expire on its own, DigitalOcean keeps billing for its full size every month until someone deletes it. Nothing in the control panel flags a snapshot as forgotten, it looks identical to one taken yesterday for a reason. This blueprint lists every snapshot on the account on a schedule, finds the real ones older than your own retention threshold, and alerts Slack naming them and their real billable size, never an invented number.
probe_snapshots (io.kestra.plugin.digitalocean.snapshot.List) lists every droplet
and volume snapshot on the account, with allowFailure: true so a bad or expired API
token does not stop the flow before it can be classified as PROBE_FAILED below.classify_snapshot_status (io.kestra.plugin.core.debug.Return) computes each
snapshot's real age from its created_at timestamp with fromdateiso8601, and
returns PROBE_FAILED when the probe did not return at all, CRITICAL or WARNING
from the real count older than stale_age_days_threshold, against the two
thresholds, HEALTHY otherwise.route_by_snapshot_status (io.kestra.plugin.core.flow.Switch) branches on that
status: HEALTHY logs it, the other three alert Slack, WARNING and CRITICAL name
the real stale snapshots with their real billable size in GB, never an invented list.
A defaults branch logs anything unexpected.log_audit_summary always runs last. The errors block posts to Slack if the flow
itself fails outside the gate logic above.snapshot.List across both droplet and volume snapshots in one call.DigitalOcean has no retention policy or alert for an aging snapshot, deleting one is always a manual, deliberate action. Kestra adds the schedule, the age threshold, and the Slack alert naming the real snapshots as one declarative, auditable flow, instead of a manual sweep someone has to remember to run.
DIGITALOCEAN_TOKEN: personal access token used by the probe task.
SLACK_WEBHOOK: Slack incoming webhook URL used by the alert tasks and the errors
block.
In Kestra OSS, set these as base64-encoded environment variables, for example
SECRET_DIGITALOCEAN_TOKEN, SECRET_SLACK_WEBHOOK.
stale_age_days_threshold (INT, default 90): Snapshot age in days at or above which it is classified stale.stale_count_warning_threshold (INT, default 1): Number of stale snapshots triggering a WARNING classification.stale_count_critical_threshold (INT, default 5): Number of stale snapshots triggering a CRITICAL classification.{{ outputs.probe_snapshots.total }}: Total number of droplet and volume snapshots discovered across all pages.{{ outputs.probe_snapshots.rows }}: List of snapshot maps containing id, name, created_at, size_gigabytes, and resource_type.{{ outputs.classify_snapshot_status.value }}: The resulting staleness classification (HEALTHY, WARNING, CRITICAL, or PROBE_FAILED).stale_age_days_threshold to a value below that snapshot's real age, run this
flow manually, it should report WARNING naming that exact snapshot.HEALTHY.every_monday trigger.auto_remediate and dry_run
pair, never a bulk delete of everything found stale.size_gigabytes across the stale snapshots and multiply by DigitalOcean's current
per-GB snapshot storage rate to put a real monthly cost figure in the Slack alert.resource_id against a current list of droplet or volume IDs to flag
a snapshot whose source resource was already deleted, the snapshot it came from no
longer exists to restore back onto.size_gigabytes is the snapshot's own billable size, not the size of the droplet or
volume it was taken from, a mostly-empty disk can still produce a small, cheap
snapshot.probe_snapshots follows
pagination automatically, but an account with a very large number of snapshots will
make more than one API call per run.