Infrastructure as Code: Why Your Server Setup Belongs in Git
You are about to deploy a new feature. The application code is ready, the tests pass, and the pull request has been approved. But then someone asks: "Did we update the load balancer config for the new endpoint?" Nobody is sure. The person who set up the last load balancer change is on leave. The cloud dashboard shows the current configuration, but nobody knows what changed last week or why.
This scenario plays out in teams every day. Infrastructure changes depend on tribal knowledge, manual steps, and the availability of specific people. If that person is unavailable, the change waits. If they forget a step, the server breaks silently. And when something goes wrong, finding the root cause means digging through chat logs, emails, and memory.
There is a better way. Write your infrastructure as code.
What Infrastructure as Code Actually Means
Infrastructure as Code (IaC) is the practice of defining your servers, networks, databases, load balancers, and all other infrastructure components in text files. These files live in a version control repository, just like your application code.
The key shift is in how you describe infrastructure. Instead of writing step-by-step commands like "SSH into the server, run this command to install Nginx, then edit this config file," you write a declaration of what the infrastructure should look like. For example: "I want a server running Nginx version 1.24, listening on port 443, with this TLS certificate and these proxy rules."
The tool that processes these files reads your declaration, compares it to the current state of your infrastructure, and makes whatever changes are necessary to match. This is called desired state management.
Here is what a simple declarative configuration looks like in Terraform:
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
user_data = <<-EOF
#!/bin/bash
apt-get update
apt-get install -y nginx
systemctl enable nginx
systemctl start nginx
EOF
tags = {
Name = "web-server"
}
}
resource "aws_security_group" "web_sg" {
name = "web-sg"
description = "Allow HTTP and SSH"
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["10.0.0.0/8"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
This file declares exactly what you want: an EC2 instance with Nginx installed and a security group that allows web traffic. Terraform compares this declaration to the current state of your AWS account and makes only the changes needed to match.
How It Changes Your Daily Work
When infrastructure is code, changing a server configuration no longer means logging into a machine and typing commands. It means editing a file, opening a pull request, getting a review, and merging the change. The server updates automatically when the new configuration is applied.
This shifts the entire workflow:
Every change is tracked. The commit history in your repository tells you exactly what the production load balancer configuration looked like last Tuesday. No more guessing, no more "I think someone changed that setting."
Changes are reviewable. Before any infrastructure change reaches production, it goes through the same code review process as application changes. Team members can comment, suggest improvements, and catch mistakes before they cause downtime.
Anyone can propose changes. A developer who needs a new database connection pool can write the configuration and submit a pull request. They don't need to wait for the operations team to have free time. The operations team reviews the change instead of executing it.
Rollbacks are straightforward. If a configuration change causes problems, you revert the commit and reapply. You don't need to remember the exact sequence of manual steps to undo the change.
The Concept of Desired State
The most important idea in infrastructure as code is desired state. Your configuration files describe the ideal condition of your infrastructure. The IaC tool continuously ensures that the actual state matches the desired state.
If someone manually changes a setting on a server, the tool detects the drift and reverts it. If a server crashes and gets replaced, the tool provisions the new server with the correct configuration automatically. You are not writing scripts that run once. You are defining a target, and the system maintains that target.
This is fundamentally different from the old approach where you wrote a script to set up a server, ran it once, and hoped nothing changed afterward. With desired state, the system is self-healing and self-auditing.
What Infrastructure as Code Is Not
Infrastructure as Code is not a specific tool. Terraform, AWS CloudFormation, Ansible, Pulumi, and many others are implementations of the concept. The tool matters less than the practice of treating infrastructure configuration as version-controlled, reviewable, declarative code.
It is also not about eliminating manual work entirely. You still need to understand your infrastructure. You still make design decisions about networking, security, and scaling. But those decisions are encoded in files that can be reviewed, tested, and reproduced.
Why This Matters for CI/CD
Infrastructure as Code is the foundation for treating infrastructure changes the same way you treat application changes in your CI/CD pipeline. When your infrastructure is code, you can:
- Run automated tests on infrastructure changes before applying them
- Validate that configuration files are syntactically correct
- Check for security policy violations automatically
- Apply changes through the same pipeline that deploys your application
- Ensure consistency across environments
Without infrastructure as code, your CI/CD pipeline can only handle application code. Infrastructure changes remain manual, error-prone, and invisible to the pipeline. This creates a gap where application deployments are automated but the infrastructure they run on is not.
Practical Checklist for Getting Started
If you are considering adopting infrastructure as code, here is a practical starting point:
- Pick one environment to start. Do not try to convert your entire infrastructure at once. Start with a staging or development environment.
- Write declarative files, not scripts. Focus on what you want, not how to achieve it. Declarative files are easier to review and maintain.
- Store everything in version control. Your repository should contain all configuration files, not just the ones that change frequently.
- Review infrastructure changes like code. Require pull requests and approvals for infrastructure changes, just as you do for application changes.
- Test before applying. Use tools that can validate your configuration files and simulate changes before they hit real servers.
- Document the desired state, not the steps. Your files should describe the target configuration, not the sequence of commands to reach it.
The Takeaway
Infrastructure as Code transforms servers from black boxes understood by a few people into documented, reproducible, and manageable components. Every configuration change leaves a trail in your commit history. Every server can be rebuilt from scratch with consistent results. Every team member can propose infrastructure improvements without needing deep operational access.
The next time someone asks what changed in production last week, you will not have to guess. You will open the repository and check the commit log. That is the difference between infrastructure as an art and infrastructure as code.