Problem
I wanted a public portfolio that shows off the homelab (live status, a read-only terminal, the whole thing), but my ISP blocks inbound ports. I can't just point a domain at my house and call it a day.
christopher@homelab:~$ whoami
christopher. I build and run a homelab at home
christopher@homelab:~$ cat mission.txt
christopher@homelab:~$ _
How traffic reaches the portfolio, the live terminal, and the Fabric SMP game server.
A quick case study on shipping a public portfolio when your ISP won't let inbound ports through.
I wanted a public portfolio that shows off the homelab (live status, a read-only terminal, the whole thing), but my ISP blocks inbound ports. I can't just point a domain at my house and call it a day.
I put the static site on Cloudflare Pages (free, fast, static hosting, no public origin on this box). My afraid.org subdomain forwards to Pages so I get a clean URL. For the live terminal, a Cloudflare quick tunnel on the homelab exposes a WebSocket to a read-only gateway with sensitive paths blocked and output redacted. Status syncs from Maintenant on a cron job.
Public site with zero open inbound ports. Visitors get HTTPS, live monitoring, and a sandboxed shell peek at the stack. Deploys are a script on the homelab that pushes to Pages. When the box reboots, Docker brings everything back in about two minutes.
Docker on the OptiPlex 5040, with Fabric SMP on the same host. Caddy terminates TLS for LAN services under dobasmp.net. Mail is self-hosted too.
Self-hosted email on the OptiPlex: Stalwart for IMAP/SMTP with Let's Encrypt, Roundcube for webmail over a Cloudflare Tunnel. Phone Mail on cellular reaches home through Tailscale (exit node). Outbound relay keeps deliverability sane on a residential line.
I use AdGuard Home with Unbound for network-wide blocking and recursive DNS. Queries go to the root servers, not some upstream resolver. LAN rewrites keep mail and apps on the OptiPlex when I'm home.
DoH, DoT, and DoQ endpoints so devices can resolve DNS privately. Works off-network too when I'm on the VPN.
Tailscale mesh VPN with subnet routes, an exit node, and MagicDNS. I reach LAN services from anywhere without opening extra ports - including Mail on cellular when carriers block IMAP/SMTP.
Portfolio presence logging: every hit on christopher.isageek.net records IP, geo, device, and referer into a local SQLite dashboard. Public ingest; dashboard is password-protected.
Caddy as reverse proxy and TLS. Let's Encrypt certs via DNS-01, so every service gets a clean HTTPS URL. Local status page at proxy.dobasmp.net lists configured hosts.
Maintenant handles HTTP/TCP checks, host and container metrics, container logs, TLS expiry, and a status page.
LAN startpage for AdGuard, Maintenant, Mail, Trades, Wazuh, CrowdSec, OpenCanary, Kismet, Minecraft, Visitors, and the rest.
Single-node SIEM (indexer + manager + dashboard) with host agent and phone alerts for high-severity events. Capped so it shares the box with Minecraft.
Collaborative IDS: reads Caddy access logs and SSH auth, detects scanners and brute-force, and the host firewall bouncer drops bad IPs automatically. Alerts and decisions live in the CrowdSec Console (app.crowdsec.net); CLI cscli still available on the box.
LAN honeypot (SSH, RDP, MySQL, Redis, FTP, Telnet, VNC, NAS login). Hits send ntfy alerts with source IP and service.
Dedicated USB Wi-Fi radio in monitor mode on the OptiPlex (Ethernet stays the uplink). Kismet passively watches nearby APs, clients, probes, and 802.11 alerts. The console is LAN/Tailscale only — not on this site and not tunneled.
Nightly encrypted snapshots to the USB stick: stack configs, AdGuard, cron, and compose files. Retention: 7 daily, 4 weekly, 2 monthly. Disaster recovery for configs — not a full mirror of every uploaded file.
A modded Fabric server with Geyser for Bedrock cross-play. Java players hit TCPShield first so the home IP stays out of DNS, then playit tunnels TCP to the box. Bedrock goes through playit only. Maintenant and this site show online status and player count - no join info posted here.
Read-only shell into /opt/stacks over a Cloudflare Tunnel. You can't edit anything. Sensitive paths are blocked and output gets redacted.
Pulled live from Maintenant and the homelab host. Updates every minute.
What happens when the box reboots, how status stays fresh, and what I actually monitor.
fetch_status.py runs every minute via cron. It pulls Maintenant endpoint checks, host CPU/RAM/disk, deploy timestamp, and incident log into status.json, then deploys if the hash changed./mnt/backup-usb/restic - compose files, Caddyfile, AdGuard config, cron. Password in /opt/stacks/.restic.pass (root only)..bak files before edits.How I limit blast radius on a box that is partly public-facing.
Portfolio stays on Cloudflare Pages with outbound tunnels for the live terminal. UI services stay on tunnels or Tailscale — nothing admin-facing is published on the WAN.
Path jail to /opt/stacks only. Blocked dotfiles, keys, env files, and databases. Output redacted. Commands audit-logged.
Terminal gateway runs read-only rootfs, non-root user, cap_drop: ALL, and no-new-privileges. Secrets stay outside deploy paths.
Caddy terminates HTTPS with Let's Encrypt via DNS-01. HSTS on proxied services. Encrypted DNS (DoH/DoT/DoQ) for clients that support it.
OpenCanary listens on unused LAN ports that are not published to the internet. Probes raise ntfy alerts with source IP and service.
Kismet on a USB adapter in monitor mode. It sees SSIDs, probe requests, and layer-2 wireless alerts in the room. It does not join Wi-Fi, does not decrypt sessions, and the UI is not published on the WAN.
One recycled desktop today. Three phases to a fully local, VLAN-segmented homelab with zero cloud AI bills.
Goal: VLAN segmentation + zero-utility-cost local AI coding (Aider / Continue.dev in VS Code)
Dodge the error logs. Click, tap, or press Space to jump.
Power-ups: Tailscale = one free hit · AdGuard = block one error log
I run a homelab at home: DNS, VPN, reverse proxy, monitoring, and Docker. I built this portfolio site and the automation around it myself.
Building toward systems administration. The lab is where I practice what I do at work: monitoring, DNS, Linux, documentation, and recovery.