ArbindBuilds LogoArbindBuilds
Blog
CheatsheetsProjectsLinksAbout
Hire Me

ArbindBuilds

Build. Design. Repeat.

© 2026 ArbindBuilds.
All rights reserved.

Site Map

  • Home
  • Blog
  • Projects
  • About
  • Uses

Content

  • Cheatsheets
  • AI Tools
  • AI Prompts
  • Links

Products

  • Speakify
  • Gumroad Store
  • GitHub
  • Twitter / X

Made with care in Assam, India.

  1. Home/
  2. Cheatsheets/
  3. Self-Hosting Developer Cheatsheet

Self-Hosting Developer Cheatsheet

Dense reference card for self-hosting apps on VPS — reverse proxy, containers, SSL, DNS, backups, monitoring, and security hardening.

ArbindBuilds·June 1, 2026·
selfhostingdevopsvpsdockernginxlinuxsysadmininfrastructure

Core Stack Decisions

  • Reverse proxy — Nginx (raw control) or Caddy (auto-HTTPS, easier config)
  • Runtime — Docker + Compose for isolation; bare-metal for low-latency or single-binary apps
  • DNS — Cloudflare (proxy + DDoS) or authoritative-only (less latency, no WAF)
  • Secrets — .env files never committed; use docker secret or Infisical for production
  • Storage — named Docker volumes for data; bind mounts only for config you edit manually
  • OS — Ubuntu 24 LTS (widest support) or Debian 12 (minimal footprint)

Server Initial Setup

# Update, create non-root user, harden SSH
apt update && apt upgrade -y
adduser deploy && usermod -aG sudo deploy
mkdir -p /home/deploy/.ssh && cp ~/.ssh/authorized_keys /home/deploy/.ssh/
chmod 700 /home/deploy/.ssh && chmod 600 /home/deploy/.ssh/authorized_keys

# Disable root + password login
sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart sshd

# Firewall
ufw allow OpenSSH && ufw allow 80 && ufw allow 443 && ufw enable

Nginx Reverse Proxy

server {
    listen 80;
    server_name app.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name app.example.com;

    ssl_certificate     /etc/letsencrypt/live/app.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;

    location / {
        proxy_pass         http://localhost:3000;
        proxy_set_header   Host $host;
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   X-Forwarded-Proto $scheme;
        proxy_buffering    off;
    }
}
DirectivePurpose
proxy_buffering offRequired for SSE / streaming responses
client_max_body_size 50MRaise for file upload endpoints
proxy_read_timeout 120Raise for long-running LLM calls

Docker Compose Essentials

services:
  app:
    image: myapp:latest
    restart: unless-stopped
    env_file: .env
    ports:
      - "127.0.0.1:3000:3000"   # Bind to loopback only
    volumes:
      - app_data:/app/data
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_DB: ${DB_NAME}
      POSTGRES_USER: ${DB_USER}
      POSTGRES_PASSWORD: ${DB_PASS}
    volumes:
      - pg_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
      interval: 10s
      retries: 5

volumes:
  app_data:
  pg_data:
  • Always bind ports to 127.0.0.1 — never expose DB to 0.0.0.0
  • restart: unless-stopped survives reboots but respects manual docker stop

SSL — Certbot (Let's Encrypt)

apt install certbot python3-certbot-nginx -y

# Issue cert + auto-configure Nginx
certbot --nginx -d app.example.com

# Dry-run renewal (run before cron)
certbot renew --dry-run

# Cron auto-renewal (runs twice daily)
echo "0 0,12 * * * root certbot renew --quiet" >> /etc/crontab
OptionWhen to use
--nginxAuto-patches Nginx config
--standaloneNo web server running yet
--dns-cloudflareWildcard certs (*.domain.com)
--webroot -w /var/www/htmlBehind existing proxy

DNS + Cloudflare Setup

RecordTypeValueProxy
@AVPS IPOrange (CDN)
wwwCNAME@Orange
apiAVPS IPOrange
dbAVPS IPGrey (DNS only)
  • Turn proxy OFF for direct TCP (DB, SSH, mail)
  • Use Full (Strict) SSL mode — not Flexible (MITM risk)
  • Add X-Forwarded-For to Nginx real_ip_header or logs show Cloudflare IPs
  • Set Always Use HTTPS + HSTS in Cloudflare SSL/TLS settings

Systemd Service (non-Docker)

# /etc/systemd/system/myapp.service
[Unit]
Description=My App
After=network.target

[Service]
User=deploy
WorkingDirectory=/home/deploy/myapp
EnvironmentFile=/home/deploy/myapp/.env
ExecStart=/usr/bin/node dist/server.js
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable --now myapp
journalctl -u myapp -f        # tail logs

Backup Strategy

# Postgres dump (run via cron or pre-deploy hook)
docker exec postgres pg_dump -U $DB_USER $DB_NAME | \
  gzip > /backups/db_$(date +%Y%m%d_%H%M).sql.gz

# Docker volume backup
docker run --rm \
  -v myapp_pg_data:/data \
  -v /backups:/out \
  alpine tar czf /out/vol_$(date +%Y%m%d).tar.gz /data

# Prune backups older than 7 days
find /backups -mtime +7 -delete
  • 3-2-1 rule: 3 copies, 2 media, 1 offsite (Backblaze B2 / S3)
  • Test restores quarterly — untested backups are not backups

Monitoring Quickref

ToolWhat it coversSelf-host
htop / btopCPU, RAM, processesBuilt-in
Uptime KumaHTTP/TCP uptime + alertsDocker
NetdataReal-time system metricsDocker
Loki + GrafanaLog aggregation + dashboardsCompose stack
Fail2banAuto-ban brute-force IPsapt install fail2ban
# Quick server health snapshot
free -h && df -h && docker stats --no-stream

Security Hardening Checklist

  • SSH key-only auth, root login disabled
  • UFW: default deny-in, allow only 22/80/443
  • fail2ban on SSH + Nginx (5 fails = 1h ban)
  • Unattended security upgrades: apt install unattended-upgrades
  • No secrets in docker-compose.yml — use .env + .gitignore
  • DB not exposed to public internet (loopback only or internal Docker network)
  • Cloudflare WAF rules enabled (block bad bots, SQLi, XSS)
  • Rate limiting in Nginx: limit_req_zone $binary_remote_addr zone=api:10m rate=20r/s
arbindbuilds.com/cheatsheets/self-hosting-developer-cheatsheet
← Back to Cheatsheets