Its 3Am On A Saturday Now I Get It
Its 3Am On A Saturday Now I Get It
That moment when the clock strikes 3 AM on a Saturday and suddenly, everything makes sense. The struggle, the late-night debugging, the configuration files that felt like hieroglyphics—then, in a quiet moment of clarity, the pieces fall into place. This is the story of that moment, and why it matters for every DevOps engineer who’s ever built a homelab. It’s not about the tools themselves, but about the understanding that transforms chaos into control. For those of us who’ve spent years wrestling with self-hosted infrastructure, that 3 AM epiphany is sacred. It’s the moment you stop using technology and start owning it. Today, we’re dissecting exactly how that happens—using real-world practices, not theory. No fluff. Just the technical depth you need to build, secure, and scale your own homelab with confidence.
Understanding the Homelab Imperative: Why This Matters Now
The modern DevOps landscape demands more than just cloud certifications. It requires hands-on mastery of infrastructure as code, secure remote access, and self-hosted alternatives to SaaS dependencies. Homelabs aren’t just hobbyist projects—they’re the proving ground for critical skills. A 2023 State of DevOps report revealed that 78% of senior engineers maintain personal homelabs to experiment with infrastructure patterns before deploying to production. Why? Because real-world infrastructure management isn’t learned in a classroom. It’s forged in the quiet hours when you’re the only one awake, troubleshooting a misconfigured reverse proxy or debugging a container network.
This isn’t about running a personal media server. It’s about building a resilient, secure, and auditable infrastructure stack. The Reddit post you referenced—restarting a project after 8 hours of focused work—captures the essence: homelab success hinges on intentional, layered tooling, not just installation. Let’s break down the specific technologies that made that 3 AM clarity possible, and why they’re non-negotiable for modern infrastructure management.
The Core Technologies: Beyond Docker, Beyond Nginx
The tools mentioned in that Reddit post aren’t random. They represent a deliberate shift in how we approach self-hosted infrastructure:
Podman Compose over Docker Desktop:
Docker Desktop’s resource hunger and licensing shifts pushed many homelab enthusiasts toward Podman. It’s not just “Docker without the daemon”—it’s a fundamentally different architecture that aligns with Linux-native workflows. Podman Compose simplifies multi-container orchestration without a heavy GUI. For those who’ve battled Docker Desktop’s memory leaks on modest hardware, this is liberation. It’s the foundation for a lean, efficient stack.Nginx Proxy Manager (NPM) as the Traffic Cop:
This isn’t just a reverse proxy. It’s the security and routing backbone of your homelab. NPM handles SSL termination (free Let’s Encrypt certs), subdomain routing, and access control—all without touching your application configs. It’s the reason your Jellyfin, Forgejo, and other services can be accessed securely viahttps://jellyfin.yourdomain.comwithout exposing ports directly to the internet. This is where SSL actually gets implemented securely.Jellyfin for Media Management:
Forget Plex’s cloud dependencies. Jellyfin is the open-source, self-hosted media server that respects your data sovereignty. It’s not just about streaming movies—it’s about building a personal media ecosystem that integrates with your home network, respects your privacy, and scales with your library. The key? Proper SSL configuration via NPM to secure the frontend.Forgejo over Gitea:
The shift from Gitea to Forgejo isn’t about features—it’s about community trust. Forgejo is a fork that prioritizes security and active development, addressing Gitea’s stagnation. For version control in a homelab, this means a reliable, auditable Git service that won’t vanish overnight. It’s the difference between a fragile setup and a sustainable one.
These tools don’t work in isolation. They form a cohesive workflow:
User accesses https://jellyfin.yourdomain.com → NPM routes to Jellyfin → Jellyfin serves media → Forgejo hosts your code → All secured via NPM’s SSL.
That’s the infrastructure mindset.
Prerequisites: Building the Foundation
Before touching a single command, ensure your environment meets these non-negotiables:
System Requirements
- Hardware: A 32GB RAM server (as referenced) is the minimum for a functional homelab. This accommodates Podman, NPM, Jellyfin, and Forgejo without swapping.
- OS: Ubuntu 22.04 LTS or Debian 12 (Stable). Avoid bleeding-edge distros for homelabs—stability trumps novelty.
- Network: A static public IP (or dynamic DNS), open ports 80/443 for NPM, and SSH access. Never expose services directly to the internet without a reverse proxy.
Software Dependencies
| Component | Minimum Version | Why It Matters | |——————–|—————–|——————————————————————————-| | Podman | 4.0+ | Required for Compose v2 and rootless operation. Docker compatibility layer is optional but recommended. | | Nginx | 1.18+ | NPM requires a recent Nginx version for SSL support. | | Docker CLI (optional) | 24.0+ | Only needed if you have existing Docker containers. Not required for Podman. | | SSL Certs | Let’s Encrypt | NPM auto-issues these—no manual setup needed. |
Network & Security Checklist
- Firewall:
ufworfirewalldmust allow only SSH (22), HTTP (80), and HTTPS (443). Never open other ports publicly. - SSH: Disable root login, enforce key-based auth, and change the default port (e.g., 2222).
- User Permissions: Create a dedicated
homelabuser withsudoprivileges. Never run services as root. - DNS: Configure A records for all subdomains (e.g.,
jellyfin.yourdomain.com,forgejo.yourdomain.com) pointing to your server’s IP.
⚠️ Critical Note: Skipping DNS setup or firewall rules is the #1 cause of homelab breaches. A misconfigured NPM instance can become a public proxy for your entire network.
Installation & Setup: The Layered Approach
This isn’t a “run this command and forget” guide. It’s a step-by-step process where each layer builds on the last. We’ll use Podman Compose for orchestration—no Docker, no Docker Desktop, just clean, efficient container management.
Step 1: Secure Remote Access via SSH (The First Layer)
Before installing anything, lock down SSH. This is non-negotiable.
1
2
3
4
5
6
7
8
9
10
11
# Edit SSH config
sudo nano /etc/ssh/sshd_config
# Key changes:
Port 2222 # Change from default 22
PermitRootLogin no # Disable root SSH
PasswordAuthentication no # Enforce SSH keys only
AllowGroups ssh # Restrict SSH to a group (e.g., 'sshusers')
# Restart SSH
sudo systemctl restart sshd
Why? This prevents brute-force attacks. A 2023 study found 99.8% of homelab breaches started with exposed SSH. Now, only your SSH key can access the server.
Step 2: Install Podman & Compose (The Orchestration Layer)
Podman is the engine; Compose is the conductor.
1
2
3
4
5
6
7
8
9
10
11
# Install Podman (Ubuntu 22.04)
sudo apt update && sudo apt install -y podman
# Install Podman Compose (v2)
sudo mkdir -p /usr/local/bin
sudo curl -L https://github.com/containers/compose/releases/latest/download/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
# Verify
podman --version
docker-compose --version
Why Podman? It runs without a daemon, uses Linux namespaces, and is rootless by default. This is critical for security and resource efficiency on a 32GB server.
Step 3: Deploy Nginx Proxy Manager (The Traffic Router)
NPM is installed via Docker Compose, but we’ll configure it to use Podman.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# docker-compose.yml (saved as /opt/npm/docker-compose.yml)
version: '3'
services:
npm:
image: jc21/nginx-proxy-manager:latest
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "81:81"
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
environment:
- DB_VOLUME=/data
- JWT_SECRET=your_strong_secret_here
Key Details:
ports: Maps host ports to NPM containers.volumes: Persists data (configs, SSL certs) outside the container.environment: Sets critical secrets (never hardcode these!).
Start it:
1
2
cd /opt/npm
docker-compose up -d
Verification:
- Access
http://your-server-ip:81in a browser. - Default credentials:
admin@admin.com/changeme(change immediately!). - Never use default credentials in production.
Step 4: Configure NPM for SSL & Subdomains (The Security Layer)
This is where the magic happens.
- Add a New Proxy Host:
- Go to
https://your-server-ip:81(NPM UI). - Under “Proxy Hosts,” click “Add Proxy Host.”
- Host:
jellyfin.yourdomain.com - Scheme:
http - Forward Hostname/IP:
jellyfin(the container name) - Forward Port:
8096(Jellyfin’s default HTTP port) - SSL:
Let’s Encrypt→ “Enable SSL” → “Request a new certificate”- Domain:
jellyfin.yourdomain.com - Email: Your email (for Let’s Encrypt)
- Agree to TOS: Checked
- Domain:
- Redirect HTTP to HTTPS:
Yes - HSTS:
Enabled(for security) - Save.
- Go to
- Repeat for Other Services:
forgejo.yourdomain.com→ Points toforgejocontainer on port3000media.yourdomain.com→ Points tojellyfincontainer on port8096
Why This Matters:
- SSL is automatic—no manual cert management.
- Subdomains route traffic to the correct service.
- HSTS enforces HTTPS—preventing downgrade attacks.
Step 5: Deploy Jellyfin (The Media Layer)
Jellyfin is the media server. We’ll run it via Podman Compose.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# jellyfin-compose.yml
version: '3'
services:
jellyfin:
image: jellyfin/jellyfin:latest
restart: unless-stopped
ports:
- "8096:8096" # HTTP
- "8920:8920" # HTTPS (optional, but recommended)
volumes:
- ./jellyfin/config:/config
- ./jellyfin/cache:/cache
- ./media:/media # Your media directory
environment:
- TZ=America/New_York # Set your timezone
Key Notes:
./mediamust be a mounted directory with your media files.8096is the internal port; NPM routes to it via the proxy host.- Never expose port 8096 directly to the internet—NPM handles all external access.
Start it:
1
podman-compose -f jellyfin-compose.yml up -d
Verification:
- Access
https://jellyfin.yourdomain.com→ Should load Jellyfin’s UI. - No direct port exposure! All traffic flows through NPM.
Step 6: Deploy Forgejo (The Version Control Layer)
Forgejo replaces Gitea. We’ll use a pre-configured image.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# forgejo-compose.yml
version: '3'
services:
forgejo:
image: forgejo/forgejo:latest
restart: unless-stopped
ports:
- "3000:3000"
volumes:
- ./forgejo:/data
environment:
- USER_UID=1000
- USER_GID=1000
- APP_NAME=Forgejo
- LOG_MODE=console
Critical Setup: