Modern IT teams of all sizes have adopted DevOps practices to ship changes faster while maintaining stable software on reliable infrastructure. A core enabler of this shift is treating infrastructure the same way software developers treat application code: with version control, automated testing, and repeatable deployments.
This article explains how to build a version control process for DevOps infrastructure, why Infrastructure as Code (IaC) is the foundation, and how to get started with Git and GitHub using a practical command-line walkthrough.
Why Infrastructure Needs Version Control
Infrastructure as Code (IaC) is the practice of defining servers, networks, storage, and other infrastructure resources in machine-readable configuration files rather than through manual processes or interactive consoles. IaC enables automation, testability, quality gates, and predictable deployments — the same benefits that application developers have enjoyed for years.
Storing IaC templates, shell scripts, and configuration artifacts in a version control repository brings additional benefits:
- Full audit history: Every change is recorded with a description, timestamp, and author, giving teams a clear point-in-time history from one revision to the next.
- Rollback capability: If a change breaks something, you can revert to any previous known-good state without guesswork.
- Collaboration without conflicts: Team members work from a shared central repository, merging changes in a controlled way rather than overwriting each other’s work.
- Automation triggers: Commits to a version control repository can automatically kick off pipelines — running tests, linting configuration files, or deploying to a development or staging environment.
- Shared understanding: Everyone on the team — developers, operations engineers, and security — can inspect a single source of truth for all resources that support a given application.
How Version Control Works for Infrastructure Teams
The mechanics of version control are the same whether you are tracking application code or an Ansible playbook. A central repository stores all files. Changes are tracked incrementally — each new commit layers on top of the previous state, preserving the full history. Team members synchronize a local copy of the repository, make changes, and merge those changes back to the central repository when ready.
This workflow reduces risk. Bugs introduced by a change can be isolated and reverted. Updates that break a deployment can be rolled back without affecting unrelated work. Multiple engineers can work on the same code base simultaneously, improving velocity without sacrificing consistency.
For infrastructure-focused engineers, adopting version control also means embracing software development habits: writing descriptive commit messages (e.g., “Applied security patch to base OS image referenced in Terraform module”), using branches for experimental changes, and submitting pull requests for peer review before merging to a main branch.
Choosing a Version Control System: Git and GitHub
Git is the most widely used version control system in the industry. It is distributed, meaning every engineer works with a full local copy of the repository — including its complete history — and can commit changes offline. Those commits are then pushed to a shared remote server when connectivity is available.
Git is open source, and a wide ecosystem of hosted platforms is built on top of it, including GitHub, GitLab, and Bitbucket. When selecting a platform, consider:
- Public vs. private repositories: Public repositories are visible to anyone. Infrastructure code almost always belongs in a private repository.
- Access controls: Fine-grained permissions determine who can read, write, or merge changes to protected branches.
- CI/CD integration: Most hosted Git platforms offer built-in pipelines or integrate with external tools such as Jenkins, GitHub Actions, or GitLab CI.
- Secrets management: Never store secrets, credentials, or API keys in a version control repository. Use a dedicated secrets manager and reference secrets by name in your IaC templates.
Practical Walkthrough: Git Version Control from the Command Line
The following walkthrough demonstrates the core Git version control workflow using the command line and a GitHub remote repository. The same workflow applies whether you are tracking a Terraform module, a Kubernetes manifest, or a CloudFormation template.
Step 1: Initialize a Local Repository
Open a terminal and navigate to the directory where you want to store your repositories. Create a new folder for this repository and initialize it with Git:
mkdir my-first-repo
cd my-first-repo
git init
Git responds with: Initialized empty Git repository in .../my-first-repo/.git/
Step 2: Add a File and Stage the Change
Create a README file. On GitHub, this file is rendered as the repository’s landing page:
echo "# my-first-repo" >> README.mdCode language: PHP (php)
The file exists on disk but is not yet tracked by Git. Stage it to tell Git you want to include this change in the next commit:
git add README.mdCode language: CSS (css)
Step 3: Commit the Change
Commit the staged change to the local repository with a descriptive message:
git commit -m "Initial commit: add README"Code language: JavaScript (javascript)
Write commit messages that describe why a change was made, not just what changed. For infrastructure code, this habit is especially valuable during incident investigation.
Step 4: Connect to a Remote Repository on GitHub
Sign in to GitHub and create a new private repository named my-first-repo. Then link the local repository to the remote:
git remote add origin https://github.com/[your-github-username]/my-first-repo.gitCode language: JavaScript (javascript)
Push the local commits to the remote repository:
git push -u origin main
Other team members can now clone the repository to their own machines:
git clone https://github.com/[your-github-username]/my-first-repo.gitCode language: PHP (php)
Step 5: Make a Change and Inspect the Diff
Edit README.md using any text editor. Add a line of text describing what the repository contains. Check which files have been modified:
git status
Stage and commit the update:
git add README.md
git commit -m "Update README with repository description"Code language: JavaScript (javascript)
Push the change to GitHub:
git push -u origin main
Navigate to the repository on GitHub and view the latest commit. GitHub displays a split-screen diff showing what was added (highlighted in green) and what was removed (highlighted in red) between the two versions. This visibility is one of the most practical benefits of version control for infrastructure teams: you can immediately see what changed and when.
Step 6: Extend the Repository with Infrastructure Artifacts
With the workflow in place, add the infrastructure files you need to track: Terraform modules, CloudFormation templates, Ansible playbooks, Kubernetes manifests, shell scripts, and pipeline definitions. Follow the same pattern for every change:
- Modify or add a file.
- Stage the change with
git add. - Commit with a descriptive message using
git commit -m. - Push to the remote repository with
git push.
Branching Strategies for Infrastructure Code
Many teams use multiple branches to manage infrastructure changes safely. A common pattern is:
- Main branch: Represents the production-ready state of all infrastructure. Direct commits are typically blocked; changes arrive via pull requests.
- Feature branches: Short-lived branches where engineers develop and test a specific change — for example, adding a new VPC configuration or updating an AMI reference.
- Environment branches: Some teams maintain separate branches for development, staging, and production environments, promoting changes upward through the chain after validation.
Pull requests serve as the gatekeeping mechanism. Before a change merges to the main branch, other engineers can review the diff, leave comments, and require that automated tests pass. This process catches errors in infrastructure code before they reach production.
Connecting Version Control to Automation
Version control becomes even more powerful when it triggers automation. A commit or a merge to a protected branch can automatically:
- Run linting and static analysis on infrastructure templates (e.g.,
terraform validate,cfn-lint, oransible-lint). - Execute integration tests against a temporary environment.
- Deploy the updated infrastructure to a development or staging environment.
- Notify the team of the outcome via a chat integration or email.
This automation loop is the foundation of a mature DevOps pipeline. The version control repository becomes the single trigger point for all downstream activity, eliminating manual handoffs and reducing the opportunity for human error.
IaC Tooling That Works Well with Version Control
Version control is tool-agnostic, but the following categories of IaC tooling are commonly stored in Git repositories:
- Provisioning tools: Terraform, AWS CloudFormation, Azure Resource Manager templates, Google Cloud Deployment Manager, and OpenStack Heat define the infrastructure resources themselves.
- Configuration management: Ansible, Chef, and Puppet manage the software and settings installed on provisioned servers. These artifacts complement IaC provisioning templates and belong in the same repository or a linked one.
- Container and orchestration manifests: Kubernetes YAML files, Helm charts, and Docker Compose files define how applications run on the infrastructure.
Public and private cloud platforms — including AWS, Microsoft Azure, and Google Cloud Platform — provide APIs that these tools use to provision resources consistently and repeatedly. With cloud APIs and IaC templates version-controlled in Git, infrastructure deployments become as repeatable and auditable as application deployments.
Summary
Building a version control process for DevOps infrastructure is not complicated, but it does require a deliberate shift in how infrastructure teams work. The steps are straightforward: adopt Infrastructure as Code, store all artifacts in Git, follow a consistent commit-and-push workflow, use branches and pull requests to gate changes, and wire the repository to an automated pipeline.
The result is infrastructure that is auditable, reversible, collaborative, and automated — the same properties that software development teams have relied on for decades, now applied to the systems that run your applications.