Introduction

ARA Records Ansible (ARA) is an open-source tool that records Ansible playbook execution data and makes it available through a web interface, CLI, and REST API. Every time you run a playbook, ARA captures tasks, results, hosts, files, and timing — giving you a searchable history of what ran, when, where, and what changed. This guide covers installation, configuration, and practical usage.

What ARA Records

ansible-playbook site.yml
        │
        ▼
┌──────────────────────┐
│ ARA Callback Plugin  │  ← Intercepts all task results
└──────────────────────┘
        │
        ▼
┌──────────────────────┐
│ ARA Database         │  ← SQLite (default) or PostgreSQL
│                      │
│ • Playbook metadata  │
│ • Play/task results  │
│ • Host facts         │
│ • File contents      │
│ • Timing data        │
│ • Changed/failed     │
└──────────────────────┘
        │
        ▼
┌──────────────────────┐
│ Browse Results       │
│ • Web UI             │
│ • CLI (ara)          │
│ • REST API           │
└──────────────────────┘

Installation

Install ARA with pip

# Install ARA with the API server (includes web UI)
pip install "ara[server]"

# Verify installation
ara --version

# Minimal install (recording only, no server)
pip install ara

Install with Ansible

# install-ara.yml
---
- name: Install ARA Records Ansible
  hosts: control_node
  become: true

  tasks:
    - name: Install ARA with server components
      ansible.builtin.pip:
        name: "ara[server]"
        state: present

    - name: Get ARA callback plugin path
      ansible.builtin.command: python3 -m ara.setup.callback_plugins
      register: ara_callback_path
      changed_when: false

    - name: Display callback path
      ansible.builtin.debug:
        msg: "Add this to ansible.cfg: callback_plugins = {{ ara_callback_path.stdout }}"

Configuration

# ansible.cfg
[defaults]
# Enable ARA callback plugin
callback_plugins = %(python_path)s/ara/plugins/callback
# Or get the path dynamically:
#   python3 -m ara.setup.callback_plugins

[ara]
# API client (default: offline — uses local database)
api_client = offline

# Database location (default: ~/.ara/server/ansible.sqlite)
# api_server = http://ara-server:8000
# api_username = ara
# api_password = password

Method 2: Environment Variables

# Enable ARA callback
export ANSIBLE_CALLBACK_PLUGINS=$(python3 -m ara.setup.callback_plugins)

# Or set all at once using ARA's helper
eval $(python3 -m ara.setup.env)

# Verify it's active
ansible-playbook --list-callbacks | grep ara

Method 3: Ansible Configuration with Environment

# Configure ARA via playbook environment
- name: Run playbook with ARA recording
  hosts: all
  environment:
    ARA_API_CLIENT: "offline"
    ARA_DATABASE_NAME: "/var/lib/ara/{{ lookup('pipe', 'date +%Y-%m') }}.sqlite"
  tasks:
    - name: Example task (will be recorded by ARA)
      ansible.builtin.debug:
        msg: "This task execution is recorded by ARA"

Using the Web UI

# Start the built-in development server
ara-manage runserver 0.0.0.0:8000

# Access at http://localhost:8000

# For production, use gunicorn
pip install gunicorn
ara-manage migrate
gunicorn --workers 4 --bind 0.0.0.0:8000 ara.server.wsgi

Deploy ARA Server with Ansible

# deploy-ara-server.yml
---
- name: Deploy ARA server
  hosts: ara_server
  become: true

  vars:
    ara_db_backend: "django.db.backends.postgresql"
    ara_db_name: "ara"
    ara_db_user: "ara"
    ara_db_password: "{{ vault_ara_db_password }}"
    ara_db_host: "localhost"
    ara_listen: "0.0.0.0:8000"
    ara_workers: 4

  tasks:
    - name: Install ARA server
      ansible.builtin.pip:
        name:
          - "ara[server]"
          - gunicorn
          - psycopg2-binary
        state: present

    - name: Create ARA settings file
      ansible.builtin.template:
        src: ara-settings.yaml.j2
        dest: /etc/ara/settings.yaml
        mode: "0640"
      notify: Restart ARA

    - name: Run database migrations
      ansible.builtin.command: ara-manage migrate
      environment:
        ARA_SETTINGS: /etc/ara/settings.yaml
      register: migrate
      changed_when: "'Applying' in migrate.stdout"

    - name: Create systemd service
      ansible.builtin.copy:
        content: |
          [Unit]
          Description=ARA Records Ansible API Server
          After=network.target postgresql.service

          [Service]
          Type=notify
          User=ara
          Group=ara
          Environment=ARA_SETTINGS=/etc/ara/settings.yaml
          ExecStart=/usr/local/bin/gunicorn \
            --workers {{ ara_workers }} \
            --bind {{ ara_listen }} \
            ara.server.wsgi
          Restart=on-failure

          [Install]
          WantedBy=multi-user.target
        dest: /etc/systemd/system/ara.service
      notify: Restart ARA

    - name: Enable and start ARA service
      ansible.builtin.systemd:
        name: ara
        state: started
        enabled: true
        daemon_reload: true

  handlers:
    - name: Restart ARA
      ansible.builtin.systemd:
        name: ara
        state: restarted
        daemon_reload: true

Using the CLI

# List recent playbooks
ara playbook list
# +----+---------------------+----------+--------+
# | id | started             | duration | status |
# +----+---------------------+----------+--------+
# | 1  | 2026-04-15 10:00:00 | 00:02:30 | completed |
# | 2  | 2026-04-15 09:30:00 | 00:05:12 | failed    |
# +----+---------------------+----------+--------+

# Show details of a specific playbook run
ara playbook show 1

# List results (tasks) from a playbook
ara result list --playbook 1

# Show failed tasks only
ara result list --status failed

# Show specific task result with full output
ara result show 42

# List hosts and their status
ara host list --playbook 1

# Show facts for a host
ara host show webserver1 --with-facts

# Search across all playbook runs
ara playbook list --label "deploy" --status failed

# Export playbook run as HTML report
ara playbook generate-html 1 --output /tmp/report/

# Delete old records (cleanup)
ara playbook delete 1

ARA with CI/CD

# .github/workflows/ansible.yml (GitHub Actions)
name: Ansible Deploy
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Ansible + ARA
        run: pip install ansible "ara[server]"

      - name: Configure ARA
        run: |
          mkdir -p ~/.ara
          eval $(python3 -m ara.setup.env)
          echo "ANSIBLE_CALLBACK_PLUGINS=$(python3 -m ara.setup.callback_plugins)" >> $GITHUB_ENV

      - name: Run playbook
        run: ansible-playbook -i inventory/production site.yml

      - name: Generate ARA report
        if: always()
        run: |
          ara playbook list
          ara playbook generate-html --output ara-report/
      
      - name: Upload ARA report
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: ara-report
          path: ara-report/

ARA with Centralized Server

# Configure all control nodes to report to central ARA server
# ansible.cfg on each control node
[defaults]
callback_plugins = /path/to/ara/plugins/callback

[ara]
api_client = http
api_server = https://ara.internal.example.com:8000
api_username = ansible-runner
api_password = {{ vault_ara_api_password }}
api_timeout = 30
# On the ARA server — configure authentication
# /etc/ara/settings.yaml
---
default:
  READ_LOGIN_REQUIRED: false   # Allow anonymous read
  WRITE_LOGIN_REQUIRED: true   # Require auth to write
  EXTERNAL_AUTH: false
  DATABASE_ENGINE: django.db.backends.postgresql
  DATABASE_NAME: ara
  DATABASE_USER: ara
  DATABASE_PASSWORD: "{{ ara_db_password }}"
  DATABASE_HOST: localhost
  DISTRIBUTED_SQLITE: false
  PAGE_SIZE: 100

Common Mistakes

# ❌ MISTAKE 1: ARA not recording — callback not loaded
# Check if ARA callback is active:
ansible-playbook --list-callbacks 2>&1 | grep ara
# If missing, verify ANSIBLE_CALLBACK_PLUGINS path
python3 -m ara.setup.callback_plugins

# ❌ MISTAKE 2: Permission error on SQLite database
# Default location: ~/.ara/server/ansible.sqlite
# Fix: Ensure the user running ansible-playbook owns the directory
mkdir -p ~/.ara/server
# Or use a custom location:
export ARA_DATABASE_NAME=/var/lib/ara/ansible.sqlite

# ❌ MISTAKE 3: ARA slowing down playbooks
# ARA adds minimal overhead (~1-2% for offline mode)
# HTTP mode adds network latency per task
# Fix for HTTP mode: use async recording
# [ara]
# api_timeout = 15
# Or batch results (ARA 1.7+)

# ❌ MISTAKE 4: Database growing too large
# Fix: Rotate or prune old records
ara playbook prune --days 90 --confirm
# Or use separate databases per month:
export ARA_DATABASE_NAME=~/.ara/$(date +%Y-%m).sqlite

ARA vs Other Tools

Feature          | ARA           | AWX/Tower      | Ansible Log
─────────────────|───────────────|────────────────|───────────────
Setup complexity | Low (pip)     | High (k8s/vm)  | None
Web UI           | ✅ Yes        | ✅ Yes          | ❌ No
REST API         | ✅ Yes        | ✅ Yes          | ❌ No
CLI              | ✅ Yes        | ❌ No           | ✅ grep
Auth/RBAC        | Basic         | Full RBAC      | None
Job scheduling   | ❌ No         | ✅ Yes          | ❌ No
Cost             | Free          | Subscription   | Free
Overhead         | Minimal       | Significant    | None
Best for         | Recording +   | Enterprise     | Quick
                 | browsing      | automation     | debugging

Conclusion

ARA Records Ansible turns ephemeral playbook output into searchable, browsable history. Install it with pip install "ara[server]", enable the callback plugin, and every playbook run is automatically recorded. Use the web UI for browsing, the CLI for scripting, and the REST API for integration. For teams, deploy a central ARA server so all engineers' playbook runs are visible in one place. It's the easiest way to answer "what changed, when, and why" across your Ansible automation.