Documentation Index
Fetch the complete documentation index at: https://mintlify.com/pyinfra-dev/pyinfra/llms.txt
Use this file to discover all available pages before exploring further.
The Vagrant connector generates pyinfra inventory from running Vagrant VMs. This enables easy integration between Vagrant for local development and pyinfra for configuration management.
Overview
The Vagrant connector automatically detects running Vagrant VMs and generates inventory with SSH connection details.
# Deploy to all running Vagrant VMs
pyinfra @vagrant deploy.py
# Deploy to specific VM
pyinfra @vagrant/web deploy.py
Requirements
- Vagrant installed and accessible
- One or more running Vagrant VMs
- Current directory contains Vagrantfile (or specify path)
Basic Usage
All Running VMs
# Start VMs
vagrant up
# Deploy to all running VMs
pyinfra @vagrant deploy.py
Specific VM
# Deploy to specific VM by name
pyinfra @vagrant/web deploy.py
pyinfra @vagrant/database deploy.py
Multiple VMs
# Deploy to multiple specific VMs
pyinfra @vagrant/web,@vagrant/database deploy.py
Vagrantfile Example
A typical multi-machine Vagrantfile:
# Vagrantfile
Vagrant.configure("2") do |config|
# Web server
config.vm.define "web" do |web|
web.vm.box = "ubuntu/jammy64"
web.vm.hostname = "web"
web.vm.network "private_network", ip: "192.168.56.10"
end
# Database server
config.vm.define "database" do |db|
db.vm.box = "ubuntu/jammy64"
db.vm.hostname = "database"
db.vm.network "private_network", ip: "192.168.56.11"
end
# Cache server
config.vm.define "cache" do |cache|
cache.vm.box = "ubuntu/jammy64"
cache.vm.hostname = "cache"
cache.vm.network "private_network", ip: "192.168.56.12"
end
end
# Start all VMs
vagrant up
# Deploy to all
pyinfra @vagrant deploy.py
# Or deploy to specific VMs
pyinfra @vagrant/web,@vagrant/database deploy.py
Configuration
The connector reads SSH configuration from vagrant ssh-config:
# View SSH config for a VM
vagrant ssh-config web
# Output:
# Host web
# HostName 127.0.0.1
# User vagrant
# Port 2222
# IdentityFile /path/to/.vagrant/machines/web/virtualbox/private_key
Pyinfra automatically uses these settings.
Custom Options
Provide additional host data via @vagrant.json:
{
"web": {
"app_port": 8000,
"app_name": "myapp"
},
"database": {
"db_port": 5432,
"db_name": "myapp"
}
}
# deploy.py
from pyinfra import host
from pyinfra.operations import apt
if host.name == "web":
print(f"Deploying {host.data.app_name} on port {host.data.app_port}")
apt.packages(
name="Install nginx",
packages=["nginx"],
_sudo=True,
)
elif host.name == "database":
print(f"Deploying database {host.data.db_name}")
apt.packages(
name="Install postgresql",
packages=["postgresql"],
_sudo=True,
)
Deployment Workflow
Typical Vagrant + pyinfra workflow:
# 1. Create Vagrantfile
cat > Vagrantfile <<EOF
Vagrant.configure("2") do |config|
config.vm.define "web" do |web|
web.vm.box = "ubuntu/jammy64"
end
end
EOF
# 2. Start VM
vagrant up
# 3. Deploy with pyinfra
pyinfra @vagrant deploy.py
# 4. Make changes and redeploy
vi deploy.py
pyinfra @vagrant deploy.py
# 5. Destroy when done
vagrant destroy
Testing Deploys Locally
Use Vagrant to test deploys before running on production:
# Vagrantfile - mimics production
Vagrant.configure("2") do |config|
config.vm.define "staging" do |staging|
staging.vm.box = "ubuntu/jammy64"
staging.vm.hostname = "staging"
# Same as production
staging.vm.network "private_network", ip: "192.168.56.10"
end
end
# Same deploy.py used for staging and production
from pyinfra.operations import apt, files, systemd
apt.packages(
name="Install nginx",
packages=["nginx"],
_sudo=True,
)
files.put(
name="Upload nginx config",
src="configs/nginx.conf",
dest="/etc/nginx/nginx.conf",
_sudo=True,
)
systemd.service(
name="Restart nginx",
service="nginx",
restarted=True,
_sudo=True,
)
# Test locally
vagrant up
pyinfra @vagrant deploy.py
# Verify it works
curl http://192.168.56.10
# Deploy to production
pyinfra production.py deploy.py
Multi-Machine Examples
Web + Database Setup
# Vagrantfile
Vagrant.configure("2") do |config|
# Web server
config.vm.define "web" do |web|
web.vm.box = "ubuntu/jammy64"
web.vm.network "private_network", ip: "192.168.56.10"
web.vm.provider "virtualbox" do |vb|
vb.memory = "1024"
end
end
# Database server
config.vm.define "db" do |db|
db.vm.box = "ubuntu/jammy64"
db.vm.network "private_network", ip: "192.168.56.11"
db.vm.provider "virtualbox" do |vb|
vb.memory = "2048"
end
end
end
# @vagrant.json
{
"web": {
"role": "webserver",
"db_host": "192.168.56.11"
},
"db": {
"role": "database"
}
}
# deploy.py
from pyinfra import host
from pyinfra.operations import apt, files
if host.data.role == "webserver":
apt.packages(
name="Install nginx",
packages=["nginx", "php-fpm", "php-pgsql"],
_sudo=True,
)
files.template(
name="Configure app",
src="templates/app.conf.j2",
dest="/etc/app/config.ini",
db_host=host.data.db_host,
_sudo=True,
)
elif host.data.role == "database":
apt.packages(
name="Install postgresql",
packages=["postgresql", "postgresql-contrib"],
_sudo=True,
)
Load Balanced Web Cluster
# Vagrantfile
Vagrant.configure("2") do |config|
# Load balancer
config.vm.define "lb" do |lb|
lb.vm.box = "ubuntu/jammy64"
lb.vm.network "private_network", ip: "192.168.56.10"
end
# Web servers
(1..3).each do |i|
config.vm.define "web#{i}" do |web|
web.vm.box = "ubuntu/jammy64"
web.vm.network "private_network", ip: "192.168.56.#{10 + i}"
end
end
end
# deploy.py
from pyinfra import host
from pyinfra.operations import apt, files
if host.name == "lb":
# Configure load balancer
apt.packages(
name="Install nginx",
packages=["nginx"],
_sudo=True,
)
files.template(
name="Configure load balancer",
src="templates/nginx-lb.conf.j2",
dest="/etc/nginx/nginx.conf",
backends=[
"192.168.56.11:80",
"192.168.56.12:80",
"192.168.56.13:80",
],
_sudo=True,
)
elif host.name.startswith("web"):
# Configure web server
apt.packages(
name="Install nginx",
packages=["nginx"],
_sudo=True,
)
Inventory API
Use in Python API:
from pyinfra import Inventory, Config, State
# Vagrant connector generates hosts automatically
inventory = Inventory(
(["@vagrant"], {})
)
# Or specific VMs
inventory = Inventory(
([
"@vagrant/web",
"@vagrant/database",
], {})
)
config = Config(SUDO=True)
state = State(inventory, config)
state.init(inventory, config)
Checking VM Status
# Check which VMs are running
vagrant status
# Check global VM status
vagrant global-status
# SSH config for specific VM
vagrant ssh-config web
Limitations
- Only detects VMs in “running” state
- Must run from directory containing Vagrantfile
- Slower than SSH connector (queries Vagrant for each VM)
- Requires Vagrant in PATH
Troubleshooting
No VMs Found
# Check VMs are running
vagrant status
# Start VMs
vagrant up
# Verify pyinfra can see them
pyinfra @vagrant --facts server.Hostname
Connection Errors
# Check SSH config
vagrant ssh-config
# Try SSHing manually
vagrant ssh web
# Restart VMs
vagrant halt
vagrant up
The Vagrant connector queries vagrant ssh-config for each VM, which can be slow:
# Time the connector
time pyinfra @vagrant --facts server.Hostname
# For better performance with many VMs, consider using SSH connector
# with static inventory generated from Vagrant
Comparison with SSH Connector
| Feature | @vagrant | @ssh |
|---|
| Auto-discovery | Yes | No |
| Dynamic config | Yes | No |
| Speed | Slower | Faster |
| Setup | Automatic | Manual |
| Use case | Dev/testing | Production |
Source Reference
Location: src/pyinfra/connectors/vagrant.py:90
Key Methods
make_names_data() - Generate hosts from Vagrant
get_vagrant_config() - Read Vagrant SSH config (line 30)
get_vagrant_options() - Read @vagrant.json (line 83)