amvm is a simple, powerful shell script for managing multiple versions of Ansible + Mitogen in isolated Python virtual environments and switching between them seamlessly.
It's designed for Ansible developers and operators who need to work with different versions for different projects without the hassle of manually managing virtual environments, control-node Python versions, or ansible.cfg files.
Version 2.0 uses uv to create environments with uv-managed Python instead of relying on the system python3 and venv module.
If you've ever found yourself juggling multiple Ansible projects, you've likely faced these challenges:
- Forgetting to activate the correct environment, leading to dependency errors.
- Managing different
ansible.cfgfiles for standard vs. Mitogen-enabled runs. - Manually creating and activating Python
venvs for each version (source .../activate). - Keeping Ansible,
ansible-core,ansible-lint, Mitogen, and control-node Python mutually compatible.
amvm solves these problems by automating the entire workflow.
It's more than just a venv wrapper; it's a complete management tool that lets you set your Ansible version once and forget about it.
amvm provides flexibility and speed through a set of powerful, integrated features:
-
π Atomic Version & Config Switching: This is the core feature. A single
amvmcommand lets you switch both the activeansibleversion and its corresponding configuration (ansible.cfg) in one atomic operation. Instantly toggle between a standard setup, a Mitogen-optimized one, or your own project-specific config without any manual file editing. No moresource .../activateor juggling paths by hand. -
π uv-managed Python: Built-in environments are created with
uv venv --managed-pythonand an explicit Python pin from the compatibility matrix. amvm 2.0 no longer depends on your system Python having a workingvenvmodule. -
π§ Built-in Compatibility Matrix: Each built-in Ansible line pins
ansible,ansible-core,ansible-lint, Mitogen, and Python. Print the current table at any time withamvm --matrix. -
π» Fast Interactive UI: Switch between any version and config combination in seconds using a modern, interactive menu powered by
fzf(if installed), with a simple numbered menu as a fallback. -
β‘ Integrated Mitogen & Custom Configs:
- Mitogen Support:
amvmautomatically creates a Mitogen-ready configuration when installed. This appears as a separate option in the menu (e.g.,ansible-14.3.1 (mitogen)), letting you enable or disable Mitogen with a single keystroke. - Per-Environment Configs: Drop an
ansible-customized.cfgfile into any version's directory, andamvmautomatically offers it as a switching option (e.g.,ansible-14.3.1 (custom)) - perfect for project-specific settings.
- Mitogen Support:
-
π§ Extensive Customization:
- Custom Versions: Easily define your own version sets (Ansible, ansible-core, ansible-lint, Mitogen, and optional Python) in a simple configuration file (
~/.amvm.cfg). - Custom Root Directory: Change the default installation path (
~/.amvm) to anywhere on your system.
- Custom Versions: Easily define your own version sets (Ansible, ansible-core, ansible-lint, Mitogen, and optional Python) in a simple configuration file (
-
π¦ Batteries-Included Installation: Each environment is created with not just Ansible, but also common dependencies like
ansible-lint,boto3,pywinrm, andjmespath, saving you setup time. You can easily skip optional packages. -
π‘οΈ Safe and Clean Management:
- Isolated Environments: Each version lives in its own uv-created virtual environment, preventing dependency conflicts.
- Metadata & Stale Detection: amvm 2.0 writes
.amvm.envmetadata into each environment and can identify old or mismatched environments. - Reinstall Support:
amvm --reinstall <ver|all>rebuilds built-in environments after confirmation, preserving custom configs and restoring the old environment on failure. - Interactive Uninstall: Safely remove specific versions with a confirmation prompt.
- Total Cleanup: A single
amvm --cleanupcommand interactively removes allamvm-managed files and directories.
-
π Central Storage: Environments, shims, metadata, and configs live under
~/.amvmby default. Their Python interpreters are stored separately by uv; copying AMVM_ROOT alone is not a portable backup.
- Compatibility: Works on Linux and macOS.
- Bash: Version
3.2or newer. uv: Required inPATH.amvmdoes not auto-install it.curl: Required to download the script.fzf(Optional but Recommended): For the best interactive menu experience.
Install uv first if needed:
curl -LsSf https://astral.sh/uv/install.sh | shamvm installs specific, tested combinations of packages. To use other versions, define them in your ~/.amvm.cfg. To skip installing ansible-lint or mitogen, use 0 as the version number.
| Key | ansible |
ansible-core |
ansible-lint |
mitogen |
python |
Notes |
|---|---|---|---|---|---|---|
10 |
10.7.0 |
2.17.14 |
25.8.2 |
0.3.53 |
3.12.13 |
Legacy; known unpatched CVEs; excluded from all |
11 |
11.13.0 |
2.18.19 |
26.8.0 |
0.3.53 |
3.12.13 |
Community EOL; core EOL May 2026 |
12 |
12.3.0 |
2.19.12 |
26.8.0 |
0.3.53 |
3.12.13 |
Community EOL; core security fixes until Nov 2026 |
13 |
13.8.0 |
2.20.8 |
26.8.0 |
0.3.53 |
3.12.13 |
Final community package; core EOL May 2027 |
14 |
14.3.1 |
2.21.3 |
26.8.0 |
0.3.53 |
3.12.13 |
Current community package; core EOL Nov 2027 |
Print the built-in matrix from the CLI:
amvm --matrix-
Install
uvuvmust be available in yourPATHbefore installing Ansible environments.curl -LsSf https://astral.sh/uv/install.sh | sh -
Download the Script Place the
amvmscript in a directory that is already in your$PATH, such as~/.local/bin.# Create the directory if it doesn't exist mkdir -p ~/.local/bin # Download and make executable curl -Lo ~/.local/bin/amvm https://raw.githubusercontent.com/ishad0w/amvm/main/amvm chmod +x ~/.local/bin/amvm
-
Configure Your PATH
amvmworks by creating small wrapper scripts (shims) for theansible*executables in~/.amvm/bin. For your system to find these shims, you must add this directory to your$PATH. This is a one-time setup step.The script will remind you if your
PATHis not configured and will provide the exact command to run.For zsh:
echo 'export PATH="$HOME/.amvm/bin:$PATH"' >> ~/.zshrc
For bash:
echo 'export PATH="$HOME/.amvm/bin:$PATH"' >> ~/.bashrc
After running the command, restart your terminal or source your profile (
source ~/.zshrc) to apply the changes.
Use the amvm --install command. You can install a specific predefined version or all of them.
# Install a specific built-in version (e.g., 14)
amvm --install 14
# Install built-in versions 11β14 (excludes vulnerable legacy 10)
amvm --install allUse amvm --reinstall when an old environment already exists but the matrix changed.
Directories without recognized amvm metadata are left untouched; back them up and move them out of the way before installing at the same path.
# Rebuild one built-in environment
amvm --reinstall 14
# Rebuild every current built-in environment
amvm --reinstall allYou will be asked for confirmation before rebuilding. Pause Ansible jobs using that environment while it is rebuilt at its original path. A failed install restores the previous directory; a successful one retains ansible-customized.cfg. An interrupted process that cannot run cleanup (for example, SIGKILL) may leave .amvm-reinstall.*/environment and .amvm-lock; recover the backup before retrying.
Run amvm with no arguments to open the interactive selection menu.
With fzf:
amvm>
βββββββββββββββββββββββββββββ
β > ansible-14.3.1 (mitogen)β
β ansible-14.3.1 β
β ansible-13.8.0 (mitogen)β
β ansible-13.8.0 (custom) β
β ansible-13.8.0 β
βββββββββββββββββββββββββββββ
Without fzf (fallback):
1) ansible-14.3.1
2) ansible-14.3.1 (project)
3) ansible-14.3.1 (mitogen)
4) ansible-13.8.0
5) ansible-13.8.0 (project)
6) ansible-13.8.0 (mitogen)
7) ansible-13.8.0 (custom)
Enter a number: _
After switching, you can immediately verify the change:
$ ansible --version
ansible [core 2.21.3]
config file = /home/user/.amvm/ansible-14.3.1/ansible-mitogen.cfg
...Standard, (mitogen), and (custom) selections explicitly set ANSIBLE_CONFIG, taking precedence over project configs and an inherited ANSIBLE_CONFIG. Select (project) to keep native Ansible configuration discovery. Switching never creates or overwrites ~/.ansible.cfg. Generated configs use in-memory fact caching, so separate projects do not share cached host facts. Reinstall older environments to regenerate their configs.
Use amvm --matrix to inspect the currently built-in compatibility table.
amvm --matrixUse amvm --uninstall to open an interactive menu to select and remove a version.
uninstall>
ββββββββββββββββββββ
β > ansible-14.3.1 β
β ansible-13.8.0 β
ββββββββββββββββββββ
You will be asked for confirmation before anything is deleted.
amvm --matrix # Print the built-in compatibility matrix
amvm --install <10|11|12|13|14|all> # Install a built-in version or all
amvm --reinstall <10|11|12|13|14|all> # Rebuild with rollback; all excludes 10
amvm --install-custom <key> # Install a user-defined version from your config
amvm --uninstall # Interactively remove an installed version
amvm --list # List all installed versions and stale status
amvm --cleanup # Remove recognized amvm data; keep unrelated files
amvm --help, -h # Show the help message
amvm --version, -v # Show amvm versionYou can configure amvm by creating a Bash file at ~/.amvm.cfg, or selecting one with AMVM_CONFIG_FILE. Only load config files you trust.
To install versions not built into amvm, define them in ~/.amvm.cfg using a Bash array named AMVM_CUSTOM_VERSIONS.
Each entry is a string with the format: "KEY:ansible|ansible-core|ansible-lint|mitogen|python".
The final python field is optional for backward compatibility. If omitted, amvm defaults to Python 3.12.
Use 0 to skip installing ansible-lint or mitogen.
Example ~/.amvm.cfg:
# Define your custom versions here
AMVM_CUSTOM_VERSIONS=(
# Key '14-no-lint': Ansible 14 without optional packages
"14-no-lint:14.3.1|2.21.3|0|0|3.12.13"
)With this config, you can now run:
amvm --install-custom 14-no-lintCustom sets use ansible-X.Y.Z-custom-KEY/, allowing multiple sets with the same Ansible version alongside the built-in environment.
For ultimate control, you can use your own hand-tuned ansible.cfg for any version.
Simply create a file named ansible-customized.cfg inside the desired environment's directory:
~/.amvm/ansible-X.Y.Z/ansible-customized.cfg
The next time you run amvm, a new (custom) option will automatically appear in the menu for that version, allowing you to switch to it instantly.
You can change the main directory where amvm stores everything by setting AMVM_ROOT in your config.
# ~/.amvm.cfg
AMVM_ROOT="/opt/ansible_versions"~/.amvm/: The root directory for everythingamvmmanages.ansible-X.Y.Z/: Each environment is a uv-created virtual environment referencing an external uv-managed Python. It containsansible.cfg,ansible-mitogen.cfg(if applicable), your ownansible-customized.cfg, and.amvm.envmetadata.bin/: This directory contains the active "shims" - small wrapper scripts that prepend the active environment'sbindirectory toPATHand then execute the matchingansible*command. This is the directory you add to yourPATH.
~/.amvm/.amvm-active: A symlink to the selected config. Stable shims read this pointer once to select both the environment and config. Switching replaces the pointer atomically; a mutation lock prevents overlapping amvm operations.~/.ansible.cfg: Left unchanged when switching. Uninstall/cleanup removes an old amvm symlink only when it points into the recognized environment being deleted. Unrelated files in AMVM_ROOT and user-modified shims are retained.
This shim-based approach is what makes switching seamless and eliminates the need to source or deactivate environments.
-
uvis missing Installuvand confirmuv --versionworks in the same shell. amvm 2.0 intentionally does not auto-installuv. -
amvm: command not foundEnsure theamvmscript is in a directory listed in yourPATH(like~/.local/bin) and that it has execute permissions (chmod +x amvm). -
ansible: command not foundafter switching YourPATHis likely misconfigured. Runamvmand it will show a warning with the exact command needed to fix your shell profile file. Remember to restart your terminal after making the change. -
Existing environment is skipped If an environment already exists but does not match the current matrix, use
amvm --reinstall <key>. -
Mitogen fails on a Python path containing spaces or quotes Mitogen parses
ansible_python_interpreteras shell syntax, while standard Ansible treats it as a path. For an inventory used with both strategies, select a Python path without spaces or shell quotes. This also affects implicit localhost when the environment's path contains these characters.