Most people discover self-hosting the same way: they get a SaaS bill that makes them wince. $49/month for a simple form backend. $79/month for a basic analytics tool. $120/month for a database that's mostly idle. At some point you do the math and realize you're paying enterprise prices for indie hacker workloads.
This self-hosting guide for beginners won't sugarcoat the tradeoffs. You'll trade some convenience for control — and that's a good deal, if you go in with the right expectations. I've been running production services on cheap VPS boxes for nine years. Most of what I know came from breaking things at 2am. This guide is the shortcut I wish I'd had.
Here's what we'll cover: picking a server, locking it down, getting apps running with Docker, and keeping things alive without a dedicated ops team. Let's move.
Why Self-Host at All?
Let me be blunt: self-hosting isn't for everyone. If you're non-technical and just need a landing page, use Netlify and move on. But if you're an indie hacker running a SaaS, a side project with real users, or a small team that processes sensitive data — self-hosting starts making serious financial and operational sense.
The economics are stark. A $6/month Hetzner CAX11 (2 vCPU ARM, 4GB RAM, as of 2024) can comfortably run Plausible Analytics, a small Postgres database, and a Node.js API simultaneously. That same stack on managed services would run you $60–120/month easy. Over a year, that's $650–$1,400 back in your pocket.
Beyond cost, you own the data. No vendor lock-in. No surprise deprecations. No "we're sunsetting the free tier" emails.
Choosing Your First Server
Don't overthink this. For a first server, you want:
- A reputable VPS provider with hourly billing so you can kill it if you mess up
- At least 2GB RAM — 1GB sounds fine until you run Docker
- A data center close to your users — latency matters more than you think
- Linux — specifically Ubuntu 24.04 LTS or Debian 12
My current go-to is Hetzner. Their CAX21 (4 vCPU ARM, 8GB RAM) is €7.49/month and genuinely fast. DigitalOcean and Vultr are solid alternatives if you need US-based infrastructure. Avoid any provider that makes you call sales to get pricing — that's not for us.
Once you've picked a provider, spin up an Ubuntu 24.04 instance. Don't add anything fancy yet. Just get SSH access working.
Securing the Box Before Anything Else
I see beginners skip this step constantly. Don't. An unsecured VPS gets compromised within hours — there are bots scanning for default credentials around the clock.
Here's the minimal hardening checklist:
1. Create a non-root user
adduser deploy
usermod -aG sudo deploy
2. Copy your SSH key to the new user
rsync --archive --chown=deploy:deploy ~/.ssh /home/deploy
3. Disable password auth and root login
Edit /etc/ssh/sshd_config:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
Then restart SSH: systemctl restart sshd
4. Set up a basic firewall with UFW
ufw default deny incoming
ufw default allow outgoing
ufw allow ssh
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
That's it for now. You're not Fort Knox, but you're not a sitting duck either. If you want to go deeper on hardening, this guide on how to harden SSH on Linux covers the topic in detail.
Docker Is Your Best Friend Here
If there's one tool that makes self-hosting actually manageable for a one-person operation, it's Docker. Not Kubernetes — that's overkill until you're running dozens of services and have a team. Docker Compose is the sweet spot.
Install Docker on Ubuntu 24.04:
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker deploy
Log out and back in for the group change to take effect.
Now here's a real example. Say you want to run Plausible Analytics — a privacy-friendly Google Analytics alternative — on your own server. Their self-hosted setup uses Docker Compose. You clone their repo, edit a .env file with your domain and a few secrets, and run:
docker compose up -d
That's genuinely it. Plausible, Postgres, and Clickhouse all spin up and talk to each other. No manual database setup. No dependency hell.
This pattern repeats across almost every self-hosted app worth running: Gitea, Umami, Miniflux, Listmonk, n8n. They all ship with a docker-compose.yml. The ecosystem has matured a lot in the last three years.
Getting a Domain and HTTPS Working
A server without a domain and HTTPS is a half-finished project. Here's the fast path.
Domain: Buy from Cloudflare Registrar or Namecheap. Point an A record at your server's IP. Wait 5 minutes (or up to an hour if you're unlucky).
Reverse proxy: You need something to sit in front of your Docker containers and route traffic. I use Caddy — it handles HTTPS certificates automatically via Let's Encrypt, the config is human-readable, and it doesn't require a PhD to operate.
Install Caddy:
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update && sudo apt install caddy
A basic Caddyfile to proxy your app:
analytics.yourdomain.com {
reverse_proxy localhost:8000
}
Run systemctl reload caddy and Caddy will automatically get a TLS certificate. HTTPS, done. No Certbot juggling, no cron jobs renewing certificates at 3am.
Nginx with Certbot works too, but honestly Caddy's automatic HTTPS alone makes it worth learning first. I switched everything over about two years ago and haven't thought about certificate renewal since.
Keeping Things Running: Backups and Monitoring
This is where most beginners drop the ball. They get the app running, feel great, and then six months later a disk fills up or a Docker volume gets accidentally pruned. No backup. RIP.
Two tools. That's all you need to start.
Backups with restic
restic is a fast, encrypted backup tool that works with S3-compatible storage. Hetzner Object Storage is €0.023/GB/month — cheap enough that there's no excuse.
sudo apt install restic
export RESTIC_REPOSITORY="s3:https://your-bucket-endpoint/backups"
export RESTIC_PASSWORD="a-strong-passphrase"
export AWS_ACCESS_KEY_ID="your-key"
export AWS_SECRET_ACCESS_KEY="your-secret"
restic init
restic backup /home/deploy/apps
Set this up as a daily cron job:
crontab -e
# Add:
0 3 * * * /usr/bin/restic backup /home/deploy/apps --quiet
Test restoring before you need it. Seriously. An untested backup is just a false sense of security.
Monitoring with UptimeRobot
UptimeRobot's free tier monitors 50 URLs at 5-minute intervals and sends you an email when something goes down. That's enough for most indie hackers. Set it up, point it at your domain, done. You'll know within 5 minutes if your server is dead.
For more detailed server metrics — CPU, RAM, disk — Netdata has a free self-hosted agent that gives you a real-time dashboard with zero configuration. wget -O /tmp/netdata-kickstart.sh https://my-netdata.io/kickstart.sh && sh /tmp/netdata-kickstart.sh and you're up.
A Realistic Stack for Your First Self-Hosted Setup
Here's what I'd actually run on a fresh $7/month Hetzner box as a starting point:
| Tool | Purpose | Monthly Cost (SaaS equivalent) |
|---|---|---|
| Plausible (self-hosted) | Analytics | $9–19/month saved |
| Listmonk | Email newsletters | $29–99/month saved |
| Umami | Secondary analytics | $9/month saved |
| Miniflux | RSS reader | $3/month saved |
| Gitea | Private Git repos | $4–21/month saved |
All five run comfortably on 4GB RAM. Total self-hosted cost: your $7 server plus maybe $1/month in storage. Total SaaS equivalent: $54–$151/month. The math isn't subtle.
You don't have to run all five on day one. Pick one app that solves a real pain point — probably analytics or email — and get that working first. Build confidence before adding complexity.
Common Beginner Mistakes (And How to Avoid Them)
Running everything as root. Don't. Use the deploy user we set up earlier. If something gets compromised, blast radius matters.
No swap space. If your app spikes memory and you have no swap, the OOM killer starts shooting processes. Add 2GB swap on any server with less than 4GB RAM:
fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
Ignoring logs. docker compose logs -f your-service-name is your first debugging tool. Check it before assuming the server is broken.
Upgrading in production without testing. Docker makes this easier — pull the new image, test with docker compose up on a staging copy, then switch. Don't just docker pull and restart on live without knowing what changed.
Not setting resource limits. One misbehaving container can starve everything else. Add mem_limit: 512m to your Docker Compose services as a sanity check.
The Self-Hosting Learning Curve Is Real, But Short
The first time you set up a server, it'll take you a weekend. The second time, maybe two hours. By the fifth time, you'll do it in under an hour from memory. The skills compound fast because the underlying concepts don't change much — Linux, Docker, DNS, reverse proxy. Learn those four things and you can run almost anything.
This self-hosting guide for beginners is deliberately opinionated. I'm not covering every possible tool or every edge case. I'm telling you what I'd do if I were starting over today, on a budget, trying to get something real running without a DevOps team.
There's more to learn — setting up automated Docker updates and managing multiple apps on one server are good next steps once you've got the basics down.
Your action for tomorrow: spin up a Hetzner CAX11 (€3.79/month), harden it with the steps above, and deploy one self-hosted app using Docker Compose. Pick Plausible or Umami if you want analytics, Listmonk if you need email. Get one thing working. That's it. The rest follows naturally.