Terraform won't make you cloud-portable — here's what actually will
There’s a line you hear every time someone talks about leaving a big cloud: “Just use Terraform, then you can move off AWS whenever you want.” I’m about to sit down and learn it properly — and because I advocate for getting off the big three (AWS, Azure, Google) onto flat-rate boxes like Hetzner and OVH, or even onto hardware in a client’s own offices, I want to get the pitch right before I put my name on it. So this is me working through the honest version, out loud, for anyone with a background like mine who’s wondering if it’s worth the time.
Short answer: yes, learn it — it’s close to essential for this kind of work. But the reason most people give for learning it is half-wrong, and if you repeat the half-wrong version to a client you’ll eventually get caught out. Let me take it apart.
First, what Terraform actually is (if you’ve never touched it)
If your infra life so far has been the AWS console, some systemctl on a box you SSH into, and a
couple of cron jobs — that was me for a lot of years — here’s the one-sentence version. Terraform is
a file that describes the infrastructure you want, and a command that makes reality match the file.
Instead of clicking through the EC2 console to create a server, a security group, and a Route53 record, you write something like:
resource "aws_instance" "web" {
ami = "ami-0abcd1234"
instance_type = "t3.small"
}
…and run terraform plan (it shows you what it would change) then terraform apply (it does it).
The language is HCL — declarative, boring, easy; you describe the end state, not the steps. It
keeps a state file that records what it created, so next time it knows the difference between
“create,” “change,” and “leave alone.” That state file is the whole ballgame, and I’ll come back to it.
For someone with 20 years of writing actual programs, HCL is not the hard part. You’ll be productive in an afternoon. The concepts that do take a beat are state management and the discipline that comes with it — more on that at the end.
The myth: “Terraform makes you provider-agnostic”
Here’s the thing to get straight, because it’s the load-bearing misconception. Terraform does not give you copy-paste portability between clouds. A config written for AWS is not a Hetzner config with a variable flipped.
Why not? Because Terraform talks to each provider through a provider plugin, and the resources are provider-specific all the way down:
- On AWS a server is an
aws_instance. - On Hetzner it’s an
hcloud_server. - On OVH it’s an
ovh_cloud_project_instance.
Different resource names, different arguments, different networking models, different everything. Security groups, load balancers, object storage, managed databases, IAM — none of it maps one-to-one. Move from AWS to Hetzner and you are rewriting the provider-specific parts, not re-running the same file against a new endpoint. Anyone who shows a client a single Terraform repo and says “see, we’re provider-agnostic now” is selling a fiction, and it’s the kind that falls apart exactly when the migration gets real.
If I learned nothing else before advocating this tool, it’d be that. Say “portable” loosely and you’re wrong. So what’s the right claim?
What it actually buys you (bigger than the myth, just less magic)
The real win isn’t that the file moves. It’s that your infrastructure stops being invisible. Right now, on a lot of teams, the true description of “what’s running and why” lives in one overworked person’s head plus a thousand clicks nobody wrote down. Terraform turns that into:
- Legible, reviewable infra. The whole environment is a set of text files. You can read it,
grepit, diff it, put it in a pull request, and have a second person review a change to production networking the same way they’d review code. - Reproducibility. You can destroy an environment and stand it back up from the file. That’s disaster recovery, that’s spinning up an identical staging env, and — crucially — that’s the honest basis for leaving a provider.
- One workflow across every provider. You learn
plan/applyonce. Whether the target is AWS, Hetzner, OVH, or a box in an office, the loop is identical. You’re not re-learning three consoles. - No more console drift. The file is the source of truth, so “someone changed something by hand and now nobody knows what’s live” stops being your normal state.
Now connect that to cloud-exit. The reason leaving AWS is hard is almost never the compute — it’s that nobody can say precisely what’s running. When your infra is code, leaving becomes a project you can scope — read the file, list what has to be recreated on the new provider, rewrite those resources, apply. It’s still work. But it’s an engineering task with a known shape, not an archaeology dig through a console at 2am. That’s the real anti-lock-in win, and it’s worth saying in exactly those words: operational reproducibility, not copy-paste configs.
Say "portable" loosely and you're wrong.
The genuinely portable layer sits above Terraform
Here’s the part that made the whole picture click for me, and it’s why I’m not learning Terraform in isolation.
Terraform is day-zero provisioning — it makes the servers, networks, volumes, DNS records, and firewall rules exist. It says nothing about what’s on the box. And the stuff on the box — the packages, the users, the systemd units, the app itself — that layer genuinely is portable, because a Linux server is a Linux server whether Hetzner or OVH or a Dell in an office rented it to you.
So the stack that actually delivers on the “we can move” promise is two layers, and the second one is where my existing muscle memory pays off:
- OpenTofu / Terraform — provisions the infrastructure (the “what exists”). Provider-specific.
- Ansible + cloud-init — configures the machines: install packages, write config, set up systemd services, cron, users (the “how they’re set up”). Ansible playbooks move across providers almost unchanged, because they operate on a generic Linux box, not on a cloud API.
- (optional) Docker / k3s / Nomad for the workloads themselves — the most portable layer of all.
If you already live in systemctl and cron and SSH, Ansible is the natural next tool for you, not a
new planet. It’s basically “the steps I’d type on the box, written down and made repeatable.” That’s
why I’m pairing OpenTofu with Ansible rather than trying to make Terraform do everything. Terraform
is bad at configuring the inside of a machine, and Ansible is the portable half of the story. Divide
the labor: provision with one, configure with the other.
Provider reality for the places I actually want to send people
Since the whole point is getting off the big three, here’s the honest state of provider support for the targets I care about:
- AWS — the gold-standard provider. Everything is covered. (Useful even on the way out — you can import your existing AWS estate into code first, then plan the exit against it.)
- Hetzner Cloud — the
hcloudprovider is first-class and genuinely pleasant. This is the smoothest of the flat-rate targets, and the reason the numbers are so different from AWS: a small shared-vCPU box runs about $5/month, a comfortable 8 GB one around $15–20, and a whole dedicated server from roughly $45. (Hetzner bills in euros, so treat the dollar figures as approximate — they drift a little with the exchange rate.) - OVH — the
ovhprovider is real and covers public cloud, dedicated servers, domains, and managed Kubernetes. Rougher edges than Hetzner, but it works, and it’s in the same ballpark: VPS instances from around $6/month and dedicated servers from roughly $60. (Same caveat — OVH also prices in euros, so the exact dollar amount fluctuates.) - On-prem — this is the interesting one for the multi-office idea below. Terraform needs an API
to talk to, so it shines when the office runs a hypervisor with one: Proxmox (the actively
maintained
bpg/proxmoxprovider is the one to use, not the older Telmate one), VMware vSphere, or libvirt/KVM. Point Terraform at a Proxmox cluster and you can codify VMs in an office the same way you codify them in a cloud.
And the honest limit: for truly bare metal — a box with no virtualization layer, nothing with an API in front of it — Terraform is weak. That’s a different category of tool (MAAS, Tinkerbell) that does bare-metal provisioning. Know where that line is so you don’t promise Terraform somewhere it can’t help.
The multi-office / on-prem-backup case specifically
One thing I push with clients who have more than one physical office: those offices are latent infrastructure. Anything that isn’t customer-facing in real time — backups, batch jobs, internal tools, a warm standby, CI runners — can often live on a cheap box in an office instead of metered cloud. Can Terraform help there? Yes, with a caveat worth being straight about.
If each office runs a Proxmox (or vSphere) cluster, then absolutely — you point Terraform at each one and the whole fleet, across sites, becomes code. That’s a legitimately nice setup: one repo, VMs in three offices, all declared and reproducible.
But be honest about scale. For a handful of backup boxes, the value of Terraform over just… setting them up… is small, because there isn’t much provisioning to declare. There, the Ansible + cloud-init layer carries more weight — the value is in configuring those boxes identically and repeatably, not in provisioning a big fleet. Terraform earns its keep when there’s an API and enough resources to make “declare it as code” pay off. Below that threshold, reach for Ansible first. Matching the tool to the actual size of the job is the difference between advice and cargo-culting.
Divide the labor: provision with one, configure with the other.
Why I’m learning OpenTofu, not Terraform
This one matters more for me than for most, because I publicly argue against vendor lock-in — so I’d better not build my own practice on a tool that got relicensed out from under its community.
Quick history: for a decade Terraform was open source (MPL 2.0). In August 2023, HashiCorp relicensed it to the Business Source License (BUSL 1.1) — source-available, not open source, with a non-compete clause. The community forked the last open version into OpenTofu, now governed by the Linux Foundation. Then HashiCorp itself was acquired by IBM (closed in 2025). So the tool everyone means when they say “Terraform” is now a source-available IBM product.
OpenTofu is a drop-in replacement: same HCL, same workflow, same providers, state-compatible — you
just type tofu instead of terraform. Everything I learn transfers 1:1. And it’s on message:
recommending a BUSL-licensed IBM tool as the foundation of a “escape single-vendor dependence” strategy
is a contradiction people will rightly call out. Picking OpenTofu is the same decision as leaving the
hyperscalers, applied one layer up — an open, foundation-governed tool that can’t be relicensed on a
whim.
Practically: I learn the exact same skills, I lose nothing, and I don’t have to explain an awkward inconsistency. Easy call. (I wrote up the full license story, the feature differences, and how to migrate an existing setup in an afternoon over here: Terraform vs OpenTofu.)
Where Pulumi fits — and why I’m not learning it right now
There’s a real alternative I want to name honestly, because for someone with my background it’s tempting: Pulumi. Instead of HCL, you write your infrastructure in an actual programming language — TypeScript, Python, Go. For a Node/React developer that can feel more natural than a new declarative DSL: real loops, real types, real functions, the tooling you already use.
So why am I parking it?
- HCL is the lingua franca. When a client or a job post says “Infrastructure as Code,” they overwhelmingly mean Terraform/OpenTofu and HCL. That’s the interoperable skill, the one other people on a team will already share. I want the common tongue first.
- One tool at a time. I’d rather be genuinely good at the OpenTofu + Ansible stack than shallow across three tools. Depth beats breadth when you’re advising people with money on the line.
- The trade-offs are real. Pulumi’s ecosystem is smaller, and “infra in a full programming language” is a double-edged sword — you also get to make full-programming-language messes in your infrastructure.
None of that means Pulumi is wrong — if your team is all TypeScript and allergic to a DSL, it’s a legitimately good fit, and knowing it exists is worth something. It’s in my back pocket as the “I already know TypeScript” accelerator. But I’m not spending the learning budget on it now. OpenTofu + Ansible first.
What the learning curve is actually like (for someone like us)
If you’ve got a couple of decades of tools behind you, don’t over-estimate this. The language is trivial — HCL is simpler than any programming language you already know. The parts that genuinely require attention:
- State. Terraform/OpenTofu keeps a
statefile mapping your code to real resources. Where it lives (locally? in a shared backend?), how it’s locked so two people don’tapplyat once, and how you keep secrets out of it (OpenTofu’s client-side state encryption is a nice edge here) — that’s the real operational learning, not the syntax. - The discipline. The tool only helps if the code stays the source of truth. The moment someone fixes something by clicking in the console, reality and the file drift apart, and drift is where the pain lives. The habit — change it in code, or don’t change it — is more of the skill than the tool.
- Modules and structure. Once past hello-world, you’ll want to factor repeated infra into modules. That’s just software design, which you already do.
Realistically: productive in a day, comfortable in a couple of weeks of real use, and the state/drift lessons land the first time they bite you. Cheap, for a skill this leveraged.
The bottom line
Is Terraform (as OpenTofu) worth learning for a 20-year developer who wants to help people get off the big clouds? Yes — it’s one of the highest-leverage skills for exactly this work, because “I’ll codify your infrastructure so leaving is a scoped project, not a hostage situation” is a real, sellable, honest pitch.
Just hold the claim precisely. It does not make configs portable between clouds. It makes your infrastructure legible and reproducible, which is what actually makes leaving possible. Pair it with Ansible for the portable configuration layer. Choose OpenTofu so the tool matches the philosophy. Keep Pulumi in your pocket, not on your plate. Get that framing right and you’re already ahead of most of the people confidently saying “just use Terraform.”
I’m learning this in the open because it’s the exact toolset behind the migrations I do: taking a team off metered hyperscaler billing and onto flat-rate boxes — or their own offices — with the whole setup written down so they’re never locked in again, to me or to a cloud. Every message comes straight to me and I reply to each one myself, usually within a day. If you want to know what your own exit would actually look like, send me a recent cloud bill and I’ll show you where the money goes and what a bounded, portable version costs instead — free, within a business day. Or if you’re weighing the bigger move, that’s what the Cloud-Exit Assessment is for.