What provisioning WordPress on Hetzner taught me about Terraform
A WordPress environment became a lesson in private networks, reusable modules, provisioning, persistence, and where my infrastructure thinking still needs work.
I set out to create a basic WordPress environment on Hetzner. The plan was simple enough: network, firewall, server, volume, and DNS. What I ended up with was a much better picture of how those pieces depend on each other.
The database should not be public
The first thing that clicked was the private network. A database should only be reachable by the server that needs it, not by the public internet. The firewall then became more than a checklist: I deliberately kept public access limited to HTTP, HTTPS, and SSH.
That made the architecture feel more real. It was not just “a server with WordPress”; it was a small environment with a public edge and private services behind it.
Modules are a repeatable decision
I had already made a Terraform module to spin up and destroy a server. For this project, I reused it and added the details this environment needed, such as location and cloud-init configuration.
The useful lesson was not only that modules reduce repeated code. They force me to think about what should be repeatable. If the initial setup is good, applying it again should lead to the same intended outcome.
templatefile made provisioning click
At first, my user_data was a small inline Bash script. That became awkward once I needed Nginx, MySQL, PHP, and WordPress setup steps.
Using Terraform's templatefile let me move that provisioning work into a proper script and pass in the values it needed. It was the first time I understood why templating is useful in infrastructure: the Terraform configuration describes the environment, while the script handles the machine setup.
Persistence needs its own place
I originally considered using only the server disk. Then I understood why a volume matters: a server can be replaced, but application data needs a life beyond that server.
A volume is attached block storage, not a network service. The private network protects communication with services such as the database; the volume is there to keep data persistent when the compute instance changes.
What I would improve next
This was a learning build, not a finished production environment. My next improvements would be:
- Keep real credentials out of committed Terraform variables, using environment variables or a secrets manager instead.
- Make the provisioning script more idempotent so rerunning it is safe and predictable.
- Finish the DNS path and document why each part belongs where it does.
The project started as a WordPress deployment. It became a useful step toward thinking in systems: network boundaries, persistent state, automation, and the difference between “it works once” and “it can be operated.”
The original project README is available on GitHub.