The Complete Self-Hosting Guide: From Zero to Running Your Own Server

8 min read
Intermediate Self-Hosting VPS Docker Server Guide

Prerequisites

  • Basic command line knowledge
  • A credit card for VPS (costs $3-5/month)

Quick Answer: Get a VPS ($3-5/month), SSH in, create a non-root user, set up SSH keys, enable firewall, install Docker, set up Nginx reverse proxy with Let's Encrypt SSL. Then deploy any app with docker compose up -d. This guide walks through every step.

Need a VPS? Get started with DigitalOcean ($200 free credit for 60 days) or Vultr , or RackNerd ($100 free credit).

Self-hosting means running your own services on your own server instead of relying on third-party companies. Your own VPN, your own cloud storage, your own website, your own email — you control the data, you control the uptime, you control the cost.

This guide takes you from zero to a fully configured, secured, production-ready server. Every section builds on the previous one, and by the end, you will have the foundation to host anything.


Part 1: Get a Server

Choosing a VPS Provider

A VPS (Virtual Private Server) is a virtual machine in a data center that you rent by the month. You get root access, a public IP, and full control.

Provider Starting Price Strengths
DigitalOcean $4/month Beginner-friendly UI, great docs
Vultr , or RackNerd $3.50/month Budget-friendly, many locations
Hetzner $3.29/month Best value in Europe
Linode (Akamai) $5/month Reliable, good support
Oracle Cloud Free tier 4 ARM cores free forever (limited)

Recommended Starting Specs

  • 1 vCPU, 1 GB RAM, 25 GB SSD — enough for 2-3 lightweight apps
  • Ubuntu 22.04 LTS or Debian 12 — most tutorials target these
  • Location — pick the closest to your users

Create and Connect

After creating the VPS, you get an IP address and root password.

ssh root@YOUR_SERVER_IP

First time? Type yes when asked about the fingerprint.

For the full initial setup walkthrough, see our VPS Setup Guide.


Part 2: Secure Your Server

An unsecured server gets compromised within hours. Do these before anything else.

Create a Non-Root User

adduser sam
usermod -aG sudo sam

Set Up SSH Keys

On your local machine:

ssh-keygen -t ed25519
ssh-copy-id sam@YOUR_SERVER_IP

Now disable password login:

sudo nano /etc/ssh/sshd_config
PasswordAuthentication no
PermitRootLogin no
sudo systemctl restart sshd

Full guide: How to Set Up SSH Keys

Enable Firewall

sudo ufw allow 22/tcp       # SSH
sudo ufw allow 80/tcp       # HTTP
sudo ufw allow 443/tcp      # HTTPS
sudo ufw enable

Full guide: UFW Firewall Cheat Sheet

Install Fail2ban

sudo apt install fail2ban -y
sudo systemctl enable --now fail2ban

Blocks brute-force attacks automatically. Full guide: Fail2ban Setup

Enable Automatic Security Updates

sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure unattended-upgrades

For a comprehensive security checklist, see our Server Hardening Guide and SSH Hardening Guide.


Part 3: Install Docker

Docker lets you run applications in isolated containers. Instead of installing software directly on your server (and dealing with dependency conflicts), you run each app in its own container with everything it needs bundled in.

Install

curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker sam

Log out and back in for the group change to take effect.

Verify

docker --version
docker compose version
docker run hello-world

Essential Docker Commands

docker ps                          # Running containers
docker ps -a                       # All containers
docker logs container-name         # View logs
docker exec -it container-name bash  # Shell into container
docker stop container-name         # Stop
docker rm container-name           # Remove
docker system prune -a             # WARNING: removes ALL unused images and containers — re-downloads needed

Full reference: Docker Cheat Sheet


Part 4: Set Up a Reverse Proxy (Nginx)

A reverse proxy sits in front of all your apps and handles:

  • SSL certificates (HTTPS) — one place for all certs
  • Domain routing — app1.yourdomain.com goes to container A, app2.yourdomain.com goes to container B
  • Security — rate limiting, headers, access control

Install Nginx

sudo apt install nginx -y
sudo systemctl enable --now nginx

Basic Config

Create /etc/nginx/sites-available/myapp:

server {
    listen 80;
    server_name app.yourdomain.com;

    location / {
        proxy_pass http://localhost:3000;
        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;
    }
}
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

Full guide: Nginx Reverse Proxy Guide


Part 5: Get SSL Certificates (HTTPS)

sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d app.yourdomain.com

Certbot automatically:

  • Gets a free Let's Encrypt certificate
  • Configures Nginx for HTTPS
  • Sets up auto-renewal

That is it. Your app is now at https://app.yourdomain.com.

Full guide: SSL/TLS Explained


Part 6: Deploy Applications

Now your server is secured, Docker is running, Nginx handles routing, and SSL is active. Deploy anything.

Example: Deploy a Web App

Create compose.yml:

services:
  app:
    image: your-app-image
    restart: unless-stopped
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgres://db:5432/myapp
    depends_on:
      - db

  db:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      - POSTGRES_PASSWORD=secret
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:
docker compose up -d

Add an Nginx config pointing app.yourdomain.com to localhost:3000, run certbot, and it is live.

More examples: Docker Compose Examples

Popular Self-Hosted Apps

App What It Does Docker Image
Nextcloud Cloud storage (like Google Drive) nextcloud
Vaultwarden Password manager (Bitwarden compatible) vaultwarden/server
Gitea Git hosting (like GitHub) gitea/gitea
Uptime Kuma Uptime monitoring louislam/uptime-kuma
Portainer Docker management UI portainer/portainer-ce
Pi-hole Network-wide ad blocker pihole/pihole
Jellyfin Media server (like Plex) jellyfin/jellyfin
n8n Workflow automation n8nio/n8n
Grafana + Prometheus Monitoring and dashboards grafana/grafana

Each of these is a single docker compose up -d away from running.


Part 7: Set Up a VPN

Run your own VPN so your internet traffic goes through your server. No commercial VPN needed.

WireGuard (Recommended)

The fastest, simplest VPN protocol.

Easy method with SamNet-WG:

curl -sSL https://raw.githubusercontent.com/SamNet-dev/wg-orchestrator/main/install.sh | sudo bash

SamNet-WG (wg-orchestrator) gives you a full management TUI, web dashboard, QR codes for mobile, per-peer bandwidth tracking, and zero-touch setup.

sudo samnet                    # Open the TUI
# Peers → Add Peer → Scan QR on your phone

Full guide: WireGuard Setup Guide

For Censorship Bypass

If you are in a country with heavy internet censorship (Iran, China, Russia), standard VPN protocols get blocked. Use tools designed to bypass Deep Packet Inspection:

  • paqctl — Paqet (KCP over raw TCP) and GFW-Knocker (violated TCP + QUIC) — specifically designed to defeat censorship firewalls
  • VLESS + Reality — via 3X-UI panel — makes traffic look like a real TLS connection to a legitimate website
  • MTProxyMax — Telegram-specific proxy with FakeTLS obfuscation

More: Every Way to Bypass Internet Censorship


Part 8: Domain and DNS

Get a Domain

Buy a domain from Namecheap, Cloudflare Registrar, or Porkbun. Cost: $8-15/year.

Point DNS to Your Server

Create an A record pointing your domain to your server's IP:

Type: A
Name: @ (or subdomain)
Value: YOUR_SERVER_IP
TTL: Auto

Use Cloudflare (Recommended)

Cloudflare adds a free CDN, DDoS protection, and SSL in front of your server:

  1. Add your domain to Cloudflare (free plan)
  2. Change nameservers at your registrar
  3. Set SSL mode to Full (Strict)
  4. Enable "Always Use HTTPS"

Full guide: Cloudflare Setup Guide


Part 9: Backups

The most important thing most self-hosters forget.

Automated Database Backups

# Create backup script
cat > /opt/backup.sh << 'EOF'
#!/bin/bash
DATE=$(date +%Y%m%d)
docker exec postgres pg_dumpall -U postgres | gzip > /backups/db-$DATE.sql.gz
find /backups -name "*.sql.gz" -mtime +30 -delete
EOF
chmod +x /opt/backup.sh

# Run daily at 2 AM
(crontab -l; echo "0 2 * * * /opt/backup.sh") | crontab -

Backup Docker Volumes

# Stop containers, backup volumes, restart
docker compose stop
tar czf /backups/volumes-$(date +%Y%m%d).tar.gz /var/lib/docker/volumes/
docker compose start

Off-Site Backup

Always keep a copy outside your server:

# Sync to a backup server
rsync -avz /backups/ backup-server:/remote-backups/

# Or use rclone for cloud storage (S3, B2, etc.)
rclone sync /backups remote:bucket-name/backups/

Full guide: Crontab Tutorial


Part 10: Monitoring

Know when something breaks before your users tell you.

Simple Monitoring: Uptime Kuma

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

volumes:
  uptime-data:

Web-based dashboard that pings your services and alerts you via email, Telegram, Slack, or Discord when something goes down.

Server Monitoring: htop + journalctl

# Real-time resource usage
htop

# Service logs
journalctl -u nginx -f
journalctl -u docker -f

# Disk usage
df -h

# Check for issues
systemctl --failed

Full guide: Linux Log Files Explained


Part 11: Maintenance Routine

Weekly

  • Check df -h — make sure disk isn't full
  • Check docker system df — prune if needed
  • Review journalctl --since "1 week ago" -p err — any errors?
  • Verify backups exist and are recent

Monthly

  • sudo apt update && sudo apt upgrade — update system packages
  • docker compose pull && docker compose up -d — update containers
  • Review Fail2ban logs — sudo fail2ban-client status
  • Test restoring a backup (actually try it)

After Any Change

  • nginx -t — test config before reloading
  • Check app logs: docker compose logs -f
  • Verify SSL cert: certbot certificates

The Complete Stack (Summary)

┌──────────────────────────────────┐
│           Cloudflare CDN         │  Free DDoS protection + caching
├──────────────────────────────────┤
│           Nginx + SSL            │  Reverse proxy + Let's Encrypt
├──────────────────────────────────┤
│     Docker Containers            │  Your apps in isolated containers
│  ┌─────┐ ┌─────┐ ┌─────┐       │
│  │App 1│ │App 2│ │ DB  │       │
│  └─────┘ └─────┘ └─────┘       │
├──────────────────────────────────┤
│     Ubuntu 22.04 LTS             │  Base OS
│     UFW + Fail2ban + SSH keys    │  Security
├──────────────────────────────────┤
│     VPS ($3-5/month)             │  DigitalOcean / Vultr / Hetzner
└──────────────────────────────────┘

Related Guides on SamNet

Setup:

Security:

VPN & Proxy:

Tools: