Skip to content

Tech Story: Terraform infrastructure configuration for Linode VPS #106

@GitAddRemote

Description

@GitAddRemote

Tech Story

As a platform engineer, I want all Linode infrastructure defined as code using Terraform so that the server, DNS records, and firewall rules can be reproduced exactly, version-controlled, and never manually clicked together in a dashboard.

ELI5 Context

What is Terraform? It's a tool where you write a file describing what you want — "I want a server with 2GB RAM, a firewall that only allows ports 22, 80, and 443, and DNS records pointing three subdomains at my server's IP" — and Terraform creates all of it. If something breaks and you need to rebuild, you run one command and get the exact same setup back. It's the difference between a recipe card and "I cooked it from memory."

What is a DNS A record? When someone types api.drdnt.org into their browser, their computer asks "what IP address is that?" A DNS A record is the answer: it maps a domain name to an IP address. We need three: one for each subdomain.

What is a firewall? Your server is connected to the internet. Without a firewall, anyone can try to connect to any port (think: 65,535 doors on the server). A firewall slams all doors shut except the ones you explicitly open: 22 (SSH for admin access), 80 (HTTP), and 443 (HTTPS).

Technical Elaboration

New files

infra/terraform/main.tf

  • Configure the linode/linode Terraform provider (version pinned)
  • Declare a linode_instance resource referencing the existing VPS (use terraform import — do not recreate the live server)
  • Declare a linode_firewall resource: inbound allow TCP 22, 80, 443; all else deny. Attach to the instance.

infra/terraform/dns.tf

  • Declare linode_domain resource for drdnt.org (or import if already managed in Linode DNS)
  • Declare three linode_domain_record A records:
    • api.drdnt.org → VPS public IPv4
    • station.drdnt.org → VPS public IPv4
    • bot.drdnt.org → VPS public IPv4

infra/terraform/variables.tf

  • linode_token (sensitive) — Linode API personal access token
  • vps_ip — public IPv4 of the existing Linode instance
  • ssh_public_key — deploy user's public key content

infra/terraform/outputs.tf

  • Output the VPS IP, all three subdomain FQDNs

infra/terraform/terraform.tfvars.example

  • Template with placeholder values, committed to repo
  • Real terraform.tfvars added to .gitignore

infra/terraform/.terraform.lock.hcl

  • Committed to repo (locks provider versions for reproducibility)

infra/README.md

  • Step-by-step: how to install Terraform, authenticate with Linode token, import existing resources, run terraform plan and terraform apply
  • Explanation of what each resource does in plain English

Commands (for documentation)

cd infra/terraform
terraform init                        # download providers
terraform import linode_instance.vps <linode-instance-id>
terraform plan                        # preview changes — ALWAYS review before apply
terraform apply                       # create/update resources

Definition of Done

  • infra/terraform/ directory exists with main.tf, dns.tf, variables.tf, outputs.tf
  • .gitignore updated: terraform.tfvars, .terraform/, *.tfstate, *.tfstate.backup excluded
  • terraform.tfvars.example committed with all required variable names and placeholder values
  • terraform plan runs with zero errors against the real Linode account
  • All three A records (api, station, bot) resolve to the VPS IP (verify with dig api.drdnt.org)
  • Linode firewall attached to instance: only ports 22, 80, 443 reachable from public internet
  • infra/README.md written with ELI5 setup instructions

Dependencies

  • None — this is the foundation everything else builds on.
  • Blocks: VPS baseline provisioning, CI/CD pipeline (needs server IP)

Notes

  • Do not terraform apply without first running terraform plan and reviewing the diff. Terraform can destroy resources if misconfigured.
  • Import the existing instance rather than letting Terraform create a new one: terraform import linode_instance.vps <instance-id>
  • Store the Linode API token in GitHub Secrets (LINODE_TOKEN) for future CI use, not in any file.

Metadata

Metadata

Assignees

Labels

backendBackend services and logicconfigConfiguration and feature flagstech-storyTechnical implementation story

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions