Ansible Cache Plugins — Fact Caching for Performance

Introduction

Every time Ansible runs a playbook, it gathers facts from every host — hardware info, network configuration, OS details. For large inventories (hundreds or thousands of hosts), this adds minutes to every run.

Cache plugins solve this by storing gathered facts between playbook runs. The next run reads cached facts instead of re-gathering them, dramatically reducing execution time.

How Fact Caching Works

``` Without caching: Run 1: gather_facts (60s) → tasks (30s) = 90s Run 2: gather_facts (60s) → tasks (30s) = 90s

With caching: Run 1: gather_facts (60s) → cache facts → tasks (30s) = 90s Run 2: read cache (1s) → tasks (30s) = 31s ← 66% faster ```

Available Cache Plugins

PluginBackendSharedPersistentBest For
`memory`RAMNoNoDefault (single run)
`jsonfile`JSON filesYes*YesSimple setups
`yaml`YAML filesYes*YesHuman-readable cache
`redis`Redis serverYesYesTeams, CI/CD, Tower
`memcached`MemcachedYesNoHigh-speed, ephemeral
`mongodb`MongoDBYesYesLarge inventories
`pickle`Pickle filesYes*YesFast serialization

* File-based plugins are shared if the cache directory is on shared storage.

JSON File Cache (Simplest)

```ini

ansible.cfg

[defaults] gathering = smart fact_caching = jsonfile fact_caching_connection = /tmp/ansible_facts_cache fact_caching_timeout = 86400 # 24 hours in seconds ```

```bash

Create cache directory

mkdir -p /tmp/ansible_facts_cache

After first run, check cached facts

ls /tmp/ansible_facts_cache/

web1.example.com web2.example.com db1.example.com

View cached facts for a host

cat /tmp/ansible_facts_cache/web1.example.com | python3 -m json.tool | head -20 ```

Setup Redis

```bash

Install Redis

sudo apt install redis-server # Debian/Ubuntu sudo dnf install redis # RHEL/Fedora

Start Redis

sudo systemctl enable --now redis

Install Python Redis client on control node

pip install redis ```

Configure Ansible

```ini

ansible.cfg

[defaults] gathering = smart fact_caching = redis fact_caching_connection = localhost:6379:0 fact_caching_timeout = 86400 fact_caching_prefix = ansible_facts_ ```

Redis with Authentication

```ini

ansible.cfg

[defaults] fact_caching = redis fact_caching_connection = redis://mypassword@redis.example.com:6379/0 fact_caching_timeout = 3600 ```

Redis with TLS

```ini fact_caching_connection = rediss://mypassword@redis.example.com:6380/0 ```

Verify Redis Cache

```bash

Check cached keys

redis-cli KEYS "ansible_facts_*"

View cached facts for a host

redis-cli GET "ansible_facts_web1.example.com" | python3 -m json.tool | head

Clear cache

redis-cli KEYS "ansible_facts_*" | xargs redis-cli DEL ```

Memcached Cache

```bash

Install memcached

sudo apt install memcached pip install python-memcached ```

```ini

ansible.cfg

[defaults] gathering = smart fact_caching = memcached fact_caching_connection = ['localhost:11211'] fact_caching_timeout = 3600 ```

Gathering Modes

The `gathering` setting controls when facts are collected:

ModeBehavior
`implicit`Always gather facts (default)
`explicit`Only gather when `gather_facts: true`
`smart`Gather only if not in cache

```ini

ansible.cfg

[defaults] gathering = smart # Required for fact caching to work ```

Complete Performance-Optimized Configuration

```ini

ansible.cfg — optimized for large inventories

[defaults] gathering = smart fact_caching = redis fact_caching_connection = redis.example.com:6379:0 fact_caching_timeout = 86400 forks = 50 strategy = free host_key_checking = False

[ssh_connection] pipelining = True ssh_args = -o ControlMaster=auto -o ControlPersist=600s ```

Playbook-Level Cache Control

```yaml

Skip fact gathering entirely (fastest)

  • name: Deploy config files hosts: webservers gather_facts: false tasks:
    • name: Copy config ansible.builtin.template: src: nginx.conf.j2 dest: /etc/nginx/nginx.conf

Force fresh facts (ignore cache)

  • name: Inventory audit hosts: all gather_facts: true module_defaults: ansible.builtin.setup: gather_timeout: 30 tasks:
    • name: Report OS versions ansible.builtin.debug: msg: "{{ ansible_distribution }} {{ ansible_distribution_version }}" ```

Clearing the Cache

```bash

Clear JSON file cache

rm -rf /tmp/ansible_facts_cache/*

Clear Redis cache

redis-cli KEYS "ansible_facts_*" | xargs redis-cli DEL

Clear cache programmatically in a playbook

ansible-playbook site.yml --flush-cache

Or with the meta module

```

```yaml

  • name: Clear facts cache hosts: all tasks:
    • name: Flush cached facts ansible.builtin.meta: clear_facts ```

Benchmarks

Tested with 500 hosts on a standard control node:

ConfigurationTime (first run)Time (cached)Speedup
No caching4m 12s4m 12s—
JSON file4m 15s1m 38s2.6x
Redis4m 14s1m 22s3.1x
Redis + pipelining4m 14s0m 58s4.3x
No facts + Redis—0m 42s6.0x

Troubleshooting

Cache not working: ```bash

Verify gathering mode is 'smart'

ansible-config dump | grep CACHE ansible-config dump | grep GATHERING ```

Stale facts: ```bash

Reduce cache timeout or flush

ansible-playbook site.yml --flush-cache ```

Redis connection refused: ```bash

Check Redis is running

redis-cli ping # Should return PONG

Check firewall

sudo ss -tlnp | grep 6379 ```

Conclusion

Cache plugins are one of the easiest performance wins in Ansible. Start with JSON file caching for development, switch to Redis for production and CI/CD pipelines. Combined with `gathering = smart` and SSH pipelining, you can reduce playbook execution time by 3-6x for large inventories.