See more details at https://blog.andygol.co.ua/en/2025/12/26/static-ip-for-multipass-vm/
# VM 1 - 10.10.0.10
cat > vm1.yaml << 'EOF'
#cloud-config
write_files:
- path: /etc/netplan/60-custom-network.yaml
content: |
network:
version: 2
ethernets:
enp0s2:
addresses:
- 10.10.0.10/24
routes:
- to: 10.10.0.0/24
scope: link
permissions: '0600'
runcmd:
- netplan apply
hostname: vm1
EOF
# VM 2 - 10.10.0.11
cat > vm2.yaml << 'EOF'
#cloud-config
write_files:
- path: /etc/netplan/60-custom-network.yaml
content: |
network:
version: 2
ethernets:
enp0s2:
addresses:
- 10.10.0.11/24
routes:
- to: 10.10.0.0/24
permissions: '0600'
runcmd:
- netplan apply
hostname: vm2
EOF
# VM 3 - 10.10.0.12
cat > vm3.yaml << 'EOF'
#cloud-config
write_files:
- path: /etc/netplan/60-custom-network.yaml
content: |
network:
version: 2
ethernets:
enp0s2:
addresses:
- 10.10.0.12/24
routes:
- to: 10.10.0.0/24
permissions: '0600'
runcmd:
- netplan apply
hostname: vm3
EOF
# Launch all VMs
multipass launch --name vm1 --network name=en0,mode=manual --cloud-init vm1.yaml
multipass launch --name vm2 --network name=en0,mode=manual --cloud-init vm2.yaml
multipass launch --name vm3 --network name=en0,mode=manual --cloud-init vm3.yaml
# Wait for completion
sleep 30
# Add alias to bridge101
sudo ifconfig bridge101 10.10.0.1/24 alias
# Check all VMs
multipass list#!/bin/bash
# Create file: create-vms.sh
BASE_IP="10.10.0"
START_NUM=10
COUNT=3
for i in $(seq 0 $((COUNT-1))); do
VM_NAME="vm$((i+1))"
VM_IP="$BASE_IP.$((START_NUM+i))"
cat > ${VM_NAME}.yaml << EOF
#cloud-config
write_files:
- path: /etc/netplan/60-custom-network.yaml
content: |
network:
version: 2
ethernets:
enp0s2:
addresses:
- ${VM_IP}/24
routes:
- to: ${BASE_IP}.0/24
permissions: '0600'
runcmd:
- netplan apply
hostname: ${VM_NAME}
EOF
echo "Creating $VM_NAME with IP $VM_IP"
multipass launch --name $VM_NAME --cloud-init ${VM_NAME}.yaml
done
echo "Waiting for cloud-init to complete..."
sleep 30
TARGET_BRIDGE=$(ifconfig -v | grep -B 20 "member: vmenet" | grep "bridge" | awk -F: '{print $1}' | tail -n 1)
if [ -z "$TARGET_BRIDGE" ]; then
echo "Error: Bridge not found. Check if VM is running."
else
echo "VM found on $TARGET_BRIDGE. Assigning $BASE_IP.1..."
sudo ifconfig $TARGET_BRIDGE $BASE_IP.1/24 alias
fi
echo "VM List:"
multipass listRun:
chmod +x create-vms.sh
./create-vms.sh# From vm1 to others
multipass exec -n vm1 -- ping -c 2 10.10.0.11
multipass exec -n vm1 -- ping -c 2 10.10.0.12
# From vm2 to others
multipass exec -n vm2 -- ping -c 2 10.10.0.10
multipass exec -n vm2 -- ping -c 2 10.10.0.12
# From host to all
for ip in 10.10.0.10 10.10.0.11 10.10.0.12; do
echo "Ping $ip:"
ping -c 2 $ip
done# Remove static IP from VM
multipass exec -n test-vm -- sudo ip addr del 10.10.0.10/24 dev enp0s2
# Check
multipass exec -n test-vm -- ip addr show enp0s2
# VM remains with DHCP IP# Remove configuration file
multipass exec -n test-vm -- sudo rm /etc/netplan/60-custom-network.yaml
# Apply changes
multipass exec -n test-vm -- sudo netplan apply# If IP added via ip addr add (not via netplan)
multipass restart test-vm
# IP will disappear after restart
multipass exec -n test-vm -- ip addr show enp0s2# List all VMs
multipass list
# Stop all VMs
multipass stop --all
# Delete specific VMs
multipass delete test-vm
multipass delete vm1
multipass delete vm2
multipass delete vm3
# Or delete all VMs
multipass delete --all
# Permanently clean (purge)
multipass purge
# Check that everything is deleted
multipass list# Remove alias from bridge100, if you added it earlier
sudo ifconfig bridge100 -alias 10.10.0.1
# Check that it's removed
ifconfig bridge100 | grep "inet "
# Should not have 10.10.0.1# Remove all entries for subnet 10.10.0.0
sudo arp -d -a
# Or specific IPs
sudo arp -d 10.10.0.10
sudo arp -d 10.10.0.11
sudo arp -d 10.10.0.12
# Check
arp -an | grep 10.10.0
netstat -rn | grep 10.10.0# Check routes
netstat -rn | grep 10.10.0
# Remove if present
sudo route delete 10.10.0.0/24
# Check again
netstat -rn | grep 10.10.0# Remove cloud-init files
rm -f multipass-static-ip.yaml
rm -f vm1.yaml vm2.yaml vm3.yaml
rm -f create-vms.sh# Restart daemon for clean state
sudo launchctl unload /Library/LaunchDaemons/com.canonical.multipassd.plist
sudo launchctl load /Library/LaunchDaemons/com.canonical.multipassd.plist# Check that everything is clean
echo "=== Multipass VM ==="
multipass list
echo "=== Bridge IP ==="
ifconfig bridge100 | grep "inet "
echo "=== Routes ==="
netstat -rn | grep 10.10.0
echo "=== ARP cache ==="
arp -an | grep 10.10.0
echo "=== Launch Daemons ==="
ls -la /Library/LaunchDaemons/ | grep multipass
echo "=== Cloud-init files ==="
ls -la *.yaml 2>/dev/null || echo "No cloud-init files"
Expected Result After Cleanup:
- Multipass VM:
No instances found. - Bridge IP: only primary IP (not 10.10.0.1)
- Routes: no entries with 10.10.0
- ARP cache: no entries with 10.10.0
- Files: deleted
✅ Infrastructure completely dismantled!
# Host
sudo ifconfig bridge100 10.10.0.1/24 alias
# VM
multipass launch --name test-vm --cloud-init multipass-static-ip.yaml
# Check
ping 10.10.0.10# VM
multipass delete test-vm
multipass purge
# Host
sudo ifconfig bridge100 -alias 10.10.0.1
sudo arp -d -a
# Check
multipass list
ifconfig bridge100 | grep 10.10.0# Add
multipass exec -n test-vm -- sudo ip addr add 10.10.0.10/24 dev enp0s1
# Remove
multipass exec -n test-vm -- sudo ip addr del 10.10.0.10/24 dev enp0s1#!/bin/bash
# File: setup.sh
BRIDGE="bridge100"
BRIDGE_IP="10.10.0.1"
SUBNET="10.10.0.0/24"
VM_NAME="test-vm"
VM_IP="10.10.0.10"
echo "=== Setting up bridge ==="
sudo ifconfig $BRIDGE $BRIDGE_IP/24 alias
ifconfig $BRIDGE | grep "inet "
echo "=== Creating cloud-init ==="
cat > ${VM_NAME}.yaml << EOF
#cloud-config
write_files:
- path: /etc/netplan/60-custom-network.yaml
content: |
network:
version: 2
ethernets:
default:
dhcp4: true
addresses:
- ${VM_IP}/24
routes:
- to: default
via: ${BRIDGE_IP}
metric: 200
permissions: '0600'
runcmd:
- netplan apply
hostname: ${VM_NAME}
EOF
echo "=== Creating VM ==="
multipass launch --name $VM_NAME --cloud-init ${VM_NAME}.yaml
echo "=== Waiting for cloud-init ==="
sleep 30
multipass exec -n $VM_NAME -- cloud-init status --wait
echo "=== Check ==="
echo "VM list:"
multipass list
echo "VM IP:"
multipass exec -n $VM_NAME -- ip addr show enp0s1 | grep "inet "
echo "Ping test:"
ping -c 4 $VM_IP
echo "✅ Infrastructure created!"#!/bin/bash
# File: teardown.sh
BRIDGE="bridge100"
BRIDGE_IP="10.10.0.1"
VM_NAME="test-vm"
echo "=== Deleting VM ==="
multipass stop $VM_NAME 2>/dev/null
multipass delete $VM_NAME 2>/dev/null
multipass purge
echo "=== Removing bridge IP ==="
sudo ifconfig $BRIDGE -alias $BRIDGE_IP
echo "=== Clearing ARP ==="
sudo arp -d -a
echo "=== Removing routes ==="
sudo route delete 10.10.0.0/24 2>/dev/null
echo "=== Removing configs ==="
rm -f ${VM_NAME}.yaml
echo "=== Check ==="
echo "Multipass VM:"
multipass list
echo "Bridge IP:"
ifconfig $BRIDGE | grep "inet " | grep -v "127.0.0.1"
echo "Routes:"
netstat -rn | grep 10.10.0
echo "✅ Infrastructure dismantled!"# Make executable
chmod +x setup.sh teardown.sh
# Creation
./setup.sh
# Teardown
./teardown.sh# Force delete
multipass stop test-vm --force
multipass delete test-vm --force
multipass purge
# If still problems
sudo pkill -9 multipassd
sudo launchctl unload /Library/LaunchDaemons/com.canonical.multipassd.plist
sudo launchctl load /Library/LaunchDaemons/com.canonical.multipassd.plist# Try again
sudo ifconfig bridge100 -alias 10.10.0.1
# If not helping, check all aliases
ifconfig bridge100
# Remove each alias separately
sudo ifconfig bridge100 -alias [IP]
# In extreme case restart bridge
sudo ifconfig bridge100 down
sudo ifconfig bridge100 up# Force delete entire ARP cache
sudo arp -d -a
# Restart network (carefully!)
sudo ifconfig bridge100 down
sudo ifconfig bridge100 up# Check all routes
netstat -rn
# Remove specific route
sudo route delete [IP or subnet]
# Example
sudo route delete 10.10.0.10
sudo route delete 10.10.0.0/24If you want the bridge IP to be created automatically:
# Create Launch Daemon
sudo tee /Library/LaunchDaemons/com.local.multipass-bridge.plist << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.local.multipass-bridge</string>
<key>ProgramArguments</key>
<array>
<string>/bin/sh</string>
<string>-c</string>
<string>sleep 10 && /sbin/ifconfig bridge100 10.10.0.1/24 alias</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<false/>
</dict>
</plist>
EOF
# Load
sudo launchctl load /Library/LaunchDaemons/com.local.multipass-bridge.plist
# To remove
sudo launchctl unload /Library/LaunchDaemons/com.local.multipass-bridge.plist
sudo rm /Library/LaunchDaemons/com.local.multipass-bridge.plist