The No-BS Self-Hosting Guide for Developers (2026)

The No-BS Self-Hosting Guide for Developers (2026 Edition)

Replace $200/month in SaaS subscriptions with a $5 VPS.

By DevToolKit — Built by devs, for devs.


Table of Contents

  1. Why Self-Host in 2026?
  2. Choosing Your Server
  3. Initial Server Setup (15 minutes)
  4. Essential Services to Self-Host
  5. Monitoring & Alerting
  6. Security Hardening
  7. Backup Strategy
  8. The Cost Breakdown
  9. Appendix: 21 Free API Endpoints

Chapter 1: Why Self-Host in 2026?

The SaaS tax is real. Here’s what a typical developer pays monthly:

Service SaaS Price Self-Hosted Cost
Uptime monitoring (UptimeRobot Pro) $7/mo $0
Website change detection (Visualping) $29/mo $0
SSL monitoring (multiple domains) $15/mo $0
SEO analysis tools $30/mo $0
DNS/WHOIS lookups Pay-per-query $0
Email validation API $25/mo $0
URL shortener (Bitly Pro) $35/mo $0
Crypto price alerts $10/mo $0
Total $151/mo $5/mo VPS

That’s $1,752/year you’re throwing away.

The objection is always “but my time is worth more than the setup.” Fair. That’s why this guide exists — every service below takes under 30 minutes to deploy, and you’ll never touch it again.


Chapter 2: Choosing Your Server

The $5/month Sweet Spot

For most developers, here’s what you need:

Oracle Cloud Free Tier — The Best Kept Secret

Oracle offers an absurdly generous free tier: - 4 Ampere ARM cores (equivalent to ~8 x86 cores) - 24 GB RAM - 200 GB storage - Forever free (not a trial)

The catch? ARM architecture means some software needs recompiling. But Node.js, Python, Go, and Docker all work perfectly.

Pro tip: Sign up with a non-Gmail address. Corporate/custom domains have higher approval rates.

Ubuntu 24.04 LTS — boring, stable, everything works. Don’t be clever with Arch or NixOS for a server you want to forget about.


Chapter 3: Initial Server Setup (15 Minutes)

# 1. Update everything
sudo apt update && sudo apt upgrade -y

# 2. Create a non-root user
adduser deploy
usermod -aG sudo deploy

# 3. Set up SSH key auth (from your local machine)
ssh-copy-id deploy@your-server-ip

# 4. Disable password auth
sudo sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo systemctl restart sshd

# 5. Set up firewall
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
sudo ufw enable

# 6. Install essentials
sudo apt install -y nginx certbot python3-certbot-nginx nodejs npm git htop

# 7. Set up automatic security updates
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades

Time: ~10 minutes. You now have a secure server with nginx, Node.js, and auto-updates.


Chapter 4: Essential Services to Self-Host

4.1 Uptime Monitor

Replaces: UptimeRobot ($7/mo), Pingdom ($15/mo)

// uptime-monitor.js — checks every 60 seconds, alerts via webhook
const https = require('https');
const http = require('http');

const SITES = [
  { url: 'https://yoursite.com', name: 'Main Site' },
  { url: 'https://api.yoursite.com/health', name: 'API' },
];

const WEBHOOK = process.env.ALERT_WEBHOOK; // Discord/Slack webhook

async function check(site) {
  return new Promise((resolve) => {
    const mod = site.url.startsWith('https') ? https : http;
    const start = Date.now();
    const req = mod.get(site.url, (res) => {
      resolve({ 
        up: res.statusCode < 400, 
        status: res.statusCode, 
        ms: Date.now() - start 
      });
    });
    req.on('error', () => resolve({ up: false, status: 0, ms: 0 }));
    req.setTimeout(10000, () => { req.destroy(); resolve({ up: false, status: 0, ms: 0 }); });
  });
}

async function checkAll() {
  for (const site of SITES) {
    const result = await check(site);
    if (!result.up) {
      console.log(`🔴 DOWN: ${site.name} (${result.status})`);
      if (WEBHOOK) {
        // Send alert to Discord/Slack
        const payload = JSON.stringify({
          content: `🔴 **${site.name}** is DOWN! Status: ${result.status}`
        });
        // ... webhook POST
      }
    } else {
      console.log(`🟢 UP: ${site.name} (${result.ms}ms)`);
    }
  }
}

setInterval(checkAll, 60000);
checkAll();

Run with: node uptime-monitor.js or better, use PM2: pm2 start uptime-monitor.js

4.2 SSL Certificate Monitor

Replaces: SSLMate ($15/mo), manual checking

#!/bin/bash
# ssl-check.sh — checks cert expiry, alerts if < 14 days
DOMAINS=("yoursite.com" "api.yoursite.com" "blog.yoursite.com")

for domain in "${DOMAINS[@]}"; do
  expiry=$(echo | openssl s_client -connect "$domain:443" 2>/dev/null | \
    openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
  
  if [ -z "$expiry" ]; then
    echo "⚠️  $domain: Could not check SSL"
    continue
  fi
  
  expiry_epoch=$(date -d "$expiry" +%s)
  now_epoch=$(date +%s)
  days_left=$(( (expiry_epoch - now_epoch) / 86400 ))
  
  if [ $days_left -lt 14 ]; then
    echo "🔴 $domain: SSL expires in $days_left days!"
  else
    echo "🟢 $domain: SSL OK ($days_left days left)"
  fi
done

Add to crontab: 0 9 * * * /home/deploy/ssl-check.sh

4.3 Website Change Detection

Replaces: Visualping ($29/mo), ChangeTower ($15/mo)

// page-watcher.js — detects content changes on any webpage
const crypto = require('crypto');
const https = require('https');
const fs = require('fs');

const WATCHED = [
  { url: 'https://competitor.com/pricing', name: 'Competitor Pricing' },
  { url: 'https://jobs.company.com', name: 'Job Board' },
];

const STATE_FILE = './page-hashes.json';
const state = fs.existsSync(STATE_FILE) ? JSON.parse(fs.readFileSync(STATE_FILE)) : {};

async function fetchPage(url) {
  return new Promise((resolve, reject) => {
    https.get(url, (res) => {
      let data = '';
      res.on('data', chunk => data += chunk);
      res.on('end', () => resolve(data));
    }).on('error', reject);
  });
}

async function checkChanges() {
  for (const page of WATCHED) {
    try {
      const content = await fetchPage(page.url);
      const hash = crypto.createHash('md5').update(content).digest('hex');
      
      if (state[page.url] && state[page.url] !== hash) {
        console.log(`📝 CHANGED: ${page.name}`);
        // Alert via webhook, email, etc.
      }
      state[page.url] = hash;
    } catch (e) {
      console.log(`❌ Error checking ${page.name}: ${e.message}`);
    }
  }
  fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
}

setInterval(checkChanges, 3600000); // Every hour
checkChanges();

4.4 DNS & WHOIS Lookup API

Replaces: DomainTools ($99/mo), SecurityTrails API ($50/mo)

const dns = require('dns').promises;
const { exec } = require('child_process');

async function dnsLookup(domain) {
  const [a, aaaa, mx, ns, txt, cname] = await Promise.allSettled([
    dns.resolve(domain, 'A'),
    dns.resolve(domain, 'AAAA'),
    dns.resolve(domain, 'MX'),
    dns.resolve(domain, 'NS'),
    dns.resolve(domain, 'TXT'),
    dns.resolve(domain, 'CNAME'),
  ]);
  
  return {
    A: a.status === 'fulfilled' ? a.value : [],
    AAAA: aaaa.status === 'fulfilled' ? aaaa.value : [],
    MX: mx.status === 'fulfilled' ? mx.value : [],
    NS: ns.status === 'fulfilled' ? ns.value : [],
    TXT: txt.status === 'fulfilled' ? txt.value : [],
    CNAME: cname.status === 'fulfilled' ? cname.value : [],
  };
}

// Usage
dnsLookup('example.com').then(console.log);

4.5 SEO Analyzer

Replaces: Ahrefs Site Audit ($99/mo), SEMrush ($130/mo)

Not a full replacement, but for quick on-page SEO analysis:

const https = require('https');
const { JSDOM } = require('jsdom');

async function analyzeSEO(url) {
  const html = await fetchPage(url);
  const dom = new JSDOM(html);
  const doc = dom.window.document;
  
  const title = doc.querySelector('title')?.textContent || '';
  const meta_desc = doc.querySelector('meta[name="description"]')?.content || '';
  const h1s = [...doc.querySelectorAll('h1')].map(h => h.textContent);
  const images = [...doc.querySelectorAll('img')];
  const imgsWithoutAlt = images.filter(i => !i.alt);
  
  return {
    title: { text: title, length: title.length, good: title.length >= 30 && title.length <= 60 },
    description: { text: meta_desc, length: meta_desc.length, good: meta_desc.length >= 120 && meta_desc.length <= 160 },
    h1: { count: h1s.length, texts: h1s, good: h1s.length === 1 },
    images: { total: images.length, missingAlt: imgsWithoutAlt.length },
    score: calculateScore(title, meta_desc, h1s, images, imgsWithoutAlt),
  };
}

Chapter 5: Monitoring & Alerting

PM2 — Process Manager

Every service should run under PM2:

npm install -g pm2

# Start a service
pm2 start uptime-monitor.js --name uptime

# Auto-restart on crash
pm2 startup
pm2 save

# View status
pm2 status
pm2 logs uptime

Resource Monitoring

# Quick system health check
#!/bin/bash
echo "=== CPU ==="
top -bn1 | head -5

echo "=== Memory ==="
free -h

echo "=== Disk ==="
df -h /

echo "=== Services ==="
pm2 jlist | python3 -c "
import json, sys
for p in json.load(sys.stdin):
    status = '🟢' if p['pm2_env']['status'] == 'online' else '🔴'
    print(f\"{status} {p['name']}: {p['pm2_env']['status']} (restarts: {p['pm2_env']['restart_time']})\")"

Chapter 6: Security Hardening

The Essentials (Do These First)

# 1. Fail2ban — blocks brute force SSH attempts
sudo apt install fail2ban
sudo systemctl enable fail2ban

# 2. Change SSH port (optional but effective)
sudo sed -i 's/#Port 22/Port 2222/' /etc/ssh/sshd_config
sudo ufw allow 2222/tcp
sudo systemctl restart sshd

# 3. Disable root login
sudo sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config

# 4. Set up Let's Encrypt SSL
sudo certbot --nginx -d yourserver.com

Nginx Reverse Proxy

Run all services on localhost and proxy through nginx:

server {
    listen 80;
    server_name api.yourserver.com;
    
    location / {
        proxy_pass http://127.0.0.1:3456;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Chapter 7: Backup Strategy

The 3-2-1 rule: 3 copies, 2 different media, 1 offsite.

#!/bin/bash
# backup.sh — daily backup to object storage
BACKUP_DIR="/tmp/backup-$(date +%Y%m%d)"
mkdir -p "$BACKUP_DIR"

# Backup configs
cp -r /etc/nginx/sites-enabled "$BACKUP_DIR/"
cp -r /home/deploy/services "$BACKUP_DIR/"

# Backup databases (if any)
# pg_dump mydb > "$BACKUP_DIR/mydb.sql"

# Compress
tar czf "/tmp/backup-$(date +%Y%m%d).tar.gz" "$BACKUP_DIR"

# Upload to object storage (Backblaze B2 = $0.005/GB/mo)
# b2 upload-file mybucket "/tmp/backup-$(date +%Y%m%d).tar.gz" "backups/"

# Clean up local
rm -rf "$BACKUP_DIR" "/tmp/backup-$(date +%Y%m%d).tar.gz"

Chapter 8: The Cost Breakdown

Item Monthly Cost
Hetzner CX22 VPS $4.50
Domain (optional, .dev) $1.00
Backblaze B2 backups $0.05
Total $5.55/mo

SaaS equivalent of services running: $150-300/mo

Annual savings: $1,740 - $3,534

Time investment: 2-3 hours initial setup, ~15 min/month maintenance


Chapter 9: Appendix — 21 Free API Endpoints

All available right now at http://5.78.129.127:3456:

  1. GET /email/validate/:email — Email validation (syntax + MX)
  2. GET /seo/analyze?url= — Full SEO analysis with scoring
  3. GET /screenshot?url= — Website screenshots
  4. GET /qr/generate?text= — QR code generation
  5. GET /dns/lookup/:domain — Complete DNS lookup
  6. GET /whois/:domain — WHOIS domain information
  7. GET /ssl/:domain — SSL certificate details
  8. GET /speed?url= — Website speed test (TTFB, size)
  9. GET /headers/inspect?url= — HTTP header security audit
  10. GET /ip/info — IP geolocation
  11. GET /hash?text=&algo= — Text hashing (md5/sha1/sha256/sha512)
  12. GET /uuid?count= — UUID v4 generation
  13. GET /timestamp — Current timestamps
  14. POST /text/analyze — Text analysis (word count, sentiment)
  15. POST /shorten — URL shortener with analytics
  16. GET /color/:hex — Color conversion (hex→rgb/hsl)
  17. POST /json/validate — JSON formatter & validator
  18. GET /github/trending — GitHub trending repos
  19. GET /jobs/remote?search= — Remote job listings
  20. GET /tools — Web-based developer tools UI
  21. GET /seo-checker — Interactive SEO checker UI

Rate limit: 100 requests/minute per IP. No API key needed.


About

Built and maintained by DevToolKit — an experiment in replacing SaaS with self-hosted alternatives.

All code is open and all APIs are free. If you found this guide useful, consider sending a Lightning tip: devtoolkit@coinos.io

Follow on Nostr: npub1ls9940xh8gtmlt046hnx3vssy5jrumee8j5gxznkxk3swz37c39sxjfzfp


© 2026 DevToolKit. This guide is free to share. Attribution appreciated.