What Is Self-Hosting?
What You Can Replace
Hardware: What You Actually Need
The Free Option: An Old PC or Laptop
The $80 Option: Raspberry Pi 4
The Best Value: Used Mini PC ($100-200)
The Software Stack: How It Works
Step 1: Install Ubuntu Server
Step 2: Install Docker
Step 3: Run Apps With Docker Compose
The 5 Apps to Install First
1. Pi-hole — Block Ads on Every Device
2. WireGuard — Your Own VPN
3. Vaultwarden — Self-Hosted Password Manager
4. Nextcloud — Your Own Google Drive
5. Immich — Unlimited Photo Backup
Security Basics (Don't Skip This)
Accessing Everything Remotely
Where to Go From Here I was paying $147/year for services I could run myself on a $35 Raspberry Pi. Google Drive. A VPN. A password manager. All services where the "cloud" is just someone else's computer — and you're paying monthly to use it. This guide shows you how to replace all of it. No prior Linux experience required. Self-hosting means running software on your own hardware instead of paying a company to run it for you. For most people, the tradeoffs are worth it easily. The best homelab is the one you already own. An old laptop or desktop that's collecting dust works perfectly. That's it. If you have this, you can start today for $0. Tiny, quiet, uses almost no power (~5 watts). Runs 24/7 without raising your power bill. Best for: Pi-hole, WireGuard VPN, Vaultwarden, basic file storage
Struggles with: video transcoding, large Nextcloud installs Buy the 4GB or 8GB version. Skip the 2GB — it's not worth the savings. A used Intel NUC or Beelink mini PC from eBay. Roughly laptop-sized, quiet, efficient, and powerful enough for everything including Jellyfin 4K transcoding. This is what most serious homelabbers run. Download Ubuntu Server 22.04 LTS from ubuntu.com. Boot from a USB drive. Follow the installer. You now have a secure, updated server. Docker lets you run apps in isolated containers. Each app is self-contained — no dependency conflicts, easy to remove. Log out and back in. Docker is ready. Every app below comes with a docker-compose.yml file. You paste it, run one command, app is running. No complicated installation. That's the magic. -d means "run in background." Pi-hole blocks ads at the DNS level. Every device on your network — phones, smart TVs, game consoles — gets ad blocking without installing anything on them. After starting, log into your router and set your DNS server to your Pi-hole's IP. Done. Access your home network securely from anywhere. Faster and simpler than OpenVPN. Access the web UI at http://YOUR-IP:51821. Create a client, download the config, import it in the WireGuard app on your phone. The Bitwarden app on every device connects to YOUR server instead of Bitwarden's cloud. After starting: go to http://YOUR-IP:3002, create your account. In the Bitwarden app, go to Settings → Server URL → enter your IP. All passwords now sync to your server. File sync, calendar, contacts, notes. Install the app on your phone and it syncs automatically. Paste the full docker-compose (it needs a database container too — the full config is in my guide below). Like Google Photos but yours. Auto-backup from your phone, facial recognition, timeline view. No storage limits. SSH Key Authentication — disable password login, use keys only: Firewall — only allow what you need: Tailscale is the easiest option. Install it on your server and your devices. Everything becomes accessible by hostname from anywhere without opening ports on your router. The rabbit hole goes deep. Once Pi-hole and Vaultwarden are running, you'll want Jellyfin. Then Nextcloud. Then Home Assistant. This is normal. For a complete beginner guide with all 10 apps, full Docker Compose configs, security setup, and remote access options: The Homelab Starter Guide → ($12) It's everything I wish existed when I started — real configs, not just descriptions. Prim Ghost builds practical guides for self-hosters and tinkerers. Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse
$ -weight: 600;">sudo -weight: 500;">apt -weight: 500;">update && -weight: 600;">sudo -weight: 500;">apt -weight: 500;">upgrade -y
-weight: 600;">sudo -weight: 500;">apt -weight: 500;">install -y -weight: 500;">curl -weight: 500;">wget -weight: 500;">git ufw fail2ban
-weight: 600;">sudo ufw allow ssh && -weight: 600;">sudo ufw -weight: 500;">enable
-weight: 600;">sudo -weight: 500;">apt -weight: 500;">update && -weight: 600;">sudo -weight: 500;">apt -weight: 500;">upgrade -y
-weight: 600;">sudo -weight: 500;">apt -weight: 500;">install -y -weight: 500;">curl -weight: 500;">wget -weight: 500;">git ufw fail2ban
-weight: 600;">sudo ufw allow ssh && -weight: 600;">sudo ufw -weight: 500;">enable
-weight: 600;">sudo -weight: 500;">apt -weight: 500;">update && -weight: 600;">sudo -weight: 500;">apt -weight: 500;">upgrade -y
-weight: 600;">sudo -weight: 500;">apt -weight: 500;">install -y -weight: 500;">curl -weight: 500;">wget -weight: 500;">git ufw fail2ban
-weight: 600;">sudo ufw allow ssh && -weight: 600;">sudo ufw -weight: 500;">enable
-weight: 500;">curl -fsSL https://get.-weight: 500;">docker.com -o get--weight: 500;">docker.sh
-weight: 600;">sudo sh get--weight: 500;">docker.sh
-weight: 600;">sudo usermod -aG -weight: 500;">docker $USER
-weight: 500;">curl -fsSL https://get.-weight: 500;">docker.com -o get--weight: 500;">docker.sh
-weight: 600;">sudo sh get--weight: 500;">docker.sh
-weight: 600;">sudo usermod -aG -weight: 500;">docker $USER
-weight: 500;">curl -fsSL https://get.-weight: 500;">docker.com -o get--weight: 500;">docker.sh
-weight: 600;">sudo sh get--weight: 500;">docker.sh
-weight: 600;">sudo usermod -aG -weight: 500;">docker $USER
-weight: 500;">docker compose up -d
-weight: 500;">docker compose up -d
-weight: 500;">docker compose up -d
version: "3"
services: pihole: image: pihole/pihole:latest container_name: pihole ports: - "53:53/tcp" - "53:53/udp" - "80:80/tcp" environment: TZ: 'America/Chicago' WEBPASSWORD: 'changeme' volumes: - './etc-pihole:/etc/pihole' -weight: 500;">restart: unless-stopped
version: "3"
services: pihole: image: pihole/pihole:latest container_name: pihole ports: - "53:53/tcp" - "53:53/udp" - "80:80/tcp" environment: TZ: 'America/Chicago' WEBPASSWORD: 'changeme' volumes: - './etc-pihole:/etc/pihole' -weight: 500;">restart: unless-stopped
version: "3"
services: pihole: image: pihole/pihole:latest container_name: pihole ports: - "53:53/tcp" - "53:53/udp" - "80:80/tcp" environment: TZ: 'America/Chicago' WEBPASSWORD: 'changeme' volumes: - './etc-pihole:/etc/pihole' -weight: 500;">restart: unless-stopped
version: "3.8"
services: wg-easy: environment: - WG_HOST=YOUR_PUBLIC_IP - PASSWORD=changeme image: ghcr.io/wg-easy/wg-easy container_name: wg-easy ports: - "51820:51820/udp" - "51821:51821/tcp" cap_add: - NET_ADMIN -weight: 500;">restart: unless-stopped
version: "3.8"
services: wg-easy: environment: - WG_HOST=YOUR_PUBLIC_IP - PASSWORD=changeme image: ghcr.io/wg-easy/wg-easy container_name: wg-easy ports: - "51820:51820/udp" - "51821:51821/tcp" cap_add: - NET_ADMIN -weight: 500;">restart: unless-stopped
version: "3.8"
services: wg-easy: environment: - WG_HOST=YOUR_PUBLIC_IP - PASSWORD=changeme image: ghcr.io/wg-easy/wg-easy container_name: wg-easy ports: - "51820:51820/udp" - "51821:51821/tcp" cap_add: - NET_ADMIN -weight: 500;">restart: unless-stopped
version: "3"
services: vaultwarden: image: vaultwarden/server:latest container_name: vaultwarden volumes: - ./vw-data:/data ports: - "3002:80" -weight: 500;">restart: unless-stopped
version: "3"
services: vaultwarden: image: vaultwarden/server:latest container_name: vaultwarden volumes: - ./vw-data:/data ports: - "3002:80" -weight: 500;">restart: unless-stopped
version: "3"
services: vaultwarden: image: vaultwarden/server:latest container_name: vaultwarden volumes: - ./vw-data:/data ports: - "3002:80" -weight: 500;">restart: unless-stopped
ssh-keygen -t ed25519
ssh-copy-id user@your-server-ip
# Then set PasswordAuthentication no in /etc/ssh/sshd_config
ssh-keygen -t ed25519
ssh-copy-id user@your-server-ip
# Then set PasswordAuthentication no in /etc/ssh/sshd_config
ssh-keygen -t ed25519
ssh-copy-id user@your-server-ip
# Then set PasswordAuthentication no in /etc/ssh/sshd_config
-weight: 600;">sudo ufw default deny incoming
-weight: 600;">sudo ufw allow ssh
-weight: 600;">sudo ufw allow 80
-weight: 600;">sudo ufw allow 443
-weight: 600;">sudo ufw -weight: 500;">enable
-weight: 600;">sudo ufw default deny incoming
-weight: 600;">sudo ufw allow ssh
-weight: 600;">sudo ufw allow 80
-weight: 600;">sudo ufw allow 443
-weight: 600;">sudo ufw -weight: 500;">enable
-weight: 600;">sudo ufw default deny incoming
-weight: 600;">sudo ufw allow ssh
-weight: 600;">sudo ufw allow 80
-weight: 600;">sudo ufw allow 443
-weight: 600;">sudo ufw -weight: 500;">enable
-weight: 600;">sudo -weight: 500;">apt -weight: 500;">install unattended-upgrades
-weight: 600;">sudo dpkg-reconfigure -plow unattended-upgrades
-weight: 600;">sudo -weight: 500;">apt -weight: 500;">install unattended-upgrades
-weight: 600;">sudo dpkg-reconfigure -plow unattended-upgrades
-weight: 600;">sudo -weight: 500;">apt -weight: 500;">install unattended-upgrades
-weight: 600;">sudo dpkg-reconfigure -plow unattended-upgrades
-weight: 500;">curl -fsSL https://tailscale.com/-weight: 500;">install.sh | sh
-weight: 600;">sudo tailscale up
-weight: 500;">curl -fsSL https://tailscale.com/-weight: 500;">install.sh | sh
-weight: 600;">sudo tailscale up
-weight: 500;">curl -fsSL https://tailscale.com/-weight: 500;">install.sh | sh
-weight: 600;">sudo tailscale up - No monthly fees — pay once for hardware, run software forever
- Your data stays yours — not on someone else's servers
- No feature limits — you control everything
- Learning opportunity — Docker, Linux, networking are career-relevant skills - You maintain it (maybe 30 minutes a month)
- You're responsible for backups
- If your power goes out, it goes down - At least 4GB RAM (8GB+ ideal)
- Any processor from the last 10 years
- 120GB+ of storage
- Internet connection (wired is better) - r/homelab — hardware, networking, builds
- r/selfhosted — software and Docker
- selfh.st/apps — searchable directory of self-hosted apps