Building a Commercial Proxmox VPS Service: Complete Guide to Setup, Frontend Integration, and Payment Processing
Create Your Own VPS Hosting Business
Proxmox Virtual Environment (VE) is a powerful open-source virtualization platform that can be transformed into a commercial VPS hosting service with proper configuration, automation, and payment integration. This comprehensive guide will walk you through building a complete infrastructure-as-a-service platform, from Proxmox installation to creating a customer-facing portal with automated billing and resource provisioning.
Proxmox Virtual Environment (VE) is a powerful open-source virtualization platform that can be transformed into a commercial VPS hosting service with proper configuration, automation, and payment integration. This comprehensive guide will walk you through building a complete infrastructure-as-a-service platform, from Proxmox installation to creating a customer-facing portal with automated billing and resource provisioning.
Project Overview and Architecture
System Architecture Components
Proxmox VE Cluster: Core virtualization infrastructure
Web Frontend: Customer portal for service management
Payment Gateway: Stripe/PayPal integration for billing
API Middleware: Node.js/Python backend for automation
Database: MySQL/PostgreSQL for customer and billing data
Monitoring System: Resource usage tracking and billing
Service Flow Overview
Customer visits web portal and selects VPS plan
Payment processing through integrated gateway
Automated VM provisioning via Proxmox API
Customer receives credentials and access information
Ongoing billing and resource monitoring
Self-service management through customer portal
Service Flow Overview
Proxmox Installation and Initial Configuration
Hardware Requirements and Planning
Minimum Server Specs: 64GB RAM, 16 CPU cores, 1TB NVMe SSD
Network Setup: Multiple public IP addresses for customer VMs
Storage Planning: Separate storage pools for different service tiers
Backup Infrastructure: Dedicated backup storage and scheduling
Proxmox Installation Steps
Download the Proxmox VE ISO from proxmox.com
Installation Process:
Create bootable USB with Proxmox ISO
Boot server and follow installation wizard
Configure management IP address and hostname
Set root password and email for notifications
Complete installation and reboot to web interface
Post-Installation Configuration:
Access the web interface at https://your-server-ip:8006
# Update package repository
echo "deb http://download.proxmox.com/debian/pve bullseye pve-no-subscription" > /etc/apt/sources.list.d/pve-install-repo.list
# Update system
apt update && apt upgrade -y
# Install additional packages
apt install -y curl wget git htop iftop
Proxmox Installation Steps
Download the Proxmox VE ISO from proxmox.com
Installation Process:
Post-Installation Configuration:
Access the web interface at https://your-server-ip:8006
# Update package repository
echo "deb http://download.proxmox.com/debian/pve bullseye pve-no-subscription" > /etc/apt/sources.list.d/pve-install-repo.list
# Update system
apt update && apt upgrade -y
# Install additional packages
apt install -y curl wget git htop iftop
Storage Configuration
Create Storage Pools for Different Tiers:
# Create directory storage for customer VMs
pvesm add dir customer-vms --path /var/lib/vz/customer-vms --content images,vztmpl
# Create directory for backups
pvesm add dir backups --path /var/lib/vz/backups --content backup
# Configure shared storage if using cluster
pvesm add nfs shared-storage --server nfs-server.local --export /shared --content images,iso,vztmpl
Network Configuration:
Configure network bridges for customer isolation
# Edit network configuration
nano /etc/network/interfaces
# Add customer bridge
auto vmbr1
iface vmbr1 inet manual
bridge-ports none
bridge-stp off
bridge-fd 0
bridge-vlan-aware yes
Create Storage Pools for Different Tiers:
# Create directory storage for customer VMs
pvesm add dir customer-vms --path /var/lib/vz/customer-vms --content images,vztmpl
# Create directory for backups
pvesm add dir backups --path /var/lib/vz/backups --content backup
# Configure shared storage if using cluster
pvesm add nfs shared-storage --server nfs-server.local --export /shared --content images,iso,vztmpl
Network Configuration:
Configure network bridges for customer isolation
# Edit network configuration
nano /etc/network/interfaces
# Add customer bridge
auto vmbr1
iface vmbr1 inet manual
bridge-ports none
bridge-stp off
bridge-fd 0
bridge-vlan-aware yes
API Configuration and Automation Setup
Proxmox API Authentication
Create API User for Automation:
Navigate to Datacenter > Permissions > Users
Add user: "automation@pve" with strong password
Create API token for programmatic access
Assign appropriate permissions for VM management
API Token Creation:
Go to Datacenter > Permissions > API Tokens
Add token for automation user
Record Token ID and Secret (shown only once)
Configure permissions: VM.Allocate, VM.Config, VM.Console, VM.PowerMgmt
Test API Connection:
# Test API access with curl
curl -k -d "username=automation@pve&password=your-password" \
https://your-proxmox-ip:8006/api2/json/access/ticket
# Using token authentication
curl -k -H "Authorization: PVEAPIToken=automation@pve!token1=your-token-secret" \
https://your-proxmox-ip:8006/api2/json/nodes
Create API User for Automation:
API Token Creation:
Test API Connection:
# Test API access with curl
curl -k -d "username=automation@pve&password=your-password" \
https://your-proxmox-ip:8006/api2/json/access/ticket
# Using token authentication
curl -k -H "Authorization: PVEAPIToken=automation@pve!token1=your-token-secret" \
https://your-proxmox-ip:8006/api2/json/nodes
VM Template Creation
Create standardized templates for quick provisioning
Ubuntu Server Template:
Download Ubuntu Server cloud image
Create VM with cloud-init support
Install standard packages and security updates
Configure SSH keys and user accounts
Convert to template for customer deployment
Template Creation Script:
#!/bin/bash
# Create Ubuntu template
# Download cloud image
wget https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img
# Create VM
qm create 9000 --name ubuntu-template --memory 2048 --cores 2 --net0 virtio,bridge=vmbr0
# Import disk
qm importdisk 9000 jammy-server-cloudimg-amd64.img local-lvm
# Configure VM
qm set 9000 --scsihw virtio-scsi-pci --scsi0 local-lvm:vm-9000-disk-0
qm set 9000 --ide2 local-lvm:cloudinit
qm set 9000 --boot c --bootdisk scsi0
qm set 9000 --serial0 socket --vga serial0
# Convert to template
qm template 9000
Create standardized templates for quick provisioning
Ubuntu Server Template:
Template Creation Script:
#!/bin/bash
# Create Ubuntu template
# Download cloud image
wget https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img
# Create VM
qm create 9000 --name ubuntu-template --memory 2048 --cores 2 --net0 virtio,bridge=vmbr0
# Import disk
qm importdisk 9000 jammy-server-cloudimg-amd64.img local-lvm
# Configure VM
qm set 9000 --scsihw virtio-scsi-pci --scsi0 local-lvm:vm-9000-disk-0
qm set 9000 --ide2 local-lvm:cloudinit
qm set 9000 --boot c --bootdisk scsi0
qm set 9000 --serial0 socket --vga serial0
# Convert to template
qm template 9000
Backend API Development
Node.js Backend Setup
Initialize Project:
mkdir proxmox-service
cd proxmox-service
npm init -y
# Install dependencies
npm install express mysql2 stripe axios bcryptjs jsonwebtoken cors helmet
npm install -D nodemon
Basic Server Structure:
Create server.js:
const express = require('express');
const mysql = require('mysql2/promise');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const axios = require('axios');
const cors = require('cors');
const helmet = require('helmet');
const app = express();
app.use(helmet());
app.use(cors());
app.use(express.json());
// Database connection
const db = mysql.createConnection({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME
});
// Proxmox API configuration
const proxmoxConfig = {
host: process.env.PROXMOX_HOST,
token: process.env.PROXMOX_TOKEN,
node: process.env.PROXMOX_NODE
};
app.listen(3000, () => {
console.log('Server running on port 3000');
});
Initialize Project:
mkdir proxmox-service
cd proxmox-service
npm init -y
# Install dependencies
npm install express mysql2 stripe axios bcryptjs jsonwebtoken cors helmet
npm install -D nodemon
Basic Server Structure:
Create server.js:
const express = require('express');
const mysql = require('mysql2/promise');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const axios = require('axios');
const cors = require('cors');
const helmet = require('helmet');
const app = express();
app.use(helmet());
app.use(cors());
app.use(express.json());
// Database connection
const db = mysql.createConnection({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME
});
// Proxmox API configuration
const proxmoxConfig = {
host: process.env.PROXMOX_HOST,
token: process.env.PROXMOX_TOKEN,
node: process.env.PROXMOX_NODE
};
app.listen(3000, () => {
console.log('Server running on port 3000');
});
Proxmox API Integration
VM Management Functions:
// proxmox.js - Proxmox API wrapper
const axios = require('axios');
class ProxmoxAPI {
constructor(host, token, node) {
this.host = host;
this.token = token;
this.node = node;
this.baseURL = `https://${host}:8006/api2/json`;
}
async createVM(vmid, config) {
const headers = {
'Authorization': `PVEAPIToken=${this.token}`,
'Content-Type': 'application/json'
};
try {
// Clone from template
const response = await axios.post(
`${this.baseURL}/nodes/${this.node}/qemu/${config.template}/clone`,
{
vmid: vmid,
name: config.name,
full: 1,
target: this.node
},
{ headers, httpsAgent: new https.Agent({ rejectUnauthorized: false }) }
);
// Configure VM resources
await this.configureVM(vmid, config);
return response.data;
} catch (error) {
throw new Error(`VM creation failed: ${error.message}`);
}
}
async configureVM(vmid, config) {
const headers = {
'Authorization': `PVEAPIToken=${this.token}`,
'Content-Type': 'application/json'
};
const vmConfig = {
memory: config.ram,
cores: config.cpu,
sockets: 1,
ciuser: config.username,
cipassword: config.password,
sshkeys: encodeURIComponent(config.sshKey),
ipconfig0: `ip=${config.ip}/24,gw=${config.gateway}`
};
return axios.put(
`${this.baseURL}/nodes/${this.node}/qemu/${vmid}/config`,
vmConfig,
{ headers, httpsAgent: new https.Agent({ rejectUnauthorized: false }) }
);
}
async startVM(vmid) {
const headers = { 'Authorization': `PVEAPIToken=${this.token}` };
return axios.post(
`${this.baseURL}/nodes/${this.node}/qemu/${vmid}/status/start`,
{},
{ headers, httpsAgent: new https.Agent({ rejectUnauthorized: false }) }
);
}
async getVMStatus(vmid) {
const headers = { 'Authorization': `PVEAPIToken=${this.token}` };
const response = await axios.get(
`${this.baseURL}/nodes/${this.node}/qemu/${vmid}/status/current`,
{ headers, httpsAgent: new https.Agent({ rejectUnauthorized: false }) }
);
return response.data.data;
}
}
module.exports = ProxmoxAPI;
VM Management Functions:
// proxmox.js - Proxmox API wrapper
const axios = require('axios');
class ProxmoxAPI {
constructor(host, token, node) {
this.host = host;
this.token = token;
this.node = node;
this.baseURL = `https://${host}:8006/api2/json`;
}
async createVM(vmid, config) {
const headers = {
'Authorization': `PVEAPIToken=${this.token}`,
'Content-Type': 'application/json'
};
try {
// Clone from template
const response = await axios.post(
`${this.baseURL}/nodes/${this.node}/qemu/${config.template}/clone`,
{
vmid: vmid,
name: config.name,
full: 1,
target: this.node
},
{ headers, httpsAgent: new https.Agent({ rejectUnauthorized: false }) }
);
// Configure VM resources
await this.configureVM(vmid, config);
return response.data;
} catch (error) {
throw new Error(`VM creation failed: ${error.message}`);
}
}
async configureVM(vmid, config) {
const headers = {
'Authorization': `PVEAPIToken=${this.token}`,
'Content-Type': 'application/json'
};
const vmConfig = {
memory: config.ram,
cores: config.cpu,
sockets: 1,
ciuser: config.username,
cipassword: config.password,
sshkeys: encodeURIComponent(config.sshKey),
ipconfig0: `ip=${config.ip}/24,gw=${config.gateway}`
};
return axios.put(
`${this.baseURL}/nodes/${this.node}/qemu/${vmid}/config`,
vmConfig,
{ headers, httpsAgent: new https.Agent({ rejectUnauthorized: false }) }
);
}
async startVM(vmid) {
const headers = { 'Authorization': `PVEAPIToken=${this.token}` };
return axios.post(
`${this.baseURL}/nodes/${this.node}/qemu/${vmid}/status/start`,
{},
{ headers, httpsAgent: new https.Agent({ rejectUnauthorized: false }) }
);
}
async getVMStatus(vmid) {
const headers = { 'Authorization': `PVEAPIToken=${this.token}` };
const response = await axios.get(
`${this.baseURL}/nodes/${this.node}/qemu/${vmid}/status/current`,
{ headers, httpsAgent: new https.Agent({ rejectUnauthorized: false }) }
);
return response.data.data;
}
}
module.exports = ProxmoxAPI;
Database Schema Design
MySQL Database Structure
Create Database and Tables:
-- Create database
CREATE DATABASE proxmox_service;
USE proxmox_service;
-- Users table
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
first_name VARCHAR(100),
last_name VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
status ENUM('active', 'suspended', 'cancelled') DEFAULT 'active'
);
-- Service plans table
CREATE TABLE service_plans (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
cpu_cores INT NOT NULL,
ram_mb INT NOT NULL,
disk_gb INT NOT NULL,
bandwidth_gb INT NOT NULL,
monthly_price DECIMAL(10,2) NOT NULL,
hourly_price DECIMAL(10,4) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Customer services table
CREATE TABLE customer_services (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
plan_id INT NOT NULL,
vm_id INT NOT NULL,
server_name VARCHAR(100),
ip_address VARCHAR(15),
status ENUM('pending', 'active', 'suspended', 'terminated') DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
expires_at TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (plan_id) REFERENCES service_plans(id)
);
-- Billing and payments table
CREATE TABLE billing (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
service_id INT NOT NULL,
amount DECIMAL(10,2) NOT NULL,
currency VARCHAR(3) DEFAULT 'USD',
billing_period_start DATE,
billing_period_end DATE,
status ENUM('pending', 'paid', 'failed', 'refunded') DEFAULT 'pending',
stripe_payment_id VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (service_id) REFERENCES customer_services(id)
);
-- Resource usage tracking
CREATE TABLE resource_usage (
id INT PRIMARY KEY AUTO_INCREMENT,
service_id INT NOT NULL,
cpu_usage_percent DECIMAL(5,2),
ram_usage_mb INT,
disk_usage_gb DECIMAL(10,2),
network_in_mb DECIMAL(10,2),
network_out_mb DECIMAL(10,2),
recorded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (service_id) REFERENCES customer_services(id)
);
Create Database and Tables:
-- Create database
CREATE DATABASE proxmox_service;
USE proxmox_service;
-- Users table
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
first_name VARCHAR(100),
last_name VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
status ENUM('active', 'suspended', 'cancelled') DEFAULT 'active'
);
-- Service plans table
CREATE TABLE service_plans (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
cpu_cores INT NOT NULL,
ram_mb INT NOT NULL,
disk_gb INT NOT NULL,
bandwidth_gb INT NOT NULL,
monthly_price DECIMAL(10,2) NOT NULL,
hourly_price DECIMAL(10,4) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Customer services table
CREATE TABLE customer_services (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
plan_id INT NOT NULL,
vm_id INT NOT NULL,
server_name VARCHAR(100),
ip_address VARCHAR(15),
status ENUM('pending', 'active', 'suspended', 'terminated') DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
expires_at TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (plan_id) REFERENCES service_plans(id)
);
-- Billing and payments table
CREATE TABLE billing (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
service_id INT NOT NULL,
amount DECIMAL(10,2) NOT NULL,
currency VARCHAR(3) DEFAULT 'USD',
billing_period_start DATE,
billing_period_end DATE,
status ENUM('pending', 'paid', 'failed', 'refunded') DEFAULT 'pending',
stripe_payment_id VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (service_id) REFERENCES customer_services(id)
);
-- Resource usage tracking
CREATE TABLE resource_usage (
id INT PRIMARY KEY AUTO_INCREMENT,
service_id INT NOT NULL,
cpu_usage_percent DECIMAL(5,2),
ram_usage_mb INT,
disk_usage_gb DECIMAL(10,2),
network_in_mb DECIMAL(10,2),
network_out_mb DECIMAL(10,2),
recorded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (service_id) REFERENCES customer_services(id)
);
Sample Service Plans Data
-- Insert sample service plans
INSERT INTO service_plans (name, cpu_cores, ram_mb, disk_gb, bandwidth_gb, monthly_price, hourly_price) VALUES
('Starter', 1, 1024, 25, 1000, 5.00, 0.0074),
('Basic', 2, 2048, 50, 2000, 10.00, 0.0148),
('Standard', 4, 4096, 80, 4000, 20.00, 0.0297),
('Professional', 6, 8192, 160, 6000, 40.00, 0.0595),
('Enterprise', 8, 16384, 320, 10000, 80.00, 0.119);
-- Insert sample service plans
INSERT INTO service_plans (name, cpu_cores, ram_mb, disk_gb, bandwidth_gb, monthly_price, hourly_price) VALUES
('Starter', 1, 1024, 25, 1000, 5.00, 0.0074),
('Basic', 2, 2048, 50, 2000, 10.00, 0.0148),
('Standard', 4, 4096, 80, 4000, 20.00, 0.0297),
('Professional', 6, 8192, 160, 6000, 40.00, 0.0595),
('Enterprise', 8, 16384, 320, 10000, 80.00, 0.119);
Payment Integration with Stripe
Stripe Configuration
Install and Setup Stripe:
Create Stripe account at stripe.com
Get API keys from dashboard
Configure webhooks for payment notifications
Set up products and pricing in Stripe dashboard
Payment Processing Functions:
// payments.js - Stripe payment handling
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
class PaymentProcessor {
async createPaymentIntent(amount, currency = 'usd', customerId) {
try {
const paymentIntent = await stripe.paymentIntents.create({
amount: Math.round(amount * 100), // Convert to cents
currency: currency,
customer: customerId,
automatic_payment_methods: {
enabled: true
},
metadata: {
service: 'vps_hosting'
}
});
return {
clientSecret: paymentIntent.client_secret,
paymentIntentId: paymentIntent.id
};
} catch (error) {
throw new Error(`Payment creation failed: ${error.message}`);
}
}
async createCustomer(email, name) {
try {
const customer = await stripe.customers.create({
email: email,
name: name
});
return customer.id;
} catch (error) {
throw new Error(`Customer creation failed: ${error.message}`);
}
}
async createSubscription(customerId, planId) {
try {
const subscription = await stripe.subscriptions.create({
customer: customerId,
items: [{
price: planId
}],
payment_behavior: 'default_incomplete',
expand: ['latest_invoice.payment_intent']
});
return {
subscriptionId: subscription.id,
clientSecret: subscription.latest_invoice.payment_intent.client_secret
};
} catch (error) {
throw new Error(`Subscription creation failed: ${error.message}`);
}
}
async handleWebhook(body, signature) {
try {
const event = stripe.webhooks.constructEvent(
body,
signature,
process.env.STRIPE_WEBHOOK_SECRET
);
switch (event.type) {
case 'payment_intent.succeeded':
await this.handlePaymentSuccess(event.data.object);
break;
case 'payment_intent.payment_failed':
await this.handlePaymentFailure(event.data.object);
break;
case 'invoice.payment_succeeded':
await this.handleSubscriptionPayment(event.data.object);
break;
}
return { received: true };
} catch (error) {
throw new Error(`Webhook handling failed: ${error.message}`);
}
}
}
module.exports = PaymentProcessor;
Install and Setup Stripe:
Payment Processing Functions:
// payments.js - Stripe payment handling
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
class PaymentProcessor {
async createPaymentIntent(amount, currency = 'usd', customerId) {
try {
const paymentIntent = await stripe.paymentIntents.create({
amount: Math.round(amount * 100), // Convert to cents
currency: currency,
customer: customerId,
automatic_payment_methods: {
enabled: true
},
metadata: {
service: 'vps_hosting'
}
});
return {
clientSecret: paymentIntent.client_secret,
paymentIntentId: paymentIntent.id
};
} catch (error) {
throw new Error(`Payment creation failed: ${error.message}`);
}
}
async createCustomer(email, name) {
try {
const customer = await stripe.customers.create({
email: email,
name: name
});
return customer.id;
} catch (error) {
throw new Error(`Customer creation failed: ${error.message}`);
}
}
async createSubscription(customerId, planId) {
try {
const subscription = await stripe.subscriptions.create({
customer: customerId,
items: [{
price: planId
}],
payment_behavior: 'default_incomplete',
expand: ['latest_invoice.payment_intent']
});
return {
subscriptionId: subscription.id,
clientSecret: subscription.latest_invoice.payment_intent.client_secret
};
} catch (error) {
throw new Error(`Subscription creation failed: ${error.message}`);
}
}
async handleWebhook(body, signature) {
try {
const event = stripe.webhooks.constructEvent(
body,
signature,
process.env.STRIPE_WEBHOOK_SECRET
);
switch (event.type) {
case 'payment_intent.succeeded':
await this.handlePaymentSuccess(event.data.object);
break;
case 'payment_intent.payment_failed':
await this.handlePaymentFailure(event.data.object);
break;
case 'invoice.payment_succeeded':
await this.handleSubscriptionPayment(event.data.object);
break;
}
return { received: true };
} catch (error) {
throw new Error(`Webhook handling failed: ${error.message}`);
}
}
}
module.exports = PaymentProcessor;
API Routes for Payment Processing
// routes/payments.js
const express = require('express');
const router = express.Router();
const PaymentProcessor = require('../payments');
const payments = new PaymentProcessor();
// Create payment intent
router.post('/create-payment-intent', async (req, res) => {
try {
const { planId, customerId } = req.body;
// Get plan details from database
const [plan] = await db.execute(
'SELECT * FROM service_plans WHERE id = ?', [planId]
);
if (!plan.length) {
return res.status(404).json({ error: 'Plan not found' });
}
const paymentIntent = await payments.createPaymentIntent(
plan[0].monthly_price, 'usd', customerId
);
res.json(paymentIntent);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Stripe webhook handler
router.post('/webhook', express.raw({type: 'application/json'}), async (req, res) => {
try {
const signature = req.headers['stripe-signature'];
await payments.handleWebhook(req.body, signature);
res.json({ received: true });
} catch (error) {
console.error('Webhook error:', error.message);
res.status(400).send(`Webhook Error: ${error.message}`);
}
});
module.exports = router;
// routes/payments.js
const express = require('express');
const router = express.Router();
const PaymentProcessor = require('../payments');
const payments = new PaymentProcessor();
// Create payment intent
router.post('/create-payment-intent', async (req, res) => {
try {
const { planId, customerId } = req.body;
// Get plan details from database
const [plan] = await db.execute(
'SELECT * FROM service_plans WHERE id = ?', [planId]
);
if (!plan.length) {
return res.status(404).json({ error: 'Plan not found' });
}
const paymentIntent = await payments.createPaymentIntent(
plan[0].monthly_price, 'usd', customerId
);
res.json(paymentIntent);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Stripe webhook handler
router.post('/webhook', express.raw({type: 'application/json'}), async (req, res) => {
try {
const signature = req.headers['stripe-signature'];
await payments.handleWebhook(req.body, signature);
res.json({ received: true });
} catch (error) {
console.error('Webhook error:', error.message);
res.status(400).send(`Webhook Error: ${error.message}`);
}
});
module.exports = router;
Frontend Development
React.js Frontend Setup
Initialize React Project:
npx create-react-app proxmox-frontend
cd proxmox-frontend
# Install additional dependencies
npm install axios react-router-dom @stripe/stripe-js @stripe/react-stripe-js
npm install react-bootstrap bootstrap
npm install react-chartjs-2 chart.js
Project Structure:
src/
├── components/
│ ├── Auth/
│ │ ├── Login.js
│ │ ├── Register.js
│ │ └── ProtectedRoute.js
│ ├── Dashboard/
│ │ ├── Dashboard.js
│ │ ├── ServiceList.js
│ │ └── UsageChart.js
│ ├── Plans/
│ │ ├── PlanSelection.js
│ │ └── PlanCard.js
│ └── Payment/
│ ├── PaymentForm.js
│ └── CheckoutProcess.js
├── services/
│ ├── api.js
│ └── auth.js
└── App.js
Initialize React Project:
npx create-react-app proxmox-frontend
cd proxmox-frontend
# Install additional dependencies
npm install axios react-router-dom @stripe/stripe-js @stripe/react-stripe-js
npm install react-bootstrap bootstrap
npm install react-chartjs-2 chart.js
Project Structure:
src/
├── components/
│ ├── Auth/
│ │ ├── Login.js
│ │ ├── Register.js
│ │ └── ProtectedRoute.js
│ ├── Dashboard/
│ │ ├── Dashboard.js
│ │ ├── ServiceList.js
│ │ └── UsageChart.js
│ ├── Plans/
│ │ ├── PlanSelection.js
│ │ └── PlanCard.js
│ └── Payment/
│ ├── PaymentForm.js
│ └── CheckoutProcess.js
├── services/
│ ├── api.js
│ └── auth.js
└── App.js
Service Plan Selection Component
// components/Plans/PlanSelection.js
import React, { useState, useEffect } from 'react';
import { Container, Row, Col, Card, Button } from 'react-bootstrap';
import axios from 'axios';
const PlanSelection = ({ onPlanSelect }) => {
const [plans, setPlans] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchPlans();
}, []);
const fetchPlans = async () => {
try {
const response = await axios.get('/api/plans');
setPlans(response.data);
} catch (error) {
console.error('Failed to fetch plans:', error);
} finally {
setLoading(false);
}
};
if (loading) return
return (
{plans.map(plan => (
))}
|
);
};
export default PlanSelection;
// components/Plans/PlanSelection.js
import React, { useState, useEffect } from 'react';
import { Container, Row, Col, Card, Button } from 'react-bootstrap';
import axios from 'axios';
const PlanSelection = ({ onPlanSelect }) => {
const [plans, setPlans] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchPlans();
}, []);
const fetchPlans = async () => {
try {
const response = await axios.get('/api/plans');
setPlans(response.data);
} catch (error) {
console.error('Failed to fetch plans:', error);
} finally {
setLoading(false);
}
};
if (loading) return
Loading plans...
;return (
Choose Your VPS Plan
{plans.map(plan => (
{plan.name}
${plan.monthly_price}/month
- CPU: {plan.cpu_cores} cores
- RAM: {plan.ram_mb / 1024}GB
- Storage: {plan.disk_gb}GB SSD
- Bandwidth: {plan.bandwidth_gb}GB
))}
);
};
export default PlanSelection;
Payment Form Component
// components/Payment/PaymentForm.js
import React, { useState } from 'react';
import { loadStripe } from '@stripe/stripe-js';
import {
Elements,
CardElement,
useStripe,
useElements
} from '@stripe/react-stripe-js';
import { Form, Button, Alert } from 'react-bootstrap';
import axios from 'axios';
const stripePromise = loadStripe(process.env.REACT_APP_STRIPE_PUBLISHABLE_KEY);
const PaymentForm = ({ plan, onSuccess }) => {
const stripe = useStripe();
const elements = useElements();
const [processing, setProcessing] = useState(false);
const [error, setError] = useState(null);
const handleSubmit = async (event) => {
event.preventDefault();
setProcessing(true);
setError(null);
if (!stripe || !elements) return;
try {
// Create payment intent
const { data } = await axios.post('/api/payments/create-payment-intent', {
planId: plan.id
});
// Confirm payment
const result = await stripe.confirmCardPayment(data.clientSecret, {
payment_method: {
card: elements.getElement(CardElement),
billing_details: {
name: 'Customer Name'
}
}
});
if (result.error) {
setError(result.error.message);
} else {
// Payment succeeded
onSuccess(result.paymentIntent);
}
} catch (err) {
setError(err.message);
} finally {
setProcessing(false);
}
};
return (
);
};
const PaymentWrapper = (props) => (
);
export default PaymentWrapper;
// components/Payment/PaymentForm.js
import React, { useState } from 'react';
import { loadStripe } from '@stripe/stripe-js';
import {
Elements,
CardElement,
useStripe,
useElements
} from '@stripe/react-stripe-js';
import { Form, Button, Alert } from 'react-bootstrap';
import axios from 'axios';
const stripePromise = loadStripe(process.env.REACT_APP_STRIPE_PUBLISHABLE_KEY);
const PaymentForm = ({ plan, onSuccess }) => {
const stripe = useStripe();
const elements = useElements();
const [processing, setProcessing] = useState(false);
const [error, setError] = useState(null);
const handleSubmit = async (event) => {
event.preventDefault();
setProcessing(true);
setError(null);
if (!stripe || !elements) return;
try {
// Create payment intent
const { data } = await axios.post('/api/payments/create-payment-intent', {
planId: plan.id
});
// Confirm payment
const result = await stripe.confirmCardPayment(data.clientSecret, {
payment_method: {
card: elements.getElement(CardElement),
billing_details: {
name: 'Customer Name'
}
}
});
if (result.error) {
setError(result.error.message);
} else {
// Payment succeeded
onSuccess(result.paymentIntent);
}
} catch (err) {
setError(err.message);
} finally {
setProcessing(false);
}
};
return (
);
};
const PaymentWrapper = (props) => (
);
export default PaymentWrapper;
Service Provisioning Automation
VM Provisioning Workflow
Service Creation Handler:
// services/provisioning.js
const ProxmoxAPI = require('../proxmox');
const db = require('../database');
class ProvisioningService {
constructor() {
this.proxmox = new ProxmoxAPI(
process.env.PROXMOX_HOST,
process.env.PROXMOX_TOKEN,
process.env.PROXMOX_NODE
);
}
async provisionVPS(userId, planId, paymentIntentId) {
try {
// Get next available VM ID
const vmid = await this.getNextVMID();
// Get plan details
const [plan] = await db.execute(
'SELECT * FROM service_plans WHERE id = ?', [planId]
);
// Allocate IP address
const ipAddress = await this.allocateIP();
// Generate credentials
const credentials = this.generateCredentials();
// Create VM configuration
const vmConfig = {
template: 9000, // Ubuntu template ID
name: `vps-${vmid}`,
cpu: plan[0].cpu_cores,
ram: plan[0].ram_mb,
disk: plan[0].disk_gb,
ip: ipAddress,
gateway: process.env.NETWORK_GATEWAY,
username: credentials.username,
password: credentials.password,
sshKey: credentials.sshKey
};
// Create VM in Proxmox
await this.proxmox.createVM(vmid, vmConfig);
// Start VM
await this.proxmox.startVM(vmid);
// Record in database
const [result] = await db.execute(
`INSERT INTO customer_services
(user_id, plan_id, vm_id, server_name, ip_address, status, expires_at)
VALUES (?, ?, ?, ?, ?, 'active', DATE_ADD(NOW(), INTERVAL 1 MONTH))`,
[userId, planId, vmid, vmConfig.name, ipAddress]
);
// Send welcome email with credentials
await this.sendWelcomeEmail(userId, {
ipAddress,
username: credentials.username,
password: credentials.password,
sshKey: credentials.privateKey
});
return {
serviceId: result.insertId,
vmid,
ipAddress,
credentials
};
} catch (error) {
console.error('Provisioning failed:', error);
throw new Error(`VPS provisioning failed: ${error.message}`);
}
}
async getNextVMID() {
// Get highest VM ID from database and increment
const [result] = await db.execute(
'SELECT MAX(vm_id) as max_id FROM customer_services'
);
return (result[0].max_id || 1000) + 1;
}
async allocateIP() {
// Simple IP allocation from pool
// In production, use IPAM system
const baseIP = '192.168.100.';
const [result] = await db.execute(
'SELECT ip_address FROM customer_services WHERE ip_address LIKE ?',
[`${baseIP}%`]
);
const usedIPs = result.map(row =>
parseInt(row.ip_address.split('.')[3])
);
for (let i = 10; i < 254; i++) {
if (!usedIPs.includes(i)) {
return `${baseIP}${i}`;
}
}
throw new Error('No available IP addresses');
}
generateCredentials() {
const crypto = require('crypto');
const { generateKeyPairSync } = require('crypto');
// Generate SSH key pair
const { publicKey, privateKey } = generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
});
return {
username: 'vpsuser',
password: crypto.randomBytes(12).toString('base64'),
sshKey: publicKey.replace(/\n/g, ''),
privateKey
};
}
}
module.exports = ProvisioningService;
Service Creation Handler:
// services/provisioning.js
const ProxmoxAPI = require('../proxmox');
const db = require('../database');
class ProvisioningService {
constructor() {
this.proxmox = new ProxmoxAPI(
process.env.PROXMOX_HOST,
process.env.PROXMOX_TOKEN,
process.env.PROXMOX_NODE
);
}
async provisionVPS(userId, planId, paymentIntentId) {
try {
// Get next available VM ID
const vmid = await this.getNextVMID();
// Get plan details
const [plan] = await db.execute(
'SELECT * FROM service_plans WHERE id = ?', [planId]
);
// Allocate IP address
const ipAddress = await this.allocateIP();
// Generate credentials
const credentials = this.generateCredentials();
// Create VM configuration
const vmConfig = {
template: 9000, // Ubuntu template ID
name: `vps-${vmid}`,
cpu: plan[0].cpu_cores,
ram: plan[0].ram_mb,
disk: plan[0].disk_gb,
ip: ipAddress,
gateway: process.env.NETWORK_GATEWAY,
username: credentials.username,
password: credentials.password,
sshKey: credentials.sshKey
};
// Create VM in Proxmox
await this.proxmox.createVM(vmid, vmConfig);
// Start VM
await this.proxmox.startVM(vmid);
// Record in database
const [result] = await db.execute(
`INSERT INTO customer_services
(user_id, plan_id, vm_id, server_name, ip_address, status, expires_at)
VALUES (?, ?, ?, ?, ?, 'active', DATE_ADD(NOW(), INTERVAL 1 MONTH))`,
[userId, planId, vmid, vmConfig.name, ipAddress]
);
// Send welcome email with credentials
await this.sendWelcomeEmail(userId, {
ipAddress,
username: credentials.username,
password: credentials.password,
sshKey: credentials.privateKey
});
return {
serviceId: result.insertId,
vmid,
ipAddress,
credentials
};
} catch (error) {
console.error('Provisioning failed:', error);
throw new Error(`VPS provisioning failed: ${error.message}`);
}
}
async getNextVMID() {
// Get highest VM ID from database and increment
const [result] = await db.execute(
'SELECT MAX(vm_id) as max_id FROM customer_services'
);
return (result[0].max_id || 1000) + 1;
}
async allocateIP() {
// Simple IP allocation from pool
// In production, use IPAM system
const baseIP = '192.168.100.';
const [result] = await db.execute(
'SELECT ip_address FROM customer_services WHERE ip_address LIKE ?',
[`${baseIP}%`]
);
const usedIPs = result.map(row =>
parseInt(row.ip_address.split('.')[3])
);
for (let i = 10; i < 254; i++) {
if (!usedIPs.includes(i)) {
return `${baseIP}${i}`;
}
}
throw new Error('No available IP addresses');
}
generateCredentials() {
const crypto = require('crypto');
const { generateKeyPairSync } = require('crypto');
// Generate SSH key pair
const { publicKey, privateKey } = generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: { type: 'spki', format: 'pem' },
privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
});
return {
username: 'vpsuser',
password: crypto.randomBytes(12).toString('base64'),
sshKey: publicKey.replace(/\n/g, ''),
privateKey
};
}
}
module.exports = ProvisioningService;
Customer Dashboard
Dashboard Component
// components/Dashboard/Dashboard.js
import React, { useState, useEffect } from 'react';
import { Container, Row, Col, Card, Table, Button, Badge } from 'react-bootstrap';
import axios from 'axios';
const Dashboard = () => {
const [services, setServices] = useState([]);
const [usage, setUsage] = useState({});
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchServices();
}, []);
const fetchServices = async () => {
try {
const token = localStorage.getItem('authToken');
const response = await axios.get('/api/services', {
headers: { Authorization: `Bearer ${token}` }
});
setServices(response.data);
} catch (error) {
console.error('Failed to fetch services:', error);
} finally {
setLoading(false);
}
};
const handleServiceAction = async (serviceId, action) => {
try {
const token = localStorage.getItem('authToken');
await axios.post(`/api/services/${serviceId}/${action}`, {}, {
headers: { Authorization: `Bearer ${token}` }
});
fetchServices(); // Refresh list
} catch (error) {
console.error(`Failed to ${action} service:`, error);
}
};
const getStatusBadge = (status) => {
const variants = {
active: 'success',
pending: 'warning',
suspended: 'danger',
terminated: 'secondary'
};
return{status} ;
};
if (loading) return
return (
{services.length === 0 ? (
) : (
)}
|
);
};
export default Dashboard;
// components/Dashboard/Dashboard.js
import React, { useState, useEffect } from 'react';
import { Container, Row, Col, Card, Table, Button, Badge } from 'react-bootstrap';
import axios from 'axios';
const Dashboard = () => {
const [services, setServices] = useState([]);
const [usage, setUsage] = useState({});
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchServices();
}, []);
const fetchServices = async () => {
try {
const token = localStorage.getItem('authToken');
const response = await axios.get('/api/services', {
headers: { Authorization: `Bearer ${token}` }
});
setServices(response.data);
} catch (error) {
console.error('Failed to fetch services:', error);
} finally {
setLoading(false);
}
};
const handleServiceAction = async (serviceId, action) => {
try {
const token = localStorage.getItem('authToken');
await axios.post(`/api/services/${serviceId}/${action}`, {}, {
headers: { Authorization: `Bearer ${token}` }
});
fetchServices(); // Refresh list
} catch (error) {
console.error(`Failed to ${action} service:`, error);
}
};
const getStatusBadge = (status) => {
const variants = {
active: 'success',
pending: 'warning',
suspended: 'danger',
terminated: 'secondary'
};
return
};
if (loading) return
Loading...
;return (
Your VPS Services
Active Services
{services.length === 0 ? (
No services found. Order your first VPS
) : (
| Server Name | Plan | IP Address | Status | Expires | Actions |
|---|---|---|---|---|---|
| {service.server_name} | {service.plan_name} | {service.ip_address} | {getStatusBadge(service.status)} | {new Date(service.expires_at).toLocaleDateString()} |
)}
);
};
export default Dashboard;
Monitoring and Resource Tracking
Resource Monitoring System
Monitoring Script:
#!/bin/bash
# monitor-resources.sh - Collect resource usage data
MYSQL_USER="your_user"
MYSQL_PASS="your_password"
MYSQL_DB="proxmox_service"
# Get list of active services
services=$(mysql -u$MYSQL_USER -p$MYSQL_PASS -D$MYSQL_DB -se \
"SELECT vm_id, id FROM customer_services WHERE status = 'active'")
while read vm_id service_id; do
# Get CPU usage
cpu_usage=$(qm monitor $vm_id --command "info cpustats" | grep "cpu_usage" | awk '{print $2}')
# Get memory usage
mem_usage=$(qm monitor $vm_id --command "info memory" | grep "actual" | awk '{print $2}')
# Get disk usage
disk_usage=$(qm monitor $vm_id --command "info block" | grep "size" | awk '{print $2}')
# Insert into database
mysql -u$MYSQL_USER -p$MYSQL_PASS -D$MYSQL_DB -e \
"INSERT INTO resource_usage (service_id, cpu_usage_percent, ram_usage_mb, disk_usage_gb) \
VALUES ($service_id, $cpu_usage, $mem_usage, $disk_usage)"
done <<< "$services"
# Set up cron job to run every 5 minutes
# */5 * * * * /path/to/monitor-resources.sh
Monitoring Script:
#!/bin/bash
# monitor-resources.sh - Collect resource usage data
MYSQL_USER="your_user"
MYSQL_PASS="your_password"
MYSQL_DB="proxmox_service"
# Get list of active services
services=$(mysql -u$MYSQL_USER -p$MYSQL_PASS -D$MYSQL_DB -se \
"SELECT vm_id, id FROM customer_services WHERE status = 'active'")
while read vm_id service_id; do
# Get CPU usage
cpu_usage=$(qm monitor $vm_id --command "info cpustats" | grep "cpu_usage" | awk '{print $2}')
# Get memory usage
mem_usage=$(qm monitor $vm_id --command "info memory" | grep "actual" | awk '{print $2}')
# Get disk usage
disk_usage=$(qm monitor $vm_id --command "info block" | grep "size" | awk '{print $2}')
# Insert into database
mysql -u$MYSQL_USER -p$MYSQL_PASS -D$MYSQL_DB -e \
"INSERT INTO resource_usage (service_id, cpu_usage_percent, ram_usage_mb, disk_usage_gb) \
VALUES ($service_id, $cpu_usage, $mem_usage, $disk_usage)"
done <<< "$services"
# Set up cron job to run every 5 minutes
# */5 * * * * /path/to/monitor-resources.sh
Billing Automation
Monthly Billing Script:
// scripts/monthly-billing.js
const mysql = require('mysql2/promise');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const nodemailer = require('nodemailer');
class BillingProcessor {
constructor() {
this.db = mysql.createConnection({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME
});
}
async processMonthlyBilling() {
try {
// Get services expiring in the next 3 days
const [services] = await this.db.execute(
`SELECT s.*, u.email, u.stripe_customer_id, p.monthly_price
FROM customer_services s
JOIN users u ON s.user_id = u.id
JOIN service_plans p ON s.plan_id = p.id
WHERE s.expires_at <= DATE_ADD(NOW(), INTERVAL 3 DAY)
AND s.status = 'active'`
);
for (const service of services) {
try {
// Create invoice in Stripe
const invoice = await stripe.invoices.create({
customer: service.stripe_customer_id,
collection_method: 'charge_automatically',
auto_advance: true,
metadata: {
service_id: service.id,
renewal: 'monthly'
}
});
// Add line item
await stripe.invoiceItems.create({
customer: service.stripe_customer_id,
invoice: invoice.id,
amount: Math.round(service.monthly_price * 100),
currency: 'usd',
description: `VPS Hosting - ${service.server_name} (Monthly)`
});
// Finalize and pay invoice
const finalizedInvoice = await stripe.invoices.finalizeInvoice(invoice.id);
const paidInvoice = await stripe.invoices.pay(finalizedInvoice.id);
if (paidInvoice.status === 'paid') {
// Extend service for another month
await this.db.execute(
'UPDATE customer_services SET expires_at = DATE_ADD(expires_at, INTERVAL 1 MONTH) WHERE id = ?',
[service.id]
);
// Record billing
await this.db.execute(
`INSERT INTO billing (user_id, service_id, amount, status, stripe_payment_id, billing_period_start, billing_period_end)
VALUES (?, ?, ?, 'paid', ?, NOW(), DATE_ADD(NOW(), INTERVAL 1 MONTH))`,
[service.user_id, service.id, service.monthly_price, paidInvoice.id]
);
console.log(`Successfully renewed service ${service.id}`);
}
} catch (error) {
console.error(`Failed to renew service ${service.id}:`, error);
// Suspend service if payment fails
await this.db.execute(
'UPDATE customer_services SET status = "suspended" WHERE id = ?',
[service.id]
);
}
}
} catch (error) {
console.error('Monthly billing process failed:', error);
}
}
}
// Run billing process
const billing = new BillingProcessor();
billing.processMonthlyBilling().then(() => {
console.log('Monthly billing process completed');
process.exit(0);
}).catch(error => {
console.error('Billing process failed:', error);
process.exit(1);
});
Monthly Billing Script:
// scripts/monthly-billing.js
const mysql = require('mysql2/promise');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const nodemailer = require('nodemailer');
class BillingProcessor {
constructor() {
this.db = mysql.createConnection({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME
});
}
async processMonthlyBilling() {
try {
// Get services expiring in the next 3 days
const [services] = await this.db.execute(
`SELECT s.*, u.email, u.stripe_customer_id, p.monthly_price
FROM customer_services s
JOIN users u ON s.user_id = u.id
JOIN service_plans p ON s.plan_id = p.id
WHERE s.expires_at <= DATE_ADD(NOW(), INTERVAL 3 DAY)
AND s.status = 'active'`
);
for (const service of services) {
try {
// Create invoice in Stripe
const invoice = await stripe.invoices.create({
customer: service.stripe_customer_id,
collection_method: 'charge_automatically',
auto_advance: true,
metadata: {
service_id: service.id,
renewal: 'monthly'
}
});
// Add line item
await stripe.invoiceItems.create({
customer: service.stripe_customer_id,
invoice: invoice.id,
amount: Math.round(service.monthly_price * 100),
currency: 'usd',
description: `VPS Hosting - ${service.server_name} (Monthly)`
});
// Finalize and pay invoice
const finalizedInvoice = await stripe.invoices.finalizeInvoice(invoice.id);
const paidInvoice = await stripe.invoices.pay(finalizedInvoice.id);
if (paidInvoice.status === 'paid') {
// Extend service for another month
await this.db.execute(
'UPDATE customer_services SET expires_at = DATE_ADD(expires_at, INTERVAL 1 MONTH) WHERE id = ?',
[service.id]
);
// Record billing
await this.db.execute(
`INSERT INTO billing (user_id, service_id, amount, status, stripe_payment_id, billing_period_start, billing_period_end)
VALUES (?, ?, ?, 'paid', ?, NOW(), DATE_ADD(NOW(), INTERVAL 1 MONTH))`,
[service.user_id, service.id, service.monthly_price, paidInvoice.id]
);
console.log(`Successfully renewed service ${service.id}`);
}
} catch (error) {
console.error(`Failed to renew service ${service.id}:`, error);
// Suspend service if payment fails
await this.db.execute(
'UPDATE customer_services SET status = "suspended" WHERE id = ?',
[service.id]
);
}
}
} catch (error) {
console.error('Monthly billing process failed:', error);
}
}
}
// Run billing process
const billing = new BillingProcessor();
billing.processMonthlyBilling().then(() => {
console.log('Monthly billing process completed');
process.exit(0);
}).catch(error => {
console.error('Billing process failed:', error);
process.exit(1);
});
Security and Best Practices
Security Hardening
Proxmox Security:
Firewall Configuration: Restrict access to management interface
SSL Certificates: Use valid SSL certificates for web interface
User Permissions: Principle of least privilege for API users
Regular Updates: Keep Proxmox and underlying OS updated
Network Isolation: Separate management and customer networks
Application Security:
Input Validation: Validate all user inputs and API parameters
SQL Injection Prevention: Use parameterized queries
Authentication: JWT tokens with proper expiration
Rate Limiting: Prevent API abuse and brute force attacks
HTTPS Enforcement: All communications over encrypted connections
Customer VM Security:
Default Passwords: Generate strong random passwords
SSH Key Authentication: Disable password authentication
Firewall Rules: Default deny with specific allow rules
Regular Backups: Automated backup scheduling
Resource Limits: Prevent resource exhaustion attacks
Proxmox Security:
Application Security:
Customer VM Security:
Backup and Disaster Recovery
Automated Backup System:
#!/bin/bash
# backup-vms.sh - Automated VM backup script
BACKUP_STORAGE="backups"
RETENTION_DAYS=30
# Get list of customer VMs
vms=$(mysql -u$MYSQL_USER -p$MYSQL_PASS -D$MYSQL_DB -se \
"SELECT vm_id FROM customer_services WHERE status = 'active'")
for vm_id in $vms; do
echo "Backing up VM $vm_id"
# Create backup
vzdump $vm_id --storage $BACKUP_STORAGE --compress gzip --remove 0
if [ $? -eq 0 ]; then
echo "Backup successful for VM $vm_id"
else
echo "Backup failed for VM $vm_id"
# Send alert email
echo "Backup failed for VM $vm_id" | mail -s "Backup Alert" admin@example.com
fi
done
# Cleanup old backups
find /var/lib/vz/backups -name "*.gz" -mtime +$RETENTION_DAYS -delete
# Schedule daily backups
# 0 2 * * * /path/to/backup-vms.sh
Automated Backup System:
#!/bin/bash
# backup-vms.sh - Automated VM backup script
BACKUP_STORAGE="backups"
RETENTION_DAYS=30
# Get list of customer VMs
vms=$(mysql -u$MYSQL_USER -p$MYSQL_PASS -D$MYSQL_DB -se \
"SELECT vm_id FROM customer_services WHERE status = 'active'")
for vm_id in $vms; do
echo "Backing up VM $vm_id"
# Create backup
vzdump $vm_id --storage $BACKUP_STORAGE --compress gzip --remove 0
if [ $? -eq 0 ]; then
echo "Backup successful for VM $vm_id"
else
echo "Backup failed for VM $vm_id"
# Send alert email
echo "Backup failed for VM $vm_id" | mail -s "Backup Alert" admin@example.com
fi
done
# Cleanup old backups
find /var/lib/vz/backups -name "*.gz" -mtime +$RETENTION_DAYS -delete
# Schedule daily backups
# 0 2 * * * /path/to/backup-vms.sh
Deployment and Production Setup
Production Environment Setup
Server Requirements:
Proxmox Cluster: Multiple nodes for high availability
Load Balancer: Nginx or HAProxy for frontend distribution
Database Server: MySQL/PostgreSQL with replication
Application Servers: Multiple Node.js instances behind load balancer
Monitoring: Prometheus/Grafana for system monitoring
Docker Deployment:
# Dockerfile for Node.js backend
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
USER node
CMD ["node", "server.js"]
Docker Compose Configuration:
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DB_HOST=mysql
- STRIPE_SECRET_KEY=${STRIPE_SECRET_KEY}
depends_on:
- mysql
- redis
mysql:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
- MYSQL_DATABASE=proxmox_service
volumes:
- mysql_data:/var/lib/mysql
redis:
image: redis:alpine
volumes:
- redis_data:/data
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
volumes:
mysql_data:
redis_data:
Server Requirements:
Docker Deployment:
# Dockerfile for Node.js backend
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
USER node
CMD ["node", "server.js"]
Docker Compose Configuration:
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DB_HOST=mysql
- STRIPE_SECRET_KEY=${STRIPE_SECRET_KEY}
depends_on:
- mysql
- redis
mysql:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
- MYSQL_DATABASE=proxmox_service
volumes:
- mysql_data:/var/lib/mysql
redis:
image: redis:alpine
volumes:
- redis_data:/data
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
volumes:
mysql_data:
redis_data:
Monitoring and Alerting
System Monitoring Setup:
Server Health: CPU, RAM, disk usage monitoring
Application Metrics: API response times, error rates
Business Metrics: New signups, revenue, churn rate
Customer VM Health: Resource usage, uptime monitoring
Payment Processing: Failed payments, refund rates
Alert Configuration:
High Priority: Service outages, payment failures
Medium Priority: Resource threshold breaches
Low Priority: Backup completion, maintenance reminders
Notification Channels: Email, Slack, SMS for critical alerts
System Monitoring Setup:
Alert Configuration:
Scaling and Growth Considerations
Horizontal Scaling
Proxmox Clustering: Add nodes to distribute VM load
Geographic Distribution: Multiple data centers for global presence
API Rate Limiting: Prevent abuse and ensure fair usage
Database Optimization: Read replicas, connection pooling
CDN Integration: Static asset delivery optimization
Feature Expansion
Additional Services: Dedicated servers, managed databases
Advanced Networking: Private networks, load balancers
Backup Services: Automated backups as premium feature
Monitoring Tools: Customer-facing monitoring dashboards
API Access: Third-party integrations and automation
Feature Expansion
Conclusion
Building a commercial VPS hosting service with Proxmox requires careful planning, robust automation, and attention to security and scalability. This comprehensive guide provides the foundation for creating a competitive infrastructure-as-a-service platform that can grow from a small startup to an enterprise-level hosting provider.
The key to success lies in automation, customer experience, and operational excellence. By implementing proper monitoring, billing, and provisioning systems, you can create a service that competes with established hosting providers while maintaining the flexibility and cost advantages of open-source virtualization.
Remember to start small, validate your market, and continuously iterate based on customer feedback. The hosting industry is competitive, but there's always room for providers who focus on specific niches, exceptional service, or innovative features.
As you grow, consider expanding into related services, geographic markets, and enterprise solutions to build a sustainable and profitable hosting business.
The key to success lies in automation, customer experience, and operational excellence. By implementing proper monitoring, billing, and provisioning systems, you can create a service that competes with established hosting providers while maintaining the flexibility and cost advantages of open-source virtualization.
Remember to start small, validate your market, and continuously iterate based on customer feedback. The hosting industry is competitive, but there's always room for providers who focus on specific niches, exceptional service, or innovative features.
As you grow, consider expanding into related services, geographic markets, and enterprise solutions to build a sustainable and profitable hosting business.