Every few months this debate resurfaces on Hacker News. “Is Ansible or Terraform better for infrastructure?” The answer is: you are asking the wrong question. Here is the right one.
The Mental Model
Terraform is a declarative state manager. You describe the desired end state, and Terraform figures out how to get there (and crucially, what to destroy to make room for it). It maintains a state file that represents what it believes the world looks like. Plan, apply, done.
Ansible is a procedural task runner with an idempotency layer. You describe the sequence of operations to perform, and Ansible tries to make each step safe to re-run. It has no persistent state file — it discovers the current state at run time.
This distinction matters because:
- Terraform knows what it created and can delete it. Ansible does not.
- Ansible can handle conditionals based on live state. Terraform’s plan is based on declared state, not observed state.
- Terraform is terrible at config files, package installs, service management. Ansible is excellent at these.
- Ansible is not great at managing cloud resource lifecycles (create VPC, wait, create subnets, wait, attach gateway). Terraform handles this graph natively.
Where Each Excels
Terraform strengths:
- Cloud resource provisioning (VMs, VPCs, load balancers, databases)
- DNS records and CDN configuration
- IAM roles and policies
- Kubernetes cluster creation (not workload deployment)
- Network device configuration via providers
Ansible strengths:
- OS configuration and hardening
- Package installation and updates
- Application deployment (copying files, restarting services)
- Rolling upgrades with serial limits
- Ad-hoc operational tasks (gathering facts, log collection)
- Network device operational tasks (show commands, firmware upgrades)
The Production Stack
The pattern that scales is clear: Terraform provisions the infrastructure, Ansible configures it.
Terraform:
└── Creates EC2 instance
└── Assigns security group
└── Registers DNS record
Ansible (runs after Terraform):
└── Installs nginx
└── Deploys config templates
└── Starts services
└── Runs smoke tests
The Terraform output (IP address, hostname) feeds the Ansible inventory. They are not competing — they are sequential.
The Mutable Infrastructure Problem
Terraform is designed for immutable infrastructure: you do not SSH into a VM and change it. You update the Terraform config and replace the resource. This is the right approach for cloud VMs.
Ansible exists partly because the world has mutable infrastructure that cannot be replaced easily: bare-metal servers, network gear, VMs with 5 years of accumulated config. For these, Ansible’s approach of “apply changes to running systems” is not a compromise — it is the only realistic option.
When You Only Have Ansible
If your team only knows Ansible and you are starting a cloud project: use Ansible’s amazon.aws collection for cloud provisioning. It is slower and less clean than Terraform, but it works and keeps the toolchain unified. Adopt Terraform when the team grows or the cloud footprint gets complex.
When You Only Have Terraform
If your team only knows Terraform and you need to configure a VM post-provisioning: use user_data / cloud-init for simple cases. For complex configuration, the null_resource + remote-exec provisioner works but is fragile. The honest answer is: add Ansible for the configuration layer.
Training
Sphinx-Digital offers separate training courses for Ansible and Terraform, both 3 days with real infrastructure labs. We also run a combined DevOps track that covers the integration pattern described here.