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

Build production-ready SaaS infrastructure for under €50/month. Complete guide covering VPS, database, cache, storage, email, monitoring, and deployment—with real cost breakdowns and architecture patterns.

Adrian Silaghi

20 min read

969 views

saasstartupinfrastructurebudgetvpspostgresqlredisarchitectureself-hostedcost 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.ro

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

Application Configuration

# .env (Node.js example)
S3_ENDPOINT=https://s3.danubedata.ro
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.ro
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.ro

# 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.ro | 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.

Ready to get started?

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