Ansible Navigator: A Practical Deep Dive
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.
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.
Navigator vs. ansible-playbook¶
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:
Verify the runtime:
or:
Install Ansible Navigator¶
For a full Ansible development toolchain:
For Navigator only:
Make sure your user install directory is on PATH:
Verify the Installation¶
If ansible-builder was installed with your toolchain, verify it too:
Check that Navigator can see your container runtime:
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 Modes¶
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:
Run with output:
Inventory and Playbooks¶
Navigator uses the normal Ansible inventory and playbook concepts.
The inventory tells Ansible what hosts exist:
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:
Create inventory.yml:
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:
- Start at the playbook run summary.
- Select the play.
- Select a task.
- Select a host result.
- 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:
Use stdout mode for automation, shell history, and CI:
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:
If you use Docker instead of Podman:
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:
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:
- Open the failed play.
- Open the failed task.
- Open the host result for
localhost. - Inspect the failure message.
- Move back to the previous task.
- 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:
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:
Recommended Practices¶
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:
- Move repeated CLI options into
ansible-navigator.yml. - Build a custom execution environment with
ansible-builder. - Pin collections and Python dependencies.
- Publish the image to a registry.
- Use the same image in CI.
- Move mature jobs into AWX or Ansible Automation Platform.
- 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.