By LogicSystemIQ | May 24, 2026 | 9 min read

The hardware (less than you think)

The host is a Dell OptiPlex 7060 Micro. It runs Windows 11 because I had it lying around. The CPU is an Intel i7-8700T, which is the 35-watt low-power variant of the i7-8700. Not the fastest chip on the planet, but it sips power and stays quiet under a desk.

Component Spec
Form factor Dell OptiPlex 7060 Micro (1L footprint)
CPU Intel i7-8700T (6 cores, 12 threads, 35W TDP)
RAM 32GB DDR4 2666 MT/s
Storage 2.73TB total, 1.25TB free
GPU Intel UHD 630 integrated (no dedicated)
Network 1GbE built in

The host runs Windows because that is where I do client work, and the VM is just one window I open when I need to. If you have a Linux-only machine, this works even better because you skip the VM layer entirely and run Docker on the host.

The VM (where everything actually lives)

Setting Value
Hypervisor VMware Workstation Pro 17.x (free for commercial use, November 2024)
Guest OS Ubuntu Server 24.04 LTS
vCPU 1 socket, 4 cores
RAM 12GB
Disk 150GB on a single VMDK file
Networking Bridged (gets a real LAN IP from my router)
VT-x passthrough Disabled (Hyper-V holds the extensions for WSL2)
VMware Tools Installed
Time sync Enabled

The 12GB allocation is intentional. The host has 32GB, but Windows + Edge + Slack + whatever else I am doing easily eats 16GB. Leaving 12GB to the VM and keeping a 4GB buffer for the host has worked smoothly.

The container roster (all 27)

Here is exactly what runs on the VM, grouped by deploy phase.

Phase 1: core infrastructure (8 containers)

Container Image Job
dockge louislam/dockge:1 Web UI for managing all the compose stacks
caddy caddy:2-alpine Reverse proxy, auto-HTTPS for LAN via internal CA, internal routing
cloudflared cloudflare/cloudflared:latest Outbound tunnel to Cloudflare edge for public hostnames
postgres postgres:16-alpine Shared Postgres for Vaultwarden, Twenty, GoTrue, n8n
dozzle amir20/dozzle:latest Real-time log viewer for every container
vaultwarden vaultwarden/server:latest Password manager (Bitwarden-compatible, but self-hosted)
beszel henrygd/beszel:latest System and container monitoring with ntfy alerts
beszel-agent henrygd/beszel-agent:latest Metric collector that reports to Beszel hub
diun crazymax/diun:latest Image update notifier (no auto-updates, just ntfy)
ntfy binwiederhier/ntfy:latest Self-hosted push notifications to my phone

Wait, that is 10, not 8. Phase 1 added the ntfy + diun pair after the initial bring-up. Same idea, slightly bigger than the original spec.

Phase 2: business apps (11 containers)

Container Image Job
redis redis:7-alpine Shared cache + queue for AppFlowy + Twenty
clickhouse clickhouse/clickhouse-server:24-alpine Analytics DB for Plausible
plausible ghcr.io/plausible/community-edition:v3 Self-hosted privacy-respecting web analytics
gotrue appflowyinc/gotrue:latest Auth service for AppFlowy (their fork of Supabase GoTrue)
minio minio/minio:latest S3-compatible storage for AppFlowy and Twenty file uploads
appflowy_postgres pgvector/pgvector:pg16 Dedicated Postgres for AppFlowy (needs pgvector extension)
appflowy_cloud appflowyinc/appflowy_cloud:latest AppFlowy backend (Notion alternative)
appflowy_web appflowyinc/appflowy_web:latest AppFlowy frontend
twenty_server twentycrm/twenty:latest Twenty CRM server (Pipedrive alternative)
twenty_worker twentycrm/twenty:latest Twenty CRM BullMQ background worker

Add-on stacks (7 containers, deployed over the last 48 hours)

Container Image Job
n8n n8nio/n8n:1 Workflow automation (greenfield home-stack instance)
firecrawl_api ghcr.io/firecrawl/firecrawl:latest Self-hosted web scraper API
firecrawl_playwright ghcr.io/firecrawl/playwright-service:latest Headless Chromium for scraping
firecrawl_redis redis:alpine Dedicated Redis for Firecrawl (upstream warns against sharing)
firecrawl_rabbitmq rabbitmq:3-management Job queue for Firecrawl
firecrawl_nuq_postgres ghcr.io/firecrawl/nuq-postgres:latest Crawl history database
openwebui ghcr.io/open-webui/open-webui:main Self-hosted Claude / ChatGPT replacement (via OpenRouter)

Add it all up and you get 27.

Where the 12GB of RAM goes

Here is the actual breakdown from free -h right now:

               total        used        free      shared  buff/cache   available
Mem:            11Gi       7.0Gi       493Mi       171Mi       4.8Gi       4.8Gi

The big consumers:

Tier Approximate RAM
Postgres + ClickHouse + the 4 Redis-class processes 1.2GB combined
AppFlowy stack (Cloud + Web + GoTrue + dedicated PG) 1.5GB
Twenty CRM (server + worker) 1.0GB
Plausible 350MB
Firecrawl playwright headless Chromium pool 600MB to 1.5GB depending on active scrapes
Open WebUI 350MB
n8n 250MB
Everything else (Caddy, cloudflared, Dockge, Dozzle, Beszel, Vaultwarden, ntfy, diun) under 600MB combined

Buffers and cache eat the rest, which is exactly what you want, that is Linux working as designed.

The biggest variable is Firecrawl during an active scrape. Each headless Chromium tab is roughly 200MB. The compose limits it to 3 concurrent jobs and 5 browser pool, which caps Firecrawl spend around 1.5GB. If a scrape kicks off while Twenty is indexing and AppFlowy is doing background sync, that is the load spike where I might hit 9GB used, 3GB free. Still fine.

The two architectural choices that made this work

One: shared Postgres for almost everything

Instead of running a dedicated Postgres per app, one shared postgres:16-alpine container hosts databases for Vaultwarden, Twenty CRM, GoTrue, and n8n. Each gets its own database and role, scoped with grants. The exception is AppFlowy, which needs pgvector and gets a dedicated pgvector/pgvector:pg16 container.

Single Postgres saves about 700MB of RAM versus running four of them. The trade-off is one upgrade hits all apps. Acceptable in exchange for the savings.

Two: Caddy + Cloudflare Tunnel instead of port forwarding

Every public-facing service routes through Caddy on a private Docker network. Caddy talks to cloudflared, which holds an outbound persistent tunnel to Cloudflare edge. No ports forwarded on my router. No public IP needed on the host. Cloudflare Access sits in front of admin tools with email OTP gating, and webhook-only endpoints (Stripe receivers, n8n triggers) get bypass policies so external services can hit them without an OTP prompt.

This setup costs zero dollars on Cloudflare's free tier and is more secure than anything I could have hand-rolled.

What I cancelled, what I saved

Rough monthly savings, based on what each replaced:

Replaced SaaS Self-hosted equivalent Monthly savings
Last password manager subscription Vaultwarden $3
Notion team plan AppFlowy $10
Pipedrive starter Twenty CRM $14
Google Analytics + privacy concerns Plausible $0 in dollars, big in privacy
Uptime monitoring SaaS Beszel + ntfy $7
n8n cloud Self-hosted n8n $20
ChatGPT Plus Open WebUI via OpenRouter $20 (replaced with ~$5 pay-as-you-go)
Firecrawl SaaS tier Self-hosted Firecrawl $20 to $50
Total ~$50 to $90 per month

Annualized, somewhere between $600 and $1,080. Enough to fund the GMKtec K11 mini PC that this whole stack will migrate to once it arrives.

What does not work, and why I do not care

A few things this setup is honestly bad at:

  • Heavy LLM inference: i7-8700T with no dedicated GPU is useless for local Ollama. Open WebUI uses OpenRouter for model calls. That is a permanent decision until the K11 hardware arrives.
  • Sustained Playwright scraping: Firecrawl works fine for ad-hoc and low-volume crawls. If I tried to scrape 10,000 pages a day, the thermal throttling on the 35W chip would punish me. I run it under 100 pages a day in practice.
  • Real high availability: if the host dies, everything goes down until I boot another machine. Daily Kopia snapshots to Backblaze B2 means I can restore to new hardware in roughly 30 minutes. Good enough for personal infrastructure.

What is next

The K11 (GMKtec, Ryzen 9 8945HS, 64GB DDR5, dual NVMe) is the planned upgrade. Once it lands, the migration is roughly:

  1. Restore Kopia snapshot from B2 to the K11 host
  2. Rebuild Docker network with the same compose files
  3. Update Cloudflare Tunnel public hostnames to point at the new tunnel
  4. Cut over DNS, verify each service end to end
  5. Repurpose the OptiPlex as a dev workstation

The stack is designed to be portable. Everything is in git, every secret is in Vaultwarden, every data path is in the backup. The whole thing should migrate cleanly in an afternoon.

Frequently asked questions

Why not Proxmox or unRAID instead of VMware Workstation?

I will use Proxmox on the K11. On the Dell OptiPlex, VMware Workstation Pro was the path of least resistance because it ran on my existing Windows install. No bare metal swap needed. Free for commercial use since November 2024.

Why not Kubernetes?

For 27 containers across 6 stacks on a single host, Docker Compose plus Dockge for the UI is the right tool. Kubernetes would be roughly 1.5GB of overhead for the control plane and zero practical benefit at this scale.

How do you back this up?

Kopia handles snapshots of /opt/stack/data and the Postgres dumps every night at 2 AM. Two destinations: local USB drive (when plugged in) and Backblaze B2 (always). Daily snapshots, 7 daily / 4 weekly / 6 monthly retention. Monthly test restore to a tmp directory to verify backups actually work.

What about security?

Cloudflare Access in front of admin tools (email OTP for me, IP allowlist for service-to-service calls). Vaultwarden holds every credential, with Argon2id hashed admin token. Postgres is on a separate internal Docker network with no external port. UFW on the host blocks everything except SSH from LAN. Daily automatic security updates via unattended-upgrades.

Can I see the actual compose files?

Open to it. Drop me a comment or DM on LinkedIn and I will share the relevant compose snippets sanitized for public consumption.

Bottom line

You do not need a $3,000 server, a Kubernetes cluster, or a SaaS subscription per app to run a serious self-hosted stack. A used mini PC, a free hypervisor, and Docker Compose with discipline gets you 90 percent of the way there. The remaining 10 percent (HA, GPU inference, multi-host failover) is what real infrastructure budgets are for, and most of us do not need them.

If you are thinking about starting your own self-hosted stack, the question is not "what is the perfect architecture." It is "what is the smallest thing I can stand up this weekend that I will actually use." Pick one tool you are paying for, replace it, and let momentum carry you.


LogicSystemIQ is an IT managed services and SaaS studio based in Peabody, Massachusetts. We build DaycarePro (daycarepro.cloud), a trilingual SaaS for licensed home daycare providers. Reach us at (978) 815-1047 or Support@LogicSystemiq.com.

Scroll to Top