Introduction

Running Ansible in a Python virtual environment keeps your system Python clean and lets you pin exact versions of Ansible, collections, and Python dependencies per project. Pipenv combines pip + virtualenv into a single workflow with lockfiles for reproducible builds.

Why Virtual Environments for Ansible?

ProblemVirtual Env Solution
System Ansible conflicts with pip packagesIsolated Python environment
Different projects need different Ansible versionsSeparate venv per project
"Works on my machine" issuesLockfile ensures identical deps
CI/CD needs reproducible installsPipfile.lock deterministic builds

Quick Start

Install Pipenv

pip install pipenv

Create an Ansible Project

mkdir ansible-project
cd ansible-project

# Initialize with Python 3
pipenv --python 3

# Install Ansible
pipenv install ansible

# Install development tools
pipenv install --dev ansible-lint yamllint molecule

Activate the Environment

# Start a shell inside the venv
pipenv shell

# Verify Ansible
ansible --version
# ansible [core 2.16.0]
#   python version = 3.12.0

# Run playbooks
ansible-playbook -i inventory site.yml

# Exit the venv
exit

Run Without Activating

# Run a single command in the venv
pipenv run ansible-playbook site.yml
pipenv run ansible-lint playbook.yml

Understanding Pipfile

# Pipfile
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
ansible = "~=9.0"
boto3 = "*"              # AWS modules
pywinrm = "*"            # Windows modules
netaddr = "*"            # ipaddr filter

[dev-packages]
ansible-lint = "*"
yamllint = "*"
molecule = "*"
molecule-docker = "*"
pytest-testinfra = "*"

[requires]
python_version = "3.12"

Version Pinning

# Exact version
ansible = "==9.1.0"

# Compatible release (9.x but not 10.x)
ansible = "~=9.0"

# Minimum version
ansible-lint = ">=6.0"

Pipfile.lock — Reproducible Builds

# Generate/update lockfile
pipenv lock

# Install exactly what's in the lockfile (CI/CD)
pipenv install --deploy

# Fails if Pipfile.lock is out of date
pipenv install --deploy --ignore-pipfile

Common Workflows

Add a New Dependency

# Add to [packages]
pipenv install jmespath

# Add to [dev-packages]
pipenv install --dev molecule-docker

# Remove a package
pipenv uninstall boto3

Update Dependencies

# Update all packages
pipenv update

# Update a specific package
pipenv update ansible

# Check for security vulnerabilities
pipenv check

Show Dependency Tree

pipenv graph
# ansible==9.1.0
#   - ansible-core [required: ~=2.16.0, installed: 2.16.0]
#     - cryptography [required: Any, installed: 41.0.7]
#     - jinja2 [required: >=3.0.0, installed: 3.1.2]
#     - PyYAML [required: >=5.1, installed: 6.0.1]

Pipenv vs venv vs conda

FeaturePipenvvenv + pipconda
Lockfile✅ Pipfile.lock❌ (use pip freeze)✅ environment.yml
Auto-creates venv✅❌ Manual✅
Security audit✅ pipenv check❌❌
Dependency resolution✅ AdvancedBasic✅ Advanced
SpeedSlowerFasterVaries
Ansible support✅✅✅

Using venv Instead (Lightweight Alternative)

# Create venv
python3 -m venv .venv
source .venv/bin/activate

# Install Ansible
pip install ansible ansible-lint

# Freeze dependencies
pip freeze > requirements.txt

# Reproduce elsewhere
pip install -r requirements.txt

CI/CD Integration

GitHub Actions

# .github/workflows/ansible-lint.yml
name: Ansible Lint
on: [push, pull_request]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      - name: Install dependencies
        run: |
          pip install pipenv
          pipenv install --deploy --dev
      - name: Run ansible-lint
        run: pipenv run ansible-lint

GitLab CI

# .gitlab-ci.yml
ansible-lint:
  image: python:3.12
  script:
    - pip install pipenv
    - pipenv install --deploy --dev
    - pipenv run ansible-lint

Multiple Ansible Versions

Test playbooks against different Ansible versions:

# Project A — Ansible 8.x
cd project-a
pipenv install "ansible~=8.0"

# Project B — Ansible 9.x
cd project-b
pipenv install "ansible~=9.0"

# Each project has its own venv and lockfile

Troubleshooting

"Command not found: ansible" After pipenv install

# You need to activate the shell first
pipenv shell
ansible --version

# Or use pipenv run
pipenv run ansible --version

Slow Dependency Resolution

# Skip locking (faster, less reproducible)
pipenv install --skip-lock ansible

Virtual Environment Location

# Show venv path
pipenv --venv
# /home/user/.local/share/virtualenvs/ansible-project-abc123

# Store venv in project directory instead
export PIPENV_VENV_IN_PROJECT=1
pipenv install ansible
# Creates .venv/ in project root

Conclusion

Use pipenv install ansible to create an isolated environment, pipenv install --dev ansible-lint molecule for development tools, and pipenv lock + pipenv install --deploy for reproducible CI/CD builds. Pin Ansible versions with ~=9.0 syntax, run pipenv check for security audits, and store Pipfile + Pipfile.lock in version control. For simpler projects, python3 -m venv with requirements.txt works fine too.