Automating Proxmox VM Deployment
Back to Blog

Automating Proxmox VM Deployment

November 27, 202516 min read0 comments

Automating Proxmox VM Deployment: A Modern Approach to Infrastructure ManagementIn the world of virtualization and infrastructure management, efficiency is ever...

In the world of virtualization and infrastructure management, efficiency is everything. Today, I'm excited to share our latest automation script that transforms the traditionally manual process of creating virtual machines in Proxmox into a streamlined, intelligent workflow.

The Challenge: Manual VM Creation at Scale

Anyone who has managed multiple VMs in Proxmox knows the drill: logging into the web interface, clicking through various configuration screens, setting up networking, configuring cloud-init, and hoping you didn't miss a critical setting. When you're managing dozens or even hundreds of VMs, this process becomes not just tedious, but error-prone.

We needed a solution that could:

  • Eliminate repetitive manual tasks

  • Ensure consistent VM configurations

  • Support both Debian and Ubuntu deployments

  • Provide intelligent resource allocation

  • Integrate cloud-init for automated provisioning

  • Track and manage our infrastructure efficiently

The Solution: Smart Automation with Choice

Our bash-based automation script brings together several powerful features that make VM deployment both fast and flexible.

Intelligent Resource Management

The script automatically manages critical resources behind the scenes, ensuring no conflicts occur when deploying multiple VMs. It maintains a centralized tracking system that monitors resource allocation across your entire infrastructure, preventing the common headaches that come with manual deployments.

Flexible CPU Configuration

One of the standout features is the intelligent CPU type selection. Rather than forcing a one-size-fits-all approach, the script offers three carefully chosen options:

Option 1: x86-64-v3 (Recommended for Modern AMD Processors) This is our recommended default, especially for AMD Ryzen 7950X processors. It enables advanced instruction sets including AVX and AVX2, providing excellent performance while maintaining compatibility for live migration scenarios. This strikes the perfect balance between performance and flexibility.

Option 2: Host CPU Passthrough For workloads that demand absolute maximum performance and where live migration isn't a priority, host passthrough gives your VM access to every CPU feature available on the physical hardware. This is ideal for high-performance computing tasks, specialized workloads, or development environments where you need access to specific CPU instructions.

Option 3: x86-64-v2-AES (Basic Compatibility) This option provides broad compatibility without advanced vector extensions. It's perfect for legacy workloads or when you need maximum compatibility across different hardware platforms.

Dual OS Support: Debian and Ubuntu

The script supports both Debian 13 (Trixie) and Ubuntu 24.04 LTS (Noble), automatically downloading and caching the cloud images. Once downloaded, images are stored locally, making subsequent VM deployments lightning-fast.

Debian 13 (Trixie): Perfect for those who prefer Debian's stability-focused approach and longer release cycles.

Ubuntu 24.04 LTS (Noble): Ideal for users who want access to newer packages and Ubuntu's extensive community support.

Cloud-Init Integration: Zero-Touch Configuration

One of the most powerful features is the integrated cloud-init setup. Each VM is automatically configured with:

  • Secure SSH access using public key authentication

  • Automatic package updates on first boot

  • Essential tools pre-installed including curl, wget, jq, htop, vim, git, unzip, and net-tools

  • Networking fully configured with static IP addressing

  • Service auto-start ensuring SSH is enabled and running

This means your VMs are production-ready the moment they boot. No manual configuration required.

Tagging System for Organization

The script includes optional VM tagging, allowing you to organize your infrastructure logically. Tags like production, webserver, database, or client-projectname make it easy to:

  • Filter VMs in the Proxmox interface

  • Create automation rules based on tags

  • Generate reports and track resources by category

  • Implement tag-based backup policies

The Deployment Workflow

Here's how the entire process works in practice:

1. Resource Selection

When you launch the script, you're first presented with resource management options. You can either let the system automatically assign the next available resources, or manually select from the available pool. This flexibility is crucial when you need to deploy VMs in specific network segments or have particular infrastructure requirements.

The script displays all available resources across your infrastructure, organized by subnet and showing current allocation status. This transparency ensures you always know what's available before making deployment decisions.

2. VM Configuration

Next, you specify your VM parameters:

VMID: 100
Name: webserver-prod-01
Cores: 8
Memory: 16384 MB
Disk: 100G
CPU Type: x86-64-v3
Tags: production,webserver,nginx

The script validates your VMID to ensure it's not already in use, preventing deployment conflicts.

3. Operating System Selection

Choose between Debian 13 or Ubuntu 24.04. The script automatically downloads the appropriate cloud image if it's not already cached locally.

4. Automated Deployment

Once you confirm your configuration, the script:

  1. Creates the VM with your specified resources

  2. Imports and configures the cloud image as the boot disk

  3. Attaches a cloud-init configuration drive

  4. Configures networking with your allocated resources

  5. Resizes the disk to your specified size

  6. Generates a custom cloud-init configuration file

  7. Applies your tags

  8. Updates resource tracking

  9. Starts the VM

The entire process typically completes in under a minute.

5. Immediate Access

As soon as deployment completes, you get a clean summary:

=========================================
✅ VM 100 (webserver-prod-01) created successfully!
=========================================
SSH connection: ssh root@[VM_IP_ADDRESS]
Gateway: [GATEWAY_IP]
Tags: production,webserver,nginx
Use your private key to authenticate.

You can immediately SSH into your new VM and begin working. All packages are updated, all services are running, and the system is fully configured.

Real-World Benefits

We've been using this automation in production for managing our wholesale textile business infrastructure, and the results have been transformative:

Time Savings

What used to take 15-20 minutes per VM now takes less than 2 minutes, including the time to decide on configuration parameters. When you're deploying multiple VMs, this adds up quickly.

Consistency

Every VM deployed through this script has the same base configuration, security settings, and package set. This consistency makes troubleshooting easier and reduces configuration drift across your infrastructure.

Reduced Errors

Manual VM creation inevitably leads to mistakes: typos in IP addresses, forgotten package installations, misconfigured networking. Automation eliminates these human errors.

Better Documentation

The script itself serves as documentation of your VM deployment standards. New team members can understand your infrastructure setup by reading the script, and they can deploy VMs correctly from day one.

Resource Visibility

The integrated tracking system provides instant visibility into resource allocation across your entire infrastructure. You always know what's in use, what's available, and where you can deploy next.

Advanced Features for Power Users

Custom Cloud-Init Configurations

While the script provides sensible defaults, you can easily customize the cloud-init configuration for specific use cases. The generated cloud-init file is stored at /var/lib/vz/snippets/user-data-[VMID].yml, making it easy to modify before or after deployment.

Batch Deployment

Need to deploy multiple identical VMs? The script can be easily adapted to loop through a configuration file, deploying entire environments with a single command.

Integration with Configuration Management

The cloud-init setup makes these VMs perfect for integration with Ansible, Puppet, Chef, or Salt. You can include your configuration management bootstrap commands in the cloud-init script, creating fully autonomous deployments.

Best Practices We've Learned

Through extensive use of this automation, we've developed several best practices:

1. Use Descriptive VM Names

Instead of generic names like "vm-100" or "test-server," use descriptive names that indicate purpose and environment: webserver-prod-01, database-staging-02, api-dev-03. This makes management much easier as your infrastructure grows.

2. Tag Consistently

Develop a tagging standard early and stick to it. We use tags for:

  • Environment (production, staging, development)

  • Purpose (webserver, database, application)

  • Project or client name

  • Backup requirements (daily, weekly, monthly)

3. Document Your Subnet Strategy

Keep clear documentation of which subnets are used for which purposes. This helps prevent confusion when deploying new VMs.

4. Regular Resource Audits

Periodically review your resource allocation. Decommission unused VMs promptly to free up resources for new deployments.

5. Maintain Your Cloud Images

Keep your cached Debian and Ubuntu images updated. While the script downloads the latest images automatically, periodically clearing old cache files ensures you're always working with recent releases.

Security Considerations

The script includes several security-focused features:

SSH Key Authentication Only

Password authentication is not configured. All access requires your private SSH key, significantly reducing the attack surface.

Automatic Security Updates

Cloud-init is configured to update all packages on first boot, ensuring your VMs start with the latest security patches.

Minimal Package Set

Only essential packages are installed by default. This reduces the attack surface and makes security auditing easier.

Network Isolation

Each VM is deployed with its own network configuration, and you can leverage Proxmox's built-in firewall capabilities for additional security.

Future Enhancements

We're continuously improving this automation. Some planned enhancements include:

  • Storage backend selection: Choose between local storage, Ceph, NFS, or other Proxmox storage options

  • Multiple network interface support: For VMs that need connections to multiple networks

  • VLAN configuration: Automated VLAN tagging for network segmentation

  • Backup policy automation: Automatically configure Proxmox backup jobs based on VM tags

  • Monitoring integration: Automatic registration with monitoring systems like Prometheus or Zabbix

  • Template support: Create and deploy from custom VM templates for specialized workloads

Conclusion

Infrastructure automation doesn't have to be complex. This script proves that with thoughtful design and attention to common use cases, you can create powerful automation that's both flexible and easy to use.

Whether you're managing a handful of VMs or hundreds, automation like this pays dividends in time saved, errors prevented, and consistency maintained. The initial time investment in creating the script has been repaid many times over in our daily operations.

For businesses running on Proxmox, especially those in e-commerce, wholesale, or service industries like ours, having reliable, fast VM deployment is crucial for maintaining agility and responding to changing infrastructure needs.

The script handles the tedious details, tracks resources intelligently, and ensures every deployment follows best practices. This frees your team to focus on what really matters: building and running the applications that drive your business.

#!/bin/bash
set -e

# ============================================================================
# Proxmox VM Creation Script with Intelligent Resource Management
# ============================================================================
# This script automates the creation of VMs in Proxmox with:
# - Automatic IP address management and tracking
# - Cloud-init integration for zero-touch configuration
# - Support for Debian 13 and Ubuntu 24.04 LTS
# - Flexible CPU configuration options
# - VM tagging for organization
# - SSH key-based authentication
# - Created By Sohaib R. Khan
# SETUP REQUIRED:
# 1. Edit the initialize_ip_pool() function below to add YOUR IP addresses
# 2. Update SUBNET_GATEWAYS with YOUR gateway addresses
# 3. Replace the SSH public key with YOUR public key (two locations)
# 4. Ensure jq is installed: apt-get install jq
# ============================================================================

# IP Management Configuration
IP_POOL_FILE="/root/vm-ip-pool.json"

# ============================================================================
# CONFIGURATION SECTION - EDIT THIS FOR YOUR NETWORK
# ============================================================================

# Define your subnet gateway addresses
# Format: SUBNET_GATEWAYS["first.three.octets"]="gateway.ip.address"
# Example: SUBNET_GATEWAYS["192.168.1"]="192.168.1.1"
declare -A SUBNET_GATEWAYS
SUBNET_GATEWAYS["YOUR.SUBNET.1"]="YOUR.GATEWAY.1"
SUBNET_GATEWAYS["YOUR.SUBNET.2"]="YOUR.GATEWAY.2"

# Network configuration - adjust for your subnet size
NETMASK="255.255.255.248"  # /29 subnet
CIDR_SUFFIX="29"            # CIDR notation

# ============================================================================
# IP POOL INITIALIZATION - EDIT THIS TO ADD YOUR IP ADDRESSES
# ============================================================================
initialize_ip_pool() {
    if [ ! -f "$IP_POOL_FILE" ]; then
        echo "Initializing IP pool with all available IPs from your subnets..."
        cat > "$IP_POOL_FILE" << 'EOF'
{
  "ips": {
    "YOUR.IP.ADDRESS.1": {"status": "used", "vmid": "existing", "vmname": "ExistingVM1", "gateway": "YOUR.GATEWAY.1"},
    "YOUR.IP.ADDRESS.2": {"status": "used", "vmid": "existing", "vmname": "ExistingVM2", "gateway": "YOUR.GATEWAY.1"},
    "YOUR.IP.ADDRESS.3": {"status": "free", "vmid": null, "vmname": null, "gateway": "YOUR.GATEWAY.1"},
    "YOUR.IP.ADDRESS.4": {"status": "free", "vmid": null, "vmname": null, "gateway": "YOUR.GATEWAY.1"},
    "YOUR.IP.ADDRESS.5": {"status": "free", "vmid": null, "vmname": null, "gateway": "YOUR.GATEWAY.1"},
    "YOUR.IP.ADDRESS.6": {"status": "used", "vmid": "existing", "vmname": "ExistingVM3", "gateway": "YOUR.GATEWAY.2"},
    "YOUR.IP.ADDRESS.7": {"status": "free", "vmid": null, "vmname": null, "gateway": "YOUR.GATEWAY.2"},
    "YOUR.IP.ADDRESS.8": {"status": "free", "vmid": null, "vmname": null, "gateway": "YOUR.GATEWAY.2"},
    "YOUR.IP.ADDRESS.9": {"status": "free", "vmid": null, "vmname": null, "gateway": "YOUR.GATEWAY.2"},
    "YOUR.IP.ADDRESS.10": {"status": "free", "vmid": null, "vmname": null, "gateway": "YOUR.GATEWAY.2"}
  }
}
EOF
        echo ""
        echo "⚠️  IMPORTANT: IP pool initialized with PLACEHOLDER addresses!"
        echo "⚠️  Edit $IP_POOL_FILE and replace with your actual IP addresses"
        echo "⚠️  Each IP should have: status (used/free), vmid, vmname, and gateway"
        echo ""
        read -p "Press Enter after you've updated the IP pool file..."
    fi
}

# ============================================================================
# IP MANAGEMENT FUNCTIONS
# ============================================================================

get_available_ip() {
    initialize_ip_pool
    
    local free_ip=$(jq -r '.ips | to_entries[] | select(.value.status == "free") | .key' "$IP_POOL_FILE" | head -n 1)
    
    if [ -z "$free_ip" ]; then
        echo ""
    else
        echo "$free_ip"
    fi
}

get_ip_gateway() {
    local ip=$1
    jq -r --arg ip "$ip" '.ips[$ip].gateway' "$IP_POOL_FILE"
}

mark_ip_used() {
    local ip=$1
    local vmid=$2
    local vmname=$3
    local gateway=$4
    
    jq --arg ip "$ip" --arg vmid "$vmid" --arg vmname "$vmname" --arg gateway "$gateway" \
        '.ips[$ip] = {"status": "used", "vmid": $vmid, "vmname": $vmname, "gateway": $gateway}' \
        "$IP_POOL_FILE" > "${IP_POOL_FILE}.tmp" && mv "${IP_POOL_FILE}.tmp" "$IP_POOL_FILE"
}

mark_ip_free() {
    local ip=$1
    local gateway=$(get_ip_gateway "$ip")
    
    jq --arg ip "$ip" --arg gateway "$gateway" \
        '.ips[$ip] = {"status": "free", "vmid": null, "vmname": null, "gateway": $gateway}' \
        "$IP_POOL_FILE" > "${IP_POOL_FILE}.tmp" && mv "${IP_POOL_FILE}.tmp" "$IP_POOL_FILE"
}

show_ip_status() {
    initialize_ip_pool
    echo ""
    echo "=== IP Pool Status (All Subnets) ==="
    echo ""
    
    # Get unique subnet prefixes
    local subnets=$(jq -r '.ips | keys[]' "$IP_POOL_FILE" | cut -d'.' -f1-3 | sort -u)
    
    for subnet in $subnets; do
        local gateway=$(jq -r --arg subnet "$subnet" '.ips | to_entries[] | select(.key | startswith($subnet)) | .value.gateway' "$IP_POOL_FILE" | head -n 1)
        echo "Subnet ${subnet}.x (Gateway: $gateway):"
        jq -r --arg subnet "$subnet" '.ips | to_entries[] | select(.key | startswith($subnet)) | "\(.key) - \(.value.status | ascii_upcase) \(if .value.status == "used" then "- VM \(.value.vmid) (\(.value.vmname))" else "" end)"' "$IP_POOL_FILE"
        echo ""
    done
    
    local total_free=$(jq '[.ips[] | select(.status == "free")] | length' "$IP_POOL_FILE")
    local total_used=$(jq '[.ips[] | select(.status == "used")] | length' "$IP_POOL_FILE")
    echo "Summary: $total_free free, $total_used used"
    echo "======================================"
    echo ""
}

list_available_ips() {
    initialize_ip_pool
    echo ""
    echo "=== Available IPs ==="
    local count=1
    while IFS= read -r ip; do
        local gateway=$(jq -r --arg ip "$ip" '.ips[$ip].gateway' "$IP_POOL_FILE")
        echo "$count) $ip (Gateway: $gateway)"
        ((count++))
    done < <(jq -r '.ips | to_entries[] | select(.value.status == "free") | .key' "$IP_POOL_FILE" | sort)
    echo "====================="
    echo ""
}

# ============================================================================
# MAIN SCRIPT
# ============================================================================

# Create directory for OS images
mkdir -p /root/qcow

# Show current IP allocation status
show_ip_status

# IP Assignment Selection
echo "IP Assignment Options:"
echo "1) Auto-assign next available IP"
echo "2) Select IP from available pool"
read -p "Enter choice [1-2]: " IP_CHOICE

if [ "$IP_CHOICE" -eq 2 ]; then
    list_available_ips
    read -p "Enter the IP address you want to use: " SELECTED_IP
    
    # Validate IP exists and is free
    IP_STATUS=$(jq -r --arg ip "$SELECTED_IP" '.ips[$ip].status // "notfound"' "$IP_POOL_FILE")
    
    if [ "$IP_STATUS" == "notfound" ]; then
        echo "❌ ERROR: IP $SELECTED_IP not found in pool"
        exit 1
    elif [ "$IP_STATUS" == "used" ]; then
        echo "❌ ERROR: IP $SELECTED_IP is already in use"
        exit 1
    fi
    
    AVAILABLE_IP="$SELECTED_IP"
else
    AVAILABLE_IP=$(get_available_ip)
fi

# Check if IP is available
if [ -z "$AVAILABLE_IP" ]; then
    echo "❌ ERROR: No available IPs in the pool!"
    echo ""
    echo "All IPs are currently assigned. Please:"
    echo "1. Order more IP addresses from your provider"
    echo "2. Release unused VMs to free up IPs"
    echo "3. Update the IP pool file: $IP_POOL_FILE"
    echo ""
    echo "Current IP assignments:"
    show_ip_status
    exit 1
fi

GATEWAY=$(get_ip_gateway "$AVAILABLE_IP")

echo "✅ Assigned IP: $AVAILABLE_IP/$CIDR_SUFFIX"
echo "✅ Gateway: $GATEWAY"
echo ""

# VMID validation
while true; do
    read -p "Enter VMID (numeric only, e.g., 100, 101, 102): " VMID
    if [[ "$VMID" =~ ^[0-9]+$ ]]; then
        # Check if VMID already exists
        if qm status $VMID &>/dev/null; then
            echo "❌ ERROR: VMID $VMID already exists. Please choose a different VMID."
        else
            break
        fi
    else
        echo "❌ ERROR: VMID must be numeric only (e.g., 100, 101, 102)"
    fi
done

# VM Configuration
read -p "Enter number of CPU cores: " CORES
read -p "Enter memory in MB: " MEMORY
read -p "Enter disk size (e.g., 32G, 50G, 100G): " DISKSIZE
read -p "Enter VM name: " VMNAME

# CPU Type Selection
echo ""
echo "CPU Type Options:"
echo "1) x86-64-v3 (AVX, AVX2, best for modern AMD/Intel - RECOMMENDED)"
echo "2) host (All CPU features, best performance, no live migration)"
echo "3) x86-64-v2-AES (Basic, no AVX)"
read -p "Enter CPU type choice [1-3, default: 1]: " CPU_TYPE_CHOICE
CPU_TYPE_CHOICE=${CPU_TYPE_CHOICE:-1}

case $CPU_TYPE_CHOICE in
    1)
        CPU_TYPE="x86-64-v3"
        echo "✅ Selected: x86-64-v3 (with AVX and AVX2 support)"
        ;;
    2)
        CPU_TYPE="host"
        echo "✅ Selected: host (full CPU passthrough)"
        ;;
    3)
        CPU_TYPE="x86-64-v2-AES"
        echo "✅ Selected: x86-64-v2-AES (basic)"
        ;;
    *)
        CPU_TYPE="x86-64-v3"
        echo "✅ Default: x86-64-v3 (with AVX and AVX2 support)"
        ;;
esac

# VM Tags (optional)
echo ""
echo "Enter Proxmox tags (optional, comma-separated, e.g., production,webserver,client-abc):"
read -p "Tags: " TAGS

# Configuration Summary
echo ""
echo "=== VM Configuration Summary ==="
echo "VMID: $VMID"
echo "Name: $VMNAME"
echo "Cores: $CORES"
echo "Memory: $MEMORY MB"
echo "Disk: $DISKSIZE"
echo "CPU Type: $CPU_TYPE"
echo "IP: $AVAILABLE_IP/$CIDR_SUFFIX"
echo "Gateway: $GATEWAY"
if [ -n "$TAGS" ]; then
    echo "Tags: $TAGS"
fi
echo "==============================="
read -p "Press Enter to continue or Ctrl+C to cancel..."

IPADDR="$AVAILABLE_IP/$CIDR_SUFFIX"
DNS="8.8.8.8"

# OS Selection
echo "Select OS:"
echo "1) Debian 13 (Trixie)"
echo "2) Ubuntu 24.04 LTS (Noble)"
read -p "Enter choice [1-2]: " OSCHOICE

if [ "$OSCHOICE" -eq 1 ]; then
    OSIMG="https://cdimage.debian.org/images/cloud/trixie/latest/debian-13-generic-amd64.qcow2"
    OSNAME="debian-13-generic-amd64.qcow2"
elif [ "$OSCHOICE" -eq 2 ]; then
    OSIMG="https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img"
    OSNAME="ubuntu-noble-cloudimg-amd64.qcow2"
else
    echo "Invalid choice. Exiting."
    exit 1
fi

# Download OS image if not cached
if [ ! -f "/root/qcow/$OSNAME" ]; then
    echo "Downloading $OSNAME..."
    wget -q --show-progress "$OSIMG" -O "/root/qcow/$OSNAME"
fi

# Create VM
echo "Creating VM $VMID..."
qm create $VMID \
  --name "$VMNAME" \
  --cpu $CPU_TYPE \
  --cores $CORES \
  --memory $MEMORY \
  --net0 virtio,bridge=vmbr0 \
  --scsihw virtio-scsi-pci \
  --ostype l26

# Add tags if specified
if [ -n "$TAGS" ]; then
    echo "Adding tags: $TAGS"
    qm set $VMID --tags "$TAGS"
fi

# Import OS disk
echo "Importing OS image disk..."
IMPORT_OUTPUT=$(qm importdisk $VMID "/root/qcow/$OSNAME" local)
echo "$IMPORT_OUTPUT"

DISK_PATH=$(echo "$IMPORT_OUTPUT" | grep -o "local:$VMID/vm-$VMID-disk-[0-9]*\.[a-z]*")
if [ -z "$DISK_PATH" ]; then
    echo "Error: Failed to extract disk path from import output"
    echo "Import output was: $IMPORT_OUTPUT"
    exit 1
fi

echo "Imported disk path: $DISK_PATH"

# Attach disk
echo "Attaching imported disk..."
qm set $VMID --scsi0 "$DISK_PATH"

# Add cloud-init drive
echo "Adding cloud-init drive..."
qm set $VMID --ide2 local:cloudinit

# Set boot order
qm set $VMID --boot order=scsi0

# ============================================================================
# CONFIGURE CLOUD-INIT - REPLACE SSH KEY WITH YOUR PUBLIC KEY
# ============================================================================
echo "Configuring Cloud-Init..."
qm set $VMID \
  --ciuser root \
  --sshkeys <(echo "YOUR_SSH_PUBLIC_KEY_HERE") \
  --ipconfig0 "ip=$IPADDR,gw=$GATEWAY" \
  --nameserver "$DNS" \
  --cicustom "user=local:snippets/user-data-$VMID.yml"

# Create cloud-init user-data file
mkdir -p /var/lib/vz/snippets
cat > "/var/lib/vz/snippets/user-data-$VMID.yml" << 'EOF'
#cloud-config
users:
  - name: root
    ssh_authorized_keys:
      - YOUR_SSH_PUBLIC_KEY_HERE
package_update: true
package_upgrade: true
packages:
  - curl
  - wget
  - jq
  - htop
  - vim
  - git
  - unzip
  - net-tools
runcmd:
  - echo "Package installation completed" > /var/log/cloud-init-packages.log
  - systemctl enable ssh
  - systemctl start ssh
final_message: "VM setup completed successfully with all packages installed!"
EOF

echo "Created cloud-init user-data file at /var/lib/vz/snippets/user-data-$VMID.yml"

# Resize disk
echo "Resizing disk to use full $DISKSIZE..."
qm resize $VMID scsi0 $DISKSIZE

# Mark IP as used in pool
mark_ip_used "$AVAILABLE_IP" "$VMID" "$VMNAME" "$GATEWAY"
echo "✅ IP $AVAILABLE_IP marked as used for VM $VMID"

# Start VM
echo "Starting VM $VMID..."
qm start $VMID

# Success message
echo ""
echo "========================================="
echo "✅ VM $VMID ($VMNAME) created successfully!"
echo "========================================="
echo "SSH connection: ssh root@$AVAILABLE_IP"
echo "Gateway: $GATEWAY"
echo "Subnet: $(echo $AVAILABLE_IP | cut -d'.' -f1-3).x"
if [ -n "$TAGS" ]; then
    echo "Tags: $TAGS"
fi
echo "Use your private key to authenticate."
echo ""

# Show updated IP status
show_ip_status

Getting Started

The beauty of this automation is its simplicity. It's a single bash script with no external dependencies beyond what's already on your Proxmox server. You can start using it immediately and customize it to fit your specific needs.

If you're still creating VMs manually in Proxmox, I encourage you to explore automation. The time you'll save and the consistency you'll gain make it an investment that pays off from the very first deployment.

Enjoyed this article?

Show your appreciation with a clap

4claps
Share this article
SK
Written by

Sohaib Khan

View all posts

You might also like

View all

Comments (0)

No comments yet. Be the first to comment!