Give users a clean, secure interface to manage only their own virtual machines and containers — without exposing the Proxmox VE interface or API credentials.
Features · Screenshots · Architecture · Quick Start · Networking · Authentication · Deployment · Versions · Security · AI use · Full guide
PVE Panel turns a Proxmox VE environment into a lightweight self-service platform.
Users receive a dedicated portal where they can manage assigned servers, open consoles, view usage data, work with snapshots, and — when enabled — provision new systems from administrator-approved templates.
Administrators retain control over templates, quotas, ownership, networking, authentication, and lifecycle operations.
The browser never communicates directly with Proxmox VE and never receives the Proxmox API token.
┌──────────────────────┐
│ Customers │
└──────────┬───────────┘
│ HTTPS :3000
▼
┌──────────────────────────────────────────────┐
│ PVE Panel │
│ │
│ Customer Portal Admin Interface │
│ :3000 :3001 │
│ localhost by default │
│ │
│ Fastify · SQLite · Jobs · Auth · Audit │
└──────────────────────┬───────────────────────┘
│ Proxmox API Token
▼
┌──────────────────┐
│ Proxmox VE │
│ VM · LXC · SDN │
└──────────────────┘
The customer and administration portals run as separate web servers in the same process. They use separate session cookies and signing keys, and the customer-facing server does not expose administrator routes.
📘 This README is the overview. Every setup step, permission, firewall rule and troubleshooting tip is in the administrator guide.
PVE Panel was built with substantial help from an AI assistant (Claude by Anthropic). To be transparent about what that means:
Created with AI assistance
- most of the source code (backend, customer and admin portals, scripts)
- the Docker, Compose and GitHub Actions setup
- the documentation, including this README's content and the administrator guide
- automated tests during development, largely against simulated Proxmox, guest-agent, SMTP and WireGuard environments and a certified test OpenID provider
- the screenshots, taken from such a test environment with sample data
Done by the maintainer
- defining the requirements and deciding on features and design
- running and testing the panel against a real Proxmox VE environment and reporting problems, which were then fixed
- publishing, versioning and operating the project
What this means for you
- Not every feature has been tested in every real-world combination — especially provider-specific setups (OIDC providers, mail servers, NAS models, Windows editions).
- AI-generated code can contain mistakes, including security-relevant ones. Review the code and the security model before using PVE Panel in production, and validate customer isolation yourself (see Recommended validation).
- Issues and pull requests are reviewed and handled by the maintainer.
- Start, shut down, restart and force-stop assigned systems
- Manage both QEMU VMs and LXC containers
- Live network map of the customer's private network, servers and VPN
- View live status and guest information, with Windows / Linux recognition
- Display CPU, memory, disk and network usage graphs
- Create, restore and delete snapshots (optionally including RAM)
- Launch an integrated browser console
- Create new servers from administrator-approved templates
- Reinstall self-created servers from a template (same name, size and network address)
- Resize self-created servers within their plan (CPU, memory, disk)
- Delete self-created servers
- View provisioning progress and actionable failure messages
- Manage personal VPN devices
- Connect servers to a personal Tailscale network
- Manage their own two-factor authentication
- See which panel version they're using
- Create and manage customer accounts
- Assign existing Proxmox VMs or containers to users
- Configure provisioning quotas and limits
- Publish selected templates
- Start, stop, restart and delete customer-owned servers
- Review provisioning jobs and failures
- Reset passwords and 2FA, require 2FA per user, link/unlink single sign-on
- Review the activity/audit log
- Monitor WireGuard and Tailscale usage
- Delete customers together with their owned infrastructure
- See the running version and whether an update is available
- Configure email (SMTP) in the admin portal and invite users by email
PVE Panel supports automated provisioning for both Linux and Windows workloads.
Linux
- Cloud-init
- Hostname
- User credentials
- SSH keys
- CPU and memory sizing
- Disk expansion
- DHCP networking
- QEMU Guest Agent integration
Windows
- Sysprep-based templates
- Automated OOBE handling
- Administrator password replacement (verified inside Windows)
- Computer rename
- QEMU Guest Agent configuration
- DHCP cleanup and validation
- Windows setup-state detection
- Automatic reboot and readiness checks
| Customer dashboard | Server details |
|---|---|
![]() |
![]() |
| Admin portal | Provisioning |
|---|---|
![]() |
![]() |
| Sign-in | Version & updates |
|---|---|
![]() |
![]() |
PVE Panel deliberately separates the customer-facing application from Proxmox VE.
Customer Browser
│
│ HTTPS
▼
┌───────────────┐
│ Customer UI │
│ Port 3000 │
└───────┬───────┘
│
▼
┌───────────────────────────────┐
│ PVE Panel │
│ │
│ Authentication │
│ Ownership checks │
│ Provisioning jobs │
│ Snapshot / power operations │
│ Console proxy │
│ Audit logging │
│ SDN / VPN management │
└───────────────┬───────────────┘
│
│ API token
▼
┌───────────────┐
│ Proxmox VE │
└───────────────┘
Every server-specific request is checked against the internal ownership database before PVE Panel contacts Proxmox.
A resource that does not belong to the signed-in customer is returned as 404, just like a resource that does not exist.
- Proxmox VE 8 or 9
- Node.js 22.13+ for a native installation
- or Docker / Docker Compose (also Portainer or a NAS Docker app)
- A dedicated Proxmox API user/token
- Optional: Proxmox SDN for isolated customer networks
- Optional: QEMU Guest Agent for richer VM integration
Create a dedicated service account and a restricted role:
pveum user add panel@pve --comment "PVE Panel"
# Proxmox VE 9
pveum role add PanelCustomer --privs \
"VM.Audit VM.PowerMgmt VM.Console VM.Snapshot VM.Snapshot.Rollback VM.GuestAgent.Audit"
# On PVE 8 use VM.Monitor instead of VM.GuestAgent.Audit.
pveum pool add customers
pveum acl modify /pool/customers --users panel@pve --roles PanelCustomer
# Token inherits the user's permissions.
pveum user token add panel@pve panel --privsep 0Store the generated token secret securely.
Add customer-managed systems to the dedicated pool:
pveum pool modify customers --vms 101,102Systems outside the configured customer pool remain outside the panel's effective permission scope.
Only add these permissions when customers should be allowed to create servers:
pveum role modify PanelCustomer --append 1 \
--privs "VM.Allocate VM.Clone VM.Config.CPU VM.Config.Memory VM.Config.Disk VM.Config.Cloudinit VM.Config.Options VM.Config.Network Datastore.AllocateSpace Datastore.Audit"
pveum role modify PanelCustomer --append 1 \
--privs "VM.GuestAgent.Unrestricted"
pveum pool add templates
pveum pool modify templates --vms 9000,9001
pveum acl modify /pool/templates --users panel@pve --roles PanelCustomer
pveum acl modify /storage/VMStorage \
--users panel@pve --roles PanelCustomerNewly provisioned servers are automatically added to the pool defined by PVE_POOL.
git clone https://github.com/sebastianflint/pve-panel.git
cd pve-panel
npm install
cp example.env .envConfigure at minimum:
PVE_URL=https://pve.example.com:8006
PVE_TOKEN_ID=panel@pve!panel
PVE_TOKEN_SECRET=your-token-secret
JWT_SECRET=replace-with-a-long-random-secretCreate the first administrator:
npm run user:create -- admin@example.com 'a-long-password' --adminStart the application:
npm startDevelopment mode:
npm run devDefault endpoints:
| Interface | Address | Exposure |
|---|---|---|
| Customer portal | http://host:3000 |
Customer-facing |
| Admin portal | http://127.0.0.1:3001 |
Localhost only by default |
For local HTTP testing, set:
COOKIE_SECURE=false⚠️ Set
COOKIE_SECURE=trueagain for production and serve the customer portal over HTTPS.
cp example.env .env
docker compose up -d --buildCreate the first administrator inside the container:
docker compose exec panel \
npm run user:create -- admin@example.com 'a-long-password' --adminOr, without a terminal, let the panel create it on first start:
INITIAL_ADMIN_EMAIL=admin@example.com
INITIAL_ADMIN_PASSWORD=replace-meRemove INITIAL_ADMIN_PASSWORD after the initial account has been created.
Published container image:
ghcr.io/sebastianflint/pve-panel
The GitHub workflow builds amd64 and arm64 images from main and release tags, with an SBOM and signed build provenance.
For a server or NAS deployment, use one of:
| File | For |
|---|---|
deploy/docker-compose.yml |
Docker Compose, UGREEN Docker app (reads .env) |
deploy/docker-compose.portainer.yml |
Portainer stacks (reads the stack's variables) |
The image defaults to ghcr.io/sebastianflint/pve-panel:latest. To pin a release:
PANEL_IMAGE=ghcr.io/sebastianflint/pve-panel:1.1Then deploy:
docker compose up -dPaste deploy/docker-compose.portainer.yml into Stacks → Add stack → Web editor and enter the settings under Environment variables (or load your .env).
Portainer saves stack variables to
stack.env. The Portainer file passes them into the container withenv_file: stack.env; withenv_file: .envthe container would start without settings.
The repository includes a Caddyfile.
Set:
PANEL_DOMAIN=panel.example.com
PANEL_PUBLIC_URL=https://panel.example.com
COOKIE_SECURE=trueThen start the HTTPS profile:
docker compose --profile https up -d --buildCaddy handles certificate issuance and WebSocket proxying for the browser console.
PVE Panel can also run as a Docker project on compatible UGREEN NAS systems.
A typical deployment directory is:
/volume1/docker/pve-panel
For a prebuilt-image deployment, place the deployment docker-compose.yml and .env in the directory and create a Docker Project in UGOS (Docker → Project → Create).
Add the NAS's IP to the Proxmox management IPSet so the panel can reach the Proxmox API.
The running version is stamped into the image by the release workflow — nothing to edit by hand.
- Admin portal: version badge in the top bar (a dot = update available) and an About tab with version, commit, build date, uptime and update instructions
- Customer portal: the version number in the sidebar and on the Account page (
SHOW_VERSION_TO_CUSTOMERS=falsehides it) - Update check: asks GitHub for the latest release, at most every 6 hours (
UPDATE_CHECK=falseturns it off)
npm version patch # 1.1.0 -> 1.1.1 (fixes)
npm version minor # 1.1.1 -> 1.2.0 (new features)
npm version major # 1.2.0 -> 2.0.0 (breaking changes)
git push --follow-tagsnpm version raises the version in package.json, commits it and creates the tag. The workflow then:
- builds the image as
1.2.0,1.2,1and moveslatestto it - creates a GitHub Release with generated release notes
- refuses to build if the tag and
package.jsondisagree
Pushes to main without a tag build development versions such as 1.2.0-dev.a1b2c3d and publish them as edge — they never replace latest.
| Image tag | Contains | Use it for |
|---|---|---|
latest |
the newest release | normal deployments |
1.4 / 1 |
newest release of that line | updates within a version line only |
1.4.0 |
exactly that release | fully pinned deployments |
edge |
the newest development build from main |
testing only |
| Deployment | Update |
|---|---|
| Portainer | Stacks → your stack → Update the stack with Re-pull image and redeploy |
| UGREEN Docker app | Project → your project → redeploy |
| Command line | docker compose pull && docker compose up -d |
Data stays in the volume; database changes are applied automatically at start.
PVE Panel does not rely only on Proxmox permissions to determine which systems a customer can see.
Each assigned resource is tracked in the internal ownership database.
Before any per-server operation is sent to Proxmox, PVE Panel validates ownership.
Request
│
▼
Authenticated customer
│
▼
Ownership lookup
│
├── not owner ──► 404
│
└── owner
│
▼
Proxmox API
This applies to:
- status
- power operations
- snapshots
- usage data
- consoles
- deletion
- remote-access operations
PVE Panel can automatically create one private Proxmox SDN network for each customer.
Enable it with:
CUSTOMER_NETWORKS=trueExample:
Customer 1
│
└── cu0001 ── 10.100.1.0/24 ──┐
│
Customer 2 ├── NAT ──► Internet
│ │
└── cu0002 ── 10.100.2.0/24 ──┘
Each network receives:
- its own SDN VNet, with the customer's name as alias (e.g.
lena (example.com)) - dedicated
/24subnet - gateway
- DHCP
- NAT
- Proxmox firewall rules
- IP filtering
- MAC filtering
- inter-customer isolation
By default, customer workloads can access the public internet while private/internal networks are blocked.
Typical blocked ranges include:
10.0.0.0/8
172.16.0.0/12
192.168.0.0/16
100.64.0.0/10
169.254.0.0/16
This prevents customer workloads from using the Proxmox host as a route into internal networks.
pveum role add PanelNetwork --privs "SDN.Allocate SDN.Audit SDN.Use"
pveum acl modify /sdn --users panel@pve --roles PanelNetworkInstall dnsmasq on the Proxmox host:
apt install dnsmasq
systemctl disable --now dnsmasqSet these up before enabling the datacenter firewall:
# Keep the Proxmox UI/API reachable for your admin network and the panel host
pvesh create /cluster/firewall/ipset --name management
pvesh create /cluster/firewall/ipset/management --cidr 192.168.50.0/24
# DHCP for customer networks — no source filter (requests come from 0.0.0.0)
pvesh create /cluster/firewall/rules --type in --action ACCEPT --proto udp --dport 67 --enable 1
# Ping to the host (blocked by default)
pvesh create /cluster/firewall/rules --type in --action ACCEPT --macro Ping --enable 1The panel creates and maintains the necessary customer SDN objects when provisioning is enabled.
Important
Customer isolation depends on the Proxmox datacenter firewall being enabled. Validate management access before enabling it remotely. Locked out? From the console:
pvesh set /cluster/firewall/options --enable 0.
PVE Panel supports traditional username/password authentication with secure server-side controls and rate limiting. Password sign-in can be switched off per portal once SSO works.
TOTP-based two-factor authentication is built in.
Compatible apps include:
- 1Password
- Microsoft Authenticator
- Google Authenticator
- Authy
- other RFC 6238-compatible authenticators
Features include:
- QR-code enrollment
- recovery codes
- forced 2FA per user
- administrator reset
- one-time use protection
- encrypted TOTP secrets
Locked out yourself? npm run user:reset-2fa -- admin@example.com
PVE Panel supports OpenID Connect for both the customer and administrator portals.
Designed for standards-compliant providers such as:
- Microsoft Entra ID
- Keycloak
- Authentik
- Google Workspace
- Okta
The implementation is tested against a certified OpenID provider and uses:
- Authorization Code Flow
- PKCE (
S256) statenonce- discovery metadata
- ID-token validation (via the OpenID-certified
openid-client) - Pushed Authorization Requests (PAR) when the provider supports them
- optional verified-domain restrictions
- issuer +
subaccount binding
Example configuration:
OIDC_ENABLED=true
OIDC_ISSUER=https://id.example.com/realms/example
OIDC_CLIENT_ID=pve-panel
OIDC_CLIENT_SECRET=replace-me
PANEL_PUBLIC_URL=https://panel.example.com
ADMIN_PUBLIC_URL=http://localhost:3001Callback URLs:
https://panel.example.com/api/auth/oidc/callback
http://localhost:3001/api/auth/oidc/callback
Only the panel needs to reach the provider (outbound HTTPS); the provider never connects to the panel.
Microsoft Entra ID doesn't send
email_verified: setOIDC_REQUIRE_VERIFIED_EMAIL=falsetogether withOIDC_ALLOWED_DOMAINS. Provider notes are in the guide.
Once SSO is validated, local password login can optionally be disabled independently for each portal.
Email is configured in the admin portal → Settings, not in .env:
- SMTP server, port and encryption (TLS, STARTTLS or none for an internal relay)
- username and password — the password is stored encrypted and never shown again
- sender name and address
- the panel address used in links
- a Send test email button to verify everything end to end
Once email works, Add a customer offers Send an invitation email. Instead of a password chosen by the admin, the user receives:
- the panel address and their sign-in email
- a personal link to set their own password
- a note when 2FA is required or single sign-on is used
Admin adds user ──► Invitation email ──► User opens link ──► Sets password ──► Signs in
(single-use, 3 days)
Invitation links:
- work once and expire after 3 days
- are stored only as a SHA-256 hash
- carry the token after
#, so it never reaches server logs orRefererheaders - are replaced by Resend invitation (the old link stops working)
The customer list shows Invitation pending or Invitation expired until the user has set a password.
Changing
JWT_SECRETmakes the stored SMTP password unreadable — enter it again in Settings afterwards.
Windows systems can be deployed without cloud-init.
The repository includes:
docs/windows/unattend.xml
Recommended template workflow:
- Install Windows.
- Install VirtIO drivers.
- Install the QEMU Guest Agent.
- Enable the QEMU Guest Agent in Proxmox.
- Configure the desired base applications and settings.
- Ensure the network adapter uses DHCP.
- Copy and customize
docs/windows/unattend.xml. - Run Sysprep.
- Shut down the VM.
- Convert it to a Proxmox template without booting it again.
Example:
copy unattend.xml C:\Windows\System32\Sysprep\unattend.xml
C:\Windows\System32\Sysprep\sysprep.exe ^
/generalize ^
/oobe ^
/shutdown ^
/unattend:C:\Windows\System32\Sysprep\unattend.xmlDuring provisioning PVE Panel can:
- wait for Windows setup to complete
- set the final Administrator password
- verify the password
- rename the computer
- force DHCP on network adapters
- validate that an address was received
- reboot the VM
- report provisioning failures to the customer
Linux provisioning uses cloud-init.
A template should contain:
- a cloud-init-compatible image
- a cloud-init drive
- DHCP networking
- optionally
qemu-guest-agent
PVE Panel applies:
- hostname
- username/password
- SSH keys
- CPU
- memory
- network configuration
- disk expansion
PVE Panel supports multiple ways to reach customer systems without exposing Proxmox itself.
Customers can launch one-time console sessions directly from the panel.
Console sessions are:
- bound to the requesting user
- single-use
- short-lived
- proxied through PVE Panel
An optional central WireGuard gateway can provide remote access into each customer's private network.
Laptop / Phone
│
│ WireGuard
▼
Public IP : UDP 51820
│
▼
┌──────────────────┐
│ WireGuard Gateway│
└─────────┬────────┘
│
├── Customer 1 VPN clients → Customer 1 VNet only
└── Customer 2 VPN clients → Customer 2 VNet only
Customers can:
- add VPN devices
- download configuration files
- scan QR codes
- see connection state
- remove devices
Private keys are shown once and are not retained by the panel.
The gateway is prepared once with docs/vpn/setup-gateway.sh — see the guide for the routing and port-forward steps.
When enabled, customers can connect individual servers to their own Tailscale account.
Supported modes:
- Single server — expose only that server through the customer's tailnet
- Private-network gateway — advertise the customer's private subnet through a Linux VM
PVE Panel installs and configures Tailscale through the QEMU Guest Agent.
The customer's Tailscale account itself is never managed by the panel.
PVE Panel was designed so that giving customers self-service access does not mean giving them Proxmox access.
Key controls include:
- Dedicated Proxmox service account
- Restrictive custom Proxmox roles
- Resource pool scoping
- Application-level ownership validation
- Separate customer and admin HTTP servers
- Separate customer/admin sessions
- Admin interface bound to localhost by default
httpOnlysession cookiesSameSite=Strict- Rate limiting
- Short-lived, user-bound console tickets
- User-bound task tracking
- Audit logging
- Optional TOTP 2FA
- OIDC with PKCE and token validation
- Customer-specific SDN isolation
- VM firewall enforcement (incoming and outgoing)
- IP and MAC filtering
- Restricted VPN routing
- Proxmox API token kept server-side
- No secrets in the container image
For production deployments, use a trusted Proxmox certificate or provide the Proxmox CA:
PVE_CA_FILE=/app/certs/pve-root-ca.pemAvoid:
PVE_VERIFY_TLS=falseoutside development environments.
The administrator portal listens on localhost by default:
127.0.0.1:3001
Access it securely through SSH:
ssh -L 3001:127.0.0.1:3001 you@panel-hostThen browse to:
http://localhost:3001
If the admin interface is exposed on another interface, protect it with network-level access controls.
The customer portal can be placed behind an existing reverse proxy.
WebSocket upgrades are required for the integrated console.
Example NGINX configuration:
server {
listen 443 ssl http2;
server_name panel.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 1h;
}
}The application database lives in the persistent Docker volume.
Create a consistent backup:
docker compose exec panel npm run db:backupCopy backups from the container:
docker compose cp panel:/app/data/backups ./backupsCreate an administrator:
npm run user:create -- admin@example.com 'a-long-password' --adminCreate a customer (optionally requiring 2FA):
npm run user:create -- customer@example.com 'another-long-password' --require-2faAssign an existing VM or container:
npm run vm:assign -- 101 customer@example.com "Web server"Reset 2FA:
npm run user:reset-2fa -- admin@example.comBackup the database:
npm run db:backupChange the product name through .env:
PANEL_NAME=PVE PanelThe configured name is used on:
- sign-in pages
- navigation
- browser titles
- customer portal
- administrator portal
- the authenticator app entry for 2FA
Windows and Linux servers are shown with neutral built-in glyphs. To use your own icons, place any of these files in the branding folder:
os-windows.svg os-linux.svg (or .png / .webp)
The folder is BRANDING_DIR (default data/branding/ next to the database). With Docker, mount it:
volumes:
- ./branding:/app/branding:roBRANDING_DIR=/app/brandingOnly these file names are served, with a strict sandbox policy. Make sure you're allowed to use the images you add.
.
├── src/
│ ├── server.js
│ ├── config.js
│ ├── db.js
│ ├── pve.js
│ ├── auth.js
│ ├── bootstrap.js
│ ├── provision.js
│ ├── network.js
│ ├── windows.js
│ ├── cleanup.js
│ ├── tailscale.js
│ ├── totp.js
│ ├── oidc.js
│ ├── vpn.js
│ ├── agent.js
│ ├── version.js
│ ├── mail.js
│ ├── invite.js
│ ├── secrets.js
│ └── routes/
│ ├── vms.js
│ ├── console.js
│ ├── vpn.js
│ └── admin.js
│
├── web/
│ ├── customer/
│ ├── admin/
│ └── shared/
│
├── docs/
│ ├── GUIDE.md
│ ├── screenshots/
│ ├── windows/
│ │ └── unattend.xml
│ └── vpn/
│ └── setup-gateway.sh
│
├── scripts/
├── deploy/
│ ├── docker-compose.yml
│ └── docker-compose.portainer.yml
│
├── .github/
│ ├── workflows/docker-publish.yml
│ ├── dependabot.yml
│ └── release.yml
├── Dockerfile
├── docker-compose.yml
├── Caddyfile
├── example.env
└── package.json
| Method | Endpoint | Purpose |
|---|---|---|
GET |
/api/vms |
List owned servers |
GET |
/api/vms/:vmid |
Server details and live status |
POST |
/api/vms/:vmid/power/:action |
Power operations |
GET |
/api/vms/:vmid/rrd |
Usage metrics |
GET/POST |
/api/vms/:vmid/snapshots |
List or create snapshots |
POST |
/api/vms/:vmid/snapshots/:name/rollback |
Roll back snapshot |
DELETE |
/api/vms/:vmid/snapshots/:name |
Delete snapshot |
POST |
/api/vms/:vmid/console |
Create console session |
GET (WS) |
/api/console/:session |
Console WebSocket |
GET |
/api/account |
Limits, usage, network, panel version |
GET |
/api/templates |
Available templates |
POST |
/api/vms |
Provision a server |
POST |
/api/vms/:vmid/reinstall |
Reinstall a self-created server from a template |
POST |
/api/vms/:vmid/resize |
Change CPU cores, memory and disk within the plan |
DELETE |
/api/vms/:vmid |
Delete a self-created server |
GET |
/api/vpn |
VPN state |
POST |
/api/vpn/devices |
Add VPN device |
DELETE |
/api/vpn/devices/:id |
Remove VPN device |
GET/POST/DELETE |
/api/vms/:vmid/tailscale |
Manage Tailscale |
GET/POST |
/api/account/2fa/... |
Manage 2FA |
POST |
/api/invite/check, /api/invite/accept |
Invitation link: check it, set the password |
The administrator API is available only through the administrator server and an authenticated admin session. GET /healthz reports liveness for Docker and monitoring.
1. Customer selects template and sizing
│
▼
2. Quotas and permissions are validated
│
▼
3. A free VMID is reserved
│
▼
4. Full clone is created
│
▼
5. CPU / RAM / disk / network are configured
│
▼
6. Guest-specific setup runs
┌──────────┴──────────┐
│ │
▼ ▼
Linux / cloud-init Windows / QGA
│ │
└──────────┬──────────┘
▼
7. Readiness is verified
│
▼
8. Server becomes available to the customer
Only one provisioning job per customer can run at a time.
Customers can start a self-created server fresh from any offered template that fits its disk:
- everything on the disk is erased, including all snapshots (and a Tailscale connection)
- name, VM ID, size and MAC address stay — so DHCP usually hands out the same IP
- the server stays in the customer's private network; isolation rules are re-applied
- new sign-in details are chosen like at creation; Windows ⇄ Linux switches are possible
- confirmed by typing the server name; a failed reinstall can simply be retried
Servers assigned by an administrator cannot be reinstalled by customers.
Customers can change the size of self-created servers within their plan:
| Resource | How it changes |
|---|---|
| CPU cores, memory | Saved immediately; a running server uses them after a restart from the panel (optional right away) |
| Disk | Grow only, applied immediately — Linux cloud images expand at the next boot, Windows drive C: is extended by the panel |
- the plan check counts pending changes too, so resizing several running servers can't exceed the plan
- the server page shows Size change waiting for a restart until the change is active
- Windows needs at least 2 GB of memory; disks can never shrink
If provisioning fails, the customer receives a visible failure state instead of a permanently spinning setup process.
Before giving users access, validate the deployment with at least two test customers.
Confirm that:
- Customer A cannot see Customer B's servers
- Customer A cannot access Customer B's network
- Customer workloads cannot access internal management networks
- Public internet access works where intended
- Browser consoles are customer-bound
- VPN devices can reach only their assigned customer network
- Admin port
3001is not publicly reachable - OIDC and 2FA behave as expected
- Proxmox API permissions are limited to the intended resources
Potential future improvements include:
- Cross-node placement
- Integrated backup management
- Customer-managed firewall rules
- Additional lifecycle automation
- PostgreSQL support for multi-instance deployments
- More quota and billing-oriented functionality
Contributions and ideas are welcome through GitHub Issues and Pull Requests.
Contributions are welcome.
A typical workflow:
git clone https://github.com/sebastianflint/pve-panel.git
cd pve-panel
git checkout -b feature/my-featureAfter making and testing your changes:
git add .
git commit -m "Add my feature"
git push origin feature/my-featureThen open a Pull Request.
For larger changes, consider opening an Issue first so the design can be discussed before implementation.
Found a bug or have an idea?
Use the repository issue tracker:
https://github.com/sebastianflint/pve-panel/issues
When reporting a problem, useful information includes:
- Proxmox VE version
- deployment method
- PVE Panel version / commit (admin portal → About)
- browser
- relevant container/application logs
- the reason shown in the Activity tab, if any
- exact error message
- steps to reproduce
Never include API tokens, passwords, OIDC secrets or other credentials in an issue.
PVE Panel directly controls virtualization, networking, guest provisioning and remote-access functionality.
Large parts of the project were written with AI assistance (see AI-assisted development).
Before using it in a production or internet-facing environment:
- review the code
- review Proxmox permissions
- test customer isolation
- use TLS
- protect the admin interface
- back up the database
- validate firewall rules
- keep Proxmox, Node.js and dependencies up to date
Give customers the controls they need — while keeping the hypervisor, credentials and other customers out of reach.
⭐ Star the project · 🐛 Report an issue · 📦 Container image · 📘 Administrator guide





