Created
August 24, 2025 20:17
-
-
Save crescentrose/5d82d2a2732ef257aeb55393fb24e08f to your computer and use it in GitHub Desktop.
k3s deployment with Terraform on DigitalOcean
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env sh | |
| # Spits out a `kubeconfig` that lets you connect to the freshly build k3s cluster. | |
| # Do sometihng like `./fetch_kubeconfig.sh > ~/.kube/config` if you have no other configs. | |
| host=$(cd core && terraform output -raw cluster_ip) | |
| keyfile=$(mktemp /tmp/authkey.XXXXXX) | |
| # make sure we clean up if we fail | |
| trap "rm $keyfile" 1 2 3 6 15 | |
| (cd core && terraform output -raw ssh_private_key > "$keyfile") | |
| printf "\n" >> "$keyfile" | |
| ssh "root@$host" -o StrictHostKeyChecking=accept-new -o IdentitiesOnly=yes -i "$keyfile" -t "cat /etc/rancher/k3s/k3s.yaml" | sed -e "s/127.0.0.1/$host/" | |
| rm "$keyfile" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| terraform { | |
| required_providers { | |
| digitalocean = { | |
| source = "digitalocean/digitalocean" | |
| version = "~> 2.0" | |
| } | |
| } | |
| } | |
| # You can use a fancy secrets management solution like HashiCorp Vault | |
| # If you're a solo dev, you can also just have a `secrets.yaml` that you encrypt | |
| # with something like Agebox and just keep in the repo. If it's good enough for | |
| # Rails, it's good enough for me. | |
| data "local_sensitive_file" "secrets" { | |
| filename = "../secrets.yaml" | |
| } | |
| locals { | |
| secrets = yamldecode(data.local_sensitive_file.secrets.content) | |
| # Change this to be closer to you | |
| region = "ams3" | |
| # You might want to use CoreOS or something less intensive. | |
| # This is just for demonstration purposes. It's easy to swap out anyway. | |
| image = "ubuntu-24-04-x64" | |
| # Resize your droplet if you have the cash | |
| # I do not recommend going lower than this, though. | |
| size = "s-1vcpu-2gb" | |
| # Rename your droplet if you feel creative | |
| name = "k3s" | |
| } | |
| provider "digitalocean" { | |
| token = local.secrets["digitalocean_token"] | |
| } | |
| # You'll want to generate a SSH key for your VM. | |
| resource "digitalocean_ssh_key" "terraform" { | |
| name = "terraform" | |
| public_key = local.secrets["ssh_public_key"] | |
| } | |
| resource "digitalocean_reserved_ip" "reserved_ip"{ | |
| region = local.region | |
| } | |
| resource "digitalocean_droplet" "k3s" { | |
| depends_on = [digitalocean_reserved_ip.reserved_ip] | |
| image = local.image | |
| name = local.name | |
| region = local.region | |
| size = local.size | |
| ssh_keys = [ | |
| digitalocean_ssh_key.terraform.id | |
| ] | |
| connection { | |
| host = self.ipv4_address | |
| user = "root" | |
| type = "ssh" | |
| timeout = "2m" | |
| private_key = local.secrets["ssh_private_key"] | |
| } | |
| provisioner "remote-exec" { | |
| inline = [ | |
| "curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC=\"--node-external-ip ${digitalocean_reserved_ip.reserved_ip.ip_address}\" sh -s -", | |
| "mkdir -p /var/lib/rancher/k3s/agent/images" | |
| ] | |
| } | |
| } | |
| resource "digitalocean_reserved_ip_assignment" "reserved_ip_assignment" { | |
| ip_address = digitalocean_reserved_ip.reserved_ip.ip_address | |
| droplet_id = digitalocean_droplet.k3s.id | |
| } | |
| # This is what you will want to point your DNS to | |
| # Using a reserved IP allows you to rebuild the cluster with zero downtime, just by switching | |
| # the machine the reserved IP points to. | |
| # If you mess up, you can also tear down the old cluster and rebuild a new one without waiting | |
| # for DNS to propagate. | |
| output "reserved_ip" { | |
| description = "The IPv4 address of the reserved IP attached to the currently active cluster." | |
| value = digitalocean_reserved_ip.reserved_ip.ip_address | |
| } | |
| # This is used later on for convenience. | |
| output "ssh_private_key" { | |
| description = "The SSH private key used to access the k3s cluster." | |
| sensitive = true | |
| value = local.secrets["ssh_private_key"] | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ssh_public_key: ssh-ed25519 ... | |
| ssh_private_key: |- | |
| put your private key here | |
| digitalocean_token: dop_v1_... |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment