DigitalOcean Recipes
Production-ready examples combining multiple terraform-do-modules modules. Each recipe is self-contained — copy, adjust the locals, and run.
Recipe 1: VPC + DOKS cluster
A private Kubernetes cluster running inside a dedicated VPC with an additional app node pool.
What gets created:
- VPC with a private IP range
- DOKS cluster in high-availability mode
- System node pool + auto-scaling app node pool
# versions.tf
terraform {
required_version = ">= 1.6.0"
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = ">= 2.0"
}
}
}
variable "do_token" {
type = string
sensitive = true
}
provider "digitalocean" {
token = var.do_token
}
# main.tf
locals {
name = "myapp"
environment = "prod"
region = "nyc3"
}
# ── VPC ──────────────────────────────────────────────────────
module "vpc" {
source = "git::https://github.com/terraform-do-modules/terraform-digitalocean-vpc.git?ref=v1.0.0"
name = local.name
environment = local.environment
region = local.region
ip_range = "10.10.0.0/16"
description = "Production VPC"
}
# ── Kubernetes cluster ────────────────────────────────────────
module "kubernetes" {
source = "git::https://github.com/terraform-do-modules/terraform-digitalocean-kubernetes.git?ref=v1.0.0"
name = local.name
environment = local.environment
region = local.region
cluster_version = "1.31.1-do.5"
vpc_uuid = module.vpc.id
ha = true # high-availability control plane
auto_upgrade = false
critical_node_pool = {
name = "system"
size = "s-2vcpu-4gb"
node_count = 2
auto_scale = false
}
app_node_pools = {
app = {
name = "app"
size = "s-2vcpu-4gb"
node_count = 2
auto_scale = true
min_nodes = 2
max_nodes = 5
}
}
maintenance_policy = {
day = "sunday"
start_time = "03:00"
}
kubeconfig_path = "./kubeconfig"
}
# outputs.tf
output "cluster_id" {
value = module.kubernetes.id
}
output "cluster_endpoint" {
value = module.kubernetes.endpoint
}
output "kubeconfig" {
value = module.kubernetes.local_file
sensitive = true
}
Connect kubectl after apply:
terraform apply
terraform output -raw kubeconfig > ~/.kube/config
kubectl get nodes
Recipe 2: VPC + Droplet + Firewall + Load Balancer
A web server behind a load balancer with a restrictive firewall — the classic three-tier pattern on DigitalOcean.
What gets created:
- VPC
- Droplet (Ubuntu, monitoring + backups enabled)
- Firewall (allow ports 80 and 443 inbound, full egress)
- Load Balancer (HTTPS, HTTP-to-HTTPS redirect)
# main.tf
locals {
name = "web"
environment = "prod"
region = "nyc3"
}
# ── VPC ──────────────────────────────────────────────────────
module "vpc" {
source = "git::https://github.com/terraform-do-modules/terraform-digitalocean-vpc.git?ref=v1.0.0"
name = local.name
environment = local.environment
region = local.region
ip_range = "10.20.0.0/16"
}
# ── Droplet ───────────────────────────────────────────────────
module "droplet" {
source = "git::https://github.com/terraform-do-modules/terraform-digitalocean-droplet.git?ref=v1.0.0"
name = local.name
environment = local.environment
region = local.region
droplet_size = "s-2vcpu-2gb"
image_name = "ubuntu-22-04-x64"
vpc_uuid = module.vpc.id
monitoring = true
backups = true
enable_firewall = false # managed separately below
ssh_keys = {
deploy = {
name = "deploy-key"
public_key = file("~/.ssh/id_rsa.pub")
}
}
}
# ── Firewall ──────────────────────────────────────────────────
module "firewall" {
source = "git::https://github.com/terraform-do-modules/terraform-digitalocean-firewall.git?ref=v1.0.0"
name = local.name
environment = local.environment
droplet_ids = module.droplet.id
allowed_ports = ["80", "443"]
allowed_ip = ["0.0.0.0/0", "::/0"]
outbound_rule = [
{
protocol = "tcp"
port_range = "1-65535"
destination_addresses = ["0.0.0.0/0", "::/0"]
},
{
protocol = "udp"
port_range = "1-65535"
destination_addresses = ["0.0.0.0/0", "::/0"]
},
]
}
# ── Load Balancer ─────────────────────────────────────────────
module "load_balancer" {
source = "git::https://github.com/terraform-do-modules/terraform-digitalocean-load-balancer.git?ref=v1.0.0"
name = local.name
environment = local.environment
region = local.region
vpc_uuid = module.vpc.id
droplet_ids = module.droplet.id
lb_size = "lb-small"
enabled_redirect_http_to_https = true
forwarding_rule = [
{
entry_port = 443
entry_protocol = "https"
target_port = 443
target_protocol = "https"
},
{
entry_port = 80
entry_protocol = "http"
target_port = 80
target_protocol = "http"
},
]
healthcheck = [
{
port = 80
protocol = "http"
path = "/health"
},
]
}
# outputs.tf
output "droplet_ip" {
value = module.droplet.ipv4_address
}
output "load_balancer_ip" {
value = module.load_balancer.ip
}
Recipe 3: Spaces with versioning and CORS
Object storage bucket for static assets, with versioning and CORS policy.
# main.tf
locals {
name = "assets"
environment = "prod"
region = "nyc3"
}
module "spaces" {
source = "git::https://github.com/terraform-do-modules/terraform-digitalocean-spaces.git?ref=v1.0.0"
name = local.name
environment = local.environment
region = local.region
acl = "public-read"
versioning = true
force_destroy = false
cors_rule = [
{
allowed_headers = ["*"]
allowed_methods = ["GET", "HEAD"]
allowed_origins = ["https://yourdomain.com"]
expose_headers = ["ETag"]
max_age_seconds = 3000
},
]
}
# outputs.tf
output "bucket_domain" {
value = module.spaces.bucket_domain_name
}
output "bucket_urn" {
value = module.spaces.urn
}
Point a CDN endpoint at module.spaces.bucket_domain_name using the terraform-digitalocean-cdn module.
Variable reference for key modules
vpc
| Variable | Default | Description |
|---|---|---|
region | "blr1" | DigitalOcean region slug |
ip_range | "" | VPC CIDR — must be RFC1918, /16–/24 |
description | "VPC" | Free-text description (max 255 chars) |
Outputs: id, urn, created_at
kubernetes
| Variable | Default | Description |
|---|---|---|
cluster_version | "1.31.1-do.5" | DOKS version string |
vpc_uuid | "" | VPC ID to place the cluster in |
ha | true | High-availability control plane |
auto_upgrade | false | Auto-upgrade during maintenance window |
surge_upgrade | false | Surge upgrade (zero-downtime) |
critical_node_pool | {} | System/default node pool config |
app_node_pools | {} | Additional node pool map |
registry_integration | false | Integrate with DigitalOcean Container Registry |
Outputs: id, endpoint, local_file (kubeconfig, sensitive), token (sensitive), cluster_ca_certificate (sensitive)
droplet
| Variable | Default | Description |
|---|---|---|
droplet_size | "s-1vcpu-1gb" | Droplet size slug |
image_name | "ubuntu-22-04-x64" | Image name or slug |
droplet_count | 1 | Number of Droplets to create |
vpc_uuid | "" | VPC ID to place the Droplet in |
backups | false | Enable automated backups |
monitoring | false | Install the DO monitoring agent |
ipv6 | false | Enable IPv6 |
ssh_keys | {} | Map of SSH key objects |
inbound_rules | [] | Inbound firewall rules (when enable_firewall = true) |
Outputs: id, ipv4_address, ipv4_address_private, ipv6_address, name, status
firewall
| Variable | Default | Description |
|---|---|---|
droplet_ids | [] | Droplet IDs to attach the firewall to |
allowed_ports | [] | Inbound port numbers to allow |
allowed_ip | [] | Source IP/CIDR list for inbound rules |
protocol | "tcp" | Default protocol for inbound rules |
outbound_rule | full egress | List of outbound rule objects |
load_balancer_uids | [] | Load balancer URNs to attach |
kubernetes_ids | [] | Kubernetes cluster IDs to attach |
load-balancer
| Variable | Default | Description |
|---|---|---|
region | "blr-1" | Region for the load balancer |
vpc_uuid | "" | VPC to place the load balancer in |
droplet_ids | [] | Droplet IDs to balance traffic across |
lb_size | "lb-small" | lb-small, lb-medium, or lb-large |
forwarding_rule | [] | List of port forwarding rule objects |
healthcheck | [] | Health check configuration objects |
enabled_redirect_http_to_https | false | Redirect HTTP 80 → HTTPS 443 |
Outputs: id, ip, urn