Multi-tenancy es el patrón arquitectónico que permite a un SaaS servir múltiples organizaciones desde una sola instancia de aplicación, aislando datos y configuración por cliente. En PostgreSQL, el modelo de schema por tenant ofrece el mejor balance entre aislamiento fuerte, costos operativos razonables y capacidad de escalar a miles de clientes. En nodo. hemos construido plataformas SaaS multi-tenant en producción y este artículo documenta los patrones que funcionan.
El error más caro en SaaS es elegir el modelo de tenancy incorrecto. Migrar de base de datos compartida a schema por tenant cuando ya tienes 200 clientes en producción implica semanas de trabajo, downtime planificado y riesgo real de pérdida de datos. La decisión correcta se toma al principio, no cuando el sistema ya duele.
¿Qué modelos de multi-tenancy existen y cuál elegir?
Respuesta rápida: Existen tres modelos fundamentales — base de datos compartida con discriminador, schema por tenant y base de datos por tenant. Para SaaS B2B con menos de 5,000 clientes, schema por tenant en PostgreSQL es la elección correcta en el 80% de los casos: aislamiento fuerte sin el costo operativo de gestionar miles de bases de datos independientes.
Cada modelo resuelve un punto distinto en el espectro aislamiento vs. complejidad operativa:
1. Base de datos compartida con discriminador de tenant
Todas las tablas llevan una columna tenant_id y cada query filtra por ese valor. Es el modelo más simple de implementar: una sola base de datos, un solo pool de conexiones, migraciones triviales. Lo usas cuando el aislamiento de datos no es un requisito contractual y los tenants no necesitan personalización de schema.
El problema: un error en el código filtra datos entre organizaciones. Si un desarrollador olvida el filtro de tenant_id en un endpoint, un cliente ve datos de otro. Según un estudio de HackerOne de 2024, el 23% de las vulnerabilidades críticas en SaaS B2B fueron filtraciones de datos entre tenants por filtros de autorización faltantes. Row Level Security (RLS) de PostgreSQL mitiga esto, pero el riesgo inherente al modelo persiste.
2. Schema por tenant (recomendado)
Cada organización tiene su propio schema de PostgreSQL dentro de la misma base de datos. Las tablas son idénticas en estructura, pero los datos están físicamente separados. La aplicación establece SET search_path = tenant_xyz al inicio de cada request.
Ventajas concretas: aislamiento a nivel de base de datos sin multiplicar infraestructura. Puedes hacer backup y restore de un solo tenant. Puedes ejecutar migraciones en un tenant específico para feature flags. Puedes analizar métricas de uso por tenant con queries directos. Un solo pool de conexiones sirve a todos los schemas. En nuestra experiencia en nodo., este modelo funciona sin fricción hasta ~5,000 schemas en una instancia RDS db.r6g.xlarge (4 vCPU, 32 GB RAM).
3. Base de datos por tenant
Cada organización tiene su propia instancia de base de datos. Máximo aislamiento: las credenciales de un tenant no pueden acceder a datos de otro ni por error de código. El costo: multiplicar la infraestructura por N clientes. En AWS RDS, cada instancia db.t4g.micro cuesta USD $12.41/mes. Con 500 clientes, eso son USD $6,205/mes solo en base de datos, sin contar backups, monitoreo y migraciones coordinadas.
Este modelo se justifica en dos casos: regulación que exige aislamiento físico (datos de salud bajo HIPAA, financieros bajo SOX) o clientes enterprise que contractualmente exigen que sus datos no compartan hardware con otros clientes.
¿Cómo implementar schema por tenant en PostgreSQL?
Patrón clave: Middleware de resolución de tenant al inicio de cada request HTTP. El middleware extrae el tenant del JWT, subdominio o header, valida que existe, y establece el search_path de PostgreSQL. Cada query subsiguiente opera automáticamente dentro del schema correcto sin modificar código de negocio.
La implementación tiene cuatro componentes críticos:
Resolución de tenant. El tenant se identifica por subdominio (acme.tuapp.com), por header HTTP (X-Tenant-Id), o por claim en el JWT. La opción más robusta es el JWT: el token ya está firmado criptográficamente, no se puede falsificar, y no depende de DNS. En cada request, el middleware valida el JWT, extrae el org_id, verifica que el schema existe en un caché local (TTL 60s), y configura la conexión.
Pool de conexiones. Un solo pool de PgBouncer sirve a todos los schemas. La clave es usar session mode, no transaction mode, porque SET search_path es un comando de sesión. Con un pool de 50 conexiones puedes servir cómodamente 200+ tenants concurrentes. En producción, hemos medido que el overhead de cambiar el search_path es de ~0.1ms por request — imperceptible.
Migraciones. Cada migración se ejecuta en un loop sobre todos los schemas activos. Un schema _template sirve como referencia: las nuevas organizaciones se crean clonando este template. Herramientas como pgroll permiten ejecutar migraciones online sin bloquear tablas, lo que es crítico cuando tienes 500+ schemas y cada ALTER TABLE se multiplica por N.
Onboarding de tenant. Crear un nuevo cliente es un CREATE SCHEMA tenant_xyz seguido de ejecutar todas las migraciones en orden. En nuestros sistemas, el onboarding completo — schema, tablas, datos semilla, índices — toma 1.8 segundos para un schema con 47 tablas. El proceso es atómico: si falla, se hace rollback completo.
¿Cómo garantizar seguridad entre tenants?
Defensa en profundidad: RLS de PostgreSQL como primera línea, middleware de validación de tenant como segunda, y tests de integración automatizados que verifican cross-tenant isolation como tercera. Ninguna capa sola es suficiente — las tres juntas eliminan prácticamente toda superficie de filtración de datos.
La seguridad multi-tenant se construye en capas. Si una falla, la siguiente contiene el daño:
- Row Level Security (RLS). Incluso con schemas separados, RLS actúa como red de seguridad. Si un bug en el middleware configura el search_path incorrecto, las políticas RLS bloquean el acceso. La política es simple:
CREATE POLICY tenant_isolation ON orders USING (tenant_id = current_setting('app.current_tenant')::uuid). El overhead de RLS en PostgreSQL 16 es del 2-4% en queries típicos — insignificante para la protección que ofrece. - Audit logging por tenant. Cada operación de escritura se registra con el
tenant_id,user_id, timestamp, y la acción ejecutada. En caso de incidente, puedes reconstruir exactamente qué datos fueron accedidos y por quién. Almacenamos estos logs en una tabla separada fuera de los schemas de tenant, con particionado mensual para mantener el rendimiento. - Encriptación por tenant. Los datos sensibles (PII, documentos confidenciales) se encriptan con una key derivada específica por organización. Usamos AWS KMS con una Customer Managed Key (CMK) por tenant, lo que permite revocar el acceso a los datos de un tenant específico sin afectar a los demás. El costo: USD $1/mes por key + USD $0.03 por 10,000 operaciones criptográficas.
- Tests de aislamiento automatizados. En el pipeline de CI, un test crea dos tenants, inserta datos en ambos, y verifica que cada uno solo puede leer sus propios registros. Otro test intenta acceder al schema de un tenant con las credenciales de otro y verifica que falla. Estos tests han capturado 3 regressions en el último año antes de llegar a producción.
¿Cómo se comparan los modelos de multi-tenancy en la práctica?
Esta tabla resume las diferencias operativas basadas en nuestra experiencia construyendo plataformas SaaS desde cero:
| Criterio | DB compartida | Schema por tenant | DB por tenant |
|---|---|---|---|
| Aislamiento de datos | Lógico (RLS) | Fuerte (schema) | Físico (instancia) |
| Costo por 500 tenants | ~$50/mes | ~$150/mes | ~$6,200/mes |
| Complejidad de migraciones | Baja | Media | Alta |
| Backup/restore por tenant | No nativo | pg_dump por schema | Nativo |
| Riesgo de data leak | Alto sin RLS | Bajo | Mínimo |
| Pool de conexiones | 1 pool | 1 pool | N pools |
| Personalización por tenant | Limitada | Flexible | Total |
| Límite práctico de tenants | Ilimitado | ~5,000 | ~200 |
¿Qué errores comunes arruinan una arquitectura multi-tenant?
Hemos visto estos patrones fallar repetidamente en auditorías de código y rescates de proyectos:
- No usar RLS como red de seguridad. Incluso con schemas separados, un bug en el middleware puede exponer datos. RLS es tu última línea de defensa y cuesta 2-4% de overhead. No hay excusa para no activarlo.
- Connection pooling mal configurado. Usar
transactionmode en PgBouncer conSET search_pathprovoca que un tenant vea datos de otro cuando las conexiones se reutilizan. Este bug es silencioso y devastador. - Migraciones sin versionamiento por schema. Si no registras qué versión de schema tiene cada tenant, no puedes hacer rollouts graduales ni rollback selectivo. Mantener una tabla
_migrationsdentro de cada schema de tenant es esencial. - Ignorar el límite de schemas de PostgreSQL. PostgreSQL no tiene un límite duro de schemas, pero el rendimiento de
pg_catalogse degrada después de ~10,000 schemas. El catálogo del sistema se consulta en cada operación DDL y el overhead crece linealmente. Si tu plan de negocio apunta a 50,000 clientes, necesitas sharding horizontal desde el día uno. - No medir consumo por tenant. Sin métricas por organización, no puedes facturar basado en uso, no puedes identificar tenants que consumen recursos desproporcionados, y no puedes planificar capacidad. Instrumenta queries por tenant desde la primera línea de código.
¿Cuándo necesitas ir más allá de schema por tenant?
Schema por tenant tiene límites. Cuando los alcanzas, necesitas evolucionar la arquitectura:
Sharding horizontal cuando superas los 5,000 tenants o cuando un solo tenant genera más del 30% de la carga. La estrategia: agrupar tenants en clusters de bases de datos (shards) usando un servicio de routing que mapea tenant_id a un shard específico. Citus (extensión de PostgreSQL para distributed tables) simplifica esto al mantener compatibilidad con SQL estándar. El trade-off: los JOINs entre tenants de distintos shards son imposibles, lo que afecta funcionalidades como reportes consolidados o búsqueda global.
Modelo híbrido cuando tienes muchos clientes pequeños y pocos clientes enterprise. Los clientes pequeños comparten base de datos con discriminador de tenant (menor costo). Los clientes enterprise tienen schema dedicado o base de datos propia (mayor aislamiento). El middleware de resolución de tenant abstrae esta complejidad: la aplicación no sabe qué modelo está usando cada organización.
¿Cómo elegir el modelo correcto para tu SaaS?
La respuesta depende de tres variables: requisitos regulatorios, número proyectado de tenants, y presupuesto de infraestructura. Para la mayoría de los SaaS B2B en Latinoamérica, schema por tenant es la elección pragmática. Ofrece aislamiento suficiente para pasar auditorías SOC 2, costos razonables en infraestructura, y flexibilidad para evolucionar cuando el negocio lo requiera.
Si estás empezando un SaaS desde cero, nuestra recomendación es directa: comienza con schema por tenant. Es más fácil consolidar schemas en una DB compartida (si descubres que el aislamiento no importa) que separar datos mezclados en schemas aislados. La dirección fácil de migrar es siempre hacia menor aislamiento, nunca al revés.
En nodo. hemos construido plataformas SaaS multi-tenant que sirven cientos de organizaciones desde una sola instancia de PostgreSQL. La diferencia entre un SaaS que escala y uno que se rompe a los 100 clientes está en estas decisiones de arquitectura tomadas al principio.