Journal — May 31, 2026 · 7 min read
Cloud & DevOps in Practice — AWS, GCP, Azure and Terraform
How I actually approach cloud infrastructure across AWS, GCP and Azure — picking a provider, why Terraform matters more than the provider does, and the DevOps decisions that determine whether a team ships.
Most cloud arguments are about the wrong thing. Teams spend weeks debating AWS versus GCP versus Azure, then deploy by hand from a laptop and wonder why releases are frightening.
The provider is close to the least important decision you'll make. What determines whether your infrastructure helps or hurts is whether it's reproducible, whether deploys are boring, and whether you find out about problems before your customers do.
Here's how I actually work through it.
Picking a provider
The honest answer is that all three are good, and the differences that matter are rarely technical.
AWS is the default, and defaults have real value. The largest service catalogue, the most documentation, the most people who've hit your exact problem before, and the deepest hiring pool. When something breaks at 2am, the odds that someone has already written up your error are highest here. That's worth more than any feature comparison.
The cost is complexity. AWS has multiple services for the same job, accumulated over years, and the console is a maze. You need to know which of five options is the current right answer, and the documentation won't always tell you.
GCP is more coherent. The networking model is cleaner, the console is genuinely better, and if you're doing serious data or ML work, BigQuery is a real reason to choose it — there isn't a clean equivalent elsewhere. Kubernetes also feels most at home here, unsurprisingly.
The tradeoff is a smaller ecosystem and a reputation for deprecating things. Whether that reputation is still deserved is arguable; that clients bring it up is not.
Azure wins on organisational gravity rather than technology. If a company already runs Microsoft — Active Directory, Office, existing enterprise agreements — Azure integrates with what they have and often arrives on a contract that's already signed. In an enterprise, "our identity is already here" beats any benchmark.
How I decide: what does the team already know, what does the company already pay for, and does the workload need a specific service only one provider does well? Those three questions settle it almost every time. If none of them points anywhere, take AWS and stop deliberating.
Terraform is the decision that actually matters
Whichever provider you land on, define the infrastructure in code.
Not because infrastructure-as-code is a best practice people repeat at conferences, but because of what it prevents. Click-ops infrastructure has no history — nobody knows why a security group has that rule, staging drifts from production in ways nobody can enumerate, and recreating an environment is archaeology.
Terraform gives you three things worth having:
Reproducibility. Staging and production come from the same modules with different variables. When staging behaves differently, that's a real signal rather than the usual noise.
Review. Infrastructure changes arrive as a diff someone reads before it happens. terraform plan in CI, output posted to the pull request, and a human looks at it. Most infrastructure incidents I've seen would have been caught by someone reading a plan.
An exit. Not portability — that's oversold, since your Terraform is full of provider-specific resources. But you have a written, complete description of what exists, which is the hard part of any migration.
A few things I'd insist on:
- Remote state with locking, from the first day. Local state files end in two engineers applying at once and corrupting each other's work.
- Separate state per environment. One blast radius per environment.
- Modules for things you build repeatedly, not for everything. Premature module abstraction is as annoying in Terraform as it is in application code.
- Never edit infrastructure in the console. One exception during an incident is understandable; make it a habit and the code becomes fiction.
Terraform versus Pulumi is a real question — Pulumi's use of a general-purpose language is genuinely nicer for complex logic. I still reach for Terraform by default, because the ecosystem and the number of people who can maintain it after I leave are both larger. For infrastructure, boring and widely understood is a feature.
Kubernetes, and whether you need it
Usually you don't.
Kubernetes solves problems that appear at a scale most teams haven't reached: many services, many teams, complex deployment topologies, real need for fine-grained orchestration. If you're running a handful of services, a managed platform will serve you better and demand far less of your attention.
The cost isn't the cluster. It's that Kubernetes becomes a permanent part of your team's cognitive load — upgrades, networking, RBAC, why a pod is pending, why an ingress isn't routing. That's a real ongoing tax, and it's worth paying only when the alternative is worse.
When it is right — many services, genuine scaling needs, a team that can carry it — use managed control planes. EKS, GKE or AKS. Running your own control plane is a decision that needs a much better reason than cost.
CI/CD: make deploys boring
The goal is that shipping is unremarkable. That means:
Every merge to main builds and deploys automatically to somewhere. If deploying requires a person following a checklist, it will be skipped under pressure and done wrong at 6pm on a Friday.
Preview environments per pull request. Reviewing a running version beats reviewing a diff, and the cost of spinning one up is far lower than the cost of shipping something nobody looked at properly.
Rollback that works and is practised. An untested rollback is not a rollback. The time to find out is not during an incident.
Secrets out of the repository, in the provider's secret manager or your CI's encrypted store, injected at runtime. This is basic and it is still the thing I most often find wrong when I inherit a codebase.
GitHub Actions covers this for most teams and keeps the pipeline next to the code. The main discipline is resisting the urge to build a bespoke deployment system — the boring path is the one your team will still understand in a year.
Observability, before you need it
Instrument early, because the moment you actually need it is the moment you can't add it.
The useful minimum:
- Structured logs with a retention policy set deliberately, not left on the default.
- Metrics on the four signals that matter — latency, traffic, errors, saturation.
- Alerts that a human should act on. An alert nobody acts on trains everyone to ignore alerts, which is worse than having none.
- Tracing once you have more than a couple of services, because "which service is slow" stops being answerable by intuition.
You don't need an expensive platform to start. What you need is for someone to find out from a dashboard rather than from a customer.
Where the money actually goes
Cloud bills drift for one reason: nothing in a cloud console pushes back when you over-provision. There's no error, no warning — just a slightly larger number thirty days later.
The overspend is almost always in the same handful of places: compute sitting far below its provisioned size, NAT gateway data processing that nobody attributes, storage and snapshots nobody deleted, non-production environments running 24/7 for a working week's use, and log ingestion with no retention policy.
None of that requires a tool to find. It requires an hour in the billing console, grouped by service and then by usage type, once a month. That single recurring habit beats anything you can buy.
What I'd tell a team starting out
Pick the provider your team knows. Put it in Terraform from day one. Deploy automatically on merge. Instrument before you need to. Skip Kubernetes until something actually hurts without it.
None of that is clever, and that's the point. Infrastructure should be the least interesting part of your system. If your team is thinking about the deploy pipeline, something is wrong with it — and the fix is almost never a more sophisticated tool.
If you want this set up properly once rather than accreted over years, that's the DevOps work I do.