Introduction

The meta-runtime rule in Ansible Lint validates the requires_ansible key in a collection's meta/runtime.yml file. This key specifies which versions of ansible-core are compatible with the collection. If the version constraint references an unsupported or end-of-life Ansible version, ansible-lint raises a schema[meta-runtime] error. This article covers the rule, correct version constraints, and best practices for collection compatibility.

Understanding meta/runtime.yml

Every Ansible collection should include a meta/runtime.yml file that declares:

  • requires_ansible: The minimum ansible-core version required
  • Plugin routing: Redirects, deprecations, and removals for modules/plugins
# meta/runtime.yml
---
requires_ansible: ">=2.15.0"
plugin_routing:
  modules:
    old_module:
      redirect: namespace.collection.new_module
      deprecation:
        removal_version: "3.0.0"
        warning_text: "Use new_module instead"

The Error

Problematic Code

# meta/runtime.yml
---
requires_ansible: ">=2.9"

Lint Output

$ ansible-lint meta/runtime.yml
WARNING  Listing 1 violation(s) that are fatal
schema[meta-runtime]: $ None is not of type 'object'.
meta/runtime.yml:1

                 Rule Violation Summary
 count tag                  profile rule associated tags
     1 schema[meta-runtime] basic   core

Failed: 1 failure(s), 0 warning(s) on 1 files.

The error occurs because ansible-core 2.9 does not exist (Ansible 2.9 was the monolithic package before the split). The requires_ansible key must reference valid ansible-core versions.

Correct Code

# meta/runtime.yml
---
requires_ansible: ">=2.15.0"

Supported ansible-core Versions

As of 2026, the currently supported versions:

ansible-coreStatusEnd of Life
2.16.xMaintainedNov 2025
2.17.xMaintainedMay 2026
2.18.xCurrentNov 2026
2.19.xCurrentMay 2027
2.20.xLatestNov 2027

End of life (no longer valid for requires_ansible):

ansible-coreEOL Date
2.13.xNov 2023
2.14.xMay 2024
2.15.xNov 2024

If your requires_ansible references an EOL version, ansible-lint will flag it.

Version Constraint Syntax

The requires_ansible field uses PEP 440 version specifiers:

# Minimum version only
requires_ansible: ">=2.15.0"

# Version range
requires_ansible: ">=2.15.0,<2.19.0"

# Compatible release (any 2.17.x)
requires_ansible: "~=2.17.0"

# Exact version (not recommended)
requires_ansible: "==2.17.5"
# ✅ Support current + 2 older maintained versions
requires_ansible: ">=2.16.0"

# ✅ Restrict to tested versions
requires_ansible: ">=2.16.0,<2.21.0"

# ❌ Too old — 2.9 is pre-split, not ansible-core
requires_ansible: ">=2.9"

# ❌ Too restrictive — limits adoption
requires_ansible: "==2.17.3"

Common Causes of the Error

1. Referencing Pre-Split Ansible Versions

# ❌ Ansible 2.9 was monolithic, not ansible-core
requires_ansible: ">=2.9"

# ❌ ansible-base 2.10 was renamed to ansible-core
requires_ansible: ">=2.10"

# ✅ Use ansible-core version numbers (2.14+)
requires_ansible: ">=2.15.0"

2. Missing runtime.yml Entirely

If your collection lacks meta/runtime.yml, ansible-lint may also flag issues when it cannot determine compatibility.

# Create the file
mkdir -p meta
cat > meta/runtime.yml << 'EOF'
---
requires_ansible: ">=2.15.0"
EOF

3. Invalid YAML Structure

# ❌ Wrong — top level must be a mapping
- requires_ansible: ">=2.15.0"

# ✅ Correct
---
requires_ansible: ">=2.15.0"

4. Unsupported Version in Range

# ❌ 2.13 is EOL
requires_ansible: ">=2.13.0"

# ✅ Update to currently supported minimum
requires_ansible: ">=2.16.0"

Plugin Routing in runtime.yml

Beyond requires_ansible, runtime.yml handles module and plugin migrations:

Redirecting Deprecated Modules

---
requires_ansible: ">=2.16.0"
plugin_routing:
  modules:
    old_module_name:
      redirect: my_namespace.my_collection.new_module_name

Deprecating a Module

plugin_routing:
  modules:
    legacy_module:
      deprecation:
        removal_version: "4.0.0"
        warning_text: "Use 'modern_module' instead. This module will be removed in version 4.0.0."

Removing a Module

plugin_routing:
  modules:
    removed_module:
      tombstone:
        removal_version: "3.0.0"
        warning_text: "This module has been removed. Use 'replacement_module' instead."

Routing for Different Plugin Types

plugin_routing:
  modules:
    old_module:
      redirect: namespace.collection.new_module
  lookup:
    old_lookup:
      redirect: namespace.collection.new_lookup
  filter:
    old_filter:
      redirect: namespace.collection.new_filter
  callback:
    old_callback:
      redirect: namespace.collection.new_callback

Complete runtime.yml Example

---
requires_ansible: ">=2.16.0"
plugin_routing:
  modules:
    # Redirect old module name to new
    my_old_module:
      redirect: my_namespace.my_collection.my_new_module
    
    # Deprecate a module with warning
    legacy_config:
      deprecation:
        removal_version: "3.0.0"
        warning_text: >-
          Use 'advanced_config' module instead.
          This module will be removed in version 3.0.0.
    
    # Module completely removed
    ancient_module:
      tombstone:
        removal_version: "2.0.0"
        warning_text: >-
          This module was removed in version 2.0.0.
          Use 'modern_module' instead.
  
  action:
    old_action:
      redirect: my_namespace.my_collection.new_action

Updating requires_ansible for New Releases

When a new ansible-core version releases, update your collection's compatibility:

# Check current setting
cat meta/runtime.yml

# Update minimum version
sed -i 's/requires_ansible: ">=2.15.0"/requires_ansible: ">=2.16.0"/' meta/runtime.yml

# Validate
ansible-lint meta/runtime.yml

Testing Against Multiple Versions

Use a CI matrix to test your collection against supported versions:

# .github/workflows/test.yml
strategy:
  matrix:
    ansible-core:
      - "2.16"
      - "2.17"
      - "2.18"
steps:
  - name: Install ansible-core
    run: pip install ansible-core~=${{ matrix.ansible-core }}
  - name: Run tests
    run: ansible-test sanity --docker

Best Practices

  1. Set requires_ansible to the oldest supported version you test against — don't claim compatibility you haven't verified
  2. Update when versions reach EOL — bump the minimum version to stay current
  3. Use ranges for upper bounds — >=2.16.0,<2.21.0 prevents untested combinations
  4. Include runtime.yml in every collection — even if you only set requires_ansible
  5. Use plugin routing for renames — never break users by removing modules without redirects
  6. Run ansible-lint in CI — catch meta-runtime violations before publishing to Galaxy
  7. Follow semantic versioning for your collection — major version bumps when dropping old ansible-core support

Conclusion

The meta-runtime lint rule ensures your collection's requires_ansible key references valid, supported ansible-core versions. The fix is straightforward: update the version constraint to a currently maintained ansible-core release (2.16+). Beyond version constraints, use plugin_routing in runtime.yml to handle module renames, deprecations, and removals gracefully.