Skip to main content

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

VariableDefaultDescription
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

VariableDefaultDescription
cluster_version"1.31.1-do.5"DOKS version string
vpc_uuid""VPC ID to place the cluster in
hatrueHigh-availability control plane
auto_upgradefalseAuto-upgrade during maintenance window
surge_upgradefalseSurge upgrade (zero-downtime)
critical_node_pool{}System/default node pool config
app_node_pools{}Additional node pool map
registry_integrationfalseIntegrate with DigitalOcean Container Registry

Outputs: id, endpoint, local_file (kubeconfig, sensitive), token (sensitive), cluster_ca_certificate (sensitive)


droplet

VariableDefaultDescription
droplet_size"s-1vcpu-1gb"Droplet size slug
image_name"ubuntu-22-04-x64"Image name or slug
droplet_count1Number of Droplets to create
vpc_uuid""VPC ID to place the Droplet in
backupsfalseEnable automated backups
monitoringfalseInstall the DO monitoring agent
ipv6falseEnable 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

VariableDefaultDescription
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_rulefull egressList of outbound rule objects
load_balancer_uids[]Load balancer URNs to attach
kubernetes_ids[]Kubernetes cluster IDs to attach

load-balancer

VariableDefaultDescription
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_httpsfalseRedirect HTTP 80 → HTTPS 443

Outputs: id, ip, urn