Docker Compose for the Rest of Us: A Self-Hosting Starter Kit

Server racks glowing in a dark data center, blue LED lights reflecting off metal
Your future homelab. It starts with one container.

Let me tell you about the moment I stopped being afraid of the command line.

It wasn’t some grand revelation. It was a Tuesday night, I was trying to run a photo backup server, and the install instructions said “just use Docker Compose.” I’d been putting it off for weeks because Docker felt like infrastructure stuff — the kind of thing that required a DevOps certification and a rack of servers.

Turns out? It’s a YAML file and two commands. That’s it.

What Even Is Docker Compose?

Think of it like a recipe card for your server. Instead of installing fifteen different programs, configuring each one, and praying they don’t fight over the same port — you write down what you want, and Docker figures out the rest.

Here’s a real example. Say you want to run a personal wiki (Bookstack), a link-saving service (Linkding), and a dashboard (Homepage) — all on one little VPS or an old laptop in your closet:

version: "3"
services:
  bookstack:
    image: lscr.io/linuxserver/bookstack:latest
    container_name: bookstack
    environment:
      - PUID=1000
      - PGID=1000
      - APP_URL=http://localhost:6875
      - DB_HOST=bookstack-db
      - DB_PASS=supersecret
    volumes:
      - ./bookstack/data:/config
    ports:
      - "6875:80"
    restart: unless-stopped
    depends_on:
      - bookstack-db

  bookstack-db:
    image: lscr.io/linuxserver/mariadb:latest
    container_name: bookstack-db
    environment:
      - PUID=1000
      - PGID=1000
      - MYSQL_ROOT_PASSWORD=supersecret
      - MYSQL_DATABASE=bookstack
      - MYSQL_USER=bookstack
      - MYSQL_PASSWORD=supersecret
    volumes:
      - ./bookstack/db:/config
    restart: unless-stopped

  linkding:
    image: sissbruecker/linkding:latest
    container_name: linkding
    volumes:
      - ./linkding/data:/etc/linkding/data
    ports:
      - "9090:9090"
    restart: unless-stopped

  homepage:
    image: ghcr.io/gethomepage/homepage:latest
    container_name: homepage
    volumes:
      - ./homepage/config:/app/config
      - /var/run/docker.sock:/var/run/docker.sock:ro
    ports:
      - "3000:3000"
    restart: unless-stopped

Save that as docker-compose.yml, run docker compose up -d, and you’ve got three services running. Three. From one file. While you go make tea.

The Five Commands You Actually Need

Forget the 47-command Docker cheat sheet. You need five:

  1. docker compose up -d — Start everything. The -d means “detached” (run in background, like a proper server).
  2. docker compose down — Stop everything. Gentle and clean.
  3. docker compose logs -f [service] — Watch the logs in real time. Invaluable when something breaks at 1 AM.
  4. docker compose pull && docker compose up -d — Update everything to the latest version. This is your “patch Tuesday.”
  5. docker compose restart [service] — Restart just one thing without touching the others.

That’s genuinely it for day-to-day operations. Everything else is gravy.

The Volume Thing (Don’t Skip This)

Here’s the one concept that matters: volumes.

Containers are ephemeral. They’re like hotel rooms — when you check out, everything in the room gets cleaned up. If you don’t map your data to a volume, you lose it when the container restarts.

See those volumes: sections in the YAML above? That’s me saying “hey Docker, this folder on my actual hard drive should be inside the container.” Your data survives restarts, updates, even complete rebuilds.

Rule of thumb: if the service stores anything you care about (databases, configs, uploads), it needs a volume. No exceptions.

My Self-Hosting Stack (The Honest Version)

I’ve been running services at home for a while now. Here’s what’s actually running on my little server, unglamorous and honest:

  • Immich — Google Photos replacement. This was the one that started it all.
  • Uptime Kuma — Pings my services and yells at me (via Telegram) when something goes down.
  • Homepage — A dashboard with links to everything. Because bookmark bars are for amateurs.
  • Linkding — Saves links with tags. I have 2,847 bookmarked links. I will read none of them.
  • Bookstack — Documentation wiki. Mostly for notes like “how did I set up SSL again?”

Total cost: one old mini-PC, about 15 watts of power, and the occasional “why is the fan spinning fast” moment at 3 AM.

Why Bother?

Because it’s yours.

Your photos aren’t training someone else’s model. Your bookmarks aren’t being sold to advertisers. Your notes aren’t on a server in a jurisdiction you’ve never heard of. When the service goes down, you can SSH in and fix it yourself. When you want to add something new, you add four lines to a YAML file and hit enter.

There’s a quiet satisfaction in knowing exactly what’s running on your hardware, why it’s running, and how to fix it when it breaks. It’s the digital equivalent of growing your own herbs — not because it’s cheaper, but because the basil tastes better when you grew it yourself.

Getting Started This Weekend

If you’ve got an old laptop, a Raspberry Pi, or a $5/month VPS, you can do this. Here’s your homework:

  1. Install Docker and Docker Compose on your machine.
  2. Pick one service you want to self-host. Just one.
  3. Write a docker-compose.yml for it (the project’s README almost always has an example).
  4. Run docker compose up -d.
  5. Open the port in your browser. Marvel at what you’ve built.

Next weekend, add another one. And another. Before you know it, you’ll have a little constellation of services orbiting your home network, and you’ll wonder why you ever paid someone else to host your stuff.

The neon never fades when you’re the one powering it.

Blue and purple lights in a server room at night
The view from your future terminal. You’ll get used to it.

Leave a Reply

Your email address will not be published. Required fields are marked *