Introduction
ansible-lint is the standard tool for checking Ansible playbooks, roles, and collections against best practices. It catches syntax errors, deprecated features, security issues, and style violations before they reach production. This guide covers installation, configuration, CI/CD integration, and every essential feature.
Installation
# pip (recommended)
pip install ansible-lint
# With specific version
pip install ansible-lint==24.7.0
# Verify installation
ansible-lint --version
System Packages
# Fedora/RHEL
sudo dnf install ansible-lint
# Ubuntu/Debian
sudo apt install ansible-lint
# macOS
brew install ansible-lint
Basic Usage
Lint a Single Playbook
ansible-lint playbook.yml
Lint Entire Project
# Lint all playbooks and roles in current directory
ansible-lint
List All Available Rules
ansible-lint -L
Show Rule Details
ansible-lint -L | grep yaml
ansible-lint --show-relax-rules
Configuration File
Create .ansible-lint in your project root:
# .ansible-lint
profile: production # min, basic, moderate, safety, shared, production
# Exclude paths from linting
exclude_paths:
- .github/
- tests/fixtures/
- molecule/
# Skip specific rules
skip_list:
- yaml[line-length]
- no-changed-when
# Treat warnings as errors
strict: true
# Enable auto-fix
fix: false
# Offline mode (no network access)
offline: true
# Set working directory
# project_dir: .
Profiles
Profiles bundle rules by strictness level:
| Profile | Description | Use Case |
|---|---|---|
min | Bare minimum rules | Legacy projects |
basic | Core syntax and logic | Getting started |
moderate | + Style and naming | Active development |
safety | + Security rules | Security-conscious teams |
shared | + Collaboration rules | Shared codebases |
production | All rules | Production automation |
# Start lenient, tighten over time
profile: moderate
Key Rules
Syntax and Logic
| Rule | Description |
|---|---|
syntax-check | Playbook fails ansible-playbook --syntax-check |
parser-error | YAML parsing failures |
no-changed-when | Commands/shell without changed_when |
no-handler | Tasks that should use handlers |
no-jinja-when | Jinja2 used inside when (already templated) |
Style and Naming
| Rule | Description |
|---|---|
name[play] | All plays should be named |
name[task] | All tasks should be named |
name[casing] | Task names should start with uppercase |
yaml[line-length] | Lines exceeding 160 characters |
yaml[truthy] | Use true/false instead of yes/no |
Security
| Rule | Description |
|---|---|
no-log-password | Tasks with passwords should use no_log |
partial-become | Inconsistent privilege escalation |
risky-file-permissions | Missing file permissions on create |
Deprecations
| Rule | Description |
|---|---|
deprecated-command-syntax | Free-form shorthand in command modules |
deprecated-module | Using deprecated modules |
latest[git] / latest[hg] | Unpinned VCS checkouts |
Autofix
ansible-lint can automatically fix some issues:
# Dry run — show what would be fixed
ansible-lint --fix --diff
# Actually fix files
ansible-lint --fix
Autofixable rules include:
yaml[truthy]—yes→true,no→falseyaml[octal-values]— Unquoted octalsname[casing]— Task name capitalizationfqcn— Add fully qualified collection nameskey-order— Reorder task keys
Skipping Rules
Per-Task
- name: Run legacy script
ansible.builtin.command: ./old-script.sh # noqa: no-changed-when
Per-Play/Block
- name: Legacy play
hosts: all
vars:
noqa: [risky-file-permissions]
tasks: ...
In Configuration
# .ansible-lint
skip_list:
- yaml[line-length]
- no-changed-when
- name[casing]
Warn Instead of Error
# .ansible-lint
warn_list:
- yaml[line-length]
- no-changed-when
CI/CD Integration
GitHub Actions
# .github/workflows/lint.yml
name: Ansible Lint
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run ansible-lint
uses: ansible/ansible-lint@main
GitLab CI
# .gitlab-ci.yml
ansible-lint:
image: python:3.12
script:
- pip install ansible-lint
- ansible-lint
rules:
- changes:
- "**/*.yml"
- "**/*.yaml"
- roles/**/*
Pre-commit Hook
# .pre-commit-config.yaml
repos:
- repo: https://github.com/ansible/ansible-lint
rev: v24.7.0
hooks:
- id: ansible-lint
pip install pre-commit
pre-commit install
Output Formats
# Default rich output
ansible-lint
# SARIF (for GitHub Code Scanning)
ansible-lint -f sarif > results.sarif
# JSON
ansible-lint -f json
# Codeclimate (GitLab)
ansible-lint -f codeclimate
# Plain text (for scripts)
ansible-lint -f pep8
Common Workflow
# 1. Check current state
ansible-lint --profile production
# 2. Auto-fix what's possible
ansible-lint --fix
# 3. Review remaining issues
ansible-lint
# 4. Skip rules you consciously accept
# Add to .ansible-lint or use inline noqa
Troubleshooting
"Couldn't resolve module/action"
Install the required collection:
ansible-galaxy collection install community.general
Too Many yaml[line-length] Warnings
# .ansible-lint
skip_list:
- yaml[line-length]
Slow on Large Projects
# .ansible-lint
exclude_paths:
- .git/
- collections/
- tests/output/
Related Articles
- Ansible Lint Rule 302: deprecated-command-syntax
- Ansible Lint Rule 402: latest[hg]
- Ansible Lint Rule sanity
- Ansible Best Practices Guide
- VS Code for Ansible Development
- Ansible Roles Explained
- Ansible-Core Guide
Conclusion
ansible-lint is essential for any serious Ansible project. Start with the moderate profile and tighten to production as your codebase matures. Use autofix for quick wins, integrate into CI/CD to catch issues early, and use .ansible-lint configuration to tune rules for your project. The combination of ansible-lint, pre-commit hooks, and CI/CD ensures consistent, high-quality Ansible code across your team.