Ansible in CI/CD Pipelines
Integrate Ansible with GitHub Actions and Jenkins to automate configuration management and application deployments as part of your CI/CD pipeline.
Running Ansible from CI/CD pipelines automates configuration management so infrastructure changes are version-controlled, reviewed, and applied consistently.
Learning outcomes
By the end you can:
- run an Ansible playbook from GitHub Actions
- handle Vault secrets securely in CI
- integrate Ansible into a Jenkins deploy stage
- structure a CI-friendly project layout
1) Project layout for CI
project/
├── .github/
│ └── workflows/
│ └── deploy.yml
├── ansible/
│ ├── ansible.cfg
│ ├── inventory/
│ │ ├── production.ini
│ │ └── staging.ini
│ ├── group_vars/
│ │ ├── all/
│ │ │ ├── vars.yml
│ │ │ └── vault.yml # encrypted
│ ├── roles/
│ │ └── app/
│ ├── requirements.yml
│ └── site.yml
└── Makefile
2) GitHub Actions: deploy with Ansible
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install Ansible and dependencies
run: |
pip install ansible boto3 botocore
ansible-galaxy install -r ansible/requirements.yml
- name: Write SSH private key
run: |
install -m 600 -D /dev/null ~/.ssh/deploy_key
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/deploy_key
- name: Write Vault password
run: echo "${{ secrets.VAULT_PASSWORD }}" > /tmp/vault_pass
- name: Run Ansible playbook
run: |
ansible-playbook \
-i ansible/inventory/production.ini \
--private-key ~/.ssh/deploy_key \
--vault-password-file /tmp/vault_pass \
ansible/site.yml
- name: Cleanup secrets
if: always()
run: |
rm -f ~/.ssh/deploy_key /tmp/vault_pass
Required GitHub Secrets
SSH_PRIVATE_KEY— private key for SSH access to target hostsVAULT_PASSWORD— Ansible Vault decryption password
3) ansible.cfg for CI
# ansible/ansible.cfg
[defaults]
inventory = inventory/
roles_path = roles/
host_key_checking = False # avoid interactive SSH host verification in CI
forks = 10
pipelining = True
[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
4) Staging vs production gates
Use GitHub Environments to add manual approval before production:
jobs:
deploy-staging:
runs-on: ubuntu-latest
environment: staging
steps:
- name: Deploy to staging
run: ansible-playbook -i ansible/inventory/staging.ini ansible/site.yml ...
deploy-production:
runs-on: ubuntu-latest
needs: deploy-staging
environment: production # requires manual approval in GitHub
steps:
- name: Deploy to production
run: ansible-playbook -i ansible/inventory/production.ini ansible/site.yml ...
5) Jenkins: Ansible deploy stage
pipeline {
agent any
environment {
VAULT_PASSWORD = credentials('ansible-vault-password')
SSH_KEY = credentials('deploy-ssh-key')
}
stages {
stage('Checkout') {
steps { checkout scm }
}
stage('Install Ansible') {
steps {
sh 'pip install ansible'
sh 'ansible-galaxy install -r ansible/requirements.yml'
}
}
stage('Deploy Staging') {
steps {
sh '''
echo "$VAULT_PASSWORD" > /tmp/vault_pass
ansible-playbook \
-i ansible/inventory/staging.ini \
--private-key "$SSH_KEY" \
--vault-password-file /tmp/vault_pass \
ansible/site.yml
rm -f /tmp/vault_pass
'''
}
}
stage('Deploy Production') {
when {
branch 'main'
}
input {
message "Deploy to production?"
ok "Deploy"
}
steps {
sh '''
echo "$VAULT_PASSWORD" > /tmp/vault_pass
ansible-playbook \
-i ansible/inventory/production.ini \
--private-key "$SSH_KEY" \
--vault-password-file /tmp/vault_pass \
ansible/site.yml
rm -f /tmp/vault_pass
'''
}
}
}
post {
always {
sh 'rm -f /tmp/vault_pass'
}
}
}
6) Linting and testing before deploy
Always lint and validate before running against real servers:
# In GitHub Actions, before the deploy step:
- name: Lint playbooks
run: ansible-lint ansible/site.yml
- name: Syntax check
run: ansible-playbook ansible/site.yml --syntax-check
- name: Dry run (check mode)
run: |
ansible-playbook \
-i ansible/inventory/staging.ini \
--check \
--diff \
--vault-password-file /tmp/vault_pass \
ansible/site.yml
Install ansible-lint:
pip install ansible-lint
Next steps
- Ansible Tower / AWX: web UI and RBAC for team-scale Ansible
- Molecule: local testing of Ansible roles with Docker
- Combine with Terraform: Terraform creates infrastructure, Ansible configures it
Frequently Asked Questions
How do I pass secrets to Ansible from CI without hardcoding them?
Store the Vault password as a CI secret (e.g., VAULT_PASSWORD in GitHub Secrets). In your workflow, write it to a temporary file, pass it to ansible-playbook with --vault-password-file, and delete the file after the run.
Should Ansible run in push or pull mode from CI?
Typically push: the CI runner SSHes into target hosts and applies configuration. For large fleets, consider pull mode with ansible-pull where hosts periodically fetch and apply their own configuration.