List icon
ForEach icon
If icon
Delete icon
Create icon
Get icon
SlackIncomingWebhook icon

Blue-Green DNS Cutover on DigitalOcean Domains

Automate blue-green DNS cutovers on DigitalOcean with Kestra. Remove stale A records, create a verified low-TTL replacement, and alert Slack either way.

Categories
CloudInfrastructure

Make switching production traffic between environments a one-click, audited operation instead of a nervous console edit. This blueprint performs a blue-green DNS cutover on a DigitalOcean-managed zone: it lists the zone, deletes every existing A record for the target hostname (including accidental duplicates), creates a fresh record pointing at the new environment with a 300 second TTL, reads the record back from the API to verify what DigitalOcean actually serves, and posts the verified result to Slack. If anything fails midway, a dedicated alert tells the team the zone may be half-applied and needs eyes now.

How it works

  1. list_records (io.kestra.plugin.digitalocean.domain.record.List) fetches all records in the zone with fetchType: FETCH.
  2. remove_stale_records (io.kestra.plugin.core.flow.ForEach) iterates the records; inside, match_record (io.kestra.plugin.core.flow.If) selects only rows where type is A and name equals the record_name input, and delete_record (domain.record.Delete) removes each match by ID. Deleting per record means a zone with duplicate A records, a common leftover from manual edits, still cuts over cleanly.
  3. create_record (domain.record.Create) writes the new A record with ttl: 300, so the next cutover or an emergency rollback propagates within minutes.
  4. verify_record (domain.record.Get) reads the record back by the ID returned from the create call; the Slack message quotes these verified values rather than the flow's inputs.
  5. notify announces success; the errors block sends an urgent alert explaining that the old record may already be gone.

What you get

  • A repeatable cutover that handles duplicate stale records instead of failing on them.
  • Verification through the API before anyone declares victory, quoting what the zone really contains.
  • A low TTL baked into every cutover, keeping rollback time bounded.
  • Inputs for zone, hostname, and target IP, so one flow serves every service you cut over.

Who it's for

  • Teams running blue-green or active-passive deployments on DigitalOcean droplets or Kubernetes behind DNS.
  • Operators who today edit A records by hand in the console during release nights.
  • Anyone building disaster-recovery runbooks where the DNS switch must be fast, logged, and reversible.

Why orchestrate this with Kestra

DNS cutovers are a sequence with failure modes, not a single call: find the current records, remove them, write the replacement, and prove it took effect. Done by hand, the dangerous window between delete and create is unbounded and unlogged. Kestra executes the sequence in seconds, records every step with timestamps in the execution history, alerts the moment the sequence breaks mid-flight, and turns rollback into re-running the same flow with the previous IP as input.

Prerequisites

  • A domain managed by DigitalOcean DNS (the zone must exist; the flow manages records, not zones).
  • A personal access token with read and write scopes for domains.
  • A Slack incoming webhook.

Secrets

  • DIGITALOCEAN_TOKEN: DigitalOcean personal access token.
  • SLACK_WEBHOOK_URL: Slack incoming webhook URL.

Quick start

  1. Add the two secrets above to your Kestra namespace.
  2. Execute the flow against a test hostname in your zone, passing your real domain, a scratch record name, and any IP.
  3. Confirm in the DigitalOcean console that exactly one A record exists for the hostname afterwards.
  4. Wire it into your deployment: call it via subflow or the API with the new environment's IP once health checks pass.

How to extend

  • Add an HTTP health check with io.kestra.plugin.core.http.Request against the target IP before the cutover, refusing to switch traffic to an unhealthy environment.
  • Handle AAAA records the same way by duplicating the match-and-delete branch with type == 'AAAA' and creating a second record.
  • Store the previous IP from list_records output as a flow output, giving the rollback runbook the exact value to re-apply.
  • Chain the cutover after a io.kestra.plugin.digitalocean.loadbalancer.Update when switching between load balancers instead of single droplets.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.