Automation doesn’t need clicks or manual pushes — it needs triggers. A trigger knows when to act and what to do next. Running an Ansible playbook manually isn’t automation; it’s just repetition.
Real automation syncs with your development process, reacting to code changes without intervention. That’s where CI/CD comes in and where Ansible shines.
For example, merging a pull request into the main branch of your microservices app triggers a chain of automated tasks like provisioning servers, updating config files, restarting services, checking health, and rolling forward. Behind the scenes, Ansible makes sure each task runs in the right order and environment, monitoring every change closely.
In this article, we’ll explore how to run Ansible and connect it to your CI/CD pipelines, making it listen and behave as part of a functioning automation system.
Prerequisites
To follow along and get the most out of this guide, you need to:
- Have Ansible installed on your local machine or control node
- Set up API access to Azure using a service principal or Azure CLI login
- Install the Azure Ansible Collection (azure.azcollection) using ansible-galaxy. To quickly do this, run the command below:
ansible-galaxy collection install azure.azcollection - Have a GitHub account to be able to access GitHub Actions
- Have access to an active Azure subscription
CI/CD stands for continuous integration and continuous deployment (or delivery). Together, CI/CD represents one of the most important ideas in modern software engineering: the ability to move code from development to production without breaking anything along the way.
CI/CD is about keeping everything in sync. When a developer pushes code, the system starts to move. The code is tested, built, packaged, and deployed. There’s no gap between what’s written and what’s delivered, and no broken link between shipping and running.
CI/CD can take different forms. You might see it running through GitHub Actions, GitLab Pipelines, Azure DevOps, or AWS CodePipeline. In each case, the goal is to make deployments consistent, fast, and safe.
Platforms like Spacelift take it a step further, bringing structure, governance, and deeper control to CI/CD pipelines, especially when infrastructure as code (IaC) is involved.
Ansible fits into CI/CD workflows primarily in the deployment and post-deployment stages, handling configuration management, application provisioning, and environment orchestration.
With Ansible, you can automate the bootstrapping of new environments, apply updates to existing ones, install dependencies, restart services, or even perform health checks after a deployment.
For a new version of your backend service to be merged successfully into the main branch of your repository, the CI pipeline handles the build and runs your tests, and then Ansible takes over the deployment process.
Ansible provides a new virtual machine (VM) or leverages an existing one, updates environment variables, pulls in the right secrets, and restarts the application.
What makes Ansible especially useful is that it speaks the same language as your infrastructure, giving your CI/CD pipeline the control and clarity required to handle infrastructure just as cleanly as it handles code.
This is possible because Ansible comes with built-in modules for most cloud providers, including Azure, AWS, and Google Cloud. These models abstract away low-level API calls and let you describe infrastructure using plain YAML. Instead of writing scripts to spin up resources, you describe what you want, and Ansible handles the rest.
Without Ansible, most automation often stops at the build. But with it, your workflow goes all the way to production.
Read more: 7 Ansible Use Cases – Management & Automation Examples
Key roles of Ansible playbooks in CI/CD processes
In a CI/CD workflow, an Ansible playbook tells the system exactly what needs to be done and in what order. While a script simply executes a set of commands, a playbook describes desired outcomes. That makes it easier to maintain, review, and reuse across projects.
Ansible playbooks can play the following roles in your CI/CD processes:
- Environment provisioning: Spinning up a new VM, setting up containers, or preparing cloud services like storage or databases. This means that every deployment, no matter when or where it happens, starts with the same consistent foundation.
- Application deployment: Once your CI system builds the app, the Ansible playbook takes over to install packages, pull in the latest code, configure files, manage secrets, and launch services. It does this in a controlled, repeatable way, making sure that each deployment matches the expected state.
- Post-deployment validation: A playbook can run checks after a rollout, verifying service availability, testing endpoints, or even cleaning up temporary files. These tasks help catch silent failures before they become user-facing problems.
- Rolling updates: Playbooks help you to define update strategies, pause between actions, and include manual approvals when needed for flexibility without losing automation.
- Infrastructure updates and patching: As your systems evolve, Ansible ensures that changes are applied evenly across all environments with minimal downtime.
Ansible connects the process of delivering your application to the process of preparing the environment in which it will run. When your application is ready to ship, Ansible can provision the servers, configure load balancers and networks, and apply the necessary settings all as part of the same pipeline.
Here are a few example tasks Ansible handles:
Provisioning infrastructure
Code needs infrastructure to run.. In some CI/CD workflows, this includes creating temporary infrastructure for short-lived environments, such as running integration tests or validating how your application behaves in different cloud setups.
The following playbook creates a new Azure resource group and deploys a VM inside it. This approach works well when you need an ephemeral environment that can be created at the start of a pipeline run and destroyed after tests are complete.
- name: Create resource group and virtual machine
hosts: localhost
tasks:
- name: Create resource group
azure_rm_resourcegroup:
name: webapp-rg
location: eastus
- name: Create VM
azure_rm_virtualmachine:
name: webapp-vm
resource_group: webapp-rg
admin_username: devops
image:
offer: UbuntuServer
publisher: Canonical
sku: '18.04-LTS'
version: latest
vm_size: Standard_B1s
In the playbook above, the first task creates a resource group named webapp-rg
in the eastus
region using the azure_rm_resourcegroup
module. The second task provisions a VM named webapp-vm
inside that group.
It sets up a VM based on the Ubuntu 18.04 LTS image, assigns it the size Standard_B1s
, and configures the administrator username as devops
.
Everything runs locally using the localhost
host declaration, which is typical for provisioning workflows that don’t target remote systems directly.
Configuring application environments
Once the infrastructure is ready, Ansible can install dependencies, pull application code, and configure runtime settings.
The playbook below handles basic application deployment. It installs the necessary packages and pulls the latest version of your app code from a Git repository.
- name: Deploy app code and install dependencies
hosts: webapp
tasks:
- name: Install required packages
apt:
name: ['nginx', 'git']
update_cache: yes
- name: Pull latest code
git:
repo: 'https://github.com/example/webapp.git'
dest: /var/www/webapp
In this playbook, the apt
module installs the nginx
and git
packages on all hosts in the webapp group. The update_cache: yes
option ensures the system retrieves the latest package index before installation.
The next task uses the git
module to clone or update the https://github.com/example/webapp.git
repository, placing the code in the /var/www/webapp
directory. This setup is a common baseline for deploying lightweight web apps on Ubuntu-based servers.
Rolling updates
When applications are deployed to production, it is standard practice to ensure that updates happen gradually. Otherwise, you risk tanking users’ experience.
Ansible is able to achieve this by targeting subsets of servers at a time. Here’s how you do it:
- name: Rolling restart of backend services
hosts: backend
serial: 2
tasks:
- name: Pull latest code
git:
repo: 'https://github.com/example/backend.git'
dest: /opt/backend
- name: Restart backend service
service:
name: backend
state: restarted
This playbook targets all servers in the backend
host group, but instead of updating them all at once, it uses the serial: 2
directive to restart them two at a time. This helps reduce downtime and ensures service continuity during deployment.
The first task pulls the latest code from the https://github.com/example/backend.git
repository into the /opt/backend
directory.
The second task restarts the backend
service on each host using the service
module, ensuring that each node runs the most recent version of the application after the code update.
Post-deployment validation
After rollout, Ansible can check if services are correctly spun up and healthy. This stage is integral and helps you confirm if the deployment actually worked and if the microservices are set and function the way you intended them to.
The following is an example of a playbook checking the status of the application:
- name: Check application status
hosts: webapp
tasks:
- name: Ensure web app is reachable
uri:
url: http://localhost:80
status_code: 200
You need to have a clear structure before you start designing a CI/CD workflow. It is not about pushing code mindlessly through a pipeline. Ideally, your code should live in its own directory. The same goes for the playbooks and other environment-specific details. This ensures that every detail is kept simple, specific, and versioned.
Here’s a simple structure that works well for most teams:
ci-cd-ansible/
├── .github/
│ └── workflows/
│ └── deploy.yml # GitHub Actions pipeline
├── ansible/
│ ├── inventories/
│ │ ├── staging/
│ │ │ └── hosts.yml
│ │ └── production/
│ │ └── hosts.yml
│ ├── playbooks/
│ │ ├── provision.yml # Infra provisioning
│ │ ├── deploy.yml # App deployment
│ │ └── validate.yml # Post-deploy checks
│ └── roles/
│ ├── webserver/
│ └── database/
├── vars/
│ ├── staging.yml
│ └── production.yml
├── app/
│ ├── src/
│ └── Dockerfile
└── README.md
Once that’s in place, your CI workflow can trigger Ansible at specific points. Here’s what that looks like inside a GitHub Actions pipeline:
# .github/workflows/deploy.yml
name: Deploy with Ansible
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set up Python and Ansible
run: |
python -m pip install --upgrade pip
pip install ansible azure-cli
- name: Run provisioning playbook
run: ansible-playbook ansible/playbooks/provision.yml -i ansible/inventories/staging/hosts.yml
- name: Run deployment playbook
run: ansible-playbook ansible/playbooks/deploy.yml -i ansible/inventories/staging/hosts.yml
- name: Run post-deployment validation
run: ansible-playbook ansible/playbooks/validate.yml -i ansible/inventories/staging/hosts.yml
This GitHub Action workflow automates infrastructure provisioning, app deployment, and validation using Ansible.
On every push to main
, it installs dependencies, checks out the repo, runs three Ansible playbooks (for provisioning, deployment, and validation), and targets the staging inventory. It’s a clean CI/CD loop powered entirely by Ansible inside GitHub Actions.
Each playbook represents a phase in your pipeline.
Here’s what the deployment playbook might look like:
# ansible/playbooks/deploy.yml
- name: Deploy application
hosts: webapp
become: true
vars_files:
- ../../vars/staging.yml
tasks:
- name: Pull latest app code
git:
repo: https://github.com/example/app.git
dest: /var/www/app
- name: Restart app service
service:
name: webapp
state: restarted
This playbook deploys the latest version of your application to the webapp
hosts. It uses a vars_file
(staging.yml
) to inject environment-specific settings, ensuring flexibility across stages.
First, it pulls the most recent code from the https://github.com/example/app.git
repository into /var/www/app
.
Then it restarts the webapp
service so the updated application runs immediately. The become: true
directive ensures each task runs with elevated privileges.
After deployment, validation ensures your application is responding as expected. Here’s an example of a post-deployment check using Ansible’s uri
module:
# ansible/playbooks/validate.yml
- name: Validate application deployment
hosts: webapp
tasks:
- name: Check application health endpoint
uri:
url: http://localhost:80/health
status_code: 200
return_content: yes
register: result
- name: Display health check result
debug:
var: result.content
This playbook is the cherry on top; it verifies that the application deployed on webapp
hosts is running healthily.
The first task uses Ansible’s uri
module to send a HTTP request to the /health
endpoint at http://localhost:80
. It expects a 200
status code, meaning the request was successful, and it captures the response body in the variable result
.
The second task uses the debug
module to print the result.content
value, letting you see the exact response from the health check endpoint. This is useful for confirming not just that the service is reachable, but also that it’s returning the expected content after deployment.
This final step closes the loop and gives your pipeline a way to confirm that your infrastructure is functional.
Ansible on its own gives you clarity and control. CI/CD pipelines give you automation and speed. But when it comes to managing infrastructure workflows at scale, you often need a layer that brings consistency, visibility, and policy to the entire process. That’s where Spacelift fits in.
Spacelift serves as the control plane for IaC. It connects your version control system to your Ansible playbooks, automatically triggering runs when changes are made. This means infrastructure changes follow the same review, approval, and rollout flow as application code. You’re not running playbooks by hand, and you’re not waiting for someone to remember which environment variables to apply.
Instead of relying on hand-written CI scripts or scattered job definitions, Spacelift provides a structured, repeatable workflow. You push to Git, and Spacelift handles the execution. Each run happens in a clean, isolated environment. You can see what changed, who approved it, and what actions were taken. All from a single dashboard.
Spacelift also integrates policy enforcement directly into the workflow. Using Open Policy Agent (OPA), you can define rules for what gets deployed, where, and by whom. That might mean restricting who can touch production, blocking deployments that lack proper tagging, or requiring approvals before infrastructure updates go live. These checks happen automatically, without slowing your team down.
When paired with Ansible, Spacelift gives your playbooks more than just automation. It gives them governance. It turns every CI/CD run into a controlled change event, with logs, safeguards, and rollback visibility baked in.
This makes Spacelift a natural fit for teams already using Ansible in their CI/CD pipelines. It also gives you a platform designed for infrastructure, not just code delivery.
If you want to learn more about using Spacelift with Ansible, check our documentation, read our Ansible guide, or book a demo with one of our engineers.
Automation doesn’t start with Ansible, and it doesn’t end with CI/CD. It begins when the two work together, when your infrastructure listens to your code and responds in real time.
Ansible clarifies configuration, making infrastructure visible, repeatable, and understandable. But when placed inside a CI/CD pipeline, it becomes something more. It transforms into a system that reacts to change, deploys confidently, and validates every step along the way.
From provisioning environments to deploying services and running post-deployment checks, Ansible can serve as the core of your delivery process. Its playbooks are blueprints for how your systems should behave every time a developer merges code, pushes a fix, or rolls out a new feature.
Designing your pipeline around Ansible means treating infrastructure as part of the release, testing deployments rather than just commits, and building workflows where each step is controlled, traceable, and versioned.
Manage Ansible better with Spacelift
Managing large-scale playbook execution is hard. Spacelift enables you to automate Ansible playbook execution with visibility and control over resources, and seamlessly link provisioning and configuration workflows.
Frequently asked questions
Why should I integrate Ansible into my CI/CD workflow?
Integrating Ansible into CI/CD gives consistent, repeatable deployments and turns server configuration into testable, versioned code. It bridges the gap between built artifacts and running services through idempotent tasks, dynamic inventory, and safe rolling changes.
Is Ansible a continuous integration tool?
No, Ansible is not a continuous integration (CI) tool; it is primarily a configuration management and automation platform. Ansible is used to provision servers, deploy applications, enforce system configurations, and orchestrate workflows across infrastructure. In practice, teams often pair Ansible with a CI tool to bridge application delivery and infrastructure automation.
Can I replace Jenkins with Ansible?
No, you cannot directly replace Jenkins with Ansible because they serve different purposes, but you can use them together or partially overlap their roles. Ansible automates infrastructure and deployment, while Jenkins automates pipelines and integrations, so they are complementary rather than interchangeable.
How do I test playbooks in a pipeline?
You test playbooks in a pipeline by running them in controlled stages with linting, syntax validation, and targeted execution against test environments before deploying to production. In CI/CD systems, these steps are chained so failures prevent progression to later stages.
How do I handle Ansible secrets and inventories in CI/CD?
You should manage Ansible secrets and inventories in CI/CD by storing them in secure vaults and injecting them dynamically at runtime instead of keeping them in the repository. Ansible Vault can encrypt sensitive variables, but for CI/CD pipelines, it is more common to integrate with secret managers like HashiCorp Vault, AWS Secrets Manager, or GitHub Actions secrets.