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.
Self-hosted SearXNG for search. No tracking, no profile building, just aggregated results.
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, Nextcloud, Wazuh, Paperless, CrowdSec, OpenCanary, Minecraft map, and the rest.
Document OCR and archive. Ingests from IMAP and uploads; docs stay on the homelab next to Nextcloud and mail.
Single-node SIEM (indexer + manager + dashboard) with host agent and phone alerts for high-severity events. Capped so it shares the box with Minecraft.
Personal file sync and sharing - photos, documents, calendars - under cloud.dobasmp.net. Data lives on the homelab (not a public cloud). Desktop and phone apps talk to your box over HTTPS.
Collaborative IDS: reads Caddy access logs and SSH auth, detects scanners and brute-force, and the host firewall bouncer drops bad IPs automatically. Local status page at crowdsec.dobasmp.net shows bans and alerts; 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.
Nightly encrypted snapshots to the USB stick: stack configs, AdGuard, cron, plus PostgreSQL dumps for Nextcloud. Retention: 7 daily, 4 weekly, 2 monthly. This is disaster recovery for configs and DBs - not a full mirror of every file you upload.
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, and PostgreSQL dumps for Nextcloud. 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. Mail MX and IMAP hit the house on purpose; everything else that needs a UI stays tunnel or Tailscale.
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.
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.