Post

First Self Hosted Attempt What Does Everyone Think

First Self-Hosted Attempt: What Does Everyone Think

Introduction

The first foray into self-hosting infrastructure is a rite of passage for DevOps engineers and system administrators. This post dissects a real-world homelab setup described in a Reddit post, where a user deployed a Dell Optiplex 7040 as a multi-service server. We’ll examine why this approach matters, where it excels, and what improvements could elevate it from hobbyist experimentation to production-grade reliability.

Self-hosting combines infrastructure management with cost optimization, giving engineers full control over their data and services. According to the 2023 Docker Survey, 48% of developers now self-host at least one application, demonstrating the growing importance of this skillset. The Reddit poster’s configuration exemplifies several key DevOps principles:

  1. Resource consolidation (multiple services on one machine)
  2. Security hardening (Cloudflare SSL termination)
  3. Network optimization (ad-blocking at the DNS layer)
  4. Media automation (ARR stack + Jellyfin)

In this guide, we’ll dissect each component of this setup, explore best practices for self-hosting, and provide actionable improvements for those replicating similar architectures. You’ll learn how to balance convenience with security, optimize resource utilization, and avoid common pitfalls when hosting services from a residential connection.

Understanding the Components

Hardware Foundation: Dell Optiplex 7040

The Optiplex 7040 (Q3 2015 release) represents a cost-effective entry point for homelabs:

  • CPU: Intel i5-7400T (4C/4T @ 2.4-3.0 GHz)
  • RAM: 16GB DDR4 (supports up to 64GB)
  • Storage: NVMe boot + HDD storage (ideal for tiered storage)

Pros:

  • Low power consumption (35W TDP)
  • Enterprise-grade reliability
  • Compact form factor (Micro model)

Cons:

  • PCIe limitations (Gen3 x4 NVMe)
  • No ECC memory support
  • Limited drive bays for expansion

Recommendation: For media-heavy workloads, consider adding a SATA SSD for metadata caching and transcoding buffers.

Software Stack Architecture

1
2
3
4
5
6
7
8
9
10
11
# Base OS
Ubuntu Server 22.04 LTS

# Service Layer
├── Docker Engine
├── Nginx Proxy Manager
├── Cloudflare DNS
├── AdGuard Home
├── *ARR stack (Sonarr/Radarr/Prowlarr)
├── Jellyfin
└── CouchDB

Comparison with Alternatives

| Component | Chosen Solution | Alternatives | When to Switch | |———–|—————–|————–|—————-| | Reverse Proxy | Nginx Proxy Manager | Traefik, Caddy | Need advanced middleware | | Media Server | Jellyfin | Plex, Emby | Hardware transcoding demands | | Database | CouchDB | PostgreSQL, Redis | Complex queries needed | | Ad Blocking | AdGuard Home | Pi-hole, DNSMasq | Multi-site DNS management |

Prerequisites

Hardware Requirements

| Service | Minimum CPU | Minimum RAM | Storage | Network | |———|————-|————-|———|———| | Base OS | 2 cores | 2GB | 10GB | 1GbE | | *ARR Stack | 2 cores | 4GB | 100GB+ | 1GbE | | Jellyfin | 4 cores + HW accel | 8GB | 500GB+ | 2.5GbE | | CouchDB | 2 cores | 4GB | 50GB | 1GbE |

Critical Considerations:

  1. UPS: Essential for unclean shutdown prevention
  2. Network Segmentation: VLANs for IoT/trusted devices
  3. Backup: 3-2-1 strategy (3 copies, 2 media, 1 offsite)

Software Checklist

  • Ubuntu Server 22.04 LTS (Jammy Jellyfish)
  • Docker 24.0.6+ with Compose v2.23+
  • Cloudflare account (free tier)
  • Static IP or DHCP reservation

Security Pre-Configuration

1
2
3
4
5
# Mandatory pre-install steps
sudo ufw allow 22/tcp comment "SSH"
sudo ufw allow 80/tcp comment "HTTP"
sudo ufw allow 443/tcp comment "HTTPS"
sudo ufw enable

Installation & Setup

Base OS Optimization

1
2
3
4
5
6
7
8
9
10
11
12
13
# Install recommended packages
sudo apt install -y \
  tuned-utils \
  smartmontools \
  lm-sensors \
  nvme-cli

# Configure tuned profile
sudo tuned-adm profile throughput-performance

# Enable ZRAM swap
sudo apt install -y zram-config
sudo systemctl restart zram-config

Docker Deployment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Install Docker CE
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu jammy stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

# Post-installation
sudo usermod -aG docker $USER
newgrp docker

Nginx Proxy Manager with Cloudflare

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# docker-compose.yml snippet
version: '3.8'
services:
  npm:
    image: 'jc21/nginx-proxy-manager:latest'
    container_name: npm
    restart: unless-stopped
    ports:
      - '80:80'
      - '443:443'
      - '81:81'
    environment:
      - DB_MYSQL_HOST=npm-db
      - DB_MYSQL_USER=npm
      - DB_MYSQL_PASSWORD=securepassword
      - DB_MYSQL_NAME=npm
      - CF_API_TOKEN=your_cloudflare_token
    volumes:
      - ./npm_data:/data
      - ./letsencrypt:/etc/letsencrypt

AdGuard Home DNS Configuration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Create a macvlan network for isolated DNS
docker network create -d macvlan \
  --subnet=192.168.1.0/24 \
  --gateway=192.168.1.1 \
  -o parent=eth0 dns-net

docker run -d \
  --name adguard \
  --network dns-net \
  --ip 192.168.1.53 \
  -v /opt/adguard/work:/opt/adguardhome/work \
  -v /opt/adguard/conf:/opt/adguardhome/conf \
  --restart unless-stopped \
  adguard/adguardhome:latest

Critical Step: Configure DHCP to assign AdGuard as DNS resolver across all devices.

Configuration & Optimization

Security Hardening

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# SSH Configuration
sudo sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin no/' /etc/ssh/sshd_config
sudo sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo systemctl reload sshd

# Docker Security
sudo systemctl stop docker
cat <<EOF | sudo tee /etc/docker/daemon.json
{
  "userns-remap": "default",
  "log-driver": "journald",
  "live-restore": true,
  "iptables": false
}
EOF
sudo systemctl start docker

Jellyfin Hardware Acceleration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# docker-compose.yml snippet
services:
  jellyfin:
    image: jellyfin/jellyfin:latest
    devices:
      - /dev/dri/renderD128:/dev/dri/renderD128
    environment:
      - JELLYFIN_CONFIG_DIR=/config
      - JELLYFIN_CACHE_DIR=/cache
      - NVIDIA_DRIVER_CAPABILITIES=compute,video,utility
    volumes:
      - /path/to/media:/media
      - jellyfin_config:/config
      - jellyfin_cache:/cache

CouchDB for Obsidian Sync

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# couchdb.yml
services:
  couchdb:
    image: couchdb:3.3
    environment:
      - COUCHDB_USER=admin
      - COUCHDB_PASSWORD=securepassword
      - COUCHDB_SECRET=supersecretkey
      - ERL_FLAGS=+K true +A 16 +Bd
    volumes:
      - couchdb_data:/opt/couchdb/data
      - couchdb_logs:/opt/couchdb/var/log
      - ./couchdb.ini:/opt/couchdb/etc/local.ini
    ports:
      - "5984:5984"

Usage & Operations

Monitoring Stack

1
2
3
4
5
6
7
# Install Prometheus + Grafana
docker run -d --name=prometheus -p 9090:9090 \
  -v ./prometheus.yml:/etc/prometheus/prometheus.yml \
  prom/prometheus:latest

docker run -d --name=grafana -p 3000:3000 \
  grafana/grafana-oss:latest

Sample prometheus.yml:

1
2
3
4
5
6
7
8
9
10
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'docker'
    static_configs:
    - targets: ['dockerhost:9323']
  - job_name: 'node'
    static_configs:
    - targets: ['node_exporter:9100']

Backup Strategy

1
2
3
4
5
6
7
8
9
10
11
# Daily backup script
#!/bin/bash
# Backup Docker volumes
tar -czf /backup/$(date +%s).tar.gz \
  /opt/npm_data \
  /opt/adguard/conf \
  /opt/jellyfin/config \
  /opt/couchdb

# Restic upload to S3
restic -r s3:s3.amazonaws.com/your-bucket backup /backup

Troubleshooting

Common Issues and Solutions

1. SSL Certificate Errors with Cloudflare

  • Verify DNS records are proxied (orange cloud icon)
  • Check Nginx Proxy Manager’s SSL certificate renewal
  • Test with curl -v -k https://yourdomain.com to bypass certificate validation

2. Jellyfin Transcoding Failures

  • Confirm hardware acceleration:
    1
    
    docker exec -it jellyfin ls -l /dev/dri
    
  • Check GPU permissions:
    1
    2
    
    sudo usermod -aG render $USER
    sudo usermod -aG video $USER
    

3. CouchDB Authentication Issues

  • Reset admin password:
    1
    
    curl -X PUT http://localhost:5984/_node/_local/_config/admins/admin -d '"newpassword"'
    

Conclusion

This first self-hosted attempt demonstrates a solid foundation in DevOps principles: service consolidation, network security, and automation. The Optiplex 7040 proves capable of handling multiple services when properly configured with hardware acceleration, memory optimization, and tiered storage.

Next Steps for Improvement:

  1. Infrastructure as Code: Migrate to Terraform/Ansible
  2. High Availability: Implement a Kubernetes cluster with K3s
  3. Monitoring: Add ELK stack for log analysis
  4. Security: Implement Intrusion Detection with Suricata

Recommended Resources:

Self-hosting remains an invaluable skill for DevOps engineers, bridging the gap between theory and production realities. The key to success lies in balancing security with accessibility, automation with supervision, and performance with cost.

This post is licensed under CC BY 4.0 by the author.