Home Server Foundations

← Self-Hosting · Foundations 1 of 8 · Replaces: renting cloud services

What You'll Build

Every self-hosted app on this site runs on one small, always-on Linux computer in your home — your home server. This guide sets it up once, properly, so every later guide is just "create a folder, paste a file, run one command".

By the end you'll have:

Time: 1–2 hours · Difficulty: Medium

Step 1: Choose Your Hardware

OptionGood forWatch out for
Old laptop / desktopFree, lots of RAM and storage. A laptop's battery acts as a built-in UPS.Power use (desktops can draw 40–80 W all day).
Mini PC (Intel N100/N150 class)The sweet spot: quiet, ~6–15 W, fast enough for 20+ apps, hardware video transcoding for Jellyfin.Usually one drive bay — add a USB drive for bulk storage.
Raspberry Pi 5 (8 GB)Tiny and very low power. Great for Pi-hole, Vaultwarden, Navidrome, Home Assistant.Struggles with Immich machine learning and video transcoding. Boot from an SSD, not the SD card.

Minimum for the full Self-Hosting section: 4 CPU cores, 16 GB RAM, a 500 GB+ SSD for apps, and a separate larger drive for photos and media. 8 GB RAM works if you skip Immich's AI features.

Step 2: Install Ubuntu Server LTS

Ubuntu Server has no desktop, so all the memory goes to your apps. (Debian works identically if you prefer it.)

  1. Download Ubuntu Server LTS from ubuntu.com/download/server and write it to a USB stick — see USB installation. (Raspberry Pi: use Raspberry Pi Imager → Other general-purpose OS → Ubuntu Server, or Raspberry Pi OS Lite 64-bit.)
  2. Boot the server from the USB stick and follow the installer:
    • Network: plug in Ethernet. Leave it on DHCP for now.
    • Storage: "Use an entire disk" on your SSD. Untick LVM only if you know you don't want it.
    • Profile: server name homeserver, your username, a strong password.
    • SSH: tick Install OpenSSH server. If you have a GitHub account with SSH keys, you can import them here.
    • Featured snaps: skip them all — especially the Docker snap. We'll install Docker properly in Step 4.
  3. Reboot, remove the USB stick, and log in from your own computer:
# From your laptop/desktop
ssh yourname@homeserver.local      # or ssh yourname@<the IP shown on the server's screen>

# First job on any new server: update everything
sudo apt update && sudo apt full-upgrade -y

Step 3: Give the Server a Fixed IP Address

Your phone and apps need to find the server at the same address forever. The easiest, safest way is a DHCP reservation in your router:

  1. Find the server's current IP and its network card's MAC address:
    ip -brief address        # e.g. enp1s0   UP   192.168.1.50/24
    ip link                  # the "link/ether" value is the MAC address
  2. Log in to your router (often 192.168.1.1 or 192.168.0.1), find DHCP reservations / Static leases / Address reservation, and reserve that IP for that MAC address.
  3. Reboot the server and confirm it gets the same IP.

Throughout the self-hosting guides we'll use 192.168.1.50 as the server's address. Replace it with yours everywhere.

Step 4: Install Docker and Docker Compose

Docker runs each app in its own sealed "container" with everything it needs. Docker Compose lets you describe a container in a small text file (compose.yaml) instead of long commands.

# Docker's official install script (adds Docker's own apt repository)
curl -fsSL https://get.docker.com | sudo sh

# Let your user run docker without sudo
sudo usermod -aG docker $USER

# Log out and back in for the group change to apply
exit
ssh yourname@homeserver.local

# Check both work
docker --version
docker compose version
docker run --rm hello-world

Security note: being in the docker group is effectively root access. Only add users you trust with the whole server.

Step 5: The Folder Layout Every Guide Uses

# One folder per app, all under ~/stacks
mkdir -p ~/stacks

~/stacks/
├── portainer/
│   └── compose.yaml
├── immich/
│   ├── compose.yaml
│   └── .env            ← secrets and settings, never shared
└── vaultwarden/
    ├── compose.yaml
    └── data/           ← the app's saved data (back this up!)

And the four commands you'll use for every app, always run from inside that app's folder:

CommandWhat it does
docker compose up -dCreate/start the app in the background (also applies any changes you made to compose.yaml).
docker compose logs -fWatch the app's log output (Ctrl+C to stop watching).
docker compose pull && docker compose up -dUpdate the app to the newest image.
docker compose downStop and remove the containers (your data folders are kept).

Step 6: Basic Security

# Firewall: allow SSH, then turn it on
sudo ufw allow OpenSSH
sudo ufw enable
sudo ufw status

# Automatic security updates
sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure --priority=low unattended-upgrades

Docker and UFW: ports published by Docker (ports: in compose files) bypass UFW's rules. That's fine on a home network behind a router, but it means never forward ports on your router except the ones a guide explicitly tells you to. For remote access, use Tailscale.

Then work through First-Time Server Hardening for SSH keys and fail2ban.

Troubleshooting

What's Next?

Manage your containers from a web page instead of the terminal:

Foundations 2: Portainer →