How to Use Jenkins with Ansible for Configuration Management

How to Use Jenkins with Ansible for Configuration Management

Building and testing your application is only half the CI/CD story — at some point that code has to land on servers that are configured correctly, consistently, and repeatably. That’s where Ansible comes in. It’s agentless, uses simple YAML playbooks, and connects over SSH, which makes it a natural fit to run right from inside a Jenkins pipeline. Together, Jenkins triggers the automation and Ansible does the actual configuration and deployment work on your target servers.

This guide covers setting up Jenkins to run Ansible playbooks, managing inventories and secrets properly, and building complete pipelines for configuration management and deployment.

Why Jenkins + Ansible?

Jenkins is excellent at orchestration — deciding when a build happens, what tests gate it, and what order things run in. Ansible is excellent at describing infrastructure and application state declaratively — “this server should have these packages, this config file, this service running.” Neither tool replaces the other; combining them means every code change can trigger not just a build, but a fully automated, idempotent configuration update across your fleet of servers.

Jenkins Architecture Considerations

Because Ansible is agentless (it just needs SSH access to target hosts, not an agent installed on them), the Jenkins side of this integration is simpler than you might expect. The Jenkins agent running the pipeline just needs Ansible installed and SSH key access to your target servers — no additional infrastructure required on the servers themselves beyond Python, which Ansible depends on.

Prerequisites

  • Jenkins server with an agent that has Ansible installed
  • SSH key-based access configured from the Jenkins agent to target servers
  • An Ansible inventory file listing your target hosts
  • Existing Ansible playbooks (or willingness to write some)

Step 1: Install Ansible on the Jenkins Agent

sudo apt update
sudo apt install -y ansible
ansible --version

Step 2: Install the Ansible Plugin for Jenkins

  1. Go to Manage Jenkins > Plugins > Available Plugins
  2. Search for Ansible
  3. Install it and restart Jenkins

This plugin gives you native pipeline steps like ansiblePlaybook instead of having to shell out manually, and it also handles credential injection more cleanly.

Step 3: Set Up SSH Credentials

Jenkins needs an SSH key that has access to your target servers.

  1. Generate a key pair if you don’t have one: ssh-keygen -t ed25519 -f jenkins_ansible_key
  2. Add the public key to ~/.ssh/authorized_keys on each target server
  3. In Jenkins, go to Manage Jenkins > Credentials > Global
  4. Add a new credential of type SSH Username with private key, paste in the private key, and give it an ID like ansible-ssh-key

Step 4: Create Your Inventory

A basic static inventory (inventory/hosts.ini):

[webservers]
web1.example.com
web2.example.com
[databases]

db1.example.com

[webservers:vars]

ansible_user=deploy ansible_python_interpreter=/usr/bin/python3

For dynamic environments (like AWS), you’d use a dynamic inventory plugin instead of a static file, querying EC2 tags to build the host list at runtime.

Step 5: A Sample Playbook

Here’s a simple playbook that deploys an application (deploy.yml):

---
- name: Deploy application
  hosts: webservers
  become: true
  vars:
    app_version: "{{ app_version }}"

  tasks:
    - name: Ensure application directory exists
      file:
        path: /opt/myapp
        state: directory
        owner: deploy
        group: deploy

    - name: Pull latest Docker image
      docker_image:
        name: "myorg/myapp:{{ app_version }}"
        source: pull

    - name: Stop existing container
      docker_container:
        name: myapp
        state: absent

    - name: Start new container
      docker_container:
        name: myapp
        image: "myorg/myapp:{{ app_version }}"
        state: started
        restart_policy: always
        ports:
          - "80:8080"

    - name: Verify service is responding
      uri:
        url: http://localhost/health
        status_code: 200
      retries: 5
      delay: 5

Step 6: Jenkinsfile Using the Ansible Plugin

pipeline {
    agent any

    environment {
        APP_VERSION = "1.0.${BUILD_NUMBER}"
    }

    stages {
        stage('Checkout') {
            steps {
                git branch: 'main', url: 'https://github.com/yourorg/infra-playbooks.git'
            }
        }

        stage('Lint Playbook') {
            steps {
                sh 'ansible-lint deploy.yml'
            }
        }

        stage('Deploy with Ansible') {
            steps {
                ansiblePlaybook(
                    playbook: 'deploy.yml',
                    inventory: 'inventory/hosts.ini',
                    credentialsId: 'ansible-ssh-key',
                    extras: '-e app_version=${APP_VERSION}',
                    colorized: true
                )
            }
        }
    }

    post {
        success {
            echo "Deployment of version ${APP_VERSION} completed successfully."
        }
        failure {
            echo 'Ansible deployment failed - check playbook output above.'
        }
    }
}

Step 7: Alternative — Running Ansible via Shell Step

If you prefer not to use the plugin, you can call ansible-playbook directly:

stage('Deploy with Ansible') {
    steps {
        sshagent(credentials: ['ansible-ssh-key']) {
            sh '''
                ansible-playbook -i inventory/hosts.ini deploy.yml \
                --extra-vars "app_version=${APP_VERSION}"
            '''
        }
    }
}

Both approaches work; the plugin gives you nicer log formatting and structured credential handling, while the shell approach is more transparent about exactly what’s being executed.

Step 8: Managing Secrets with Ansible Vault

Sensitive variables (database passwords, API keys) shouldn’t live in plaintext in your playbooks or inventory. Encrypt them with Ansible Vault:

ansible-vault encrypt group_vars/webservers/secrets.yml

Then supply the vault password to Jenkins as a secret credential and pass it during the playbook run:

stage('Deploy with Ansible') {
    steps {
        withCredentials([string(credentialsId: 'ansible-vault-password', variable: 'VAULT_PASS')]) {
            sh '''
                echo "$VAULT_PASS" > /tmp/vault_pass.txt
                ansible-playbook -i inventory/hosts.ini deploy.yml \
                --vault-password-file /tmp/vault_pass.txt \
                --extra-vars "app_version=${APP_VERSION}"
                rm -f /tmp/vault_pass.txt
            '''
        }
    }
}

Always clean up the temporary vault password file so it doesn’t linger on the agent’s disk.

Full Configuration Management Pipeline Example

Here’s a broader example combining server provisioning-style configuration with application deployment, useful for teams managing full server baselines:

pipeline {
    agent any

    stages {
        stage('Checkout Playbooks') {
            steps {
                git branch: 'main', url: 'https://github.com/yourorg/infra-playbooks.git'
            }
        }

        stage('Syntax Check') {
            steps {
                sh 'ansible-playbook --syntax-check site.yml -i inventory/hosts.ini'
            }
        }

        stage('Dry Run (Check Mode)') {
            steps {
                sh 'ansible-playbook site.yml -i inventory/hosts.ini --check --diff'
            }
        }

        stage('Approve Changes') {
            steps {
                input message: 'Review the dry-run output above. Apply changes?'
            }
        }

        stage('Apply Configuration') {
            steps {
                ansiblePlaybook(
                    playbook: 'site.yml',
                    inventory: 'inventory/hosts.ini',
                    credentialsId: 'ansible-ssh-key'
                )
            }
        }
    }
}

The --check --diff dry-run stage is especially valuable for configuration management — it shows exactly what would change without actually applying it, giving a human reviewer a chance to catch anything unexpected before it hits production servers.

Integrating with Terraform for Full Infrastructure + Config Pipelines

Many teams pair Terraform (for provisioning infrastructure) with Ansible (for configuring it) in the same pipeline:

stage('Provision Infrastructure') {
    steps {
        sh '''
            terraform init
            terraform apply -auto-approve
            terraform output -json > tf_outputs.json
        '''
    }
}

stage('Generate Dynamic Inventory') {
    steps {
        sh 'python3 scripts/tf_to_ansible_inventory.py tf_outputs.json > inventory/dynamic.ini'
    }
}

stage('Configure Servers') {
    steps {
        ansiblePlaybook(
            playbook: 'site.yml',
            inventory: 'inventory/dynamic.ini',
            credentialsId: 'ansible-ssh-key'
        )
    }
}

Troubleshooting

“Permission denied (publickey)” errors: Confirm the public key is actually in the target server’s authorized_keys, and that the Jenkins credential contains the matching private key without extra whitespace.

Playbook hangs indefinitely: Usually an SSH host key verification prompt blocking non-interactively — set ANSIBLE_HOST_KEY_CHECKING=False as an environment variable in the pipeline, or better, pre-populate known_hosts on the agent.

“Python interpreter not found” on target hosts: Set ansible_python_interpreter explicitly in your inventory, since some minimal server images don’t have Python at the default path Ansible expects.

Vault decryption fails: Double-check the vault password credential is correct and that the temp file isn’t getting truncated or double-encoded when written from the Jenkins credential.

Security Best Practices

  • Never commit unencrypted secrets — always use Ansible Vault for sensitive variables
  • Use a dedicated, least-privilege SSH key for Jenkins-to-server automation, separate from personal admin keys
  • Restrict which pipelines/jobs can access the Ansible SSH credential using Jenkins credential scoping
  • Run ansible-lint as a pipeline stage to catch playbook security anti-patterns (e.g., overly permissive file modes)
  • Use --check dry-run stages with manual approval gates before applying changes to production

FAQs

Does Jenkins need an Ansible agent installed on target servers? No — Ansible is agentless. Only the Jenkins agent running the pipeline needs Ansible installed; target servers just need SSH access and Python.

Can I use dynamic inventories with cloud providers like AWS or Azure? Yes, Ansible supports dynamic inventory plugins for major cloud providers that query live infrastructure tags/metadata instead of relying on a static host list.

What’s the benefit of the Ansible Jenkins plugin over just running ansible-playbook in a shell step? The plugin gives cleaner colorized output in the Jenkins console, structured credential handling, and slightly less boilerplate, but functionally both approaches achieve the same result.

How do I handle different environments (staging vs production) with the same playbook? Use separate inventory files or inventory groups per environment, and pass an --extra-vars environment=staging flag, or maintain separate group_vars directories per environment.

Can Ansible and Jenkins work together for zero-downtime rolling deployments? Yes, using Ansible’s serial keyword in a playbook to update hosts in batches, combined with health checks between batches, achieving rolling updates orchestrated entirely from the Jenkins pipeline.

Summary

Jenkins and Ansible complement each other well: Jenkins decides when automation should run and provides the orchestration and approval workflow, while Ansible handles the actual server configuration and deployment logic in a declarative, idempotent way. With SSH credentials set up correctly, Ansible Vault protecting secrets, and a pipeline that includes a dry-run/approval step before applying changes, you get a configuration management workflow that’s both automated and safe enough for production use.

References

Total
1
Shares

Leave a Reply

Previous Post
How to Configure Jenkins for Automated Deployment

How to Configure Jenkins for Automated Deployment

Next Post
How to Set Up Jenkins for Ruby on Rails Projects

How to Set Up Jenkins for Ruby on Rails Projects

Related Posts