How I deployed treff.tech
I've already written about why treff.tech exists. This time, I want to go a level deeper and walk through how it's actually deployed. The infrastructure, not the idea.
The setup: Terraform provisions the server, Ansible configures it, and Mobilizon (the software behind treff.tech) runs in Docker. Nothing exotic, but I think there's value in showing how the pieces fit together, especially for anyone considering self-hosting a Fediverse project of their own.
The stack at a glance
├── ansible/
│ ├── group_vars/
│ ├── inventory/
│ ├── playbook.yml
│ └── roles/
│ ├── docker/
│ └── security/
├── Makefile
└── terraform/
├── main.tf
├── outputs.tf
├── terraform.tfvars.dev
└── variables.tf
Terraform handles the "what exists" the server itself and its networking. Ansible handles "what's on it" Docker, hardening, and the running services. A Makefile ties the two together so a full deploy is a single command, not a sequence of steps I have to remember.
Provisioning with Terraform
terraform/main.tf defines the actual infrastructure: the [cloud provider / VPS] instance treff.tech runs on, along with networking and DNS. variables.tf and terraform.tfvars.dev keep environment-specific values (like instance size and region) separate from the logic, so promoting the same setup to a production environment later is mostly a matter of swapping the .tfvars file. outputs.tf exposes things like the server's IP address, which Ansible's inventory then picks up.
Keeping this layer thin was a deliberate choice: Terraform's only job is to make the server exist. Everything about what runs on that server is Ansible's responsibility.
Configuration with Ansible
The ansible/ directory has two roles doing the real work:
docker — installs and configures Docker itself, so the host is ready to run containers. This is where Mobilizon (and its dependencies, like PostgreSQL) actually run.
security — hardens the server: [firewall rules / SSH configuration / fail2ban / automatic security updates — whichever you actually use]. This is a role I'd rather run once and mostly forget about, which is exactly the point of automating it — it runs identically every time, instead of relying on me remembering every step manually.
group_vars/all.yml.dev and inventory/hosts.yml.dev follow the same environment-separation pattern as the Terraform side — dev values are kept apart from what would eventually be production values.
Tying it together with Make
The Makefile wraps the Terraform and Ansible steps into simple commands — plan and apply infrastructure, then run the Ansible playbook against whatever Terraform just created. In practice, a full deploy or update usually comes down to one or two make commands rather than remembering the right terraform and ansible-playbook invocations each time.
Why this stack, and not something else
Mobilizon itself is straightforward to run — it's a Dockerized app, so the actual "install Mobilizon" step is small. The more interesting engineering problem was making the infrastructure reproducible: if the server disappeared tomorrow, I want to be able to stand it back up from these two directories, not from memory.
Terraform and Ansible are also just tools I'm comfortable with — nothing about treff.tech needed something more elaborate like Kubernetes. For a single-instance Fediverse platform, that would have been over-engineering for its own sake.
Takeaways
A few things I'd tell someone setting up something similar:
- Keep Terraform thin. Let it provision infrastructure, and let configuration management (Ansible, in this case) own everything about the software layer.
- Separate environments early, even if you only have one for now. The
.devsuffix convention here made it trivial to add a second environment later without restructuring anything. - Automate the boring stuff first. The security-hardening role was the least exciting part to write and the most valuable to have automated — it's exactly the kind of task that's easy to skip a step on when done manually. If you're thinking about self-hosting Mobilizon or a similar Fediverse project and have questions about the setup, feel free to reach out.