Full-Stack Development·10 min

Self-Hosting Web Applications in 2026: Docker, VPS, and Production Architecture

By Bahaj Abderrazak·Published July 16, 2024·Updated September 24, 2026

SaaS costs and vendor lock-in are driving founders and engineering teams back toward self-hosting. Here is how to configure a resilient, automated production environment on a budget using modern DevOps tooling.

Cloud platforms like AWS, Google Cloud, and managed PaaS providers offer unmatched scalability, but they often come with steep pricing, unpredictable bandwidth bills, and vendor lock-in. For early-stage startups, SaaS products with predictable workloads, and European or Moroccan businesses with strict data sovereignty needs, self-hosting has experienced a major resurgence in 2026.

With containerization, reverse proxies with automated SSL, and modern infrastructure-as-code, a single $20 to $40/month VPS can reliably serve hundreds of thousands of monthly active users with sub-100ms response times.

In this guide, I break down the exact production setup I deploy for self-hosted full-stack applications.

Why Self-Host? The Economics and Control

When choosing where to run your software, evaluate three core factors:

  1. Cost Predictability: A fixed-tier VPS (Hetzner, OVH, DigitalOcean) eliminates surprise charges from spikes in data transfer or database compute hours.
  2. Data Ownership: You maintain total control over your PostgreSQL instances, file storage, and audit logs without sharing multi-tenant infrastructure.
  3. Simplicity: Managing a clean Docker Compose topology is often easier to debug than navigating hundreds of AWS IAM permissions and VPC security groups.

However, self-hosting means taking full responsibility for server hardening, security updates, database backups, and SSL renewal.

Core Production Architecture

A resilient self-hosted stack typically includes:

  • Host OS: Ubuntu LTS 24.04 or Debian 12 with automatic security patches enabled.
  • Reverse Proxy: Traefik or Caddy for automatic Let's Encrypt SSL certificates and zero-downtime routing.
  • Application Layer: Next.js, React, or Laravel running in lightweight Docker containers.
  • Database: PostgreSQL with WAL archiving and automated daily off-site snapshots.
  • Cache & Queue: Redis for background tasks and caching.
  • Process Management: Docker Compose with health checks and restart: unless-stopped.

Hardening the Base VPS

Before running application containers, secure the host environment:

# 1. Update packages and configure unattended security updates
sudo apt update && sudo apt upgrade -y
sudo apt install -y ufw fail2ban unattended-upgrades

# 2. Configure firewall (UFW)
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp   # SSH (or your custom port)
sudo ufw allow 80/tcp   # HTTP
sudo ufw allow 443/tcp  # HTTPS
sudo ufw enable

# 3. Disable root password login in /etc/ssh/sshd_config
# PermitRootLogin prohibit-password
# PasswordAuthentication no
sudo systemctl restart ssh

Modern Docker Compose Topology with Caddy

Caddy has become my preferred reverse proxy for self-hosted systems because of its automatic SSL management, zero configuration certificate renewal, and clean syntax:

# docker-compose.prod.yml
services:
  caddy:
    image: caddy:2-alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config
    networks:
      - web

  app:
    image: ghcr.io/myorg/myapp:latest
    restart: unless-stopped
    expose:
      - "3000"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgres://user:${DB_PASSWORD}@postgres:5432/production_db
    depends_on:
      postgres:
        condition: service_healthy
    networks:
      - web
      - internal

  postgres:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_DB: production_db
      POSTGRES_USER: user
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user -d production_db"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - internal

volumes:
  caddy_data:
  caddy_config:
  pgdata:

networks:
  web:
  internal:

And the corresponding Caddyfile:

example.com {
    encode zstd gzip
    reverse_proxy app:3000 {
        header_up Host {host}
        header_up X-Real-IP {remote}
    }
}

Automated Backups: The Non-Negotiable Rule

A self-hosted server without automated off-site backups is an outage waiting to happen. Never store your only backup on the same physical disk as the running database.

Set up an automated cron script that dumps PostgreSQL, encrypts the archive, and synchronizes it with an S3-compatible bucket (like Cloudflare R2 or Backblaze B2):

#!/usr/bin/env bash
# backup-db.sh — automated daily backup to S3/R2
set -euo pipefail

TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
BACKUP_DIR="/var/backups/postgres"
FILENAME="db_backup_${TIMESTAMP}.sql.gz"

mkdir -p "${BACKUP_DIR}"
docker exec -t production-postgres-1 pg_dumpall -c -U user | gzip > "${BACKUP_DIR}/${FILENAME}"

# Sync with encrypted cloud bucket using rclone or aws-cli
aws s3 cp "${BACKUP_DIR}/${FILENAME}" "s3://my-offsite-backups/${FILENAME}" --endpoint-url "https://my-r2-id.r2.cloudflarestorage.com"

# Purge local files older than 7 days
find "${BACKUP_DIR}" -type f -mtime +7 -delete

When to Transition Beyond a Single VPS

A single VPS setup with Docker Compose easily supports initial product validation through hundreds of concurrent active users. You should only consider migrating to Kubernetes or managed cloud services when:

  • You need high-availability multi-region active-active database clustering.
  • Your compute requirements scale unpredictably on hourly notice.
  • Your compliance requirements mandate SOC 2 Type II or HIPAA certifications managed at the hosting layer.

If your database design is clean and your assets are offloaded to a CDN, you will be amazed at how far a single optimized VPS will take you. See Database Schema Design Best Practices for structuring your storage efficiently.

Looking for help building a self-hosted SaaS architecture or migrating away from expensive cloud vendors? Explore my full-stack development services or reach out to discuss your infrastructure.

Self-HostingDockerDevOpsVPSArchitectureFull-Stack Development

Related articles

Let's begin

Have an idea worth building?

Tell me what you are creating, what stage the project is at, and where you need technical support — I will respond with practical next steps.