Skip to main content

Modifying Infrastructure

Everything below happens by editing the infrastructure-automation repo and running a make target — there's no manual Proxmox UI or SSH-and-hand-install step in the current process. See Getting Started first if you haven't set up access and tooling yet.

Who can run make

Every make target here runs Terraform and/or Ansible directly against production — there's no staging environment in between. Because of that, only admins run these commands directly on main.

If you're not an admin:

  1. Create a branch on infrastructure-automation with your changes (Terraform files, playbooks, inventory.ini, Makefile targets, nginx vhosts, etc.).
  2. Open a pull request and add an admin as a reviewer.
  3. An admin runs the relevant make target (or merges and runs it) once the PR is approved.

Adding a new VM

  1. Add a resource block to a new .tf file in terraform/hetzner-main/, following the pattern of an existing VM (clone debian-13-template, pick an unused VMID/IP, set cores/memory/disk).
  2. Add a matching setup-<name>.yml playbook under ansible/hetzner-main/, and an entry in inventory.ini under the appropriate host group.
  3. Add the new host alias to your ~/.ssh/config (see Getting Started).
  4. Add a target for it to the Makefile, then run:
    make setup-<name>
    This runs terraform apply (via the wait dependency) to create the VM, then configures it with Ansible in one step.

See Provisioning with Terraform and Configuring with Ansible for the conventions to follow (Docker install pattern, deploy user, Grafana Alloy, UFW rules).

Adding a new application or public site

  1. Add a new directory under ansible/hetzner-main/services-user-apps/ or services-staff-apps/ (whichever VM it belongs on) containing the app's docker-compose.yml.
  2. Add the app's name to the relevant user_apps / staff_apps list in that VM's playbook, so it's picked up by the apps tag.
  3. Add an nginx vhost for it under networking/nginx-config/ in the automation repo.
  4. Deploy:
    make deploy-services-user-apps    # or deploy-services-staff-apps
    make update-webserver # pushes the new vhost + reloads nginx
    Also arrange a DNS record for the new subdomain in Cloudflare before this will be publicly reachable.

Stateful apps (their own database, hand-placed secrets/data) get their own playbook tag instead of joining the shared apps tag — see the Gitea/Rocket.Chat pattern on the Services Staff page.

Adding software to an existing VM

Edit that VM's setup-<name>.yml playbook directly, then re-apply just the configuration (no Terraform involved):

make update-<name>

Opening a port / changing firewall rules

Edit the UFW tasks in the relevant playbook and re-run make update-<name>. Keep to the existing pattern — allow SSH, allow the webserver VM as ingress, deny everything else by default — unless there's a specific cross-VM reason not to (see Networking and Ingress).