DEV Community

DEVANSHU PATIL
DEVANSHU PATIL

Posted on AI-assisted

The Architecture of a Self-Hosted Micro-VPS: Running 10 Apps on 2GB RAM

Cloud providers love selling multi-node Kubernetes clusters, managed databases, and serverless compute that cost hundreds of dollars every month. But for independent developers, side projects, and early-stage SaaS MVPs, you can easily run 8 to 12 production-grade applications on a single $5/month 2GB RAM VPS (Hetzner, DigitalOcean, or an Oracle Cloud Free Tier instance).

The secret isn't magic—it is disciplined systems engineering:

  • Strict Docker memory ceilings.
  • ZRAM compression over slow disk swap.
  • Shared database pooling.
  • Lightweight reverse proxies with automated SSL.

In this guide, we walk through the exact architecture and configuration necessary to run a resilient, multi-app self-hosted server without ever crashing into the Linux OOM (Out-Of-Memory) Killer.


The Resource Budget: Where Does 2GB RAM Go?

When you only have 2048 MB of RAM, every megabyte counts:

Total Memory: 2048 MB
|-- Linux Kernel & Base OS (Ubuntu/Debian Minimal) : 180 MB
|-- Nginx / Caddy (Reverse Proxy + TLS)            :  40 MB
|-- Shared PostgreSQL 16 Instance                 : 250 MB
|-- Shared Redis 7 Instance                       :  50 MB
|-- 6x FastAPI / Go / Node.js Microservices       : 900 MB (150 MB each)
|-- Monitoring (Prometheus Node Exporter)         :  30 MB
|-- Buffer / OS File Cache Free Space             : 598 MB
Enter fullscreen mode Exit fullscreen mode

1. Enable ZRAM: The Miracle Cure for Small RAM Servers

Standard Linux disk swap on a budget SSD is agonizingly slow. When your server swaps to disk, I/O wait spikes to 99%, the CPU locks up, and requests time out.

ZRAM creates a compressed swap device directly inside RAM using fast LZO or LZ4 compression. A 2:1 or 3:1 compression ratio effectively turns 1GB of physical RAM into 2GB to 3GB of usable memory with near-zero latency penalty!

Setup ZRAM on Ubuntu/Debian:

sudo apt update && sudo apt install -y zram-tools

# Configure ZRAM
sudo tee /etc/default/zramswap << 'EOF'
ALGO=lz4
PERCENT=50
PRIORITY=100
EOF

# Restart service
sudo systemctl restart zramswap
Enter fullscreen mode Exit fullscreen mode

Check status with zramctl:

zramctl
Enter fullscreen mode Exit fullscreen mode

2. Put Strict Memory Ceilings on Every Container

By default, Docker allows a container to consume 100% of the host system's RAM. If one Node.js or Python process has a memory leak, the Linux kernel invokes the oom-killer and abruptly kills your PostgreSQL database or SSH daemon!

Always declare mem_limit in your docker-compose.yml:

version: '3.8'

services:
  db:
    image: postgres:16-alpine
    restart: always
    environment:
      POSTGRES_DB: app_db
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: secure_password_here
    volumes:
      - pgdata:/var/lib/postgresql/data
    deploy:
      resources:
        limits:
          memory: 300M

  redis:
    image: redis:7-alpine
    restart: always
    command: redis-server --maxmemory 64mb --maxmemory-policy allkeys-lru
    deploy:
      resources:
        limits:
          memory: 80M

  api_service:
    image: myrepo/api-service:latest
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 180M

volumes:
  pgdata:
Enter fullscreen mode Exit fullscreen mode

3. Tune PostgreSQL for Low-Memory Footprint

On a micro-VPS, tune postgresql.conf parameters:

shared_buffers = 128MB
work_mem = 4MB
maintenance_work_mem = 32MB
effective_cache_size = 512MB
max_connections = 40
Enter fullscreen mode Exit fullscreen mode

Reducing max_connections from the default 100 down to 40 prevents client connection spikes from multiplying per-connection RAM buffers.


4. Edge Routing with Caddy (Zero-Config SSL)

Use Caddy for HTTP/2, HTTP/3, and automatic Let's Encrypt certificates while consuming under 35MB of memory.

api.mydomain.com {
    reverse_proxy localhost:8000
}

dashboard.mydomain.com {
    reverse_proxy localhost:3000
}
Enter fullscreen mode Exit fullscreen mode

Reloading Caddy takes under 100 milliseconds and causes zero downtime.

Top comments (0)