Claude-skill-registry digitalocean-coder
This skill guides writing DigitalOcean infrastructure with OpenTofu/Terraform. Use when provisioning Droplets, VPCs, Managed Databases, Firewalls, or other DO resources.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/digitalocean-coder" ~/.claude/skills/majiayu000-claude-skill-registry-digitalocean-coder && rm -rf "$T"
manifest:
skills/data/digitalocean-coder/SKILL.mdsource content
DigitalOcean Coder
Overview
DigitalOcean provides simple, developer-friendly cloud infrastructure. This skill covers OpenTofu/Terraform patterns for DO resources.
Provider Setup
terraform { required_providers { digitalocean = { source = "digitalocean/digitalocean" version = "~> 2.0" } } } provider "digitalocean" { # Token from environment: DIGITALOCEAN_TOKEN }
VPC (Virtual Private Cloud)
resource "digitalocean_vpc" "main" { name = "${var.project}-${var.environment}-vpc" region = var.region ip_range = "10.10.0.0/16" description = "VPC for ${var.project} ${var.environment}" }
Droplets (Compute)
Basic Droplet
resource "digitalocean_droplet" "app" { name = "${var.project}-${var.environment}-app" region = var.region size = var.droplet_size # s-1vcpu-1gb, s-2vcpu-4gb, etc. image = "ubuntu-22-04-x64" vpc_uuid = digitalocean_vpc.main.id ssh_keys = var.ssh_key_ids monitoring = true ipv6 = false tags = [var.project, var.environment] }
Droplet with Cloud-Init
resource "digitalocean_droplet" "app" { name = "${var.project}-app" region = var.region size = "s-1vcpu-2gb" image = "ubuntu-22-04-x64" vpc_uuid = digitalocean_vpc.main.id ssh_keys = var.ssh_key_ids monitoring = true user_data = <<-EOT #cloud-config package_update: true packages: - docker.io - docker-compose-plugin users: - name: deploy groups: docker sudo: ALL=(ALL) NOPASSWD:ALL shell: /bin/bash ssh_authorized_keys: - ${var.deploy_ssh_key} runcmd: - systemctl enable --now docker - sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config - systemctl restart sshd EOT tags = [var.project] }
Common Droplet Sizes
| Size | vCPUs | Memory | Use Case |
|---|---|---|---|
| 1 | 1 GB | Testing, small apps |
| 1 | 2 GB | Small production |
| 2 | 4 GB | Medium apps |
| 4 | 8 GB | Production workloads |
| 2 | 4 GB | CPU-intensive |
| 2 | 16 GB | Memory-intensive |
Reserved IP (Static IP)
resource "digitalocean_reserved_ip" "app" { region = var.region } resource "digitalocean_reserved_ip_assignment" "app" { ip_address = digitalocean_reserved_ip.app.ip_address droplet_id = digitalocean_droplet.app.id } output "app_ip" { value = digitalocean_reserved_ip.app.ip_address }
Firewall
Basic Web Server Firewall
resource "digitalocean_firewall" "web" { name = "${var.project}-web-firewall" droplet_ids = [digitalocean_droplet.app.id] # Inbound rules inbound_rule { protocol = "tcp" port_range = "22" source_addresses = var.ssh_allowed_ips # Restrict SSH access } inbound_rule { protocol = "tcp" port_range = "80" source_addresses = ["0.0.0.0/0", "::/0"] } inbound_rule { protocol = "tcp" port_range = "443" source_addresses = ["0.0.0.0/0", "::/0"] } # Outbound rules outbound_rule { protocol = "tcp" port_range = "all" destination_addresses = ["0.0.0.0/0", "::/0"] } outbound_rule { protocol = "udp" port_range = "all" destination_addresses = ["0.0.0.0/0", "::/0"] } outbound_rule { protocol = "icmp" destination_addresses = ["0.0.0.0/0", "::/0"] } }
Dynamic IP Whitelist
variable "db_allowed_ips" { type = list(string) default = [] description = "IPs allowed to access database directly" } resource "digitalocean_database_firewall" "postgres" { cluster_id = digitalocean_database_cluster.postgres.id # Always allow app droplet rule { type = "droplet" value = digitalocean_droplet.app.id } # Dynamic IP whitelist for developers dynamic "rule" { for_each = var.db_allowed_ips content { type = "ip_addr" value = rule.value } } }
Managed Database
PostgreSQL Cluster
resource "digitalocean_database_cluster" "postgres" { name = "${var.project}-${var.environment}-pg" engine = "pg" version = "16" size = var.db_size # db-s-1vcpu-1gb, db-s-2vcpu-4gb region = var.region node_count = 1 # Increase for HA # CRITICAL: Use private network private_network_uuid = digitalocean_vpc.main.id tags = [var.project, var.environment] } # Database firewall - restrict access resource "digitalocean_database_firewall" "postgres" { cluster_id = digitalocean_database_cluster.postgres.id rule { type = "droplet" value = digitalocean_droplet.app.id } } output "database_uri" { value = digitalocean_database_cluster.postgres.uri sensitive = true } output "database_private_uri" { value = digitalocean_database_cluster.postgres.private_uri sensitive = true }
Database Sizes
| Size | vCPUs | Memory | Storage | Use Case |
|---|---|---|---|---|
| 1 | 1 GB | 10 GB | Development |
| 1 | 2 GB | 25 GB | Small production |
| 2 | 4 GB | 38 GB | Production |
| 4 | 8 GB | 115 GB | High traffic |
Redis Cluster
resource "digitalocean_database_cluster" "redis" { name = "${var.project}-${var.environment}-redis" engine = "redis" version = "7" size = "db-s-1vcpu-1gb" region = var.region node_count = 1 private_network_uuid = digitalocean_vpc.main.id tags = [var.project] }
Spaces (Object Storage)
resource "digitalocean_spaces_bucket" "assets" { name = "${var.project}-assets" region = var.spaces_region # nyc3, sfo3, ams3, sgp1, fra1 acl = "private" # or "public-read" } resource "digitalocean_spaces_bucket_cors_configuration" "assets" { bucket = digitalocean_spaces_bucket.assets.id region = var.spaces_region cors_rule { allowed_headers = ["*"] allowed_methods = ["GET", "PUT", "POST"] allowed_origins = ["https://${var.domain}"] max_age_seconds = 3600 } } output "spaces_endpoint" { value = digitalocean_spaces_bucket.assets.bucket_domain_name }
DNS Records
resource "digitalocean_domain" "main" { name = var.domain } resource "digitalocean_record" "app" { domain = digitalocean_domain.main.id type = "A" name = "@" value = digitalocean_reserved_ip.app.ip_address ttl = 300 } resource "digitalocean_record" "www" { domain = digitalocean_domain.main.id type = "CNAME" name = "www" value = "@" ttl = 300 }
SSH Keys
# Reference existing key data "digitalocean_ssh_key" "deploy" { name = "deploy-key" } # Or create new key resource "digitalocean_ssh_key" "deploy" { name = "${var.project}-deploy" public_key = file("~/.ssh/deploy.pub") }
Complete Production Stack
locals { name_prefix = "${var.project}-${var.environment}" } # VPC resource "digitalocean_vpc" "main" { name = "${local.name_prefix}-vpc" region = var.region ip_range = "10.10.0.0/16" } # App Server resource "digitalocean_droplet" "app" { name = "${local.name_prefix}-app" region = var.region size = var.droplet_size image = "ubuntu-22-04-x64" vpc_uuid = digitalocean_vpc.main.id ssh_keys = [data.digitalocean_ssh_key.deploy.id] monitoring = true user_data = file("${path.module}/cloud-init.yaml") tags = [var.project, var.environment] } # Static IP resource "digitalocean_reserved_ip" "app" { region = var.region } resource "digitalocean_reserved_ip_assignment" "app" { ip_address = digitalocean_reserved_ip.app.ip_address droplet_id = digitalocean_droplet.app.id } # Firewall resource "digitalocean_firewall" "app" { name = "${local.name_prefix}-firewall" droplet_ids = [digitalocean_droplet.app.id] inbound_rule { protocol = "tcp" port_range = "22" source_addresses = var.ssh_allowed_ips } inbound_rule { protocol = "tcp" port_range = "80" source_addresses = ["0.0.0.0/0", "::/0"] } inbound_rule { protocol = "tcp" port_range = "443" source_addresses = ["0.0.0.0/0", "::/0"] } outbound_rule { protocol = "tcp" port_range = "all" destination_addresses = ["0.0.0.0/0", "::/0"] } outbound_rule { protocol = "udp" port_range = "all" destination_addresses = ["0.0.0.0/0", "::/0"] } } # Database resource "digitalocean_database_cluster" "postgres" { name = "${local.name_prefix}-pg" engine = "pg" version = "16" size = var.db_size region = var.region node_count = 1 private_network_uuid = digitalocean_vpc.main.id tags = [var.project] } resource "digitalocean_database_firewall" "postgres" { cluster_id = digitalocean_database_cluster.postgres.id rule { type = "droplet" value = digitalocean_droplet.app.id } } # Outputs output "app_ip" { value = digitalocean_reserved_ip.app.ip_address } output "database_uri" { value = digitalocean_database_cluster.postgres.private_uri sensitive = true }
Best Practices
Security
- Always use VPC for private networking
- Restrict database to droplet access only
- Use reserved IPs for consistent addressing
- Limit SSH to specific IPs via firewall
- Use cloud-init to disable root login
Cost Optimization
- Use appropriate droplet sizes
- Single-node databases for non-critical workloads
- Reserved IPs are free when assigned
- Monitor with built-in metrics
Networking
- Use
for database connectionsprivate_uri - Place all resources in same VPC
- Use firewall rules, not iptables
- Enable monitoring on droplets