Skip to main content
  1. Posts/

How I Launched My First Real Self‑Hosted Server – A Step‑by‑Step Guide from the r/selfhosted Community

·6 mins

The Community Spark #

In March 2026 a post titled “I made my first ‘real’ server!” blew up on r/selfhosted, racking up over 12 k up‑votes and sparking a flood of replies. Newcomers were eager to know:

  • What counts as a “real” server? – a dedicated machine, a VPS, or a high‑end Raspberry Pi?
  • Which OS and stack are the safest for a first‑time self‑host?
  • How to avoid the dreaded “it works on my laptop but not on the server” pitfall?

The thread quickly became a living FAQ, with veterans posting step‑by‑step screenshots, cost breakdowns, and hardening checklists. Below we synthesize that collective experience into a single, authoritative guide that you can copy‑paste into your own setup.


Synthesized Community Perspectives #

TopicConsensusCommon Counter‑Arguments
Hardware vs. VPSMost agree a low‑cost VPS (e.g., Hetzner CX21) offers the best balance of reliability, bandwidth, and isolation for a first real server.Some argue a home server gives “full control” and zero monthly fees, but requires static IP, UPS, and physical security.
Linux DistributionUbuntu Server LTS (22.04 / 24.04) dominates because of its massive documentation and apt ecosystem.Debian‑stable is praised for minimalism; Arch is loved for learning but deemed “risky for production”.
Security FirstEveryone stresses a firewall + fail2ban + SSH key auth as non‑negotiable.A few users still rely on password auth for convenience, which the community flags as a security debt.
AutomationUsing Ansible or Docker Compose to provision services is widely recommended.Some newbies find Docker’s abstraction confusing and prefer raw systemd units.
Backup StrategyRemote, encrypted backups (rclone → Backblaze B2, or borgbackup → Wasabi) are the gold standard.Local snapshots are useful, but insufficient alone for disaster recovery.

These patterns give us a clear roadmap: pick a VPS, install Ubuntu LTS, harden the OS, automate service deployment, and set up off‑site encrypted backups.


Deep‑Dive Actionable Guide #

Below is a complete, reproducible workflow that mirrors the steps most r/selfhosted members reported successful.

1️⃣ Choose and Provision Your Server #

OptionTypical Cost (USD/mo)ProsCons
Hetzner CX21 (2 vCPU, 4 GB RAM, 80 GB SSD)9.90Excellent network, German data‑privacy, easy console accessNo DDoS protection on basic plan
DigitalOcean “Droplet” (2 vCPU, 4 GB, 80 GB)12.00Global datacenters, 1‑click imagesSlightly higher latency in Asia
Home‑Lab (Intel NUC + 500 GB SSD)0 (aside from electricity)Full hardware control, no monthly feeRequires static IP, UPS, cooling

Pro tip: Use Hetzner’s Server Rescue mode to test the network before committing.

Quick SSH into the fresh VPS #

# Replace with your server’s IP and your private key path
ssh -i ~/.ssh/id_rsa_root root@203.0.113.42

All community members recommend creating a non‑root user immediately after login.

2️⃣ Harden the Base OS #

a. Create a non‑root sudo user #

adduser alice          # follow prompts (use a strong password)
usermod -aG sudo alice # give sudo rights

b. Disable password login, enforce SSH keys #

# As root or via sudo -i
sed -i 's/^#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sed -i 's/^#PubkeyAuthentication yes/PubkeyAuthentication yes/' /etc/ssh/sshd_config
systemctl restart sshd

c. Install and configure UFW (Uncomplicated Firewall) #

apt update && apt install -y ufw
ufw default deny incoming
ufw default allow outgoing
ufw allow OpenSSH
ufw allow 80/tcp    # HTTP
ufw allow 443/tcp   # HTTPS
ufw enable

d. Add Fail2Ban for brute‑force protection #

apt install -y fail2ban
cat <<'EOF' > /etc/fail2ban/jail.local
[sshd]
enabled = true
port    = ssh
logpath = %(sshd_log)s
maxretry = 5
EOF
systemctl restart fail2ban

3️⃣ Set Up a Package Management Baseline #

apt update && apt upgrade -y
apt install -y git curl wget gnupg2 software-properties-common

4️⃣ Deploy Core Services with Ansible (Community‑approved) #

Create a bare‑metal control node (your workstation) and install Ansible:

sudo apt install -y ansible

a. Directory layout #

~/ansible/
├── hosts.ini
└── playbooks/
    ├── base.yml
    ├── web.yml
    └── backup.yml

b. hosts.ini #

[server]
myserver ansible_host=203.0.113.42 ansible_user=alice ansible_ssh_private_key_file=~/.ssh/id_rsa

c. base.yml – OS hardening, common packages #

- hosts: server
  become: true
  tasks:
    - name: Install common utilities
      apt:
        name:
          - ufw
          - fail2ban
          - unattended-upgrades
        state: present
        update_cache: yes

    - name: Enable automatic security updates
      copy:
        src: files/20auto-upgrades
        dest: /etc/apt/apt.conf.d/20auto-upgrades
        mode: '0644'

Create files/20auto-upgrades with:

APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "7";
APT::Periodic::Unattended-Upgrade "1";

Run:

ansible-playbook -i hosts.ini playbooks/base.yml

Community note: Many users reported that enabling unattended-upgrades saved them from the infamous Log4j cascade.

5️⃣ Install Your First Real Application – Nginx + Docker Compose #

a. Install Docker Engine #

curl -fsSL https://get.docker.com | sh
usermod -aG docker alice
newgrp docker

b. Install Docker Compose (v2 plugin) #

mkdir -p ~/.docker/cli-plugins
curl -SL https://github.com/docker/compose/releases/download/v2.21.0/docker-compose-linux-x86_64 -o ~/.docker/cli-plugins/docker-compose
chmod +x ~/.docker/cli-plugins/docker-compose
docker compose version   # verify

c. Sample docker-compose.yml (served by the community) #

version: "3.9"
services:
  nginx:
    image: nginx:alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
      - ./certs:/etc/nginx/certs:ro
      - ./html:/usr/share/nginx/html:ro
  whoami:
    image: traefik/whoami
    restart: unless-stopped
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`whoami.example.com`)"
      - "traefik.http.routers.whoami.entrypoints=websecure"
      - "traefik.http.routers.whoami.tls=true"

Deploy:

docker compose up -d

Result: You now have a production‑grade reverse proxy ready for any additional containerized app.

6️⃣ Secure TLS with Let’s Encrypt (via certbot) #

apt install -y certbot python3-certbot-nginx
certbot --nginx -d example.com -d www.example.com

Follow the prompts to enable HTTP‑01 challenge and automatic renewal.

Tip from r/selfhosted veteran @techsquire: Add --agree-tos --redirect to fully automate the process for multiple domains.

7️⃣ Implement Encrypted Remote Backups #

We’ll use BorgBackup + rclone to push to Backblaze B2 (a favorite among community members for its low cost).

a. Install Borg and rclone #

apt install -y borgbackup rclone

b. Configure rclone remote (run rclone config) #

n) New remote
name> b2
Storage> b2
account> <your_account_id>
key> <your_app_key>

c. Create a Borg repository on B2 #

export BORG_REPO=rclone::b2:my-backup-repo
export BORG_PASSPHRASE='StrongRandomPassphrase!'
borg init --encryption=repokey

d. Daily backup script (/usr/local/bin/backup.sh) #

#!/bin/bash
set -euo pipefail

export BORG_REPO=rclone::b2:my-backup-repo
export BORG_PASSPHRASE='StrongRandomPassphrase!'

# Backup /etc, /home/alice, and Docker volumes
borg create \
  --stats \
  --progress \
  ::'{hostname}-{now:%Y-%m-%d-%H%M}' \
  /etc \
  /home/alice \
  /var/lib/docker

# Prune old snapshots (keep 7 daily, 4 weekly, 6 monthly)
borg prune \
  --keep-daily=7 \
  --keep-weekly=4 \
  --keep-monthly=6

Make it executable and schedule via cron:

chmod +x /usr/local/bin/backup.sh
(crontab -u alice -l ; echo "0 3 * * * /usr/local/bin/backup.sh >> /home/alice/backup.log 2>&1") | crontab -u alice -
  • Prometheus + Node Exporter for system metrics.
  • Grafana for dashboards.
  • UptimeRobot (free tier) to ping your public IP every 5 min.

All of these can be added as additional Docker services and wired into the same docker-compose.yml.


Pros & Cons Comparative Table #

SolutionCost (monthly)Setup ComplexityMaintenance OverheadSecurity BaselineIdeal Persona
Hetzner CX21 VPS$9.90Low (one‑click Ubuntu)Low (auto‑updates)High (dedicated isolation)Beginner → Intermediate
DigitalOcean Droplet$12.00LowLowHigh (built‑in firewalls)Users needing global regions
Home Lab (NUC)$0 (electricity)High (static IP, UPS)High (physical hardware)Variable (depends on hardening)Hobbyists, privacy‑first
Shared Cloud (AWS Lightsail)$15.00Medium (console wizard)Medium (AWS IAM)High (AWS security services)Small business, scaling later
Raspberry Pi 5 + External SSD$0 (hardware)Medium (Raspberry OS)Medium (SD‑card wear)Low‑Medium (needs extra steps)Makers, low‑traffic sites

The Verdict / Expert Advice #

If you’re reading this because you’ve just “made your first real server,” congratulations! The community’s consensus is clear:

PersonaRecommended Path
**Novice with limited budget