Virtualization & Host Nodes

Selling VPS from a Proxmox VE Host Node: Clone VMs in Bulk, Assign IPs with Cloud-Init, Set Limits and Stop IP Theft

7 steps 17 min read 11 views 0
On this page

Once Proxmox VE is installed on a host node, the real work is turning it into VPS you can sell: creating many VMs quickly, giving each one its own IP and password, enforcing the plan's limits, and making sure no customer can take over somebody else's IP. This guide covers that operational side: cloning VPS from a cloud-init template, setting IP, password and SSH key automatically, limiting CPU, disk I/O and bandwidth, preventing IP theft with the Proxmox firewall, giving customers their own console login, and a script that creates VPS in bulk.

This guide assumes Proxmox VE is installed with a working bridge (see installing Proxmox VE on a dedicated server) and that you have a cloud-init template (see creating a cloud-init template). Test everything on one VPS before you create dozens, and keep IPMI/KVM console access to the host in case a network change locks you out.

Step 1: Clone a VPS from the Template

A full clone is independent of the template, so you can delete or update the template later. Linked clones are faster and smaller, but every clone depends on the template disk.

# Template 9000 -> new VPS 101 (full clone on local-lvm)
qm clone 9000 101 --name vps101 --full 1 --storage local-lvm

# Plan resources: 2 vCPU, 2 GB RAM (no ballooning), 40 GB disk
qm set 101 --cores 2 --memory 2048 --balloon 0
qm resize 101 scsi0 40G
qm set 101 --onboot 1

Step 2: Set IP, Password and SSH Key with Cloud-Init

Cloud-init writes the network configuration and login details into the VPS on first boot, so customers receive a ready server without you logging in.

qm set 101 \
  --net0 virtio,bridge=vmbr0,firewall=1 \
  --ipconfig0 ip=203.0.113.11/24,gw=203.0.113.1 \
  --nameserver "1.1.1.1 8.8.8.8" \
  --ciuser root \
  --cipassword 'Ch4nge-This-Password' \
  --sshkeys /root/customer101.pub

qm start 101
qm guest cmd 101 network-get-interfaces   # works once qemu-guest-agent is running in the VPS

If you change cloud-init values later, regenerate the cloud-init drive (qm cloudinit update 101) and reboot the VPS. For IPv6, add ip6=2001:db8:100::11/64,gw6=2001:db8:100::1 to --ipconfig0.

Step 3: Enforce the Plan's Limits

# CPU: at most 2 cores of CPU time in total, normal scheduling weight
qm set 101 --cpulimit 2 --cpuunits 100

# Bandwidth: rate is in MB/s on the virtual NIC (12.5 MB/s = 100 Mbps)
qm set 101 --net0 virtio,bridge=vmbr0,firewall=1,rate=12.5

# Disk I/O: repeat the disk exactly as shown by "qm config 101", adding the limits
qm config 101 | grep scsi0
qm set 101 --scsi0 local-lvm:vm-101-disk-0,size=40G,iops_rd=2000,iops_wr=2000,mbps_rd=200,mbps_wr=150

Note that --net0 replaces the whole NIC definition: keep the same MAC address by copying it from qm config (virtio=BC:24:11:...), otherwise the VPS gets a new MAC and may lose its network if your upstream binds IPs to MACs. Disk limits stop one noisy VPS from slowing down every other VPS on the same disk.

Step 4: Prevent IP Theft with the Proxmox Firewall

Without filtering, a customer with root access can configure any free IP from your block and take it over. The Proxmox firewall's IP filter drops packets from a VPS whose source address is not on its allowed list. The firewall must be enabled at datacenter level first; set the input policy to ACCEPT there so you cannot lock yourself out of the host.

# /etc/pve/firewall/cluster.fw  (datacenter level)
[OPTIONS]
enable: 1
policy_in: ACCEPT
# /etc/pve/firewall/101.fw  (VPS 101)
[OPTIONS]
enable: 1
ipfilter: 1
macfilter: 1
policy_in: ACCEPT
policy_out: ACCEPT

[IPSET ipfilter-net0]
203.0.113.11
2001:db8:100::11
pve-firewall compile >/dev/null && pve-firewall restart
pve-firewall status

The NIC must have firewall=1 (set in Step 2). macfilter (on by default) stops a VPS from changing its MAC address. Test from inside the VPS: adding a second, unassigned IP and pinging out from it should now fail.

Step 5: Give Customers Their Own Console Login

A customer who breaks the VPS network still needs the console to fix it. Create a Proxmox user that can only see and control their own VPS:

pveum user add customer101@pve --password 'Another-Strong-Pass' --comment "VPS 101"
pveum acl modify /vms/101 --users customer101@pve --roles PVEVMUser

The customer logs in at https://HOST_IP:8006 with realm "Proxmox VE authentication server" and sees only VPS 101, with console, power control and its own graphs. For a fully automated shop with billing, a panel such as Virtualizor or WHMCS with a Proxmox module is the next step.

Step 6: Create VPS in Bulk

The script below reads one line per VPS (ID, IP, RAM in MB, cores, disk) and creates them from the template with the settings above. Generate random passwords, and save them somewhere safe for handover.

cat > /root/vps-list.txt <<'EOF2'
101 203.0.113.11 2048 2 40G
102 203.0.113.12 2048 2 40G
103 203.0.113.13 4096 4 80G
EOF2

cat > /root/make-vps.sh <<'EOF2'
#!/bin/bash
set -e
TEMPLATE=9000; GW=203.0.113.1; PREFIX=24
while read -r ID IP RAM CORES DISK; do
  [ -z "$ID" ] && continue
  PASS=$(openssl rand -base64 12)
  qm clone $TEMPLATE $ID --name vps$ID --full 1 --storage local-lvm
  qm set $ID --cores $CORES --memory $RAM --balloon 0 --onboot 1 --cpulimit $CORES
  qm resize $ID scsi0 $DISK
  qm set $ID --net0 virtio,bridge=vmbr0,firewall=1,rate=12.5 \
             --ipconfig0 ip=$IP/$PREFIX,gw=$GW --nameserver "1.1.1.1 8.8.8.8" \
             --ciuser root --cipassword "$PASS"
  printf '[OPTIONS]\nenable: 1\nipfilter: 1\nmacfilter: 1\npolicy_in: ACCEPT\npolicy_out: ACCEPT\n\n[IPSET ipfilter-net0]\n%s\n' "$IP" > /etc/pve/firewall/$ID.fw
  qm start $ID
  echo "$ID $IP root $PASS" >> /root/vps-credentials.txt
done < /root/vps-list.txt
chmod 600 /root/vps-credentials.txt
EOF2

chmod +x /root/make-vps.sh && /root/make-vps.sh

Step 7: Watch the Host

  • RAM: keep total assigned RAM below physical RAM minus 4–8 GB for the host and ZFS/cache. Overcommitting memory is the fastest way to make every VPS slow.
  • Disk: thin-provisioned storage (LVM-thin, ZFS) can be overcommitted, but monitor actual usage with lvs or zfs list; a full thin pool can corrupt VPS disks.
  • Abuse: one VPS sending attacks or spam can get your whole IP block listed. See detecting abusive VPS on a host node.
  • Backups: schedule them as described in Proxmox VE backup and snapshots.

FAQ

The new VPS has no network

Check the IP, prefix and gateway in qm config, that the bridge is the one carrying your IP block, and that the IP filter IPSet contains the VPS's IP. If your upstream binds IPs to MAC addresses, bridged VPS need a routed setup or virtual MACs; see Proxmox VE network configuration for host nodes.

The password set by cloud-init does not work

Some images disable root password login over SSH. Log in with the SSH key or through the console and allow password login in /etc/ssh/sshd_config if you want it, or bake that change into your template.

Can I sell Windows VPS this way?

Yes, with a Windows template prepared with cloudbase-init or sysprep, but Windows licensing is your responsibility: each VPS needs a valid Windows license.

Still stuck after following these steps? Open a support ticket and the IMIDC 24/7 technical team will help. Include the host server IP, the Proxmox VE version, the VM ID with the output of qm config ID, and the full error message so we can pinpoint the issue faster.

Was this answer helpful?

Related Tutorials