BlogArchitectureSaaS Infrastructure on a Budget: Complete Stack Guide (2025)

SaaS Infrastructure on a Budget: Complete Stack Guide (2025)

Adrian Silaghi
Adrian Silaghi
January 17, 2026
20 min read
4 views
#saas #startup #infrastructure #budget #vps #postgresql #redis #architecture #self-hosted #cost optimization
SaaS Infrastructure on a Budget: Complete Stack Guide (2025)

Building a SaaS is expensive enough without hemorrhaging money on infrastructure. AWS bills can easily hit $500-1000/month for a simple app, and that's before you have paying customers.

This guide shows you how to build production-grade SaaS infrastructure for under €50/month—the same architecture that can handle 100K+ users. We'll cover the complete stack: compute, database, cache, storage, email, monitoring, and more.

The Budget SaaS Stack

Component Service Cost/Month
Application Server VPS (4GB RAM) €8.99
Database PostgreSQL (on VPS) €0 (included)
Cache Redis (on VPS) €0 (included)
Object Storage S3-compatible €3.99
Email (Transactional) Resend / Postmark ~€0-20
Monitoring Uptime Kuma (self-hosted) €0
Error Tracking Sentry (free tier) €0
Analytics Plausible (self-hosted) €0 (on same VPS)
SSL Certificates Let's Encrypt €0
Domain Any registrar ~€1/mo
Total €13-33/mo

Architecture Overview

┌─────────────────────────────────────────────────────────────┐
│                         Internet                             │
└─────────────────────────────┬───────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                    Cloudflare (Free)                         │
│              DNS, DDoS Protection, CDN                       │
└─────────────────────────────┬───────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                     VPS (€8.99/mo)                           │
│  ┌──────────────────────────────────────────────────────┐   │
│  │                    Caddy                              │   │
│  │           (Reverse Proxy + Auto SSL)                  │   │
│  └────────────────────────┬─────────────────────────────┘   │
│                           │                                  │
│  ┌────────────────────────┴─────────────────────────────┐   │
│  │                                                       │   │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  │   │
│  │  │   App       │  │  Background │  │  Scheduler  │  │   │
│  │  │  (Gunicorn/ │  │   Worker    │  │   (Cron)    │  │   │
│  │  │   PM2)      │  │  (Celery/   │  │             │  │   │
│  │  │             │  │   Sidekiq)  │  │             │  │   │
│  │  └──────┬──────┘  └──────┬──────┘  └─────────────┘  │   │
│  │         │                │                           │   │
│  │         ▼                ▼                           │   │
│  │  ┌─────────────────────────────────────────────┐    │   │
│  │  │              PostgreSQL                      │    │   │
│  │  └─────────────────────────────────────────────┘    │   │
│  │  ┌─────────────────────────────────────────────┐    │   │
│  │  │                 Redis                        │    │   │
│  │  │     (Cache + Sessions + Queues)              │    │   │
│  │  └─────────────────────────────────────────────┘    │   │
│  │                                                       │   │
│  └───────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                 S3 Storage (€3.99/mo)                        │
│              User uploads, backups, assets                   │
└─────────────────────────────────────────────────────────────┘

Step 1: Provision Your VPS

Choosing the Right Size

Stage Expected Users Recommended Cost
MVP / Beta 0 - 1,000 2 vCPU, 4GB RAM €8.99/mo
Early Traction 1,000 - 10,000 4 vCPU, 8GB RAM €17.99/mo
Growth 10,000 - 50,000 Dedicated CPU or split services €35-100/mo

Initial Setup

# Create VPS on DanubeData
# Choose: Ubuntu 24.04, Standard plan (€8.99)

# SSH in
ssh root@your-server-ip

# Update system
apt update && apt upgrade -y

# Install essentials
apt install -y curl wget git ufw fail2ban

# Configure firewall
ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable

# Create app user
adduser deploy
usermod -aG sudo deploy

Step 2: Install PostgreSQL

# Install PostgreSQL 16
apt install -y postgresql-16 postgresql-contrib-16

# Start and enable
systemctl enable postgresql
systemctl start postgresql

# Create database and user
sudo -u postgres psql << EOF
CREATE USER myapp WITH PASSWORD 'secure_password_here';
CREATE DATABASE myapp_production OWNER myapp;
GRANT ALL PRIVILEGES ON DATABASE myapp_production TO myapp;
c myapp_production
GRANT ALL ON SCHEMA public TO myapp;
EOF

# Test connection
psql -U myapp -d myapp_production -h localhost

PostgreSQL Tuning for SaaS

# /etc/postgresql/16/main/postgresql.conf
# Optimize for 4GB RAM VPS

# Memory
shared_buffers = 1GB
effective_cache_size = 3GB
work_mem = 16MB
maintenance_work_mem = 256MB

# Connections
max_connections = 100

# Write performance
wal_buffers = 64MB
checkpoint_completion_target = 0.9

# Query planning
random_page_cost = 1.1  # SSD storage
effective_io_concurrency = 200

# Reload config
sudo systemctl reload postgresql

Step 3: Install Redis

# Install Redis
apt install -y redis-server

# Configure Redis
cat >> /etc/redis/redis.conf << EOF
# Memory limit (leave room for app + PostgreSQL)
maxmemory 512mb
maxmemory-policy allkeys-lru

# Persistence (optional for cache-only use)
save 900 1
save 300 10
save 60 10000
EOF

# Restart Redis
systemctl restart redis-server
systemctl enable redis-server

# Test
redis-cli ping
# Should return: PONG

Step 4: Install Your Application

Example: Node.js SaaS

# Install Node.js 20
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt install -y nodejs

# Install PM2
npm install -g pm2

# Deploy app
su - deploy
cd ~
git clone https://github.com/youruser/your-saas.git app
cd app
npm ci --production
npm run build

# Create .env file
cat > .env << EOF
NODE_ENV=production
PORT=3000
DATABASE_URL=postgresql://myapp:secure_password_here@localhost/myapp_production
REDIS_URL=redis://localhost:6379
SESSION_SECRET=$(openssl rand -hex 32)
EOF

# Start with PM2
pm2 start npm --name "saas-app" -- start
pm2 save
pm2 startup

Example: Laravel SaaS

# Install PHP 8.3
apt install -y php8.3-fpm php8.3-cli php8.3-pgsql php8.3-redis php8.3-mbstring php8.3-xml php8.3-curl php8.3-zip

# Install Composer
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

# Deploy app
su - deploy
cd ~
git clone https://github.com/youruser/your-saas.git app
cd app
composer install --no-dev --optimize-autoloader

# Configure environment
cp .env.example .env
php artisan key:generate

# Edit .env
nano .env
# Set: DB_CONNECTION, DB_HOST, REDIS_HOST, etc.

# Run migrations
php artisan migrate --force

# Cache config
php artisan config:cache
php artisan route:cache
php artisan view:cache

Step 5: Set Up Reverse Proxy (Caddy)

# Install Caddy
apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
apt update && apt install -y caddy

# Configure Caddy
cat > /etc/caddy/Caddyfile << 'EOF'
# Main application
app.yourdomain.com {
    reverse_proxy localhost:3000  # Node.js
    # OR for PHP:
    # root * /home/deploy/app/public
    # php_fastcgi unix//run/php/php8.3-fpm.sock
    # file_server

    encode gzip

    header {
        X-Content-Type-Options nosniff
        X-Frame-Options DENY
        Referrer-Policy strict-origin-when-cross-origin
        Strict-Transport-Security "max-age=31536000; includeSubDomains"
    }
}

# API subdomain (if separate)
api.yourdomain.com {
    reverse_proxy localhost:3001
}

# Marketing site (optional)
www.yourdomain.com, yourdomain.com {
    reverse_proxy localhost:3002
}
EOF

# Reload Caddy
systemctl reload caddy

Step 6: Set Up S3 Storage

# Create bucket on DanubeData
# Bucket name: yoursaas-uploads

# Install AWS CLI
apt install -y awscli

# Configure AWS CLI for DanubeData S3
aws configure
# Access Key: your-key
# Secret Key: your-secret
# Region: eu-central-1
# Output: json

# Add custom endpoint to ~/.aws/config
[default]
s3 =
    endpoint_url = https://s3.danubedata.com

# Test
aws s3 ls s3://yoursaas-uploads/

Application Configuration

# .env (Node.js example)
S3_ENDPOINT=https://s3.danubedata.com
S3_BUCKET=yoursaas-uploads
S3_ACCESS_KEY=your-access-key
S3_SECRET_KEY=your-secret-key
S3_REGION=eu-central-1

# Laravel (.env)
FILESYSTEM_DISK=s3
AWS_ENDPOINT=https://s3.danubedata.com
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key
AWS_DEFAULT_REGION=eu-central-1
AWS_BUCKET=yoursaas-uploads
AWS_USE_PATH_STYLE_ENDPOINT=true

Step 7: Transactional Email

Option 1: Resend (Recommended for Startups)

# Pricing: Free 3,000 emails/month, then $20/month for 50K
# Sign up at resend.com

# .env
RESEND_API_KEY=re_xxxxx

# Node.js example
import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);

await resend.emails.send({
  from: 'hello@yourdomain.com',
  to: user.email,
  subject: 'Welcome to Our SaaS!',
  html: '

Welcome aboard!

' });

Option 2: Self-Hosted (Advanced)

# Use Postal for self-hosted email
# More complex but cheaper at scale
# Not recommended until 100K+ emails/month

Step 8: Monitoring Setup

Uptime Monitoring (Self-Hosted)

# Install Uptime Kuma on same VPS
docker run -d 
  --name uptime-kuma 
  -p 3001:3001 
  -v uptime-kuma:/app/data 
  --restart unless-stopped 
  louislam/uptime-kuma:1

# Add to Caddy
# status.yourdomain.com {
#     reverse_proxy localhost:3001
# }

# Configure monitors for:
# - Main app (HTTPS)
# - API endpoints
# - Database (TCP check)
# - Redis (TCP check)

Error Tracking (Sentry)

# Sign up at sentry.io (free tier: 5K events/month)

# Node.js
npm install @sentry/node

// In your app
import * as Sentry from "@sentry/node";

Sentry.init({
  dsn: "https://xxx@sentry.io/xxx",
  environment: process.env.NODE_ENV,
});

Log Management

# Simple: Use PM2 logs + logrotate
pm2 logs

# Better: Ship to free tier of Logtail, Papertrail, or Better Stack

# Advanced: Self-host Loki + Grafana (more resources needed)

Step 9: Automated Backups

#!/bin/bash
# /home/deploy/backup.sh

set -e

DATE=$(date +%Y-%m-%d_%H-%M-%S)
BACKUP_DIR="/home/deploy/backups"
S3_BUCKET="yoursaas-backups"

mkdir -p $BACKUP_DIR

# Backup PostgreSQL
pg_dump -U myapp myapp_production | gzip > "$BACKUP_DIR/db_$DATE.sql.gz"

# Backup Redis (if using persistence)
cp /var/lib/redis/dump.rdb "$BACKUP_DIR/redis_$DATE.rdb"

# Backup application config
tar -czf "$BACKUP_DIR/config_$DATE.tar.gz" /home/deploy/app/.env

# Upload to S3
aws s3 sync $BACKUP_DIR s3://$S3_BUCKET/ --endpoint-url https://s3.danubedata.com

# Clean up old local backups (keep 3 days)
find $BACKUP_DIR -type f -mtime +3 -delete

# Clean up old S3 backups (keep 30 days) - run weekly
# aws s3 ls s3://$S3_BUCKET/ --endpoint-url https://s3.danubedata.com | while read -r line; do
#   # Delete files older than 30 days
# done

echo "Backup complete: $DATE"
# Add to crontab
crontab -e

# Daily backups at 3 AM
0 3 * * * /home/deploy/backup.sh >> /home/deploy/logs/backup.log 2>&1

Step 10: CI/CD Pipeline

# .github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npm ci
      - run: npm test

  deploy:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to VPS
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.VPS_HOST }}
          username: deploy
          key: ${{ secrets.VPS_SSH_KEY }}
          script: |
            cd ~/app
            git pull origin main
            npm ci --production
            npm run build
            pm2 restart saas-app

Scaling Path

When you outgrow the single VPS:

Stage 1: Vertical Scaling (Easiest)

  • Upgrade to larger VPS (8GB → 16GB RAM)
  • Still one server, minimal changes
  • Good until ~50K users

Stage 2: Database Separation

  • Move PostgreSQL to managed database (€19.99/mo)
  • Add read replica for reporting queries
  • App VPS focuses on application logic

Stage 3: Service Separation

  • Separate VPS for background workers
  • Managed Redis for cache (€9.99/mo)
  • Load balancer for multiple app servers

Stage 4: Full Separation

  • Multiple app servers behind load balancer
  • Database with replicas
  • Redis cluster
  • CDN for static assets

Cost Comparison: AWS vs Budget Stack

Component AWS Budget Stack
Compute EC2 t3.medium: ~$30/mo VPS: €8.99/mo
Database RDS: ~$25-50/mo On VPS: €0
Cache ElastiCache: ~$15/mo On VPS: €0
Storage S3: ~$5-20/mo S3: €3.99/mo
Load Balancer ALB: ~$20/mo Caddy: €0
Data Transfer ~$10-50/mo 20TB included
Total $105-185/mo €13-33/mo
Annual $1,260-2,220/yr €156-396/yr

Savings: $1,000-1,800/year—money you can spend on marketing, hiring, or runway.

Get Started Today

Ready to build your SaaS without breaking the bank?

  1. Create a VPS on DanubeData (€8.99/mo)
  2. Follow this guide to set up your stack
  3. Deploy your application
  4. Focus on building features, not managing infrastructure

DanubeData SaaS Starter Pack:

  • VPS Standard: €8.99/mo (4GB RAM, 2 vCPU)
  • S3 Storage: €3.99/mo (1TB included)
  • Total: €12.98/mo for production-ready infrastructure

👉 Start Your SaaS

Need help architecting your SaaS infrastructure? Contact our team—we've helped dozens of startups launch.

Share this article

Ready to Get Started?

Deploy your infrastructure in minutes with DanubeData's managed services.