Build a Private Cloud in Your Home: A Kubernetes Homelab Series

Build your own private cloud at home with K3s. This beginner-friendly series walks through creating a self-hosted Kubernetes homelab with HTTPS, remote access, media services, backups, cloud storage, and more — all running on hardware you already own.

Build a Private Cloud in Your Home: A Kubernetes Homelab Series

20+ self-hosted services behind TLS, on hardware you already own. No prior hosting experience required.


You want to run your own services at home. Your own cloud storage, media server, password manager, and Git server. You're just not sure how. This series shows you how to build exactly that.

You'll build a single-node Kubernetes homelab using k3s, with a reverse proxy that handles TLS automatically, DNS-level ad blocking, a VPN for remote access, and about 20 self-hosted services running on top of it all. Access from your network and outside it. All securly without having to expose a single port on your network.

By the end, you'll have your own private cloud running in your home.


What Is k3s?

Kubernetes is an orchestration platform. It takes a list of services you want running and makes sure they stay running. It handles restarts, networking between services, and resource management.

Standard Kubernetes is heavy. It was designed for fleets of servers at companies like Google. k3s is a lightweight distribution of Kubernetes built for exactly this use case: a single machine or small cluster that doesn't need all the enterprise overhead.

k3s ships as a single binary. It includes the control plane (the brain of the cluster), a built-in container runtime, and a local storage driver. The install takes under a minute.

Why Kubernetes over Docker?

Docker is the simpler starting point, and for one or two containers it's the right tool. For a stack this size, it has real friction. Three specific pain points drove the choice here.

Cert management. Docker Traefik cert config lives in labels scattered across compose files. In Kubernetes, you define a cert resolver once and every service picks it up automatically via an IngressRoute resource. No per-service cert wiring.

IP assignment. In Docker, all containers share your host's IP. You differentiate services by port number, which gets messy fast. MetalLB gives each Kubernetes service its own real LAN IP, so every service is reachable on standard ports without conflict.

Traefik ingress. Docker Traefik routing is configured through container labels spread across multiple files. Kubernetes IngressRoutes are standalone YAML files in one place that you can read, audit, and apply independently.

Beyond those three, Kubernetes also gives you:

  • A single way to define every service, regardless of complexity
  • A built-in secret store for passwords and tokens
  • Namespaces to logically separate unrelated services
  • kubectl, a consistent tool for inspecting and managing anything running in the cluster

Once you're comfortable with the pattern, adding a new service is just writing a YAML file and running kubectl apply.


What You'll Build

Here's the full stack you'll have at the end of this series.

Foundation (Sections 1-4)

  • k3s: the Kubernetes cluster itself
  • MetalLB: assigns real LAN IP addresses to services
  • Traefik: reverse proxy with automatic TLS certificates
  • Cloudflared: secure external access without opening ports on your router

Network (Sections 5)

  • Pi-hole: network-wide DNS ad blocking, running inside the cluster

Productivity (Sections 6-8)

  • Nextcloud + Collabora: self-hosted file storage and document editing
  • Gitea: private Git server
  • Immich: self-hosted Google Photos replacement

Access (Sections 9-10)

  • Tailscale: always-on VPN to reach your homelab from anywhere
  • Homepage: a dashboard showing all your services and their status

Media (Sections 11)

  • SABnzbd, Sonarr, Radarr, Lidarr, Plex, Tautulli, Tdarr, Seerr: a complete automated media stack

Tools (Sections 12-18)

  • Duplicati: encrypted offsite backups
  • Ghost: self-hosted blog
  • Calibre-Web: e-book library manager
  • Bookstack: internal wiki
  • PrivateBin: encrypted paste sharing
  • Vaultwarden: self-hosted Bitwarden password manager
  • Diskover: visual disk usage explorer

Every service gets its own subdomain on your domain, served over HTTPS, with a certificate from Let's Encrypt.


What You Need

Hardware: Any x86-64 machine running Ubuntu 24.04 LTS. A used mini PC or an old desktop works fine. The media stack benefits from more RAM (16 GB or more) and an Intel CPU with integrated graphics for hardware transcoding, but nothing in this series requires new hardware.

Networking: Assign a static IP to your server outside your router's DHCP range. The examples in this series use 192.168.1.x as a placeholder. Pick an IP that doesn't conflict with anything else on your network.

You also need a domain name. Cloudflare offers free DNS management.

This series assumes your domain is managed through Cloudflare.

Software: Ubuntu 24.04 LTS installed and SSH-accessible. That's it.


Set Up the Base Directory

Everything in this series lives under /data. I suggest mounting an external drive as /data. That makes it easy to move all the important parts of your server to another machine if needed. Before you install anything, create the base structure:

sudo mkdir -p /data && sudo chown $USER:$USER /data
mkdir -p /data/manifests
mkdir -p /data/appdata
mkdir -p /data/media
mkdir -p /data/files
mkdir -p /data/photos

Here's what each directory is for:

Path Purpose
/data/manifests/ Kubernetes YAML files. One file per service.
/data/appdata/<service>/ Config and runtime data for each service.
/data/media/ Movies, TV, music for the media stack.
/data/files/ Nextcloud user files.
/data/photos/ Immich photo library.

One rule to know now: every manifest file lives directly in /data/manifests/ with no subdirectories. This lets you run kubectl apply -f /data/manifests/ to apply everything at once, which is useful when you rebuild.


Placeholder Values

The examples throughout this series use placeholder values you'll replace with your own. Keep this table handy.

Placeholder What to replace it with
example.com Your actual domain
192.168.1.x Your server's static IP
192.168.1.1 Your router's IP
192.168.1.53 The IP you'll assign to Pi-hole
192.168.1.91 The IP you'll assign to Traefik
192.168.1.92 The IP you'll assign to Plex
192.168.1.94-99 A small unused IP range for other services
[email protected] Your email for Let's Encrypt
America/New_York Your timezone in TZ database format

Pick your IPs before starting. Make sure they're outside your router's DHCP range and not already assigned to any device.


Follow the Order

The phases in this series must be done in order. Later services depend on earlier ones. Traefik needs MetalLB to get an IP. Pi-hole needs Traefik to get a hostname. The media stack needs Pi-hole to resolve internal DNS.

Don't skip ahead. The foundation episodes (1-4) are the most important to get right.


Next

In Episode 1, you'll install k3s and create the namespace structure that every other episode depends on.