GitLab CI/CD Pipeline for Ansible

This article describes one approach to setting up a GitLab CI/CD pipeline for testing and deploying Ansible content.

Lab Environment

For a lab environment, we need a GitLab server and a GitLab Runner server. Both can be provisioned using Vagrant and Ansible.

An example Vagrantfile along with with provisioning playbooks and roles are available on GitHub.


This is confirmed to be working on macOS 10.13.6 with the following:

The Vagrantfile can probably be modified for different providers, but that is untested.

Provisioning Steps
  • Clone repo and change to working directory:
$ git clone
$ cd demo-gitlab-cicd-ansible-lint
  • Install roles from Ansible Galaxy:
$ ansible-galaxy install -r roles/requirements.yml -p roles
  • Launch environment (this can take 5-10 minutes on the first launch attempt):
$ vagrant up

Create Project in GitLab

After the lab environment is provisioned, the GitLab web UI should be available at

Before logging in, you are prompted to set a password. This will become the password for the root account.

After the password is set, login as root, then follow these steps to create a project:

  • Click “Create a project”.
  • Enter “my-awesome-project” in the “Project name” field.
  • Select the “Public” option under “Visibilty Level”.
  • Click the green “Create project” button.

Create Project Screenshot

Add .gitlab-ci.yml to Project

Now we have an empty GitLab project named my-awesome-project.

Enabling a GitLab CI/CD pipeline requires adding a configuration filed named .gitlab-ci.yml to the project. Details about how to configure the file are available on the GitLab documentation site.

For the the sake of simplicity, the following example will enable a basic pipeline with a single job that runs ansible-lint against any files with a .yml extension in the root of the project:

  - lint

  stage: lint
    - ansible-lint *.yml

This can be added directly to the project through the GitLab web UI:

  • Browse to
  • Click the green “New file” button.
  • Enter .gitlab-ci.yml in the “File name” field.
  • Paste the example from above in the text field.
  • Click the green “Commit changes” box.

GitLab CI YML Screenshot

The UI will provide immediate feedback indicating the GitLab CI/CD configuration file is valid.

Register GitLab Runner

Now we must register our gitlab-runner service with the project. To do so, we first need the registration token.

GitLab Runner Token

The same playbook that was executed by Vagrant to provision the environment can be used to register the GitLab Runner with these steps:

  • Uncomment and update the last two variable entries in the vars/main.yml file:
$ tail -4 vars/main.yml
# gitlab-runner register Configuration.
# (uncomment and set before "vagrant provision gitlab-runner" command to configure gitlab-runner registration)
gitlab_runner_registration_token: "8Hay-zTAr5kSKpqE46HP"
gitlab_runner_description: "this is a test runner!"
  • Re-run Vagrant provisioning playbook for gitlab-runner VM:
$ vagrant provision gitlab-runner
  • Since the playbook is idempotent, many tasks will return “ok” and not make changes. The “Register runner to GitLab” task should show as “changed”:

GitLab Runner Register Task

GitLab Runner Registered

Commit Playbooks

Now that we have a properly configured pipeline and a registered runner to handle job execution, it’s time to commit playbooks.

Here is an example playbook that will fail an ansible-lint check:

- hosts: localhost
  connection: local
  gather_facts: false

    - name: run a command
      shell: echo hello world
      changed_when: false
      register: hello_world

    - name: print stdout
        var: hello_world.stdout

Use the GitLab web UI to commit the playbook to the project with these steps:

GitLab Add Playbook

GitLab Job Fail

The ansible-lint job failed because the playbook has a task that uses the shell module when it’s not actually required. Use the GitLab web UI to change to task to use the command module instead:

GitLab Edit Playbook

GitLab Job Pass

The ansible-lint job succeeded and the pipeline passed!

Next Steps

This is just a basic example of a pipeline for testing and experimental purposes. More complex pipelines might include additional jobs that perform Ansible Tower project syncing tasks, then execute a job template against a testing inventory.

Another possiblility is additional jobs that create a GitLab merge request and send notifications to potential code reviewers.

Upcoming blog posts will cover these scenarios in detail.

Written on September 20, 2018