GitHub Actions¶
GitHub Actions can run willhallonline/ansible either as a job container or through the project-maintained/community Docker-based action. Use a job container when you want normal shell steps. Use the action when you prefer a reusable action wrapper.
Recommended image
Pin a specific tag, for example willhallonline/ansible:2.21-alpine-3.24. Do not use latest for deployment workflows.
Prerequisites¶
- A workflow in
.github/workflows/. - An Ansible playbook such as
site.yml. - Inventory files such as
inventories/staging/hosts.ymlandinventories/production/hosts.yml. - Optional
requirements.ymlfor Galaxy roles and collections. - Repository or environment secrets for SSH and Vault data.
Useful related pages:
Option 1: use the image as a job container¶
A job container makes every run step execute inside the Ansible image.
name: ansible-ci
on:
pull_request:
push:
branches:
- main
jobs:
lint:
runs-on: ubuntu-latest
container:
image: willhallonline/ansible:2.21-alpine-3.24
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Show Ansible version
run: ansible --version
- name: Run ansible-lint
run: ansible-lint
Option 2: use the project-maintained action¶
The project-maintained composite action
willhallonline/docker-ansible-github-action
runs ansible-playbook inside the willhallonline/ansible container.
Runner and permissions
The action requires Docker on the runner. Grant only contents: read for
checkout unless the workflow needs additional permissions. The
action project page lists every input
default and the sole exit-code output.
A minimal shape is:
name: ansible-action
on:
workflow_dispatch:
jobs:
ansible:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Run docker-ansible action
uses: willhallonline/docker-ansible-github-action@v1.1.0
with:
playbook: playbooks/site.yml
inventory: inventory/localhost.ini
image-tag: 2.21-alpine-3.24
The action also accepts Galaxy requirements, Vault passwords, SSH keys,
known_hosts, extra variables, and additional ansible-playbook options as
listed on the action project page.
Full worked workflow¶
This workflow runs lint and syntax checks for pull requests and deploys only from main.
name: ansible
on:
pull_request:
push:
branches:
- main
workflow_dispatch:
env:
ANSIBLE_FORCE_COLOR: "true"
ANSIBLE_HOST_KEY_CHECKING: "False"
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
container:
image: willhallonline/ansible:2.21-alpine-3.24
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Galaxy dependencies
run: |
if [ -f requirements.yml ]; then
ansible-galaxy collection install -r requirements.yml
ansible-galaxy role install -r requirements.yml || true
fi
- name: Run ansible-lint
run: ansible-lint
syntax:
name: Syntax check
runs-on: ubuntu-latest
needs: lint
container:
image: willhallonline/ansible:2.21-alpine-3.24
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Galaxy dependencies
run: |
if [ -f requirements.yml ]; then
ansible-galaxy collection install -r requirements.yml
ansible-galaxy role install -r requirements.yml || true
fi
- name: Syntax check staging playbook
run: |
ansible-playbook \
-i inventories/staging/hosts.yml \
site.yml \
--syntax-check
deploy:
name: Deploy production
runs-on: ubuntu-latest
needs: syntax
if: github.ref == 'refs/heads/main'
environment: production
container:
image: willhallonline/ansible:2.21-alpine-3.24
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install SSH key
env:
SSH_PRIVATE_KEY: ${{ secrets.ANSIBLE_SSH_PRIVATE_KEY }}
run: |
mkdir -p ~/.ssh
chmod 700 ~/.ssh
printf '%s\n' "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
- name: Add known hosts
run: |
ssh-keyscan -H example.com >> ~/.ssh/known_hosts
chmod 644 ~/.ssh/known_hosts
- name: Write Vault password
env:
ANSIBLE_VAULT_PASSWORD: ${{ secrets.ANSIBLE_VAULT_PASSWORD }}
run: |
printf '%s' "$ANSIBLE_VAULT_PASSWORD" > .vault-password
chmod 600 .vault-password
- name: Install Galaxy dependencies
run: |
if [ -f requirements.yml ]; then
ansible-galaxy collection install -r requirements.yml
ansible-galaxy role install -r requirements.yml || true
fi
- name: Deploy
run: |
ansible-playbook \
-i inventories/production/hosts.yml \
site.yml \
--vault-password-file .vault-password
Secrets handling¶
Store these values as repository, organization, or environment secrets:
| Secret | Purpose |
|---|---|
ANSIBLE_SSH_PRIVATE_KEY |
Private key used by Ansible to connect to hosts. |
ANSIBLE_VAULT_PASSWORD |
Password for encrypted variables. |
Production secrets should be environment secrets protected by required reviewers.
Caching Galaxy dependencies¶
Cache installed collections if your requirements.yml is stable.
- name: Cache Ansible collections
uses: actions/cache@v4
with:
path: ~/.ansible/collections
key: ansible-collections-${{ hashFiles('requirements.yml') }}
Place the cache step before ansible-galaxy collection install.
Tips¶
- Keep lint and syntax checks separate from deployment.
- Use
environment: productionfor review gates. - Print
ansible --versionwhen debugging image upgrades. - Prefer pinned action SHAs or release tags for third-party actions.
- Use
ANSIBLE_HOST_KEY_CHECKING=Falseonly when the risk is acceptable. - Keep inventory and playbook paths explicit.
Troubleshooting¶
| Symptom | Check |
|---|---|
Permission denied (publickey) |
Confirm the private key secret preserves newlines and has chmod 600. |
| Vault decryption fails | Ensure the secret has no trailing newline problems and the playbook uses --vault-password-file. |
| Collection not found | Install requirements.yml before lint and syntax-check stages. |
| Deployment runs on pull requests | Add if: github.ref == 'refs/heads/main' and use protected environments. |
| Host key verification fails | Add known_hosts or set ANSIBLE_HOST_KEY_CHECKING=False for ephemeral hosts. |