support@technetguy.site Remote IT Services · Worldwide · Get a Quote →
Cloud Infrastructure

Scaling the Stack

Virtualization, proxies, fleet automation, and edge computing.

The Nginx Reverse Proxy

This site sits behind a reverse proxy that terminates TLS and routes requests to the right backend.

User Browser
↓ HTTPS Request
Nginx Web Server
↓ Static File or Upstream
/var/www/html/index.html
Data center server aisle

Photo · Wikimedia Commons

Where Cloud Meets This Server

🖥️

Virtualization

This site runs on a small cloud VM, sharing physical hardware with other tenants via a hypervisor.

🔄

Reverse Proxies

Nginx terminates TLS and routes requests to the right backend — static files, PHP-FPM, or a local Flask app.

📦

Backup & Storage

Private, mesh-VPN-secured cloud backup is one of the services offered directly from this infrastructure.

Hybrid Virtualization Platform

Two hypervisor platforms, one operational model. A Core site runs Proxmox VE; a Remote site runs VMware ESXi — and both report into the exact same monitoring stack, config-management story, and backup cadence.

A Proxmox VE core site and a VMware ESXi remote site both report into one centralized Ansible/Netdata/Prometheus/Loki/Grafana operations layer, connected by a nightly encrypted backup-sync link between the two sites Core Site Proxmox VE VMs + LXCs ~14-node home lab fleet Remote Site VMware ESXi VMs ~6-node cloud fleet nightly encrypted backup sync one monitoring & config-mgmt layer Centralized Ops Layer Ansible — one config-management baseline Netdata / Prometheus / Loki / Grafana one pane of glass, both hypervisors
Different hypervisors at each site, same operational model everywhere — one Ansible baseline, one monitoring stack, one backup cadence, regardless of which hypervisor is underneath.
Blade server hardware used for virtualization workloads

Photo · Wikimedia Commons

20+
Hosts Under Active Management
80%
Capacity Ceiling, Auto-Cleanup

Why two hypervisor platforms instead of forcing everything onto one: each site runs whatever actually fits its hardware and history, rather than a costly forced migration for the sake of uniformity. What stays uniform instead is the operational layer — one Ansible baseline applied fleet-wide, one Netdata/Prometheus/Loki/Grafana monitoring stack pulling from both sites over the mesh, and one backup cadence, whether the workload underneath is a Proxmox VM, an LXC container, or a VMware guest.

That combined footprint is 20+ hosts under active management today — the ~14-node home lab fleet plus the ~6-node cloud fleet — every node tracked against an 80% disk-capacity ceiling with automated cleanup, not a manual check someone remembers to run. Full detail on each side of this in the Home Lab & Self-Hosted Infrastructure case study and the Cloud Fleet & Infrastructure-as-Code case study.

Fleet Patterns That Actually Get Used

Managing a handful of VMs by hand works for a while. Managing more than three or four means these four things stop being optional: containerized services, config-as-code, centralized monitoring, and automated backups that leave the box they were taken on.

A typical three-service stack: app, database, and a proxy in front of both — the shape most small production apps actually run in.

yaml
version: "3.9"

services:
  app:
    build: .
    restart: unless-stopped
    environment:
      - DATABASE_URL=postgresql://app:${DB_PASSWORD}@db:5432/app
    depends_on:
      - db
    networks:
      - internal

  db:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      - POSTGRES_USER=app
      - POSTGRES_PASSWORD=${DB_PASSWORD}
      - POSTGRES_DB=app
    volumes:
      - db_data:/var/lib/postgresql/data
    networks:
      - internal

  caddy:
    image: caddy:2-alpine
    restart: unless-stopped
    ports:
      - "443:443"
      - "80:80"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
    depends_on:
      - app
    networks:
      - internal

networks:
  internal:

volumes:
  db_data:
  caddy_data:

The same baseline applied to every node in a fleet — security posture, monitoring agent, and log shipping installed identically everywhere, instead of living in someone's memory of what they typed six months ago.

yaml
# playbooks/baseline.yml — applied to every node in the fleet
- hosts: all
  become: true
  vars:
    admin_ssh_key: "{{ lookup('file', 'files/admin.pub') }}"

  roles:
    - role: common          # timezone, unattended-upgrades, base packages
    - role: security         # ufw, fail2ban, sshd hardening
    - role: monitoring        # netdata agent + Prometheus node_exporter
    - role: log_shipping      # promtail -> centralized Loki

  tasks:
    - name: Ensure admin key is present
      ansible.posix.authorized_key:
        user: "{{ ansible_user }}"
        key: "{{ admin_ssh_key }}"
        state: present

    - name: Enable and start fail2ban
      ansible.builtin.systemd:
        name: fail2ban
        enabled: true
        state: started
bash
ansible-playbook -i inventory/hosts.yml playbooks/baseline.yml --limit fleet

One Prometheus instance pulling metrics from every node over the mesh VPN, feeding Grafana dashboards and alerting rules from a single pane of glass.

yaml
# /etc/prometheus/prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  - job_name: 'node'
    static_configs:
      - targets:
          - 'node1.mesh.internal:9100'
          - 'node2.mesh.internal:9100'
          - 'node3.mesh.internal:9100'
        labels:
          fleet: 'primary'

  - job_name: 'nginx'
    static_configs:
      - targets: ['node1.mesh.internal:9113']

rule_files:
  - 'alerts/*.yml'

alerting:
  alertmanagers:
    - static_configs:
        - targets: ['alertmanager:9093']

Dumps, verifies its own output isn't corrupt, ships the copy off-box, then prunes anything past the retention window. A backup that never leaves the machine it was taken on isn't a backup.

bash
#!/usr/bin/env bash
# /usr/local/sbin/pg-backup.sh — nightly logical backup with retention
set -euo pipefail

DB_NAME="app"
BACKUP_DIR="/var/backups/postgres"
RETENTION_DAYS=14
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
DEST="${BACKUP_DIR}/${DB_NAME}_${TIMESTAMP}.sql.gz"

mkdir -p "$BACKUP_DIR"

pg_dump -U app -h 127.0.0.1 "$DB_NAME" | gzip > "$DEST"

# Verify the dump isn't empty/corrupt before trusting it
gzip -t "$DEST"

# Copy off-box — a backup on the same disk as the database it backs up
# is a second copy of the same failure, not a backup
rsync -az "$DEST" backup-host:/srv/backups/postgres/

find "$BACKUP_DIR" -name "${DB_NAME}_*.sql.gz" -mtime "+${RETENTION_DAYS}" -delete

echo "$(date -Is) backup complete: $DEST"

Where This Runs For Real

This exact combination — Ansible-managed baseline, mesh VPN, centralized Netdata/Prometheus/Loki/Grafana monitoring, and fail2ban applied fleet-wide — runs a real multi-node cloud fleet today, including the incident where two nodes disappeared without warning and recovery happened from infrastructure-as-code instead of a scramble. Full details in the Cloud Fleet & Infrastructure-as-Code case study, and the home-lab side of the same architecture is in Home Lab Infrastructure.