Skip to content

Python virtual environment (venv)

Python venv Installing Packages

You can run pip by calling the python executable inside the venv folder.

# This installs 'wheel' specifically into that virtual environment
/opt/venv/bin/python -m pip install wheel

Python venv Running Scripts

you can run your Python script by pointing fully to the venv python executable.

/opt/venv/bin/python /path/to/your/script.py

When you run Python this way, it automatically looks for modules inside /opt/venv/lib/pythonX.X/site-packages. It is fully isolated from the system Python.

Python venv Example for Cron

If you wanted to run a script every day at 5 AM via Cron, you would write:

# Correct way (No activation needed)
0 5 * * * /opt/venv/bin/python /home/user/scripts/daily_task.py

Python venv + direnv (ansible example)

direvn allows automatical venv activation when you switch to the venv directory.

  • venv with ansible-core + pip deps
sudo python3 -m venv /opt/ansible_venv
sudo /opt/ansible_venv/bin/pip install --upgrade pip
sudo /opt/ansible_venv/bin/pip install "ansible-core==2.21.*"
  • direnv hook
echo 'eval "$(direnv hook bash)"' | sudo tee /etc/profile.d/direnv.sh
  • .envrc so direnv activates the venv on cd
cat > /opt/ansible/.envrc <<'EOF'
export VIRTUAL_ENV=/opt/ansible_venv
export PATH="$VIRTUAL_ENV/bin:$PATH"
EOF

cd /opt/ansible && direnv allow

After direnv allow, every cd /opt/ansible puts /opt/ansible_venv/bin first in PATH - ansible, ansible-playbook, ansible-galaxy all resolve to the venv automatically.

  • Install Galaxy roles and collections ansible-galaxy comes with ansible-core in the venv. Run from /opt/ansible so direnv has activated it:
cd /opt/ansible
ansible-galaxy install -r requirements-modern.yml
ansible-galaxy collection install -r requirements-modern.yml --force
  • ansible.cfg + /etc/ansible
[defaults]
inventory      = /opt/ansible/inventory/
forks          = 5
sudo_user      = root
ask_sudo_pass  = True
ask_pass       = True
gathering      = implicit
gather_timeout = 60
host_key_checking = False
timeout        = 60
remote_user    = <your domain user>
vault_password_file = /etc/ansible/vaultpasswd

display_skipped_hosts = False
error_on_undefined_vars = True
system_warnings = True
nocows = 0
fact_caching = memory
retry_files_enabled = False
retry_files_save_path = ~/.ansible-retry
allow_world_readable_tmpfiles = True

callbacks_enabled = ansible.posix.profile_tasks

[privilege_escalation]
become         = True
become_method  = sudo
become_user    = root
become_ask_pass = True

[ssh_connection]
pipelining   = True
retries      = 3
ssh_args     = -o ControlMaster=auto -o ControlPersist=1200s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR
control_path = %(directory)s/%%h-%%r

[persistent_connection]
connect_timeout       = 30
connect_retry_timeout = 30
command_timeout       = 15