Tu startup necesita una base sólida. Un VPS bien configurado te da el control y rendimiento que las plataformas serverless no ofrecen, a una fracción del coste.
Node.js, Python, Go, Ruby… cualquier stack funciona. Esta guía te enseña a montar la infraestructura para tu SaaS desde cero.
Por qué un VPS para tu SaaS
Comparativa de opciones
| Opción | Coste mensual | Control | Escalabilidad | Complejidad |
|---|---|---|---|---|
| VPS | €10-100 | Total | Manual/Semi | Media |
| Heroku | €25-500+ | Limitado | Fácil | Baja |
| AWS/GCP | Variable | Total | Automática | Alta |
| Vercel/Netlify | €0-20 | Mínimo | Automática | Baja |
Cuándo elegir VPS
- Presupuesto ajustado: Startup en fase inicial
- Control necesario: Configuraciones específicas
- Recursos predecibles: Sabes cuánto necesitas
- Datos sensibles: Quieres tu propia infraestructura
- Aprendizaje: Entender cómo funciona todo
Cuándo NO elegir VPS
- Tráfico muy variable (mejor serverless)
- Equipo sin experiencia en sysadmin
- Necesitas escalar a millones de usuarios rápido
Arquitectura típica SaaS
Monolito (para empezar)
┌─────────────────┐
│ Cloudflare │
│ (CDN + SSL) │
└────────┬────────┘
│
┌────────▼────────┐
│ Nginx │
│ (Reverse Proxy) │
└────────┬────────┘
│
┌────────▼────────┐
│ Tu App SaaS │
│ (Node/Python) │
└────────┬────────┘
│
┌──────────────┼──────────────┐
│ │ │
┌────────▼───┐ ┌───────▼────┐ ┌──────▼─────┐
│ PostgreSQL │ │ Redis │ │ Files │
│ (BD) │ │ (Cache) │ │ (S3) │
└────────────┘ └────────────┘ └────────────┘
Recomendación: Empieza con monolito. Separa cuando sea necesario.
Microservicios (cuando crezcas)
┌─────────────────┐
│ Load Balancer │
└────────┬────────┘
│
┌────────────────────┼────────────────────┐
│ │ │
┌───────▼───────┐ ┌────────▼───────┐ ┌───────▼───────┐
│ API Gateway │ │ Auth Service │ │ Web Frontend │
└───────┬───────┘ └────────────────┘ └───────────────┘
│
┌───────┴───────────────────────────────┐
│ │
▼ ▼ ▼
Users Service Billing Service Notifications
Dimensionamiento
Por etapa de startup
| Etapa | Usuarios | RAM | CPU | Almacenamiento |
|---|---|---|---|---|
| MVP | 0-100 | 2 GB | 2 cores | 40 GB NVMe |
| Early stage | 100-1.000 | 4 GB | 2-4 cores | 80 GB NVMe |
| Growth | 1.000-10.000 | 8 GB | 4-6 cores | 160 GB NVMe |
| Scale | 10.000+ | 16+ GB | 8+ cores | 320+ GB NVMe |
Señales de que necesitas más
- Tiempo de respuesta > 500ms
- CPU sostenida > 70%
- RAM > 85%
- Cola de jobs creciendo
Stack tecnológico
Node.js (JavaScript/TypeScript)
# Instalar Node.js 20 LTS
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
# Verificar
node --version
npm --version
Frameworks populares:
- Express.js (minimalista)
- Fastify (rendimiento)
- NestJS (enterprise)
- Next.js (fullstack)
Python
# Python 3.11+
sudo apt install python3 python3-pip python3-venv -y
# Crear entorno virtual
python3 -m venv /var/www/miapp/venv
source /var/www/miapp/venv/bin/activate
Frameworks populares:
- FastAPI (moderno, async)
- Django (baterías incluidas)
- Flask (minimalista)
Go
# Instalar Go
wget https://go.dev/dl/go1.22.0.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.22.0.linux-amd64.tar.gz
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
Ideal para: APIs de alto rendimiento, microservicios.
Configuración base del servidor
Preparar el sistema
# Actualizar
sudo apt update && sudo apt upgrade -y
# Instalar utilidades
sudo apt install -y git curl wget htop unzip
Nginx como reverse proxy
sudo apt install nginx -y
# /etc/nginx/sites-available/miapp.com
server {
listen 80;
server_name miapp.com www.miapp.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
}
sudo ln -s /etc/nginx/sites-available/miapp.com /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
SSL con Let’s Encrypt
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d miapp.com -d www.miapp.com
PostgreSQL
# Instalar
sudo apt install postgresql postgresql-contrib -y
# Crear base de datos
sudo -u postgres psql
CREATE DATABASE miapp_production;
CREATE USER miapp_user WITH ENCRYPTED PASSWORD 'password_seguro';
GRANT ALL PRIVILEGES ON DATABASE miapp_production TO miapp_user;
\q
Redis
sudo apt install redis-server -y
sudo systemctl enable redis-server
Gestionar procesos con PM2 (Node.js)
Instalar PM2
sudo npm install -g pm2
Configurar aplicación
// ecosystem.config.js
module.exports = {
apps: [{
name: 'miapp-api',
script: './dist/server.js',
instances: 'max',
exec_mode: 'cluster',
env: {
NODE_ENV: 'development',
},
env_production: {
NODE_ENV: 'production',
PORT: 3000,
DATABASE_URL: 'postgresql://...',
REDIS_URL: 'redis://localhost:6379',
},
}],
};
Comandos PM2
# Iniciar
pm2 start ecosystem.config.js --env production
# Ver estado
pm2 status
# Ver logs
pm2 logs
# Reiniciar
pm2 restart miapp-api
# Auto-arranque con el sistema
pm2 startup
pm2 save
Gestionar procesos con Systemd (Python/Go)
Crear servicio
sudo nano /etc/systemd/system/miapp.service
[Unit]
Description=Mi App SaaS
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/miapp
Environment="PATH=/var/www/miapp/venv/bin"
ExecStart=/var/www/miapp/venv/bin/gunicorn -w 4 -k uvicorn.workers.UvicornWorker main:app -b 127.0.0.1:8000
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable miapp
sudo systemctl start miapp
Variables de entorno
Archivo .env
# /var/www/miapp/.env
NODE_ENV=production
PORT=3000
# Base de datos
DATABASE_URL=postgresql://miapp_user:password@localhost:5432/miapp_production
# Redis
REDIS_URL=redis://localhost:6379
# Secrets
JWT_SECRET=tu-secreto-muy-largo-y-aleatorio
ENCRYPTION_KEY=otra-clave-secreta
# Servicios externos
STRIPE_SECRET_KEY=sk_live_xxx
SENDGRID_API_KEY=SG.xxx
AWS_ACCESS_KEY_ID=xxx
AWS_SECRET_ACCESS_KEY=xxx
Seguridad
# Permisos restrictivos
chmod 600 /var/www/miapp/.env
chown www-data:www-data /var/www/miapp/.env
Nunca commits el .env a git.
Despliegues automatizados
Script de deploy
#!/bin/bash
# /root/scripts/deploy-miapp.sh
set -e
APP_DIR="/var/www/miapp"
REPO="[email protected]:tuusuario/miapp.git"
BRANCH="main"
echo "=== Desplegando MiApp ==="
cd $APP_DIR
# Pull cambios
git fetch origin
git reset --hard origin/$BRANCH
# Instalar dependencias
npm ci --production
# Build
npm run build
# Migraciones
npm run migrate
# Reiniciar app
pm2 restart miapp-api
echo "=== Deploy completado ==="
GitHub Actions (CI/CD)
# .github/workflows/deploy.yml
name: Deploy to VPS
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy via SSH
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
script: |
/root/scripts/deploy-miapp.sh
Monitorización
Métricas clave para SaaS
| Métrica | Herramienta | Umbral alerta |
|---|---|---|
| Uptime | UptimeRobot | <99.9% |
| Response time | Nginx logs | >500ms |
| Error rate | Sentry | >1% |
| CPU/RAM | Netdata | >80% |
| DB connections | pg_stat | >80% pool |
Logging centralizado
// Winston para Node.js
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' }),
],
});
Error tracking (Sentry)
const Sentry = require('@sentry/node');
Sentry.init({
dsn: 'https://[email protected]/xxx',
environment: process.env.NODE_ENV,
});
Backups para SaaS
Script de backup
#!/bin/bash
# /root/scripts/backup-saas.sh
DATE=$(date +%Y%m%d_%H%M)
BACKUP_DIR="/backups"
# Backup PostgreSQL
pg_dump -U miapp_user miapp_production | gzip > $BACKUP_DIR/db-$DATE.sql.gz
# Backup archivos de usuario (si aplica)
tar -czf $BACKUP_DIR/uploads-$DATE.tar.gz /var/www/miapp/uploads
# Limpiar backups > 7 días
find $BACKUP_DIR -mtime +7 -delete
# Sincronizar a S3
aws s3 sync $BACKUP_DIR s3://mi-bucket-backups/
Cron
# Cada 6 horas
0 */6 * * * /root/scripts/backup-saas.sh
Seguridad para SaaS
Checklist básico
| Tarea | Prioridad |
|---|---|
| SSL en todo | Crítica |
| Firewall activo | Crítica |
| Variables de entorno seguras | Crítica |
| Rate limiting en API | Alta |
| Validación de inputs | Alta |
| Logs de auditoría | Alta |
| 2FA para admin | Media |
Rate limiting en Nginx
# Definir zona
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
server {
location /api/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://localhost:3000;
}
}
Headers de seguridad
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header Content-Security-Policy "default-src 'self'" always;
Escalabilidad
Vertical (más recursos)
Aumenta RAM/CPU del VPS. Simple pero tiene límites.
Horizontal (más servidores)
┌──────────────┐
│ Load Balancer│
└──────┬───────┘
│
┌────────────────┼────────────────┐
│ │ │
┌────▼────┐ ┌────▼────┐ ┌────▼────┐
│ VPS 1 │ │ VPS 2 │ │ VPS 3 │
│ (App) │ │ (App) │ │ (App) │
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
└────────────────┼────────────────┘
│
┌──────▼──────┐
│ PostgreSQL │
│ (Separado) │
└─────────────┘
Cuándo separar base de datos
- Más de 10.000 usuarios activos
- Queries pesados afectan la app
- Necesitas réplicas de lectura
Preguntas frecuentes
¿Puedo empezar con un VPS pequeño y escalar después?
Sí, es lo recomendado. Empieza con 2-4GB RAM y escala según crezcas. La migración vertical es simple.
¿VPS o serverless para mi MVP?
VPS si quieres control y costes predecibles. Serverless si prefieres no gestionar infraestructura y tienes tráfico muy variable.
¿Necesito Docker para mi SaaS?
No es obligatorio. Puedes empezar sin Docker y añadirlo cuando necesites portabilidad o microservicios.
¿Cómo manejo actualizaciones sin downtime?
Con PM2 en modo cluster o múltiples servidores con load balancer. Despliega gradualmente.
¿Debo separar frontend y backend?
Para empezar no es necesario. Cuando el frontend sea estático (React/Vue), puedes servirlo desde CDN.
Nuestra recomendación
Para MVP/Early stage:
- VPS 4GB RAM
- Stack monolítico
- PostgreSQL + Redis local
- PM2 o Systemd
- Backups a S3
Para Growth:
- VPS 8-16GB o múltiples VPS
- Base de datos separada
- CDN para estáticos
- CI/CD automatizado
¿No tienes equipo de DevOps? La administración gestionada de Avantys incluye configuración y mantenimiento de infraestructura SaaS.
Conclusión
Un VPS te da el control y rendimiento que tu SaaS necesita sin la complejidad de AWS. Empieza simple, automatiza todo lo posible y escala cuando sea necesario.
La clave es monitorizar desde el día uno para saber cuándo necesitas más recursos.
¿Listo para lanzar tu SaaS? Explora los VPS de Avantys con configuración optimizada para aplicaciones.
¿Quieres que lo hagamos por ti?
En Avantys gestionamos tu web, hosting y crecimiento digital de punta a punta. Tú a lo importante.