Skip to content

Ansible Navigator: A Practical Deep Dive

Ansible

An Ansible playbook, or more specifically,ansible-playbook, runs from whatever Python, collections and system packages happen to be on the control node. That works when one person owns the environment or when a bastion is already pinned. It's a weak team contract: versions drift across laptops, CI runners, and shared hosts.

Ansible Navigator adds execution environments (container images for the control plane), a TUI for inspecting runs, and CLI behavior that lines up with AWX and Ansible Automation Platform (AAP). It runs and inspects the same playbooks and inventories when you want the runtime declared in an image tag instead of implied by the local machine.

This post covers installation, a first playbook, interactive vs stdout mode, custom images, configuration, and a CI-shaped workflow.

Why Use Ansible Navigator

Component Role
ansible-playbook Runs a playbook from the current control environment
ansible-navigator run Runs a playbook; optionally inspect results in the TUI
Execution Environment Container image that acts as the Ansible control node
ansible-builder Builds custom execution environment images
AWX or Automation Controller Runs automation centrally at platform scale

Plain ansible-playbook trusts the local control node, but Navigator earns its place when developers, platform engineers, CI, and controller jobs should share the same runtime.

ansible-playbook -i inventory.yml site.yml

While this command is simple, the control node behind it isn't:

Local Control Node Carries Typical Drift
Python Interpreter Different versions or site-packages
ansible-core Mismatched releases between systemscc
Collections Upgraded on one system, but not another
Python Libraries for Plugins Missing dependencies in CI
System Packages sshpass, crypto libs, and similar tools installed ad hoc

Navigator runs playbooks inside an execution environment image: ansible-core, ansible-runner, Python, collections, and the libraries your automation needs. The same image can be used locally, in CI, and later in AWX or AAP.

Consistent Execution

Navigator pins the control plane to an image instead of whatever each system installed last:

ansible-navigator run site.yml \
  --inventory inventory.yml \
  --execution-environment-image ghcr.io/ansible-community/community-ee-minimal:latest

With the runtime is declared the container image becomes part of the automation contract.

Better Debugging and Visibility

In interactive mode, Navigator gives you a structured view of plays, tasks, hosts, stdout, and event details. You can open the failed task directly instead of scrolling a long terminal log.

Situation What the TUI Provides You
One host fails in a large inventory Jump to that host result
Registered variables are large Inspect return data without re-running
A module returns nested JSON Browse structured output
You need ok/changed/skipped/failed counts Compare task results in one place
You want to review a finished run Replay from a playbook artifact

Simpler Dependency Management

Traditional Ansible dependency management often becomes a stck of localized steps:

python3 -m venv .venv
source .venv/bin/activate
pip install ansible-core
ansible-galaxy collection install -r collections/requirements.yml
pip install -r requirements.txt

That can still be useful during collection development, but it isn't a great team boundary. The moment a Python package, collection, or package differs between systems, the there is environment drift.

Execution environments move those dependencies into a container image and Navigator then runs the playbook inside that image.

Better Reproducibility

Once a team agrees on an execution environment container image, the discussion changes from "what's installed on your system?" to "which container image tag did you run?" You can pin that tag, publish it, scan it, promote it, and reuse it across local development, CI, AWX, and Automation Controller.

Use ansible-playbook when you want the lightest possible local command and you're comfortable with the current control node. Use Navigator when the runtime matters.

Need Prefer
Local testing with local dependencies ansible-playbook
Reproducibility ansible-navigator
Execution environment testing ansible-navigator
TUI inspection of play results ansible-navigator
CI output that looks like regular Ansible ansible-navigator --mode stdout
Controller or AWX parity ansible-navigator

Plainly put, if dependency drift can break the work, use Navigator.

Installation and Setup

You'll need three things to get started:

  • Python 3
  • pip
  • Podman or Docker

Podman is commonly used in the Ansible ecosystem, but Docker works too. The container runtime is what actually runs execution environment images.

Note

The official installation documentation recommends the ansible-dev-tools package as the streamlined install path for Ansible development tools.

Direct installation of ansible-navigator is also documented.

Install the Container Runtime

Install Docker Desktop or Podman Desktop. If you use the Podman CLI directly, initialize and start the Podman machine:

podman machine init
podman machine start
sudo apt update
sudo apt install -y podman python3 python3-pip
sudo dnf install -y podman python3 python3-pip

Verify the runtime:

podman --version

or:

docker --version

Install Ansible Navigator

For a full Ansible development toolchain:

python3 -m pip install --user ansible-dev-tools

For Navigator only:

python3 -m pip install --user ansible-navigator

Make sure your user install directory is on PATH:

The exact Python user path can vary by Python version. Check the install output from pip, then add that directory to your shell profile.

echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.profile
source ~/.profile
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.profile
source ~/.profile

Verify the Installation

ansible-navigator --version

If ansible-builder was installed with your toolchain, verify it too:

ansible-builder --version

Check that Navigator can see your container runtime:

ansible-navigator images --mode stdout

The first run may take a little longer because Navigator may need to inspect or pull images.

Core Concepts

Navigator becomes much easier once the main concepts click.

Execution Environments

An execution environment is a container image used as an Ansible control node.

Typical Contents Purpose
Python Runtime for Ansible and modules
ansible-core Ansible engine
ansible-runner Runner integration Navigator uses
Collections Modules, plugins, and roles baked into the image
Python Dependencies Libraries required by plugins and modules
System Packages Tools such as openssh-clients for controller-side plugins

Think of it as a packaged control plane for your automation. The managed nodes are still remote hosts, network devices, cloud APIs, or local targets. The execution environment is where the Ansible control logic runs.

Controller-side Dependencies Matter

Lookup plugins, inventory plugins, connection plugins, filter plugins, and modules delegated to localhost need their dependencies inside the execution environment. Regular modules executed on remote managed nodes still depend on the remote target.

Navigator has two main output modes:

Mode Behavior
interactive Opens the TUI for browsing plays, tasks, hosts, and results
stdout Prints output like a normal terminal command

The interactive mode is the default and it's best when you're developing or debugging.

The stdout mode fits CI jobs, scripts, and any workflow that needs a plain terminal stream.

Run interactively:

ansible-navigator run site.yml -i inventory.yml

Run with output:

ansible-navigator run site.yml -i inventory.yml --mode stdout

Inventory and Playbooks

Navigator uses the normal Ansible inventory and playbook concepts.

The inventory tells Ansible what hosts exist:

all:
  hosts:
    localhost:
      ansible_connection: local

And the playbook tells Ansible what to do:

- name: Demo Navigator Locally
  hosts: localhost
  gather_facts: false
  tasks:
    - name: Confirm the Playbook is Running
      ansible.builtin.debug:
        msg: "Hello World."

Both the inventory and playbook formats are unchanged. Navigator changes how the control plane runs and how you inspect the result.

Basic Usage Walkthrough

Create a small working directory:

mkdir ansible-navigator-test
cd ansible-navigator-test

Create inventory.yml:

all:
  hosts:
    localhost:
      ansible_connection: local

Create site.yml:

- name: Test Ansible Navigator
  hosts: localhost
  gather_facts: false
  tasks:
    - name: Print a Message
      ansible.builtin.debug:
        msg: "Navigator is running this playbook."

    - name: Get the Control Node Python Version
      ansible.builtin.command:
        cmd: python3 --version
      register: python_version
      changed_when: false

    - name: Display Python Dersion
      ansible.builtin.debug:
        var: python_version.stdout

Run it:

ansible-navigator run site.yml \
  --inventory inventory.yml \
  --execution-environment-image ghcr.io/ansible-community/community-ee-minimal:latest \
  --mode stdout

That command runs the playbook against localhost using the community minimal execution environment image.

Note

When you run against localhost from inside an execution environment, local facts and commands are about the containerized control environment, not always your physical laptop. That is useful, but it can surprise you the first time.

Using the TUI

Now run the same playbook without --mode stdout:

ansible-navigator run site.yml \
  --inventory inventory.yml \
  --execution-environment-image ghcr.io/ansible-community/community-ee-minimal:latest

Navigator opens the interactive interface.

The exact screen can vary by version, but the workflow is consistent:

  1. Start at the playbook run summary.
  2. Select the play.
  3. Select a task.
  4. Select a host result.
  5. Inspect stdout, return data, and status.

Common movement is keyboard-driven. Use the visible menu numbers, arrow keys, and the prompts shown by Navigator. Press Esc to move back. Use :help when you need command help inside the interface.

The TUI is especially useful after a failure because you can drill into the failed task instead of searching a wall of output.

Switching Between Modes

Use interactive mode while writing or debugging:

ansible-navigator run site.yml -i inventory.yml --mode interactive

Use stdout mode for automation, shell history, and CI:

ansible-navigator run site.yml -i inventory.yml --mode stdout

You can also set the mode in configuration, which we will cover shortly.

Working With Execution Environments

Execution environments are the center of the Navigator workflow.

Pulling Prebuilt Images

The community images are useful for helping you getting started:

ansible-navigator collections \
  --execution-environment-image ghcr.io/ansible-community/community-ee-base:latest

This command lets you inspect which collections are included in the base image.

Run an ad hoc command inside the minimal image:

ansible-navigator exec -- ansible localhost -m setup \
  --execution-environment-image ghcr.io/ansible-community/community-ee-minimal:latest \
  --mode stdout

Run a playbook inside the minimal image:

ansible-navigator run site.yml \
  --inventory inventory.yml \
  --execution-environment-image ghcr.io/ansible-community/community-ee-minimal:latest \
  --mode stdout

Creating a Custom Execution Environment

Prebuilt images are great for getting started and learning, but for platform engineering teams generally need customizes images.

Create execution-environment.yml:

---
version: 3

images:
  base_image:
    name: registry.fedoraproject.org/fedora:42

dependencies:
  python_interpreter:
    package_system: python3
  ansible_core:
    package_pip: ansible-core
  ansible_runner:
    package_pip: ansible-runner
  galaxy:
    collections:
      - name: community.general
      - name: community.vmware
      - name: ansible.posix
  system:
    - openssh-clients
    - pyvmomi
    - sshpass

Build it:

ansible-builder build --tag localhost/ansible-demo-ee:latest

If you use Docker instead of Podman:

ansible-builder build \
  --tag localhost/ansible-demo-ee:latest \
  --container-runtime docker

Run your playbook with the custom image:

ansible-navigator run site.yml \
  --inventory inventory.yml \
  --execution-environment-image localhost/ansible-demo-ee:latest \
  --pull-policy never \
  --mode stdout

Tip

Use a real registry for your team's workflows. Local image names are okat for experiments, but teams need a shared place to publish, pin, scan, and promote execution environments.

Configuration

Navigator can read settings from ansible-navigator.yml.

Create one in the project root:

ansible-navigator:
  mode: interactive
  ansible:
    inventory:
      entries:
        - inventory.yml
  execution-environment:
    enabled: true
    image: ghcr.io/ansible-community/community-ee-minimal:latest
    pull:
      policy: missing
  logging:
    level: warning

Now the run command gets shorter:

ansible-navigator run site.yml

Common settings include:

Setting Why it Matters
mode Choose interactive or stdout
ansible.inventory.entries Set default inventory paths
execution-environment.enabled Turn execution environment use on or off
execution-environment.image Set the default execution environment image
execution-environment.pull.policy Control when Navigator pulls images
logging.level Set Navigator log verbosity
logging.file Choose the Navigator log path

These pull policies are worth understanding:

Policy Behavior
always Always pull the image
missing Pull only if the image is not local
never Never pull the image
tag Pull latest tags more aggressively, otherwise pull if missing

For local development, missing is usually friendly. For a local image you just built, use never. For CI with mutable tags, use a pinned immutable image tag and a deliberate pull policy.

Practical Example: Debug a Failed Task

Replace site.yml with a playbook that intentionally fails:

- name: Debug a Failed Task with Navigator
  hosts: localhost
  gather_facts: false
  vars:
    required_file: /tmp/ansible-navigator-test/important.conf
  tasks:
    - name: Check for a Required File
      ansible.builtin.stat:
        path: "{{ required_file }}"
      register: required_file_status

    - name: Fail When the Required fFle is Missing
      ansible.builtin.fail:
        msg: "Missing required file: {{ required_file }}"
      when: not required_file_status.stat.exists

Run it in interactive mode:

ansible-navigator run site.yml \
  --inventory inventory.yml \
  --execution-environment-image ghcr.io/ansible-community/community-ee-minimal:latest

In the TUI:

  1. Open the failed play.
  2. Open the failed task.
  3. Open the host result for localhost.
  4. Inspect the failure message.
  5. Move back to the previous task.
  6. Inspect required_file_status.

That sequence is the point of Navigator: structured run data you can inspect after the command exits.

Now create the file and rerun:

mkdir -p /tmp/ansible-navigator-test
touch /tmp/ansible-navigator-test/important.conf

ansible-navigator run site.yml \
  --inventory inventory.yml \
  --execution-environment-image ghcr.io/ansible-community/community-ee-minimal:latest \
  --mode stdout

The same playbook should now pass.

Playbook Artifacts

Navigator can write playbook artifacts after runs. These files capture detailed play, task, host, and stdout data from the run. They are useful when you want to review a run later or share a failure with someone else.

Replay an artifact:

ansible-navigator replay site-artifact-example.json

If artifacts become noisy in a local project, configure or disable them deliberately rather than letting random JSON files pile up in the repository.

Add them to .gitignore if needed:

*-artifact-*.json
ansible-navigator.log

See Navigator vs. ansible-playbook for when to reach for each command.

Recommended Practice Why
Commit ansible-navigator.yml Same defaults locally and in CI
Commit execution-environment.yml The image recipe is versioned with the playbooks
Pin collection versions in requirements.yml Builder inputs stay stable
Publish execution environment images to a shared registry Everyone pulls the same tag
Use immutable image tags for releases latest is not a contract
Keep local and CI commands similar Fewer "passed locally, failed in CI" gaps
Use --mode stdout in CI Logs stay plain text
Use interactive mode during development Faster failure inspection
Add artifacts and logs to .gitignore Avoid committing run output by accident

For CI, the command should look like the following:

ansible-navigator run site.yml \
  --inventory inventory.yml \
  --execution-environment-image registry.example.com/automation/platform-ee:2026.04.29 \
  --mode stdout \
  --pull-policy missing

THis is the goal: an automation runtime that's easier to explain, audit, reproduce, and fix.

Next Steps

Once Navigator feels comfortable, go deeper in this order:

  1. Move repeated CLI options into ansible-navigator.yml.
  2. Build a custom execution environment with ansible-builder.
  3. Pin collections and Python dependencies.
  4. Publish the image to a registry.
  5. Use the same image in CI.
  6. Move mature jobs into AWX or Ansible Automation Platform.
  7. Treat execution environments as release artifacts.

Navigator sits between local Ansible work and controller-backed automation. You don't need AWX or Automation Platform on day one. Pin an execution environment image early so runs stop depending on whatever happened to be installed on the control node last.

References