Skip to content

Provision DigitalOcean with Terraform

The runestack/rune/digitalocean module provisions a single Rune node on DigitalOcean: droplet, firewall, cloud-init install, optional project attachment, and an optional in-module rune admin bootstrap that hands you a ready-to-paste rune login command.

If you want to do the same install by hand, follow Deploy on a DigitalOcean droplet instead. This guide is the Terraform path.

Worker node, no TLS, no bootstrap. Useful for trying the module end-to-end before adding edge ingress.

terraform {
required_version = ">= 1.5.0"
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = ">= 2.40, < 3.0"
}
}
}
data "digitalocean_ssh_key" "main" {
name = "my-laptop"
}
module "rune" {
source = "runestack/rune/digitalocean"
version = "0.0.3"
ssh_key_ids = [data.digitalocean_ssh_key.main.id]
node_role = "worker"
}
output "ip" {
value = module.rune.ipv4_address
}
Terminal window
terraform init
terraform apply

When the apply finishes, SSH in and bootstrap manually:

Terminal window
ssh root@$(terraform output -raw ip) \
'rune admin bootstrap --out-file /tmp/rune-admin.token'

Same recipe, with edge ingress and Let’s Encrypt:

module "rune" {
source = "runestack/rune/digitalocean"
version = "0.0.3"
ssh_key_ids = [data.digitalocean_ssh_key.main.id]
node_role = "edge"
acme_email = "[email protected]"
}

What changes vs. the worker example:

  • Firewall opens :80 and :443 to 0.0.0.0/0 so the ACME HTTP-01 challenge can complete.
  • The droplet binds privileged ports as the unprivileged rune user via cap_net_bind_service — set up by the installer.
  • The ACME orchestrator is enabled and registers an account with Let’s Encrypt using acme_email.

Once the droplet is up, point your DNS at it:

api.example.com. IN A <module.rune.ipv4_address>

then deploy a service with expose.tls.mode: auto (see Expose a service).

Set bootstrap = true and the module SSHes in, runs rune admin bootstrap, copies the token to local disk, and prints a ready-to-paste rune login command:

module "rune" {
source = "runestack/rune/digitalocean"
version = "0.0.3"
ssh_key_ids = [data.digitalocean_ssh_key.main.id]
node_role = "edge"
acme_email = "[email protected]"
bootstrap = true
bootstrap_ssh_private_key = file("~/.ssh/id_ed25519")
bootstrap_token_path = "rune-admin.token"
}
output "login" {
value = module.rune.rune_login_command
}

After terraform apply:

Terminal window
$(terraform output -raw login)
rune get nodes

The token file is written to bootstrap_token_path (default ./rune-admin.token) and is reusable on the same machine.

VariableDefaultWhat it does
ssh_key_ids— (required)DigitalOcean SSH key IDs / fingerprints to install on the droplet.
node_roleedgeedge opens 80/443 + runs ACME; worker skips both.
acme_email""Let’s Encrypt account email. Required for ACME on edge nodes.
regionlon1DigitalOcean region slug.
droplet_sizes-2vcpu-4gbDroplet size. Edge nodes terminating TLS should be at least s-1vcpu-2gb.
imageubuntu-24-04-x64Base image. Tested on Ubuntu 24.04 LTS.
rune_versionv0.0.1-dev.22Release tag passed to install-server.sh. Bump per Rune release.
cluster_cidr10.96.0.0/16CIDR used by the Rune networking layer.
ssh_allowed_cidrs["0.0.0.0/0", "::/0"]Tighten in production.
api_allowed_cidrs["0.0.0.0/0", "::/0"]Locks down the gRPC + HTTP API ports.
bootstrapfalseRun rune admin bootstrap automatically after cloud-init.
bootstrap_ssh_private_key""Required (PEM, sensitive) when bootstrap = true.
enable_backupsfalseWeekly droplet backups.
enable_monitoringtrueDigitalOcean monitoring agent.
project_id""Optional project to attach the droplet to.

The full schema lives in the module README.

OutputWhat it is
ipv4_addressPublic IPv4 of the droplet.
ipv6_addressPublic IPv6 (empty if enable_ipv6 = false).
grpc_endpoint<ip>:7863 — paste into rune login --server.
http_endpointhttp://<ip>:7861 — REST API base URL.
firewall_idFirewall ID, or empty when create_firewall = false.
bootstrap_token_pathAbsolute path to the saved admin token (empty when bootstrap = false).
rune_login_commandReady-to-paste rune login command (empty when bootstrap = false).

The installer is rendered into cloud-init and runs only on first boot. Changing rune_version, cluster_cidr, or anything else that lands in runefile.toml after the droplet exists will not take effect on the running droplet. To roll a new config:

Terminal window
terraform apply -replace=module.rune.digitalocean_droplet.this

This destroys and recreates the droplet — your service state lives in /var/lib/rune on the droplet, so plan accordingly.

Terminal window
terraform apply -replace=module.rune.null_resource.bootstrap[0]

Re-runs the SSH bootstrap and overwrites the local token file. rune admin bootstrap itself refuses to run twice unless you reset the auth state on the server.

Terminal window
terraform destroy