dbjpanda/digitalocean-terraform-skill · Archived

digitalocean-terraform

Write, review, and debug Terraform/OpenTofu configuration for DigitalOcean using the digitalocean/digitalocean provider. Bundles the complete provider reference (70 resources, 61 data sources) plus cross-cutting gotchas and composed multi-resource stacks. Use this whenever the user is working with DigitalOcean infrastructure as code — Droplets, VPCs, Cloud Firewalls, Load Balancers, DOKS/Kubernetes clusters, managed databases (Postgres/MySQL/Valkey/MongoDB/Kafka/OpenSearch), Spaces object stora…

First seen Aug 12, 2026

Installation

$ npx skills add dbjpanda/digitalocean-terraform-skill --skill digitalocean-terraform

Summary

  • Write, review, and debug Terraform/OpenTofu configuration for DigitalOcean using the digitalocean/digitalocean provider.
  • Bundles the complete provider reference (70 resources, 61 data sources) plus cross-cutting gotchas and composed multi-resource stacks.
  • Use this whenever the user is working with DigitalOcean infrastructure as code — Droplets, VPCs, Cloud Firewalls, Load Balancers, DOKS/Kubernetes clusters, managed databases (Postgres/MySQL/Valkey/MongoDB/Kafka/OpenSearch), Spaces object storage, App Platform, CDN, reserved/floating IPs, DNS records, volumes, snapshots, container registry, monitoring and uptime alerts, or GradientAI — even if they don't name the provider explicitly.
  • Also use when they mention .tf files that reference `digitalocean_*` resources, a DIGITALOCEAN_TOKEN, doctl-managed infrastructure being moved to Terraform, or ask why a DigitalOcean plan shows perpetual drift.

Stronger alternatives

This repository is archived — consider an actively maintained alternative.

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

License LICENSE
Default branch main
Open issues 0
Status Archived

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 10,604 B
  • docs SUMMARY.md 937 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 1 installs

SKILL.md

DigitalOcean Terraform provider

Everything here is derived from the official digitalocean/digitalocean provider docs at v2.99.1, bundled verbatim under references/provider-docs/. Those files are authoritative on argument names, types, defaults, and import syntax — prefer them over recollection, because this provider changes often and several of its resources have near-identical siblings that take different arguments.

Start here

**Never write digitalocean_* HCL from memory.** The failure mode is not a clear error — it is configuration that looks correct, passes review, and then either fails at apply or produces a plan that never converges. Confirm arguments first; it costs one command.

# What exists? (fuzzy, matches resources and data sources)
python3 scripts/dodocs.py list database

# Exact arguments and attributes for one resource
python3 scripts/dodocs.py args droplet

# Just the example, or just the import syntax
python3 scripts/dodocs.py example loadbalancer
python3 scripts/dodocs.py import database_db

# Which resources mention an argument at all?
python3 scripts/dodocs.py search vpc_uuid

# Data source rather than resource, when both names exist
python3 scripts/dodocs.py args kubernetes_cluster --kind data

Run from the skill directory. Names work with or without the digitalocean_ prefix, and a wrong guess suggests near matches instead of failing silently.

The files

File Read it when
references/gotchas.md Before writing or reviewing any config. Cross-cutting traps: perpetual drift, forced replacement, deprecated arguments, Spaces credentials. Short, and prevents most real-world breakage.
references/patterns.md You need more than one resource wired together — web tier, database + firewall, DOKS, Spaces + CDN, App Platform, projects, imports.
references/resource-index.md You know the goal but not the resource name. All 131 entries with one-line purposes, grouped by subcategory.
references/deprecations.md Auditing an inherited config, or a plan won't converge. All nine deprecated arguments quoted from the provider's compiled schema — these messages are what terraform validate prints, and several are more specific than the markdown docs.
references/provider-docs/ You need exact arguments. Upstream docs verbatim: resources/<name>.md, data-sources/<name>.md, plus index.md for provider configuration.

Provider setup

terraform {
  required_providers {
    digitalocean = {
      source  = "digitalocean/digitalocean"
      version = "~> 2.99"
    }
  }
}

provider "digitalocean" {
  token = var.do_token # or the DIGITALOCEAN_TOKEN environment variable
}

The token grants full account access, so keep it in an environment variable or a secret manager — never committed, and never a default value on a variable.

Spaces is the exception that catches everyone. Spaces resources authenticate with S3-style keys, not the API token. Without spacesaccessid / spacessecretkey (or SPACESACCESSKEYID / SPACESSECRETACCESSKEY), Spaces resources fail while every other resource applies normally.

Other provider arguments worth knowing: requestspersecond (throttle to avoid rate limits on large applies), httpretrymax, apiendpoint, spacesendpoint. Full list in references/provider-docs/index.md.

Where things live

The provider's own subcategories, with what each covers:

Area Covers
Networking (33) VPC, peering, NAT gateway, Cloud Firewall, Load Balancer, certificates, reserved IP/IPv6 (+assignments), DNS domains and records, CDN, BYOIP, Partner Network Connect
Databases (28) Managed clusters, users, DBs, connection pools, DB firewalls, replicas, online migration, log sinks, and per-engine config resources (mysql, postgresql, valkey, redis, mongodb, kafka, opensearch)
Account (13) Projects and project resources, tags, SSH keys, account/region/size lookups
Spaces Object Storage (12) Buckets, objects, policies, CORS, logging, access keys
Droplets (8) Droplets, autoscale pools, custom images, image lookups
Dedicated Inference (8) Dedicated inference endpoints, tokens, GPU model config, sizes
NFS Storage (7) NFS shares, access points, snapshots, attachments
Backups & Snapshots (5) Droplet and volume snapshots
Kubernetes (4) DOKS clusters, node pools, version lookups
Volumes Block Storage (3) Volumes and volume attachments
Monitoring (3) Metric alerts, uptime checks, uptime alerts
Container Registry (3) Registry and Docker credentials
GradientAI (2) GradientAI agents and knowledge bases
App Platform (2) digitalocean_app and its data source

Full per-resource breakdown with purposes: references/resource-index.md.

The traps that actually bite

These cause the majority of broken DigitalOcean configurations. Details and fixes in references/gotchas.md.

  1. Attach exactly one way. Volumes, reserved IPs, reserved IPv6s, and floating

IPs can each be attached inline on the Droplet or through a separate attachment / assignment resource. Using both makes every plan show changes forever. Setting volume_ids also means Terraform claims the Droplet's entire volume set.

  1. Floating IPs are now reserved IPs. digitaloceanfloatingip* is deprecated;

use digitaloceanreservedip*.

  1. ssh_keys cannot change after creation. It takes key IDs or fingerprints, not

key material, and editing it destroys and recreates the Droplet.

  1. Don't create a DOKS cluster and its Kubernetes resources in one module.

Terraform evaluates provider blocks before resources exist. Split into two applies.

  1. **Database *_config resources don't unset remote settings on destroy.** They only

leave state. Reset values explicitly first.

  1. Managed Redis was discontinued (30 June 2025). Use Valkey for anything new.
  2. certificate_id can never converge — it is a drift bug, not a style nit.

digitaloceancertificate.id is the certificate's name, not its UUID, so the field permanently disagrees with the API. Attach by certificatename, and rotate with a new name plus createbeforedestroy.

  1. Set disableletsencryptdnsrecords = true when a load balancer has a

Let's Encrypt cert and you also manage that hostname's DNS record in Terraform — otherwise DigitalOcean's auto-created record fights yours.

  1. force_destroy on a Spaces bucket defaults to false, so destroy fails on a

non-empty bucket.

  1. Projects take URNs (.urn), not IDs.
  2. Singular data sources error on zero or multiple matches. Use the plural forms

(digitaloceandroplets, digitaloceanimages, …) for lists.

  1. **Renaming a volume's filesystemtype to initialfilesystem_type destroys the

volume and its data.** The replacement is ForceNew and never read back, so on an existing volume it always diffs null → "ext4" and forces replacement. Delete the deprecated argument instead of renaming it (gotchas.md §12b).

Working approach

Writing new configuration

  1. Find the resource — dodocs.py list <term> or references/resource-index.md.
  2. Read its real arguments — dodocs.py args <name>. Note which are (Required) and

which say "Changing this forces a new resource".

  1. If more than one resource is involved, check references/patterns.md for the

composed shape before assembling it yourself.

  1. Scan references/gotchas.md for anything touching the resources you used.
  2. Look up regions, sizes, and versions with data sources rather than hardcoding

slugs — availability changes per account and region.

  1. Verify: terraform fmt, then terraform validate, then terraform plan.

Reviewing or debugging existing configuration

  • Plan never converges / always shows changes → check three things, in this order:

the attach-once rule (gotchas.md §1), certificateid on a load balancer or CDN (§8 — the field is structurally incapable of converging), and *config resources (§5). A config can have more than one cause at once, so keep looking after the first hit.

  • Cleaning up deprecated arguments on a live resource → check gotchas.md §12b

first. The obvious fix for a volume's filesystem_type destroys the volume. Read the plan for must be replaced before applying, and stop on a -/+ against anything holding data.

  • Unexpected destroy/recreate → look for ssh_keys edits, or an argument the docs

mark as forcing a new resource. dodocs.py args <name> shows these.

  • Auth failures on only some resources → missing Spaces credentials.
  • Copied from a blog post → check the deprecated-argument table in gotchas.md §9;

privatenetworking, algorithm, certificateid, and app routes/cors are the usual survivors.

Verification honesty: terraform validate checks syntax and types but does not contact the API, so it cannot confirm that a region supports a size or that a slug exists. Only terraform plan (with credentials) does. Say which one you actually ran rather than implying more coverage than you have.

Version note

Bundled docs are v2.99.1 (published 2026-08-06). If the user pins a different major or a much newer minor version, arguments may differ — check the upstream repo at github.com/digitalocean/terraform-provider-digitalocean under docs/, or the registry, and say so rather than assuming these docs still apply.