Introduction
Cloud hosting is convenient β until you realize you're paying $50β100/month for infrastructure you already own. I had a Dell OptiPlex 7090 sitting in my office running Windows 11, handling some backend services. The setup felt fragile: everything running directly on the OS, no isolation, no proper virtualization.
So I wiped it. Replaced Windows with Proxmox VE 9.1, and in a weekend I had a proper home lab server running multiple isolated containers, network-wide DNS filtering, secure remote access from anywhere, and a network file share β all for $0/month in cloud costs.
Here's exactly how I did it, what I learned, and what surprised me along the way.
The Hardware
The Dell OptiPlex 7090 is an underrated home lab machine. It's widely available refurbished for $200β350 and punches well above its price. Mine came configured with:
It draws roughly 35W at idle, which matters when it's running 24/7. The dual NVMe setup turned out to be key β Proxmox lives on the smaller drive, and all data and container storage lives on the larger one.
If you're looking for a new machine to run as a home lab server rather than repurposing an old one, a mini PC is worth considering. The Beelink GTi13 Ultra offers an i9-13900HK with up to 64GB RAM in a tiny, silent package β significantly more headroom for running multiple VMs and containers simultaneously.
Why Proxmox?
Proxmox VE is a bare-metal hypervisor that gives you KVM virtual machines and LXC containers managed from a web UI. It's free, enterprise-grade, and built on Debian. Key reasons to choose it:
The install is straightforward: download the ISO, flash to a USB drive, boot, follow the wizard. A reliable USB drive matters here β a Samsung 64GB USB 3.1 drive works well for this.
After install, disable the enterprise repository (it requires a paid subscription) and add the no-subscription repo:
# Disable enterprise repo echo "# disabled" > /etc/apt/sources.list.d/pve-enterprise.listAdd no-subscription repo
echo "deb http://download.proxmox.com/debian/pve trixie pve-no-subscription" \ > /etc/apt/sources.list.d/pve-no-subscription.list
apt update && apt dist-upgrade -y
Mounting the Second Drive
My 1.8TB NVMe was formatted as exFAT from its previous life as a Windows data drive. Linux doesn't include exFAT support by default:
apt install -y exfatprogs
mkdir -p /mnt/data_drive
mount -t exfat /dev/nvme1n1p2 /mnt/data_drive
To make it survive reboots, add it to /etc/fstab:
UUID=YOUR-DRIVE-UUID /mnt/data_drive exfat defaults,nofail 0 0
Get the UUID with blkid /dev/nvme1n1p2. The nofail flag is important β without it, a missing drive would prevent the system from booting.
The Ubuntu LXC Container
Rather than running a full VM, I created an LXC container for application workloads. LXC containers share the host kernel, so they're much lighter than VMs while still providing full process isolation.
# Download Ubuntu 24.04 template
pveam update
pveam download local ubuntu-24.04-standard_24.04-2_amd64.tar.zstCreate container (8GB disk, 2GB RAM, 2 cores)
pct create 100 local:vztmpl/ubuntu-24.04-standard_24.04-2_amd64.tar.zst \
--hostname ubuntu \
--memory 2048 \
--cores 2 \
--net0 name=eth0,bridge=vmbr0,ip=dhcp \
--rootfs local-lvm:8
The container gets an IP from your router's DHCP. Set a static DHCP lease for it in your router so the IP never changes.
Bind Mounting the Data Drive
To give the Ubuntu container access to the data drive, add a bind mount in the container config:
pct set 100 -mp0 /mnt/data_drive,mp=/mnt/data_drive
pct reboot 100
This is one of the elegant things about LXC β you can expose host paths directly into containers without any network overhead or NFS configuration.
Running Applications with PM2
Inside the Ubuntu container, Node.js applications run under PM2 with cluster mode for zero-downtime restarts:
# Install Node.js 20
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt install -y nodejsInstall PM2 globally
npm install -g pm2Start your app
cd ~/your-app && pm2 start ecosystem.config.jsSave and enable autostart on reboot
pm2 save
pm2 startup
A minimal ecosystem.config.js:
module.exports = {
apps: [{
name: 'my-app',
script: 'src/server.js',
instances: 2,
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
PORT: 3000
}
}]
};
nginx as Reverse Proxy
With multiple services running on different ports, nginx ties everything together on a single port:
apt install -y nginx
server { listen 8080;location /api/service-one/ { proxy_pass http://localhost:3000/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }
location /api/service-two/ { proxy_pass http://localhost:3001/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }
Tailscale for Secure Remote Access
Tailscale creates a zero-trust mesh VPN β every device gets a 100.x.x.x IP accessible from anywhere, without port forwarding or managing a VPN server.
curl -fsSL https://tailscale.com/install.sh | sh
tailscale up
After authenticating, your container is immediately reachable from any of your other Tailscale devices β phone, laptop, wherever. No firewall rules, no dynamic DNS, no open ports on your router.
For services that need to be publicly accessible, Tailscale Funnel provides automatic HTTPS:
tailscale funnel 8080
This gives you a public https://your-machine.ts.net endpoint with a free TLS certificate, backed by your home server.
A good network switch helps if you're wiring things up properly. The TP-Link TL-SG108E 8-port managed switch is a solid affordable option for a home lab with VLAN support.
AdGuard Home for Network-Wide DNS Filtering
AdGuard Home runs as a DNS server that blocks ads and tracking domains for every device on your network β no client-side installation needed.
The Proxmox community scripts project makes the setup a one-liner from the Proxmox host:
bash -c "$(wget -qLO - https://github.com/community-scripts/ProxmoxVE/raw/main/ct/adguardhome.sh)"
After the container is created, point your router's DNS server setting to the AdGuard container's IP. Every device on your network instantly gets DNS-level filtering. The query logs reveal just how many tracking domains modern apps contact β it's eye-opening.
Samba File Sharing
For accessing files on the Ubuntu container from Windows machines on the same Tailscale network:
apt install -y samba
Add to /etc/samba/smb.conf:
[myfiles]
path = /home/myuser
browseable = yes
read only = no
valid users = myuser
smbpasswd -a myuser
systemctl restart smbd && systemctl enable smbd
Access from Windows: \\
> Note: Always use password authentication (valid users, smbpasswd) rather than guest access, even on a private network.
What's Running Now
Here's the final state of the server after the weekend build:
| Service | Details | |---|---| | Proxmox VE 9.1 | Hypervisor, accessible via web UI | | Ubuntu LXC | Application container, 2GB RAM | | Node.js APIs | PM2 cluster mode, auto-restart | | nginx | Reverse proxy on port 8080 | | Tailscale | Secure remote access + public HTTPS funnel | | AdGuard Home | Network-wide DNS filtering | | Samba | Authenticated file share over Tailscale | | 1.8TB data drive | exFAT, bind-mounted into container |
Total RAM used across all containers: ~2GB out of 32GB. The server idles at around 8% CPU.
Reliability Considerations
One important addition for any always-on server: a UPS (uninterruptible power supply). A sudden power cut while Proxmox is writing to disk can corrupt the filesystem. The CyberPower CP1500PFCLCD provides enough runtime for a graceful shutdown and protects against power fluctuations. It's one of those things you don't think about until you need it.
For storage reliability, if you're running critical data, consider a proper NAS alongside your server. The Synology DS220+ paired with WD Red Plus drives provides RAID redundancy that a single NVMe drive can't offer.
Lessons Learned
exFAT on Linux is fully supported now. exfatprogs on Debian Trixie just worked β no data loss, no reformatting needed.
LXC containers are underrated. Most home lab content pushes VMs, but for Linux workloads LXC is almost always the right choice. Faster, lighter, and bind mounts make storage sharing trivial.
Tailscale is transformative. I've configured WireGuard manually before β it's powerful but tedious. Tailscale handles key exchange, routing, and NAT traversal automatically, with a clean admin UI on top.
PM2 cluster mode matters. Running Node.js in cluster mode gives zero-downtime restarts during deploys and better CPU utilization. Worth setting up from day one.
Plan your disk layout. Be intentional about which disk is for the OS and which is for data before you start. Changing it later is annoying.
What's Next
The total investment here was time and electricity. If you have an old PC gathering dust, Proxmox is one of the highest-value things you can do with it.
---
Recommended Products
| Product | Use Case | Link | |---|---|---| | Beelink GTi13 Ultra | New home lab server | Amazon | | TP-Link TL-SG108E | Network switch | Amazon | | CyberPower CP1500PFCLCD | UPS / power protection | Amazon | | Synology DS220+ | NAS for redundant storage | Amazon | | WD Red Plus 4TB | NAS drives | Amazon | | Samsung 64GB USB 3.1 | Bootable install drive | Amazon |
Disclosure: Some links above are affiliate links. If you purchase through them, I may earn a small commission at no extra cost to you.
---
Ricardo Gil is a Full Stack Software Engineer based in South Florida. He writes about software development, DevOps, and home lab setups.
