PromptHub
Back to Blog
DevOps Self-hosting

Stop Wrestling With Homelab Chaos! Use juftin/homelab Instead

B

Bright Coding

Author

13 min read 22 views
Stop Wrestling With Homelab Chaos! Use juftin/homelab Instead

Stop Wrestling With Homelab Chaos! Use juftin/homelab Instead

Your homelab is a nightmare, isn't it? You've got Plex running on some random port, a reverse proxy you configured six months ago and now fear touching, OAuth scattered across three different tutorials, and every time you want to add a new service, you break three existing ones. You've spent weekends chasing DNS records, crying over certificate errors, and wondering why your media server suddenly can't reach the internet.

What if I told you there's a way to deploy your entire homelab—media, utilities, AI experiments, everything—with a single command?

No more manual port mapping. No more fragmented configurations scattered across a dozen directories. No more praying that docker↗ Bright Coding Blog-compose up doesn't explode your carefully balanced house of cards.

Enter juftin/homelab—a meticulously engineered, profile-driven Docker Compose system that transforms homelab deployment from a chaotic weekend project into a five-minute operation. This isn't just another docker-compose.yml dumped on GitHub. It's a battle-tested architecture that top homelab enthusiasts are quietly adopting to eliminate deployment friction forever.

Ready to discover why everyone is abandoning manual Docker setups? Let's dive deep.

What is juftin/homelab?

juftin/homelab is a comprehensive, open-source homelab deployment framework built around Docker Compose profiles. Created by Justin Flannery, this repository solves the single most painful problem in homelab management: orchestrating dozens of services without losing your sanity.

At its core, juftin/homelab is a single Docker Compose application that uses the include directive to pull in modular service definitions from an apps/ directory. Each service belongs to a Docker Compose profile—a native Docker feature that lets you selectively start groups of related containers. This means you can deploy your core infrastructure, spin up your media stack, or experiment with LLMs, all with surgical precision.

Why is this trending now? Three forces have converged:

  1. Docker Compose profiles matured in recent versions, making selective service deployment production-ready
  2. Self-hosting exploded post-2023 as developers reclaimed control from cloud services
  3. The "homelab as code" movement gained traction, with practitioners demanding GitOps-friendly infrastructure

What sets juftin/homelab apart from the sea of docker-compose.yml gists is its opinionated architecture. Justin didn't just bundle containers together—he engineered a complete ecosystem with Traefik reverse proxy, Google OAuth integration, Cloudflare DNS automation, and logical service grouping that mirrors how real homelabs actually function.

The repository has attracted attention because it solves the "second homelab" problem: your first homelab is fun to build, but your second needs to be maintainable. This project is designed for that second iteration—when you want reliability, security, and the ability to hand your setup to future-you without a 20-page README of tribal knowledge.

Key Features That Make This Insane

Let's dissect what makes juftin/homelab technically superior to ad-hoc deployments:

Profile-Driven Service Architecture

Instead of monolithic compose files or shell scripts that start everything, services are organized into five distinct profiles:

  • core — Traefik reverse proxy + OAuth authentication. This is your foundation. Without it, nothing else is accessible securely.
  • media — Plex, Sonarr, Radarr, Ombi. Your complete media pipeline from request to stream.
  • utilities — Watchtower (auto-updates), Portainer (container management). The operational layer that keeps everything alive.
  • miscellaneous — ChatGPT Next Web, LibreOffice Online. Productivity tools that don't fit elsewhere.
  • llm — Local LLM experimentation. Disabled by default because this is where your GPU catches fire.

Each profile is independently deployable. Need to restart your media stack without touching your reverse proxy? make media handles it. This granular control is impossible with traditional all-or-nothing compose files.

Security-First Design

Every external service is protected behind Google OAuth via Traefik Forward Auth. No more VPN requirements for basic access. No more exposing services directly to the internet. The secrets/ directory cleanly separates credentials from configuration, following Docker security best practices.

Modular File Organization

The include directive in the root docker-compose.yaml pulls in service definitions from apps/. This means:

  • Adding a service? Drop a new YAML file in apps/, add it to a profile, done.
  • Removing a service? Delete one file. No grep-ing through 800 lines of compose.
  • Debugging? Each service is isolated and readable.

Makefile Automation

Common operations are wrapped in Make targets. You don't need to remember complex docker compose invocations. The Makefile becomes your CLI for the entire homelab.

Persistent Volume Strategy

The appdata/ directory uses per-service subdirectories for persistent storage. This eliminates the volume naming chaos that plagues most Docker deployments. You always know where your data lives, and backups become trivial.

Real-World Use Cases Where This Shines

Scenario 1: The Weekend Media Server Migration

You've been running Plex on a bare-metal install for years. It's creaking, your library database is 40GB, and you're terrified of touching it. With juftin/homelab, you:

  1. Deploy the core profile on your new server
  2. Copy your Plex config to appdata/plex/
  3. Enable the media profile
  4. Your entire stack is live with HTTPS and OAuth in minutes

The migration that used to take a full weekend now takes an afternoon.

Scenario 2: The "Let Me Try This AI Thing" Experiment

You want to run a local ChatGPT interface but don't want it running 24/7 or interfering with your production services. Simply:

make llm

The llm profile spins up, you experiment, and when you're done, it shuts down without touching anything else. Complete isolation, zero side effects.

Scenario 3: The Multi-User Household

Your partner wants to request movies through Ombi. Your kids need Plex. You need everything locked down. The built-in OAuth with Google whitelisting means:

  • Family members authenticate with existing Google accounts
  • No password management overhead
  • Granular access control via the secrets/google_oauth.secret file

Scenario 4: The "I Need to Update Everything" Maintenance Window

Watchtower in the utilities profile can auto-update containers, but for manual control:

make update

This pulls latest images across your enabled profiles, with Portainer giving you visual confirmation of what changed.

Step-by-Step Installation & Setup Guide

Let's get you deployed. This assumes a Linux server with Docker and Docker Compose v2.20+ installed.

Step 1: Clone and Enter the Repository

git clone https://github.com/juftin/homelab.git
cd homelab

Step 2: Create Your Configuration Files

The project uses environment files and secrets for all configuration. Copy the examples:

# Main configuration: domains, ports, feature flags
cp docs/example.env .env

# Sensitive credentials: API keys, OAuth secrets
cp -r docs/example-secrets/ secrets/

Step 3: Configure Your Environment

Edit .env with your specifics. At minimum, you'll need:

# Your domain (must resolve to this server)
DOMAIN=yourdomain.com

# Email for Let's Encrypt certificates
ACME_EMAIL=you@example.com

# Which profiles to enable by default
COMPOSE_PROFILES=core,media,utilities

Step 4: Configure Secrets

The secrets/ directory contains sensitive files that are never committed to Git:

# Cloudflare API key for DNS challenge and DDNS
secrets/cloudflare_api_key.secret

# Google OAuth credentials and allowed email addresses
secrets/google_oauth.secret

For Google OAuth, create credentials at Google Cloud Console and add authorized redirect URIs for:

  • https://oauth.yourdomain.com/_oauth
  • https://oauth.yourdomain.com/_oauth/callback

Step 5: Deploy Your First Profile

Start with core to establish your reverse proxy and authentication:

make core

Verify Traefik is healthy:

docker compose ps

You should see traefik and oauth containers running.

Step 6: Add Services Incrementally

Once core is stable, add media:

make media

Or utilities:

make utilities

Each profile is validated independently before the next deployment.

Step 7: Verify HTTPS and OAuth

Navigate to https://traefik.yourdomain.com. You should hit the Google OAuth login screen. After authentication, Traefik's dashboard appears.

REAL Code Examples From the Repository

Let's examine the actual architecture that makes this system work. These examples are extracted directly from juftin/homelab's implementation.

Example 1: The Root Compose File Using include

The root docker-compose.yaml is remarkably clean because it delegates to modular service files:

# docker-compose.yaml
# This is the entry point for the entire homelab. Instead of defining
# all services here (which would be unmaintainable), it uses the
# 'include' directive to pull in focused service definitions.

include:
  - apps/traefik/docker-compose.yaml    # Reverse proxy foundation
  - apps/oauth.yaml                      # Authentication layer
  - apps/plex.yaml                       # Media server
  - apps/sonarr.yaml                     # TV show management
  - apps/radarr.yaml                     # Movie management
  - apps/ombi.yaml                       # Media requests
  - apps/watchtower.yaml                 # Auto-updates
  - apps/portainer.yaml                  # Container management
  - apps/chat-gpt-next-web.yaml          # Local LLM interface

# Global networks and volumes can still be defined here
networks:
  default:
    name: homelab
    driver: bridge

Why this matters: The include directive (available Docker Compose v2.20+) lets you compose your compose. Each included file can be tested independently. If Plex breaks, you debug apps/plex.yaml without scrolling through 500 unrelated lines.

Example 2: Profile Definition in a Service File

Here's how the plex.yaml service file specifies its profile membership:

# apps/plex.yaml
# Plex media server - only starts when the 'media' profile is active

services:
  plex:
    image: plexinc/pms-docker:latest
    container_name: plex
    restart: unless-stopped
    
    # CRITICAL: This is the profile assignment
    # 'docker compose --profile media up' will include this service
    # 'docker compose up' without --profile will skip it
    profiles:
      - media
    
    environment:
      - PLEX_CLAIM=${PLEX_CLAIM}
      - PUID=${PUID}
      - PGID=${PGID}
    
    volumes:
      # Persistent configuration in the organized appdata structure
      - ./appdata/plex:/config
      # Your actual media libraries
      - ${MEDIA_ROOT}/movies:/movies
      - ${MEDIA_ROOT}/tv:/tv
    
    networks:
      - default
    
    # Traefik labels for automatic reverse proxy configuration
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.plex.rule=Host(`plex.${DOMAIN}`)"
      - "traefik.http.routers.plex.entrypoints=websecure"
      - "traefik.http.routers.plex.tls.certresolver=letsencrypt"

The profile magic: Notice profiles: [media]. This single line is what enables selective deployment. Without it, Plex would start with everything else, consuming resources even when you just want core infrastructure.

Example 3: Makefile Targets for Profile Management

The Makefile abstracts Docker Compose commands into intuitive operations:

# Makefile
# Common operations wrapped for convenience and consistency

# Default target: show available commands
.PHONY: help
help:
	@echo "Available profiles: core, media, utilities, miscellaneous, llm"
	@echo "Usage: make <profile> or make <command>"

# Deploy the core infrastructure (always required first)
.PHONY: core
core:
	docker compose --profile core up -d

# Deploy media stack: Plex + *arr suite + Ombi
.PHONY: media
media: core
	docker compose --profile media up -d

# Deploy utility services for monitoring and maintenance
.PHONY: utilities
utilities: core
	docker compose --profile utilities up -d

# Deploy everything enabled in COMPOSE_PROFILES
.PHONY: all
all:
	docker compose --profile core --profile media --profile utilities up -d

# Graceful shutdown of all services
.PHONY: down
down:
	docker compose down

# View logs across all services
.PHONY: logs
logs:
	docker compose logs -f

# Update all containers to latest images
.PHONY: update
update:
	docker compose pull
	docker compose up -d

The dependency chain: Notice media: core—this ensures you can't accidentally deploy media without your reverse proxy. The Makefile encodes operational knowledge that would otherwise live in someone's head.

Example 4: Traefik Middleware for OAuth Protection

The OAuth integration happens through Traefik middleware, defined in the rules directory:

# apps/traefik/rules/middlewares.yml
# These middlewares are applied to routes to enforce authentication

http:
  middlewares:
    # The OAuth forward auth middleware
    # Every request is checked against the OAuth service before reaching the backend
    oauth-auth:
      forwardAuth:
        address: "http://oauth:4181"
        authResponseHeaders:
          - "X-Forwarded-User"
        trustForwardHeader: true
    
    # Security headers applied to all responses
    security-headers:
      headers:
        customFrameOptionsValue: "SAMEORIGIN"
        contentTypeNosniff: true
        browserXssFilter: true
        forceSTSHeader: true
        stsIncludeSubdomains: true
        stsSeconds: 31536000

The security model: This is zero-trust networking for your homelab. No service is reachable without passing OAuth validation. The middleware is declarative—you add labels to services, not configure each application individually.

Advanced Usage & Best Practices

Profile Composition for Development vs. Production

Create environment-specific .env files:

# .env.development
COMPOSE_PROFILES=core,llm
DOMAIN=dev.local
ACME_EMAIL=dev@example.com

# .env.production
COMPOSE_PROFILES=core,media,utilities
DOMAIN=yourdomain.com
ACME_EMAIL=admin@yourdomain.com

Switch with: docker compose --env-file .env.development --profile llm up -d

Backup Strategy

The appdata/ structure makes backups trivial:

# Daily incremental backup of all persistent data
rsync -avz --delete ./appdata/ /backup/homelab/$(date +%Y%m%d)/

Each service's data is isolated, so you can restore Plex without affecting Sonarr.

Resource Constraints for LLM Experiments

The llm profile can consume massive GPU resources. Add to your service definitions:

deploy:
  resources:
    limits:
      memory: 16G
    reservations:
      devices:
        - driver: nvidia
          count: 1
          capabilities: [gpu]

Health Checks and Auto-Recovery

Extend services with Docker health checks:

healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:32400/identity"]
  interval: 30s
  timeout: 10s
  retries: 3
  start_period: 40s

Comparison With Alternatives

Feature juftin/homelab Manual Docker Compose Kubernetes (k3s) Ansible + Docker
Deployment Complexity Low (single command) Medium (custom scripts) High (YAML engineering) Medium (playbook maintenance)
Profile Selectivity ✅ Native Docker feature ❌ Manual scripting ✅ Namespaces/labels ❌ Conditional tasks
Reverse Proxy Integration ✅ Built-in Traefik ❌ Manual setup ⚠️ Ingress complexity ❌ Separate configuration
OAuth Security ✅ Google OAuth out-of-box ❌ Self-configured ⚠️ OAuth2 Proxy setup ❌ Additional role needed
Service Modularity include directive ❌ Monolithic files ✅ Helm charts ⚠️ Role-per-service
Learning Curve Low Medium Very High Medium
Resource Overhead Minimal Minimal Moderate (control plane) Minimal
GitOps Friendly ✅ Yes ⚠️ Possible ✅ Native ✅ Yes

The verdict: Kubernetes is overkill for most homelabs. Manual compose files become unmaintainable. Ansible adds abstraction without solving the core orchestration problem. juftin/homelab hits the sweet spot of power and simplicity—sophisticated enough for production use, simple enough to understand in an afternoon.

FAQ

What Docker Compose version do I need?

Docker Compose v2.20.0 or later is required for the include directive. Check with docker compose version.

Can I run this without a public domain?

Yes, but you'll lose HTTPS and OAuth functionality. Use *.local domains with self-signed certificates for internal-only deployments.

How do I add a custom service not in the repository?

Create apps/yourservice.yaml, define its profile, add it to the root include list. Follow the existing patterns for Traefik labels.

Is this suitable for ARM devices like Raspberry Pi?

Most services support ARM64, but verify individual images. Plex transcoding requires Intel QuickSync or NVIDIA for hardware acceleration.

Can I use different OAuth providers?

The current implementation uses Traefik Forward Auth with Google. Other providers (GitHub, Authelia) would require modifying the OAuth service configuration.

How do updates work without breaking my configuration?

Persistent data lives in appdata/ which is .gitignored. Pulling repository updates only affects container definitions, never your data.

What's the minimum server spec?

For core + media: 4GB RAM, 2 cores, 20GB storage. Add 8GB+ for llm profile with local models.

Conclusion

The homelab community has been crying out for opinionated, maintainable infrastructure as code. juftin/homelab answers that call with a profile-driven architecture that respects your time and sanity.

What makes this project exceptional isn't any single feature—it's the cohesion. Traefik, OAuth, and Docker Compose profiles aren't novel individually. But combined with modular file organization, Makefile automation, and sensible defaults, they create a system that's genuinely delightful to operate.

If you're still maintaining a monolithic docker-compose.yml that grows more terrifying with each service addition, stop. If you're considering Kubernetes for a single-node homelab, reconsider. The future of homelab deployment is selective, modular, and profile-driven—and juftin/homelab is leading that future today.

Your move. Clone the repository, deploy your first profile, and experience what homelab management should have been all along.

👉 Get juftin/homelab on GitHub

The weekend you save might just be your own.

Comments (0)

Comments are moderated before appearing.

No comments yet. Be the first to share your thoughts!

Recommended Prompts

View All