I've been running Proxmox VE in my homelab for years, but recently needed a cloud-based virtualization environment for testing and development. Google Cloud Platform seemed like a good choice with its nested virtualization support, but I quickly discovered some significant networking quirks that required creative solutions.
The Problem: Google's Hidden Limitations
When you spin up a VM on GCP, everything seems normal at first. But try to run containers or VMs inside Proxmox, and you'll hit some walls:
1. UDP Port 53 is Blocked
This is the big one. Google Cloud blocks outbound UDP traffic on port 53. This means standard DNS resolution (querying 8.8.8.8, 1.1.1.1, etc.) simply doesn't work for nested VMs and containers. Your containers will boot up fine, but they won't be able to resolve any domain names.
2. N-Series VMs Required for Full Virtualization
Only N1 and N2 machine types support nested virtualization on GCP. If you try to use E2 or other cost-optimized instances, you'll only be able to run LXC containers, not full KVM virtual machines. The enable_nested_virtualization flag simply won't work on non-N-series VMs.
3. No LVM Storage
GCP doesn't provide LVM volumes like you'd have on physical hardware. The solution is directory-based storage at /var/lib/vz, which works fine for containers, VMs, ISOs, templates, and backups.
The Solution: DNS-over-HTTPS with Technitium
The key insight is that while Google blocks UDP port 53, they don't block TCP port 443 (HTTPS). DNS-over-HTTPS (DoH) sends DNS queries over standard HTTPS connections, completely bypassing the UDP block.
I chose Technitium DNS Server because it:
- Listens on standard port 53 for local queries from containers/VMs
- Forwards queries upstream via DoH (TCP 443) to Cloudflare's 1.1.1.1
- Has a nice web UI for configuration and monitoring
- Can integrate with Cloudflare Gateway for DNS filtering policies
Architecture Overview
Here's how all the pieces fit together:
+------------------------------------------------------------------+
| Google Cloud Platform |
| +--------------------------------------------------------------+ |
| | Proxmox VE Host (Debian 12) | |
| | | |
| | +----------------------+ | |
| | | Technitium DNS |----DoH-----> Cloudflare | |
| | | Port 53 (0.0.0.0) | (TCP:443) (1.1.1.1) | |
| | +----------+-----------+ | |
| | | | |
| | +----------v------------------------------------+ | |
| | | Bridge Networks (NAT/Masquerade) | | |
| | | vmbr0-5: 192.168.100-105.0/24 | | |
| | | ISC DHCP Server on each bridge | | |
| | +----------+------------------------------------+ | |
| | | | |
| | +----------v-----+ +----------+ +----------+ | |
| | | Container | | Container| | VM | | |
| | | 101 | | 102 | | 103 | | |
| | | DHCP: .10-.250 | | DHCP IP | | DHCP IP | | |
| | | DNS: Bridge IP | | DNS: .1 | | DNS: .1 | | |
| | +----------------+ +----------+ +----------+ | |
| +--------------------------------------------------------------+ |
| | |
| +--------v--------+ |
| | ens4 (NAT) | |
| | External IP | |
| +--------+--------+ |
+------------------------------|------------------------------------+
|
+-----------v------------+
| Cloudflare Tunnel |
| (Proxmox Web UI) |
+-----------------------+ Network Flow Explained
DNS Resolution (bypasses GCP's UDP:53 block):
Container -> Bridge Gateway (192.168.100.1:53) -> Technitium DNS -> DoH (https://1.1.1.1/dns-query) DHCP (automatic IP assignment):
Container -> ISC DHCP Server (port 67) -> Assigns IP from pool (.10-.250) Internet Access:
Container -> Bridge Network -> iptables NAT/MASQUERADE -> ens4 -> Internet Proxmox Web UI Access:
User -> Cloudflare Tunnel (HTTPS) -> Proxmox Host (Port 8006) What the Terraform Deploys
The complete solution includes:
- Proxmox VE 8.x on Debian 12 with post-install optimizations (correct repos, disabled subscription nag)
- 6 bridge networks (192.168.100-105.0/24) with NAT/masquerading for internet access
- Technitium DNS with DNS-over-HTTPS upstream to Cloudflare
- ISC DHCP Server for automatic IP assignment on all bridges
- Cloudflare Tunnel for secure Proxmox UI access (port 8006 not exposed to internet)
- Security hardening: SSH key-only auth, UFW firewall, Fail2ban, automatic security updates
How to Deploy Your Own
Prerequisites
1. A GCP account with billing enabled
2. Terraform >= 1.0 installed
3. A Cloudflare account with a domain (for the tunnel)
4. gcloud CLI installed and authenticated
Step 1: Create a Cloudflare Tunnel
In the Cloudflare Zero Trust dashboard:
1. Go to Networks -> Tunnels
2. Create a new tunnel
3. Configure a public hostname pointing to https://localhost:8006 with "No TLS Verify" enabled
4. Copy the tunnel token (starts with eyJ...)
Step 2: Configure Terraform Variables
Clone the repository and create your terraform.tfvars:
# GCP Configuration
gcp_project = "my-project-12345"
gcp_region = "us-west1"
gcp_zone = "us-west1-c"
# VM Configuration
vm_name = "proxmox-server"
hostname = "proxmox.example.com"
# SSH Configuration
ssh_user = "admin"
ssh_public_key = "ssh-ed25519 AAAAC3... user@host"
# Cloudflare Tunnel Token
cloudflare_tunnel_token = "eyJhIjoiY2ZlZDE2NTAzMGIx..." Step 3: Deploy
# Authenticate with GCP
gcloud auth application-default login
gcloud config set project <your-project-id>
# Initialize and deploy
terraform init
terraform plan
terraform apply The deployment takes about 15-20 minutes. The system will automatically reboot after Phase 1 (Proxmox installation) and complete Phase 2 (network configuration, DNS, DHCP, firewall).
Step 4: Set Root Password and Access Proxmox
# SSH into the server
ssh admin@$(terraform output -raw external_ip)
# Set the Proxmox root password
sudo passwd root
# Access via your Cloudflare hostname
# https://proxmox.example.com Creating Containers with Working DNS
With the DHCP and DNS infrastructure in place, creating containers is straightforward:
# Create a container with DHCP (recommended)
pct create 101 local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst \
--hostname my-container \
--net0 name=eth0,bridge=vmbr0,ip=dhcp \
--memory 512 \
--rootfs local:8
# Start it
pct start 101
# Verify DNS works
pct exec 101 -- getent hosts google.com The container automatically receives an IP from the DHCP pool (.10-.250), with the bridge gateway set as the DNS server. DNS queries flow through Technitium and out to Cloudflare via DoH - completely bypassing Google's UDP port 53 block.
Cost Considerations
The default configuration uses an n2-standard-4 (4 vCPUs, 16GB RAM) with a 500GB disk. This runs about $150-200/month depending on region. You can reduce costs by:
- Using preemptible/spot instances (not recommended for production)
- Reducing disk size if you don't need much storage
- Using committed use discounts
- Stopping the instance when not in use
Conclusion
Running Proxmox on GCP isn't straightforward due to Google's networking restrictions, but with the right architecture it works beautifully. The key insight is using DNS-over-HTTPS to bypass the UDP port 53 block, combined with a proper local DNS server that containers can query.
The full Terraform configuration is available in my GitLab repository. It's been battle-tested and handles all the GCP quirks automatically.
Happy virtualizing!