Running Proxmox on a Budget VPS: LXC, Docker and Portainer

9 min read

Proxmox is a free, open-source hypervisor — and one of its quieter tricks is that you can run it inside a KVM VPS, a hypervisor nested within someone else’s hypervisor. That single fact turns an ordinary low-end box into a small homelab: a handful of isolated containers, a private network, and a clean public address for each service, all on hardware that costs very little. This is the full path I use, from a bare VPS to Docker and Portainer running in an LXC container, reachable over the internet through a Cloudflare Tunnel.

We will check the VPS can virtualise, install Proxmox, tidy its repositories, give it a public address through Cloudflare, carve out a private network with Proxmox’s SDN, and finish by running Docker and Portainer inside an Ubuntu container. Every command runs either on the Proxmox host shell or inside the container — I will say which each time.

8 GBMinimum RAM
9.2Proxmox VE
1Public IPv4
190Private IPs

The idea

A hypervisor inside your VPS

Most low-end VPS plans hand you a single virtual machine and leave it there. Proxmox changes the shape of that box. Because it can run nested inside a KVM VPS, you can install it on a machine you are renting and then create your own virtual machines and containers underneath it — each isolated, each with its own slice of resources.

The requirements are modest. I recommend at least 8 GB of RAM, which has comfortably run several containers in a homelab for me. The one firm condition is that the VPS must be KVM with hardware virtualisation enabled; container-based plans such as OpenVZ cannot do this. You will also want a single public IPv4 address for convenience, and a domain hosted on Cloudflare for the tunnel later on.

Several providers make this painless. Advin Servers and Hetzner both let you install Proxmox straight from their control panels; others I have used, including RackNerd and Hostbrr, will mount the ISO for you on request. For the rest of this guide I will use example.com as the domain and proxmox.example.com as the Proxmox hostname.

Preflight

Check your VPS can actually do this

Before anything else, confirm the box can virtualise. On an x86-64 system, run:

lscpu

Look at two fields. Virtualisation should read AMD-V on AMD or VT-x on Intel, and the Hypervisor vendor should read KVM. If both are present, you can run Proxmox nested on this box. If virtualisation is blank, the plan will not do the job — worth confirming with the provider before you go any further.

Install

Installing Proxmox

If your panel does not offer Proxmox directly, ask the provider to upload the ISO. The current release is Proxmox VE 9.2, built on Debian 13 “Trixie”. Because the version moves, it is worth taking the latest from the Proxmox downloads page rather than pinning an old URL:

# current ISO at time of writing
https://enterprise.proxmox.com/iso/proxmox-ve_9.2-1.iso

# always-current list (grab the newest from here)
https://www.proxmox.com/en/downloads

Set the VM to boot from the mounted ISO, restart, and open the VPS panel’s VNC console. The installer is a straightforward GUI; the fields that matter are the network ones. Enter your public IPv4 address, the correct gateway for that IP, and a fully qualified hostname — a subdomain of your Cloudflare domain, proxmox.example.com here. No DNS changes are needed yet. Choose a strong password; it doubles as the server’s root password.

When the installer finishes, the VPS reboots. Unmount the ISO at this point — if you land back in the installer, the disc is still attached, so unmount it and restart. Proxmox then comes up on its own, and the web interface is waiting at https://your_ipv4:8006.

First run

Repositories and a first update

Out of the box, Proxmox points at its enterprise repositories, which need a paid subscription, so an update fails until you switch them. In the GUI, the Server entry sits beneath Datacenter on the left. Open Server → Repositories, disable both enterprise repositories, then Add the pve-no-subscription repository in the same view.

Proxmox lets you run host commands straight from the browser. Open Server → Shell — you are auto-logged in as root — and bring the system up to date:

apt-get update && apt-get full-upgrade -y

Front door

A public address with Cloudflare Tunnel

A Cloudflare Tunnel gives Proxmox a clean public URL with a valid certificate, without exposing the port directly to the internet. In the Cloudflare dashboard, open Zero Trust, then Networks → Tunnels → Create a tunnel. Choose Cloudflared as the type and give it a name.

Cloudflare then shows you how to install the connector. Choose Debian as the environment and paste the two commands it gives you into the Proxmox shell: the first installs cloudflared, the second registers it as a service so it survives reboots. Once it runs, the connector shows as healthy on the dashboard.

Now publish the route. Enter proxmox.example.com as the subdomain and hostname, leave the path empty, set the protocol to HTTPS, and point the URL at your_public_ipv4:8006. Open the additional-options drop-down, choose TLS, and turn on No TLS Verify and the HTTP2 connection. Complete the setup, and Proxmox is reachable at https://proxmox.example.com behind a Cloudflare certificate. Every service you add later gets its own route from the same Tunnels → your tunnel → Published Application Routes screen.

Groundwork

Preparing the host for Docker

There is one piece of host groundwork that saves a lot of grief later. If you intend to run Docker inside an LXC container — which we will — install Fuse OverlayFS on the Proxmox host first. I have never managed to get Docker running cleanly inside a container without it. On the host shell:

apt install -y fuse-overlayfs

Networking

Private networking with SDN

Proxmox’s Software-Defined Networking makes a private network for your containers almost trivial. First enable the feature on the host:

apt update
apt install -y libpve-network-perl

Then build it in the GUI. Under Datacenter → SDN, open Zones and add a Simple zone — I call mine vzone1. Leave IPAM as pve, tick the advanced view, tick automatic DHCP, and add it. Next, under VNets, create a VNet (mine is vnet1) and attach it to vzone1.

Select that VNet and a subnets column appears. Create your first subnet: I use 10.10.10.0/24 with a gateway of 10.10.10.1, and tick SNAT so the containers can reach the internet. Finally, in the DHCP ranges column, add a range — I run 10.10.10.10 to 10.10.10.200, which leaves 190 addresses to hand out.

Containers

Creating your first container

Proxmox ships with a library of LXC templates. Download an Ubuntu 24.04 one from Datacenter → Local → CT Templates → Templates, then click Create CT at the top right.

Two settings matter more than the rest. On the first panel, set the hostname and password (and an SSH key if you like), and make sure both Unprivileged container and Nesting are ticked — Nesting is what lets Docker run inside the container. Choose the Ubuntu 24.04 template you just downloaded. 20 GB of disk and a single core are plenty for Portainer; give it at least 2 GB of RAM for Docker. On the network panel, set Bridge to your VNet, IPv4 to DHCP, and leave IPv6 as Static/None. Accept the host’s DNS, confirm, and create — ticking start after creation so it boots straight away.

The payload

Docker and Portainer inside the container

From here, every command runs inside the container, not on the host. Open its console and log in as root with the password you set. A fresh Ubuntu container is minimal, so start by updating it:

apt update && apt upgrade -y

Now install Docker from its official apt repository. This is the part of my original write-up that carried a real bug, so it is worth doing carefully: the keyring directory has to exist before you download the key into it.

apt install -y ca-certificates curl
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc

Add Docker’s repository to apt, then install the engine and its plugins:

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
  | tee /etc/apt/sources.list.d/docker.list > /dev/null
apt update
apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

You are root inside the container, so none of these need sudo. With Docker in place, install Portainer’s Community Edition — create its volume, then run it:

docker volume create portainer_data
docker run -d -p 8000:8000 -p 9443:9443 \
  --name portainer --restart=always \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v portainer_data:/data \
  portainer/portainer-ce:lts

Portainer is now listening on port 9443. Find the container’s private address — for the first container on the range above it will be 10.10.10.10 — with:

ip a

Look for the inet address on eth0 within your private subnet. Back in Cloudflare, open your tunnel and add a published application route for portainer.example.com: set the service to HTTPS and the URL to that address and port — 10.10.10.10:9443 for the first container — then enable No TLS Verify and HTTP2 as before. Save, and Portainer’s admin panel is live at https://portainer.example.com.

Going further

Where to go next

That is the whole pattern: a nested hypervisor, a private network, and containers that each get a tidy public address. To go further, the community Proxmox VE Helper-Scripts are an excellent library — a huge range of services packaged as near one-line installs:

https://community-scripts.github.io/ProxmoxVE/

Run these from the Proxmox shell and choose the LXC version when prompted. When a script finishes, note the private IP and port it reports, and add a Cloudflare route for it exactly as we did for Portainer. From here, the box is yours to fill.