Infrastructure as Code: Getting Started Without Over-Engineering
The argument for infrastructure as code is not automation. It is that a server nobody can rebuild becomes an obstacle, and the obstacle compounds.
The usual argument for infrastructure as code is automation and speed. That is the weaker argument.
The stronger one is that a hand-configured server becomes, within a year, something nobody fully understands and nobody dares change. Infrastructure as code is how you keep infrastructure legible.
Why small teams benefit most
Large organisations adopt this because they have to coordinate. Small teams benefit more, for less obvious reasons:
- The person who built it will forget. Within months, "why is this configured like that?" has no answer. Code is the answer.
- The bus factor is one. A repository is knowledge that survives someone leaving.
- Rebuilding beats repairing. When a server is reproducible, a strange problem is solved by replacing the server rather than investigating it for a day.
- Review catches mistakes. A pull request that opens a firewall to the world is visible. A console click is not.
- Environments actually match. Staging that is genuinely like production only happens when both are built from the same definitions.
Start small and incrementally
The mistake is attempting to codify everything at once. That project stalls and is abandoned.
A sequence that works:
1. Version-control what already exists as documentation. Even a README describing the current setup is a start.
2. Codify new things only. The next server, the next firewall rule, the next database — created through code. Leave existing resources alone.
3. Import the stable pieces. Most tools can adopt existing resources into state. Do the ones that rarely change first.
4. Codify the network. VPCs, subnets and security groups are high-value and change infrequently — an ideal early target.
5. Then the compute. Instances, load balancers, auto-scaling.
6. Finally, configuration management. What runs on the instances — via cloud-init, Ansible, or baked images.
Each step delivers value on its own. None requires finishing the next.
Choosing tools
| Tool | Character | Good for |
|---|---|---|
| Terraform / OpenTofu | Declarative, multi-provider, HCL | The default choice for most teams |
| Pulumi | Declarative, real programming languages | Teams who want types and loops |
| Provider-native (CloudFormation, ARM/Bicep, Deployment Manager) | Deep single-provider integration | Single-platform estates |
| Ansible | Procedural, agentless | Configuration on top of provisioned hosts |
| cloud-init | Simple first-boot configuration | Small setups, bootstrapping |
For most teams, Terraform or OpenTofu plus cloud-init covers everything without adding a platform to learn. Providers exist for essentially every cloud in the catalogue, including the independent ones — a Hetzner account or DigitalOcean account is as scriptable as any hyperscaler.
State: get this right early
Declarative tools keep a state file mapping code to real resources. It is the most important operational detail and the most commonly mishandled.
- Never commit state to version control. It contains resource details and frequently secrets.
- Use remote state — object storage with versioning enabled.
- Enable locking so two people cannot apply simultaneously.
- Separate state per environment. Production and staging must not share a state file; a mistake in one should not be able to destroy the other.
- Back it up. Versioned object storage handles this, and it is worth confirming rather than assuming.
Losing state does not destroy infrastructure, but it does mean your code no longer knows what it manages, and reconciling that manually is unpleasant.
Secrets
Never put secrets in the code, and be aware that many tools write them into state.
- Use a secret manager and read at apply time.
- Prefer short-lived credentials over stored ones — the credential guide covers the patterns.
- Where a secret must be generated, generate it in the tool and store it in the secret manager, not in a variables file.
- Scan the repository for accidentally committed secrets, in CI and on push.
Practical habits
- Always read the plan. The diff before apply is the review. Skipping it defeats the purpose.
- Small changes. A large plan is hard to review and harder to reason about.
- Use modules for repetition, not for everything. Premature abstraction is as costly here as in application code.
- Pin provider and module versions. Unpinned versions produce surprise changes.
- Plan in CI on every pull request, and post the output for review.
- Tag everything through code, so attribution is structural — see the cost guide.
- Treat drift as a signal. Manual console changes are sometimes necessary; they should be reconciled, not ignored.
What not to do
- Do not codify everything before deriving any value.
- Do not build a bespoke internal framework before you have used the tool plainly.
- Do not share one state file across environments.
- Do not apply straight to production without a reviewed plan.
- Do not use it as a reason to avoid understanding the underlying platform. The code describes the platform; it does not replace knowing it.
Frequently asked questions
Is this worth it for three servers? Yes, particularly for the network and firewall definitions. The value is legibility, which matters at any size.
Terraform or OpenTofu? Functionally compatible. OpenTofu is the open-source fork; the choice is mostly about licensing preference.
How do I handle existing infrastructure? Import it gradually. Start with resources that rarely change and work outwards.
Does this replace configuration management? No. Provisioning creates the server; configuration management decides what runs on it. Most teams use both, or provisioning plus baked images.
The environments guide covers ephemeral per-branch environments, which this makes possible.
Related guides
Architecture & Migration 4 min read
Do You Need Kubernetes? An Honest Answer
Kubernetes solves real problems extremely well. Most teams adopting it do not yet have those problems, and pay the cost anyway.
Architecture & Migration 4 min read
Multi-Cloud Architecture: The Patterns That Work, and the One That Does Not
Multi-cloud is frequently adopted as insurance and paid for as complexity. Some patterns genuinely earn their cost; the most commonly attempted one rarely does.
Architecture & Migration 5 min read
Migrating from Shared Hosting to Cloud Without Breaking Anything
The hard part of leaving shared hosting is not moving the files. It is everything the control panel was quietly doing for you.
Questions about a specific configuration?
Send us the workload profile — vCPU, memory, region, open ports — and we will confirm which listings actually support it before you buy.