Choosing the Right VPS Provider for Self-Hosting

Choosing the Right VPS Provider for Self-Hosting: Specifications and Cost Comparison

When I started self-hosting three years ago, I burned through three different VPS providers before landing on the one that actually fit my needs. I'd pick a provider based on a flashy deal, deploy Nextcloud, watch it crawl under load, then migrate everything to a competitor. The problem wasn't the providers—it was that I didn't know what specifications actually mattered for my workload. This guide is the checklist I wish I'd had when I started.

A VPS is the foundation of any serious self-hosting setup. You need CPU, RAM, and storage that match your apps. You also need pricing that doesn't hurt. I'm going to walk you through the real specs from four providers I've actually used, explain what to watch for, and show you how to calculate true cost-per-month.

The Big Four: Hetzner, RackNerd, Contabo, and OVH

These four dominate the self-hosting space for good reason. Hetzner is rock-solid and fast—I use them for production apps. RackNerd undercuts almost everyone on price and their support is surprisingly responsive. Contabo gives you generous disk for the money. OVH is the European workhorse, though their interface is a pain.

Let me break down what you actually get at each:

Hetzner CX11

I've run a personal Vaultwarden instance on Hetzner's CX11 for 18 months without a single hiccup. The NVMe is fast. The uptime is legendary. But that shared vCPU means you'll see slowdowns if traffic spikes. For hobby projects, this is unbeatable value.

RackNerd KVM VPS

RackNerd's yearly promos are genuinely wild. I've seen 2GB/40GB plans for $24/year. That's $2/month. Yes, you read that right. The catch: you're on shared hardware during peak hours, and support tickets take 24-48 hours. But for a Nextcloud instance or a small media server, it's hard to justify paying more. They're transparent about overcommitment—they admit their density is higher than premium providers.

Contabo VPS S

Contabo is my sweet spot for serious homelabs. That 400 GB disk is massive—I run Jellyfin, a few Docker services, and still have breathing room. The 4 vCPUs are dedicated. Prices are fixed, no surprise renewals. Their control panel is clunky, but the infrastructure is solid. I've used them for two years without migration regrets.

OVH VPS Essential

OVH is the old guard. Their infrastructure is mature and reliable. They give you two IPv4 addresses by default, which is useful for running multiple services with separate IPs. The UI is outdated, but the servers themselves don't know that. Renewal prices are exactly what you pay upfront—no surprises. Good if you're in Europe and want European data centers.

The Real Calculation: Total Cost of Ownership

Here's where people get tricked. A $2/month VPS sounds free until you need to upgrade or migrate. Then you realize you've been on a promotional rate. Let me show you how to calculate real cost:

Watch out: Many providers offer year-one promos at 70% off. Your renewal rate might be 3–4× higher. Always check the renewal price before signing up. RackNerd is honest about this—they show renewal rates upfront. Hetzner and Contabo have predictable renewal pricing. OVH's renewal is the same as the initial price.

Here's a cost table for a realistic 36-month commitment (assuming renewal at stated rates):

Provider Plan Year 1 36-Month Total Avg/Month
Hetzner CX11 €35.88 €107.64 €2.99
RackNerd 2GB/40GB/year $24 $132* $3.67*
Contabo VPS S €47.88 €143.64 €3.99
OVH VPS Essential €54 €162 €4.50

* RackNerd renewal rates vary. This assumes $44/year for years 2–3, which is realistic based on their promotions history.

That's why Hetzner wins on pure price-per-month for lightweight workloads. But Contabo's extra CPU and disk matter if you're running Docker stacks. And RackNerd is the bet if you're comfortable with shared hardware.

Matching VPS Specs to Your Workload

I see people choose VPS providers backwards—they pick the cheapest, deploy Nextcloud, then panic when it takes 10 seconds to load. Let me help you map your apps to realistic specs:

Single Lightweight App (Vaultwarden, Pi-hole, Gitea)

CPU: 1 vCPU (shared is fine) | RAM: 512 MB–1 GB | Disk: 20 GB

Provider pick: Hetzner CX11 (€2.99/month). You'll never max it out.

Nextcloud + One Docker App

CPU: 2 vCPU (dedicated) | RAM: 2–4 GB | Disk: 100–200 GB

Provider pick: RackNerd 2GB plan ($24/year) or Contabo VPS S (€3.99/month). The Contabo gives you more disk and guaranteed CPU.

Jellyfin Media Server + Docker Compose Stack (4–6 services)

CPU: 4 vCPU (dedicated) | RAM: 8 GB | Disk: 200+ GB

Provider pick: Contabo VPS S (€3.99/month). This is what I run, and it handles Jellyfin transcoding, a couple containers, and backups without breaking a sweat.

Production Homelab (10+ services, High Availability)

CPU: 8 vCPU | RAM: 16 GB | Disk: 500+ GB

Provider pick: Contabo VPS M (€9.99/month) or Hetzner CX41 (€17.88/month). You're paying for reliability now, not just capacity.

Real Example: Setting Up a Docker Homelab on RackNerd

I want to show you the actual commands I use when deploying to a fresh RackNerd VPS. This stack runs Nextcloud, Vaultwarden, and a media server:

#!/bin/bash
# Initial setup on fresh RackNerd KVM VPS
# SSH as root

# Update system
apt update && apt upgrade -y

# Install Docker and Docker Compose
apt install -y docker.io docker-compose git curl wget htop

# Add your user to docker group (optional but convenient)
usermod -aG docker $(whoami)

# Create directory for docker-compose files
mkdir -p ~/homelab && cd ~/homelab

# Create .env file for sensitive data
cat > .env << 'EOF'
NEXTCLOUD_ADMIN_USER=admin
NEXTCLOUD_ADMIN_PASSWORD=changeme123!
DB_PASSWORD=securedbpass456!
VAULTWARDEN_ADMIN_TOKEN=yourstrongtoken789!
EOF

chmod 600 .env

# Create docker-compose.yml
cat > docker-compose.yml << 'EOF'
version: '3.8'

services:
  nextcloud-db:
    image: mariadb:latest
    container_name: nextcloud-db
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
      MYSQL_DATABASE: nextcloud
      MYSQL_USER: nextcloud
      MYSQL_PASSWORD: ${DB_PASSWORD}
    volumes:
      - ./data/db:/var/lib/mysql
    restart: unless-stopped

  nextcloud:
    image: nextcloud:latest
    container_name: nextcloud
    depends_on:
      - nextcloud-db
    ports:
      - "8080:80"
    environment:
      MYSQL_HOST: nextcloud-db
      MYSQL_USER: nextcloud
      MYSQL_PASSWORD: ${DB_PASSWORD}
      MYSQL_DATABASE: nextcloud
      NEXTCLOUD_ADMIN_USER: ${NEXTCLOUD_ADMIN_USER}
      NEXTCLOUD_ADMIN_PASSWORD: ${NEXTCLOUD_ADMIN_PASSWORD}
    volumes:
      - ./data/nextcloud:/var/www/html
    restart: unless-stopped

  vaultwarden:
    image: vaultwarden/server:latest
    container_name: vaultwarden
    ports:
      - "8081:80"
    environment:
      ADMIN_TOKEN: ${VAULTWARDEN_ADMIN_TOKEN}
      DOMAIN: