SuperAPI AI Directory

digitalocean-terraform

Deployment & operations

View repository

Updated 10-15 20:34

About this skill

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

```hcl
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 spaces_access_id / spaces_secret_key (or SPACES_ACCESS_KEY_ID / SPACES_SECRET_ACCESS_KEY), Spaces resources fail while every other resource applies normally.

Other provider arguments worth knowing: requests_per_second (throttle to avoid rate limits on large applies), http_retry_max, api_endpoint, spaces_endpoint. 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.
  2. Floating IPs are now reserved IPs. digitalocean_floating_ip* is deprecated; use digitalocean_reserved_ip*.
  3. ssh_keys cannot change after creation. It takes key IDs or fingerprints, not key material, and editing it destroys and recreates the Droplet.
  4. Don't create a DOKS cluster and its Kubernetes resources in one module. Terraform evaluates provider blocks before resources exist. Split into two applies.
  5. Database *_config resources don't unset remote settings on destroy. They only leave state. Reset values explicitly first.
  6. Managed Redis was discontinued (30 June 2025). Use Valkey for anything new.
  7. certificate_id can never converge — it is a drift bug, not a style nit. digitalocean_certificate.id is the certificate's name, not its UUID, so the field permanently disagrees with the API. Attach by certificate_name, and rotate with a new name plus create_before_destroy.
  8. Set disable_lets_encrypt_dns_records = 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.
  9. force_destroy on a Spaces bucket defaults to false, so destroy fails on a non-empty bucket.
  10. Projects take URNs (.urn), not IDs.
  11. Singular data sources error on zero or multiple matches. Use the plural forms (digitalocean_droplets, digitalocean_images, …) for lists.

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".
  3. If more than one resource is involved, check references/patterns.md for the composed shape before assembling it yourself.
  4. Scan references/gotchas.md for anything touching the resources you used.
  5. Look up regions, sizes, and versions with data sources rather than hardcoding slugs — availability changes per account and region.
  6. 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 (§1), certificate_id on a load balancer or CDN (§7 — the field is structurally incapable of converging), and *_config resources. A config can have more than one cause at once, so keep looking after the first hit.
  • 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; private_networking, algorithm, certificate_id, 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.