Cloud Migration Alternatives¶
This document covers alternative approaches to external app access beyond full k8s cluster migration, including single-app hosting, DMZ VLAN isolation, and hybrid architectures.
Related: See Cloud Migration Evaluation for full cluster migration options.
Table of Contents¶
- When to Use These Alternatives
- Option 1: Ultra-Cheap Single App Hosting
- Option 2: DMZ VLAN with Cloudflare Tunnel
- Option 3: Hybrid Architectures
- Option 4: Single VPS for One App
- Comparison Matrix
- Recommended Combinations
When to Use These Alternatives¶
Use single-app hosting when: - Only need 1-2 apps externally accessible - Want to minimize cost (potentially $0-10/month) - Don't need full k8s cluster features - Testing external access before committing to infrastructure
Use DMZ VLAN when: - Comfortable with moderate network complexity - Want to keep apps running on local hardware - Need isolation between external-facing and internal services - Already have VPN for admin access to main network
Use hybrid architectures when: - Want static frontend performance (CDN) - Backend can stay local or needs minimal cloud resources - Separating concerns (frontend vs API)
Option 1: Ultra-Cheap Single App Hosting¶
For hosting a lightweight Next.js app with database access, managed platforms offer excellent free/cheap tiers.
1A: Vercel (Free Tier)¶
What It Is: - Vercel (creators of Next.js) - optimized for Next.js apps - Generous free tier for hobby projects
Free Tier Limits: - 100GB bandwidth/month - Unlimited deployments - Unlimited sites - Serverless function execution limits (100GB-hours/month) - No credit card required
Pricing: - Hobby (Free): $0/month - suitable for personal projects - Pro: $20/month - removes limits, adds team features
Pros: - Zero configuration for Next.js (git push = deploy) - Automatic HTTPS, CDN, preview deployments - Best Next.js performance (built by same team) - Can connect to external PostgreSQL (Supabase Cloud, etc.) - Free tier is genuinely generous
Cons: - Vendor lock-in to Vercel platform - Serverless model (not traditional VPS) - Can't run custom services (only Next.js/static) - Commercial use requires Pro tier
Database Options: - Vercel Postgres: $20/month (512MB storage) - Supabase Cloud: $0 free tier, $25 Pro - Neon: Free tier (0.5GB storage), scales up - PlanetScale: Free tier (5GB storage, 1 billion reads)
Best For: - Lightweight dashboard/portal app - Prototyping external access - Apps that fit serverless model
Setup Time: 15-30 minutes (connect GitHub, configure env vars)
Total Monthly Cost: - Free tier: $0 (app) + $0-25 (database) = $0-25/month - Pro tier: $20 (app) + $0-25 (database) = $20-45/month
1B: Railway (Developer-Friendly)¶
What It Is: - Modern platform-as-a-service (PaaS) - Docker-based deployments - Free $5/month credit (no credit card), then pay-as-you-go
Pricing: - Developer Plan: Free $5 credit/month - Enough for small app (1 web service + small database) - Additional usage: $0.000231/GB-hour RAM (~$10/mo for 2GB 24/7) - Pro: $20/month minimum for team features
Pros: - Flexible (not just Next.js - any Docker container) - Can run PostgreSQL in same platform - Simple git-push deployment - Generous free tier for experimentation - Easy environment variable management
Cons: - Free credit runs out fast for 24/7 services - More expensive than dedicated VPS for high uptime - Less Next.js-specific optimization than Vercel
Database Options: - Railway PostgreSQL: Included (uses same credit/billing) - Supabase Cloud: External connection
Best For: - Full-stack apps needing database - Docker-based workflows - Experimenting before committing to infrastructure
Setup Time: 30-60 minutes (GitHub connection, configure build)
Total Monthly Cost: - Light usage: $0-5/month (within free credit) - Typical Next.js app + small DB: $10-15/month - Production app + database: $20-30/month
1C: Fly.io (Global Edge Platform)¶
What It Is: - Docker container platform - Run apps globally on edge network - Free tier with credit card
Free Tier Allowances: - 3x shared-cpu-1x VMs (256MB RAM each) - 3GB persistent storage - 160GB outbound bandwidth/month
Pricing Beyond Free Tier: - shared-cpu-1x (256MB): $1.94/month - shared-cpu-1x (1GB RAM): $5.70/month - Persistent storage: $0.15/GB-month
Pros: - True VMs (not just serverless) - Run any Docker container - Global deployment (low latency worldwide) - Can run PostgreSQL alongside app - Free tier covers small production apps
Cons: - Requires credit card for free tier - More manual configuration than Vercel - Smaller community than AWS/DO
Database Options: - Fly Postgres: Free tier covers small DB (shared VM) - Supabase Cloud: External connection
Best For: - Apps needing consistent uptime (VM-based) - Global users (edge deployment) - Learning Docker deployment
Setup Time: 1-2 hours (Dockerfile, fly.toml config)
Total Monthly Cost: - Free tier (1 app + small DB): $0/month - Small production: $5-10/month - Larger instances: $15-25/month
1D: Cloudflare Pages + Workers (Serverless)¶
What It Is: - Static site hosting (Cloudflare Pages) - Serverless functions (Cloudflare Workers) - Next.js supported via adapter
Free Tier: - Unlimited sites - Unlimited requests - Unlimited bandwidth - 100,000 Worker requests/day
Pricing: - Free: $0/month (very generous limits) - Workers Paid: $5/month (10M requests, then $0.50/1M)
Pros: - Extremely generous free tier - Cloudflare's global CDN (excellent performance) - Simple git-based deployment - No credit card for free tier
Cons: - Serverless architecture (not traditional server) - Limited to 10ms CPU time per request (Worker limits) - Database must be external (no bundled option) - Next.js support requires adaptation (not native)
Database Options: - Cloudflare D1: Free tier (5M reads/day) - SQLite-based - Supabase Cloud: External PostgreSQL - Neon: Serverless PostgreSQL
Best For: - Mostly static sites with light dynamic features - Global CDN performance priority - Absolute minimal cost
Setup Time: 1-2 hours (configure adapter, deploy)
Total Monthly Cost: - Free tier: $0 (site) + $0-25 (database) = $0-25/month
1E: Render (Vercel Alternative)¶
What It Is: - PaaS similar to Railway/Vercel - Free tier for web services
Free Tier: - Static sites: Unlimited, always free - Web services: Free (spins down after inactivity) - PostgreSQL: Free (90 days, then $7/month)
Pricing: - Free Static: $0/month - Web Service (Starter): $7/month (512MB RAM, always-on) - PostgreSQL: $7/month (1GB storage, 1GB RAM)
Pros: - True free tier for static sites - Simple deployment (git-based) - Can run full Next.js (not just static export)
Cons: - Free web services sleep after inactivity (slow cold starts) - PostgreSQL not free long-term - Less Next.js optimization than Vercel
Best For: - Static sites (free forever) - Apps okay with cold starts - Lower cost than Vercel Pro
Setup Time: 30-60 minutes
Total Monthly Cost: - Static only: $0/month - Always-on app + DB: $14/month
Option 2: DMZ VLAN with Cloudflare Tunnel¶
This approach creates an isolated network segment (DMZ) that can be safely exposed to the internet while protecting your main network.
Architecture Overview¶
Internet
│
▼
Cloudflare Tunnel
│
▼
┌────────────────┐
│ DMZ VLAN │ (e.g., 10.89.98.0/24)
│ │
│ LXC Container │ Runs cloudflared + apps
│ (External) │
└────────────────┘
│
Firewall Rules
(DROP all to main)
│
┌────────────────┐
│ Main Network │ (10.89.97.0/24)
│ │
│ • Supabase │ Can't be reached from DMZ
│ • Media Stack │
│ • NAS Storage │
└────────────────┘
│
VPN Required
Security Model: - DMZ VLAN has NO route to main network - Firewall explicitly drops DMZ → Main traffic - Cloudflare Tunnel runs in DMZ only - External users → Cloudflare → DMZ (isolated) - Main network accessible only via VPN
Implementation Steps¶
Phase 1: Network Planning¶
1. Choose VLAN ID and subnet:
# Example:
VLAN ID: 98
DMZ Subnet: 10.89.98.0/24
Gateway: 10.89.98.1 (Proxmox vmbr0.98)
Main Network: 10.89.97.0/24 (existing)
2. Document network layout: - Main network: Keep existing 10.89.97.0/24 - DMZ network: New 10.89.98.0/24 - No routing between VLANs
Phase 2: Proxmox VLAN Configuration¶
1. Create VLAN-aware bridge (if not already):
# On Proxmox host
# Edit /etc/network/interfaces
# Add VLAN to existing bridge
auto vmbr0.98
iface vmbr0.98 inet static
address 10.89.98.1/24
# No gateway - isolated network
2. Apply network configuration:
# CAREFUL: This can disrupt network connectivity
# Have console/IPMI access before running
ifreload -a
3. Verify VLAN interface:
Phase 3: Firewall Rules (Critical for Isolation)¶
1. Create firewall rules file:
# /etc/pve/firewall/cluster.fw
[RULES]
# Allow DMZ → Internet (for updates, Cloudflare tunnel)
GROUP dmz_outbound
# Deny DMZ → Main Network
-i vmbr0.98 -o vmbr0 -s 10.89.98.0/24 -d 10.89.97.0/24 -j DROP
# Deny Main → DMZ (except from Proxmox host for management)
-i vmbr0 -o vmbr0.98 -s 10.89.97.0/24 -d 10.89.98.0/24 -j DROP
IN ACCEPT -i vmbr0 -s 10.89.97.1 -d 10.89.98.0/24 # Proxmox host can manage
# Allow established connections back
-m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
2. Enable Proxmox firewall:
3. Test isolation:
# From Proxmox host, try to reach main network from DMZ
# This should FAIL after firewall rules applied
pct exec <DMZ_CONTAINER_ID> -- ping 10.89.97.1
# Should timeout or be rejected
# From DMZ, internet should work
pct exec <DMZ_CONTAINER_ID> -- ping 8.8.8.8
# Should succeed
Phase 4: Create DMZ LXC Container¶
1. Create container in DMZ VLAN:
# Use template (Debian 12)
pct create 199 \
local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst \
--hostname dmz-external \
--cores 2 \
--memory 2048 \
--rootfs local-lvm:20 \
--net0 name=eth0,bridge=vmbr0,tag=98,ip=10.89.98.10/24,gw=10.89.98.1 \
--nameserver 1.1.1.1 \
--unprivileged 1 \
--features nesting=1
# Start container
pct start 199
2. Enter and configure container:
pct enter 199
# Update and install basics
apt update && apt upgrade -y
apt install -y curl git sudo
# Create user
adduser external
usermod -aG sudo external
Phase 5: Install Cloudflare Tunnel¶
1. Install cloudflared in DMZ container:
# Inside DMZ container (pct enter 199)
# Download cloudflared
curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
# Install
dpkg -i cloudflared.deb
# Verify
cloudflared --version
2. Authenticate with Cloudflare:
3. Create tunnel:
# Create named tunnel
cloudflared tunnel create homelab-dmz
# Note the tunnel ID and credentials file location
# Example: ~/.cloudflared/<TUNNEL_ID>.json
4. Configure tunnel routing:
Example config.yml:
tunnel: <TUNNEL_ID>
credentials-file: /root/.cloudflared/<TUNNEL_ID>.json
ingress:
# Route subdomain to local app
- hostname: external.yourdomain.com
service: http://localhost:3000
# Catch-all rule (required)
- service: http_status:404
5. Configure DNS:
# Create CNAME record pointing to tunnel
cloudflared tunnel route dns homelab-dmz external.yourdomain.com
6. Run tunnel as service:
# Install as systemd service
cloudflared service install
# Start service
systemctl start cloudflared
systemctl enable cloudflared
# Check status
systemctl status cloudflared
Phase 6: Deploy App in DMZ Container¶
1. Install Node.js (for Next.js app):
# Inside DMZ container
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt install -y nodejs
# Verify
node --version
npm --version
2. Deploy your app:
# Clone or copy app to DMZ container
git clone <YOUR_APP_REPO> /opt/app
cd /opt/app
# Install dependencies
npm install
# Build
npm run build
3. Configure environment variables:
Example .env.local for isolated database:
# Option A: Supabase Cloud (recommended for DMZ)
NEXT_PUBLIC_SUPABASE_URL=https://yourproject.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-key
# Option B: External PostgreSQL
DATABASE_URL=postgresql://user:pass@external-db.com:5432/dbname
4. Run app with PM2 (process manager):
# Install PM2
npm install -g pm2
# Start app
cd /opt/app
pm2 start npm --name "external-app" -- start
# Save PM2 config
pm2 save
pm2 startup
Database Options for DMZ¶
Option A: Separate Supabase Cloud Project (Recommended) - Create new Supabase Cloud project for DMZ apps - Isolated from main network database - No connection between DMZ and main network - Cost: $0 (free tier) or $25/month (Pro)
Option B: Separate PostgreSQL in DMZ - Install PostgreSQL in DMZ container - Completely isolated - Manual backup management - Cost: Included in DMZ container resources
Option C: Read-Only Access to Main Network Database (Advanced) - Create read-only user in main network Supabase - Allow ONLY specific DMZ IP → Main DB on port 5432 - Requires careful firewall configuration - Security Risk: Creates connection between DMZ and main - Not Recommended: Violates isolation principle
Security Verification Checklist¶
After setup, verify isolation:
Test 1: DMZ cannot reach main network
# From DMZ container
pct exec 199 -- ping 10.89.97.1 # Should FAIL
pct exec 199 -- curl http://10.89.97.50 # Should TIMEOUT
Test 2: Main network cannot reach DMZ (except Proxmox)
Test 3: DMZ can reach internet
# From DMZ container
pct exec 199 -- ping 8.8.8.8 # Should SUCCEED
pct exec 199 -- curl https://google.com # Should SUCCEED
Test 4: External access via Cloudflare works
# From external network (your phone, not on VPN)
curl https://external.yourdomain.com # Should return app
Test 5: Main network requires VPN
All tests must pass for proper isolation.
DMZ Pros and Cons¶
Pros: - Apps run on local hardware (no cloud costs) - Proper network isolation (security) - Can use Cloudflare CDN/DDoS protection - Keep data local (except what's in DMZ) - Learning experience (VLANs, firewalls)
Cons: - Complex setup (network, firewall, tunnel) - Higher maintenance (manage container, tunnel, updates) - Still exposes local hardware to internet (though isolated) - Cloudflare Tunnel required (dependency on Cloudflare) - Database duplication if using separate DB
Best For: - Learning network security concepts - Want to keep apps on local hardware - Comfortable with ongoing maintenance - Okay with Cloudflare dependency
Time Investment: - Initial setup: 4-8 hours (learning VLANs, testing isolation) - Ongoing maintenance: 1-2 hours/month (updates, monitoring)
Option 3: Hybrid Architectures¶
Combine cloud and local resources for optimal cost/performance.
3A: Static Frontend (Cloud) + API (Local/DMZ)¶
Architecture:
How It Works:
1. Build Next.js as static export (next export)
2. Host static files on Cloudflare Pages (free, global CDN)
3. API routes run on separate backend (DMZ or cheap VPS)
4. Frontend makes API calls to backend
Pros: - Free frontend hosting (Cloudflare Pages) - Fast global delivery (CDN) - Backend can be minimal (just API server) - Separation of concerns
Cons: - Can't use Next.js SSR (static only) - More complex deployment (two separate deploys) - CORS configuration required
Cost: - Frontend: $0 (Cloudflare Pages) - Backend: $5-10/month (Railway, Fly.io, or DMZ) - Database: $0-25/month (Supabase Cloud) - Total: $5-35/month
3B: Lightweight Gateway (Cloud) + Full App (Local)¶
Architecture:
How It Works: 1. Small Node.js app in cloud handles authentication 2. After auth, proxies requests through Cloudflare Tunnel to local 3. Full app runs locally (keeps data local) 4. Gateway adds security layer
Pros: - Apps stay local (data locality) - Cloud gateway adds auth, rate limiting - Minimal cloud resources (tiny gateway) - Can use existing local Supabase
Cons: - Complex architecture - Latency (cloud → tunnel → local) - Requires maintaining tunnel
Cost: - Gateway: $0-5/month (Fly.io free tier or Railway) - Cloudflare Tunnel: $0 - Total: $0-5/month
3C: Edge Functions (Cloud) + Local Database (VPN)¶
Architecture:
How It Works: 1. Cloudflare Workers host API logic (serverless) 2. Workers connect to local database via Tailscale 3. Database stays on local network (VPN access only)
Pros: - Minimal cloud cost (Workers free tier generous) - Database stays local - Workers globally distributed
Cons: - Complex (VPN from Workers, not officially supported) - Security concerns (VPN from cloud to local) - Latency through VPN
Cost: - Workers: $0-5/month - Tailscale: $0 (free for personal use) - Total: $0-5/month
Not Recommended: VPN from cloud to local violates your security requirement.
Option 4: Single VPS for One App¶
Simple, traditional approach: One VPS running your app and database.
4A: Hetzner Single VPS¶
Instance: CX11 (1 vCPU, 2GB RAM, 20GB SSD) Cost: €3.79/month (~$4.10)
What You Get: - Full VPS (not shared/serverless) - Root access - 20TB included traffic - Dedicated resources
Setup: 1. Create Hetzner account and VPS 2. Install Docker and Docker Compose 3. Deploy app + PostgreSQL via docker-compose 4. Configure Caddy/nginx for HTTPS
Pros: - Extremely cheap ($4/month) - Full control - Can run multiple apps if needed - Predictable performance
Cons: - Manual server management (updates, security) - Single point of failure - Need to configure HTTPS, backups - More sysadmin work
Cost Breakdown: - VPS: $4.10/month - Total: $4.10/month
4B: DigitalOcean Basic Droplet¶
Instance: Basic Droplet (1GB RAM, 1 vCPU, 25GB SSD) Cost: $6/month
Similar to Hetzner but: - More expensive ($6 vs $4) - Better docs/tutorials - Easier dashboard
Cost: $6/month
4C: Oracle Cloud Free Tier (Single Instance)¶
Instance: VM.Standard.E2.1.Micro (1 OCPU, 1GB RAM) Cost: $0 (Always Free)
What You Get: - 2x free AMD VMs OR 4x ARM VMs - 200GB total storage - Actually free forever
Pros: - Free - Can run app + small database - Permanent free tier
Cons: - ARM architecture (2GB ARM instance free) - Account approval process - Limited resources (1GB RAM tight)
Cost: $0/month
Comparison Matrix¶
| Option | Monthly Cost | Complexity | Database Included | Local Network Exposed | Setup Time |
|---|---|---|---|---|---|
| Vercel Free | $0-25 | Low | No ($0-25 extra) | No | 30 min |
| Railway | $0-15 | Low | Yes | No | 1 hour |
| Fly.io | $0-10 | Medium | Yes (small) | No | 1-2 hours |
| Cloudflare Pages | $0-25 | Medium | No ($0-25 extra) | No | 1-2 hours |
| Render | $0-14 | Low | Yes ($7/mo) | No | 1 hour |
| DMZ VLAN + Cloudflare | $0 | High | Separate needed | No (isolated) | 4-8 hours |
| Hybrid (Static + API) | $5-35 | Medium | Depends | Varies | 2-4 hours |
| Hetzner VPS | $4 | Medium | DIY | No | 2-3 hours |
| DO Droplet | $6 | Medium | DIY | No | 2-3 hours |
| Oracle Free | $0 | Medium | DIY | No | 2-4 hours |
Recommended Combinations¶
Recommendation 1: Absolute Minimal Cost¶
Goal: Spend $0, test external access
Setup: 1. App: Vercel Free Tier (Next.js) 2. Database: Supabase Cloud Free Tier 3. Cost: $0/month
Limitations: - Supabase pauses after 7 days inactivity - Can't use for production long-term
Use Case: Experimentation, proof-of-concept
Recommendation 2: Budget Production Single App¶
Goal: Reliable, cheap, single app externally accessible
Setup: 1. App: Railway or Fly.io ($5-10/month) 2. Database: Included (Railway PostgreSQL or Fly Postgres) 3. Cost: $5-10/month
Alternative: 1. App: Hetzner CX11 VPS ($4/month) 2. Database: Self-hosted PostgreSQL on same VPS 3. Setup: Docker Compose 4. Cost: $4/month
Use Case: Single production app, minimal budget
Recommendation 3: New Lightweight App (Your Use Case)¶
Goal: Create new app specifically for external access, full Next.js + DB
Setup: 1. App: Vercel Hobby (free) or Railway ($0-10/mo) 2. Database: Supabase Cloud Pro ($25/mo) - Shared with other apps (schema isolation) - Professional backup/management 3. Cost: $0-35/month
Workflow: 1. Create new Next.js app locally (standard skeleton) 2. Add schema to existing k8s Supabase OR use Supabase Cloud 3. Deploy to Vercel/Railway (git push) 4. Configure env vars (Supabase URL, keys) 5. Access externally via Vercel URL or custom domain
Why This Works: - Minimal infrastructure (PaaS handles it) - Keep existing local setup unchanged - New app isolated from local network - Can iterate quickly (git-based deploys)
Time to Deploy: 2-4 hours (including app creation)
Recommendation 4: DMZ Isolation (Learning Experience)¶
Goal: Learn VLANs/network security, keep apps local
Setup: 1. Network: DMZ VLAN (10.89.98.0/24) 2. Container: LXC in DMZ (no access to main network) 3. Tunnel: Cloudflare Tunnel for external access 4. Database: Separate Supabase Cloud project OR PostgreSQL in DMZ 5. Cost: $0 (if using free Supabase) or $25/mo (Supabase Pro)
Learning Outcomes: - VLAN configuration on Proxmox - Firewall rule management - Network isolation verification - Cloudflare Tunnel setup
Time Investment: 6-10 hours (initial setup + testing)
Best For: Want to learn networking, have time to invest
Next Steps¶
Based on your answers: - Want new lightweight app: Vercel/Railway (Recommendation 3) - Want to learn VLANs: DMZ approach (Recommendation 4) - Want absolute cheapest: Hetzner VPS $4/mo (Recommendation 2)
Questions to decide:
- Do you want to build a new app from scratch?
- Yes → Vercel/Railway path (2-4 hours, $0-35/month)
-
No → Consider which existing app to migrate
-
Want to learn network segmentation?
- Yes → DMZ VLAN path (6-10 hours, $0-25/month)
-
No → Cloud PaaS path (simpler)
-
Budget preference?
- $0-10/month → Vercel Free + Railway OR Hetzner VPS
- $25-35/month → Professional setup (Supabase Pro + Vercel/Railway)
-
Willing to invest time for $0 → DMZ VLAN + free Supabase
-
Time available for setup?
- 2-4 hours → Vercel/Railway (quick)
- 6-10 hours → DMZ VLAN (learning investment)
Let me know which direction interests you most and I can provide detailed step-by-step implementation!
Appendix: DMZ VLAN Commands Reference¶
Quick command reference for DMZ VLAN setup:
# 1. Create VLAN interface on Proxmox host
cat >> /etc/network/interfaces <<EOF
auto vmbr0.98
iface vmbr0.98 inet static
address 10.89.98.1/24
EOF
ifreload -a
# 2. Create DMZ container
pct create 199 local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst \
--hostname dmz-external --cores 2 --memory 2048 --rootfs local-lvm:20 \
--net0 name=eth0,bridge=vmbr0,tag=98,ip=10.89.98.10/24,gw=10.89.98.1 \
--nameserver 1.1.1.1 --unprivileged 1 --features nesting=1
# 3. Configure firewall
nano /etc/pve/firewall/cluster.fw
# Add rules to block DMZ <-> Main traffic
# 4. Test isolation
pct exec 199 -- ping 10.89.97.1 # Should FAIL
pct exec 199 -- ping 8.8.8.8 # Should SUCCEED
# 5. Install Cloudflare Tunnel
pct exec 199 -- bash
curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
dpkg -i cloudflared.deb
cloudflared tunnel login
cloudflared tunnel create homelab-dmz
Firewall rule template:
# /etc/pve/firewall/cluster.fw
[OPTIONS]
enable: 1
[RULES]
# Block DMZ → Main
-i vmbr0.98 -d 10.89.97.0/24 -j DROP
# Block Main → DMZ (except Proxmox)
IN ACCEPT -i vmbr0 -s 10.89.97.1 -d 10.89.98.0/24
-i vmbr0 -d 10.89.98.0/24 -j DROP
# Allow DMZ → Internet
IN ACCEPT -i vmbr0.98 -s 10.89.98.0/24 -d 0.0.0.0/0