Self-Hosting Guide: Run Bitwarden, Gitea, and Uptime Kuma on Your Own Server

HomeLab tutorial - IT technology blog
HomeLab tutorial - IT technology blog

Cloud Services vs Self-Hosted: What’s the Real Difference?

Most developers land on the same default stack early on: LastPass for passwords, GitHub for code, Pingdom for uptime checks. These work fine — until they don’t. LastPass suffered a major breach in 2022 that exposed encrypted vaults. Pingdom’s pricing jumped from ~$15/month to $89/month for multi-location monitoring. GitHub’s free tier rules for organizations have shifted more than once.

That’s the moment self-hosting stops feeling like a hobby project and starts looking like a reasonable decision. It’s not about distrust — it’s about not depending on someone else’s pricing changes or security track record.

A few common approaches exist for running your own services:

  • Bare metal install — Install services directly on the OS. Simple at first, but updates and migrations turn into headaches fast.
  • Docker (single containers) — Run each service in isolation. Fine as a starting point, but juggling five separate docker run commands gets messy quickly.
  • Docker Compose — Define your entire stack in one YAML file. The go-to for homelabbers who want control without the chaos.
  • Kubernetes — Excellent for learning production-grade orchestration. Overkill for a personal three-service setup.

Docker Compose hits the sweet spot here. It’s straightforward enough to understand in an afternoon, and it mirrors how small teams actually manage staging environments.

Pros and Cons of Running Your Own Stack

Why self-hosting makes sense

  • Full data ownership — Your passwords, repos, and monitoring metrics live on your hardware, not a third-party server.
  • No vendor lock-in — Migrate, back up, or shut down entirely on your own terms.
  • Real cost savings — A $5–10/month VPS replaces subscriptions that add up to $30–50/month. LastPass Premium alone runs $36/year; GitHub Team is $4/user/month.
  • Hands-on learning — Keeping real services alive teaches more about Linux, networking, and containers than any tutorial ever will.

What you’re signing up for

  • You own the uptime — If your server goes down, so does your password manager. Plan accordingly.
  • Security is your responsibility — Updates, firewall rules, and backups don’t happen automatically.
  • Initial setup time — Budget 2–3 hours to get everything running cleanly the first time.

Running a homelab changed how I approach production debugging. Six months after getting my first stack stable, I started spotting failure patterns at work much earlier. There’s something about being paged for your own service at 2 AM that sharpens your instincts in a way documentation simply can’t replicate.

Recommended Setup

Here’s the stack we’ll build:

  • Vaultwarden — An unofficial, Rust-based Bitwarden-compatible server. Uses roughly 10MB of RAM at idle versus 2GB+ for the official Bitwarden stack. Fully compatible with every Bitwarden client.
  • Gitea — A self-hosted Git service. Lightweight enough to run on a $5 VPS with RAM to spare.
  • Uptime Kuma — A clean uptime monitoring tool with a polished UI. Checks your services on a schedule and fires alerts when something goes quiet.

Server requirements: Ubuntu 22.04 or Debian 12, 1–2 GB RAM minimum, Docker + Docker Compose installed. A domain name is optional but strongly recommended for Vaultwarden — browsers actively block HTTP access to password managers, so HTTPS isn’t negotiable for daily use.

Implementation Guide

Step 1: Install Docker

# Install Docker using the official script
curl -fsSL https://get.docker.com | sh

# Verify installation
docker --version
docker compose version

Step 2: Create Your Project Structure

mkdir ~/homelab && cd ~/homelab
mkdir -p vaultwarden gitea uptime-kuma
touch docker-compose.yml

Step 3: Write the Docker Compose File

version: "3.8"

services:
  vaultwarden:
    image: vaultwarden/server:latest
    container_name: vaultwarden
    restart: unless-stopped
    volumes:
      - ./vaultwarden:/data
    environment:
      - SIGNUPS_ALLOWED=true   # Set to false after creating your account
      - WEBSOCKET_ENABLED=true
    ports:
      - "8080:80"
      - "3012:3012"

  gitea:
    image: gitea/gitea:latest
    container_name: gitea
    restart: unless-stopped
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - GITEA__database__DB_TYPE=sqlite3
    volumes:
      - ./gitea:/data
    ports:
      - "3000:3000"
      - "2222:22"

  uptime-kuma:
    image: louislam/uptime-kuma:1
    container_name: uptime-kuma
    restart: unless-stopped
    volumes:
      - ./uptime-kuma:/app/data
    ports:
      - "3001:3001"

Step 4: Start the Stack

cd ~/homelab
docker compose up -d

# Confirm all three containers are running
docker compose ps

Give it about 60 seconds for initialization. Once the containers are up, your services are reachable at:

  • Vaultwarden: http://your-server-ip:8080
  • Gitea: http://your-server-ip:3000
  • Uptime Kuma: http://your-server-ip:3001

Step 5: First-Time Configuration

Vaultwarden — Open the web UI, create your account, then lock down registrations immediately. Edit docker-compose.yml and flip SIGNUPS_ALLOWED to false, then restart:

docker compose restart vaultwarden

Install the official Bitwarden browser extension. Go to Settings and point the server URL to your Vaultwarden address instead of bitwarden.com. The extension doesn’t know or care that it’s talking to Vaultwarden — it works identically.

Gitea — Head to port 3000 and walk through the setup wizard. SQLite is perfectly adequate for personal or small-team use. You won’t hit its limits until you’re running dozens of active repos with CI/CD pipelines. Create your admin account at the end of the wizard.

Uptime Kuma — Port 3001. Create an admin account, then add monitors for your other services so you’re notified before you notice something is broken yourself:

  • Monitor type: HTTP(s)
  • URL: http://localhost:8080 (use the internal address — more accurate than routing through your public IP)
  • Interval: 60 seconds

Step 6: Add HTTPS with Nginx Reverse Proxy

Raw IP and port is fine for local testing. For anything you’ll use daily — especially a password manager — HTTPS is non-negotiable. Set up Nginx with Let’s Encrypt:

sudo apt install nginx certbot python3-certbot-nginx -y
sudo nano /etc/nginx/sites-available/vaultwarden
server {
    listen 80;
    server_name vault.yourdomain.com;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /notifications/hub {
        proxy_pass http://127.0.0.1:3012;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}
sudo ln -s /etc/nginx/sites-available/vaultwarden /etc/nginx/sites-enabled/
sudo certbot --nginx -d vault.yourdomain.com
sudo systemctl reload nginx

Repeat similar blocks for Gitea (git.yourdomain.com) and Uptime Kuma (status.yourdomain.com), pointing proxy_pass to their respective ports.

Step 7: Set Up Automatic Backups

Everyone skips this step until they actually need it. Don’t be that person. A daily cron job handles it with two lines:

# Open crontab editor
crontab -e

# Add this line — runs daily at 2:00 AM
0 2 * * * tar -czf /backup/homelab-$(date +\%Y\%m\%d).tar.gz ~/homelab/vaultwarden ~/homelab/gitea ~/homelab/uptime-kuma

Keep backups somewhere off the server — an S3 bucket, a local NAS, or even a USB drive. More importantly: test a restore. A backup you’ve never restored is just compressed hope.

Keeping the Stack Healthy

Day-to-day maintenance is lighter than most people expect. Three habits cover 90% of it:

  • Update containers monthly: docker compose pull && docker compose up -d
  • Glance at Uptime Kuma’s dashboard once a week to catch silent failures before they matter
  • Check Vaultwarden logs when login issues appear: docker logs vaultwarden

Three services. One server. Full control over your passwords, code, and infrastructure visibility. Once you’re comfortable here, adding Nextcloud, Home Assistant, or Portainer follows the exact same Docker Compose pattern. The technology isn’t the hard part. Maintaining the habit of keeping it updated is.

Share: