Skip to main content

Ansible vs Terraform: what to use and when to combine them

Cloud & DevOps · 29.09.2026

The question "Ansible or Terraform" comes up for everyone automating server deployment for the first time. The short answer: these are different tools for different jobs, and in real projects they almost always work together. Terraform creates infrastructure — the server, the disk, the network — while Ansible configures what already exists. Let's look at where the line runs and how to build one working pipeline out of both tools.

What fundamentally separates Ansible and Terraform

Terraform is a tool for managing infrastructure as code. It describes the desired end state: how many servers are needed, with which disks, in which network, and it decides on its own what to create, change, or delete to reach that state. Ansible is a configuration management tool. It does not create servers by itself; it runs a sequence of steps on machines that already exist: install a package, place a file, restart a service.

The line is simple: Terraform answers "how many servers and what kind" — VDS instances, disks, a private network, firewall rules at the provider. Ansible answers "what is inside the server" — which web server, which PHP version, which users and cron jobs.

When to choose Terraform

Terraform is the right tool when infrastructure is described through a cloud provider's API: ordering a VDS, attaching a floating IP, setting up a private network between servers, creating a load balancer. If tomorrow you need to spin up an identical cluster of servers in another region, Terraform does it in one command.

  • Ordering and removing servers through the provider API without manual clicks in a panel
  • Versioning infrastructure in git — changes are visible in a diff before they are applied
  • Reproducibility: staging and production are built from the same set of files

When to choose Ansible

Ansible is the right tool where servers already exist and need to be brought to the same state: install nginx and PHP-FPM, roll out SSH keys, configure a firewall at the OS level, apply security updates. It does not care where the server came from — Terraform, a manual order in the ZevsHost panel, or an old fleet of hardware.

  • Configuring software and services on servers that already exist
  • Applying changes across dozens of servers with a single command
  • Tasks that don't fit the term "infrastructure": code deployment, log rotation, certificate renewal

Declarative versus procedural: the difference in practice

Terraform is declarative: the file describes the desired state, not a list of steps. Run terraform apply twice in a row with no changes in the files — the second run does nothing. Ansible is procedural in playbook structure but idempotent at the module level: the apt module will not reinstall an already installed package, and the copy module will not overwrite a file with the same content.

CriterionTerraformAnsible
What it describesCloud resources: servers, disks, networksOS and application state on the server
ModelDeclarative, with a state fileSequence of tasks against an inventory
State storageState file, usually in S3 or Terraform CloudNo stored state, checked on every run
Speed of a single-parameter changeRequires apply, may recreate the resourceTargeted playbook run on the needed host

How to connect Terraform and Ansible in one pipeline

The standard scheme: Terraform creates the server and exposes its IP address through an output, Ansible takes that address as an inventory and configures the server. The example VDS provider here is Hetzner Cloud, but the scheme works with any provider that has an API.

resource "hcloud_server" "web" {
  name        = "web-01"
  server_type = "cx22"
  image       = "ubuntu-24.04"
  ssh_keys    = [hcloud_ssh_key.main.id]
}

output "web_ip" {
  value = hcloud_server.web.ipv4_address
}

Next, a dynamic inventory for Ansible is built from Terraform's output with a single command, and the playbook installs the base set of packages:

- hosts: web
  become: true
  tasks:
    - name: Install nginx and PHP-FPM
      apt:
        name: ["nginx", "php-fpm"]
        state: present
        update_cache: true
    - name: Start and enable nginx
      service:
        name: nginx
        state: started
        enabled: true

The full process — from ordering the server to a ready-to-work environment — is usually set up as a separate stage in a GitLab CI pipeline: first a job with terraform apply, then a job with ansible-playbook that gets the address from the first step's artifacts. For more on describing infrastructure as code, see the article on Terraform for VPS.

Common mistakes when combining the two

The first mistake is duplicated responsibility: part of the server setup is written through cloud-init inside Terraform, and part through Ansible, and six months later nobody remembers where to change what. The second is keeping Terraform's state file locally on a developer's laptop without a backup: if the file is lost, Terraform stops knowing what already exists and tries to recreate resources from scratch.

  • Mixing OS configuration tasks between cloud-init and Ansible in one project
  • A local Terraform state file with no remote backend and no locking
  • Running Ansible without first checking the playbook with the --check mode

Summary: a short choice checklist

If you need to manage servers, disks, and networks at the provider — use Terraform. If you need to configure what is already running — use Ansible. In most projects both tools are used together, each in its own area of responsibility.

  • Cloud infrastructure and its versions in git — Terraform
  • Packages, configs, and services inside the server — Ansible
  • Keep Terraform state remote, with locking
  • Connect both steps with one CI pipeline instead of running them by hand
← Back to Knowledge Base Ask Support