Cómo escalar tu app de 1.000 a 1 millón de usuarios
Estas son las fases por las que deberías pasar para poder escalar bien tu aplicación y no perder usuarios por el camino.
Índice
1. Qué base de datos elegir
Una base de datos es el sitio donde se guardan tus datos. Sin embargo, si vas a montar algo, primero necesitas endender qué base de datos vas a necesitar:
- Relacionales, las SQL: columnas fijas y una fila por cosa, y la base sabe cómo se relacionan, así que pedirle «los pedidos de este usuario del mes pasado, por importe» es una línea. Postgres, MySQL y SQLite.
- Documentos, las NoSQL: cada cosa es una ficha suelta con la forma que tú quieras, añades un campo y ya está. MongoDB y Firestore.
- Clave y valor: le das un nombre y te devuelve un valor en menos de un milisegundo. Redis. Es la caché del apartado 4.2, no la base de datos de tu aplicación, y si se apaga se pierde lo que tenía dentro.
Estas son las seis que te vas a encontrar:
| Base de datos | Cómo guarda | Elígela si | Lo que te va a costar |
|---|---|---|---|
| PostgreSQL | Tablas con columnas fijas | No tienes un motivo concreto para otra cosa | Nada. Es la opción aburrida y correcta |
| MySQL | Igual que Postgres | Ya la conoces o te la da tu hosting | Trae menos cosas de serie que Postgres |
| SQLite | Un fichero en el disco | Prototipo, app de escritorio o una sola máquina | No hay servidor: no puedes conectar varias copias |
| MongoDB | Fichas sueltas, cada una con su forma | Tus datos no tienen forma fija de verdad | Los cruces entre colecciones los escribes tú |
| Redis | Un diccionario en memoria | Caché, sesiones y colas. Nunca como única base | Si se reinicia, se pierde. Es su trabajo |
| Firestore | Fichas sueltas que avisan al móvil solas | App móvil en tiempo real y sin backend propio | Se paga por lectura y las consultas son limitadas |
Utiliza en el 90% de los casos una PostgreSQL.
Sin embargo, que no se te escape el 10% restante (cuando no elegir una PostgreSQL):
- Tus datos no tienen forma fija de verdad, cada registro lleva campos distintos, por ejemplo, cuando en el cliente hay un formulario de productos que rellena el cliente y que varía mucho entre clientes. Ahí Mongo.
- Quieres que la pantalla del móvil se actualice sola cuando cambia el dato, sin backend que mantener. Firestore, o el tiempo real de Supabase.
- Escribes millones de eventos al día y solo los lees por fecha. Eso es una base de series temporales, TimescaleDB o ClickHouse, y vive aparte de la principal.
- Preguntas por amigos de amigos de amigos. Ahí una de grafos, Neo4j.
2. Los usuarios no son la unidad
«Un millón de usuarios» no le dice nada a tu servidor. Lo que nota son peticiones por segundo, consultas a la vez contra la base de datos y megabytes servidos: un millón de registrados con doscientos dentro en el mismo minuto es una máquina aburrida y barata. Haz el cálculo antes de comprar nada, porque casi siempre da muy por debajo de lo que llevabas en la cabeza.
Y cuando lo midas, mira el p95 y no la media. Coge cien peticiones a tu web, ponlas en fila de la más rápida a la más lenta y quédate con la número 95: lo que tarde esa es tu p95. Si son 400 milisegundos, 95 de cada 100 personas esperan 400 milisegundos o menos y a 5 les va peor. El nombre viene de percentil 95, que es el sitio de la fila.
La media no vale porque se la comen los números buenos. Noventa peticiones de 50 milisegundos y diez de cuatro segundos dan una media de 445, que suena bien, y mientras tanto uno de cada diez usuarios lleva cuatro segundos mirando una pantalla parada. El p95 de eso son cuatro segundos.
Una vez entendido esto, empieza a escalar:
| Usuarios | QUÉ NECESITAS | QUÉ hace |
|---|---|---|
| 0 | Una base de datos bien hecha | Índices en lo que filtras. No es una fase |
| 1.000 | Un servidor | Un monorepo con una buena arquitectura y ordenado |
| 10.000 | Redis y pool de conexiones | Caché en tres capas y el pool |
| 50.000 | Balanceador con Nginx | Varias copias, y que el servidor no guarde nada dentro |
| 100.000 | Réplicas de lectura e índices | Réplicas. Los índices ya estaban puestos |
| 500.000 | Colas de eventos | Colas, y fuera de la petición todo lo que pueda esperar |
| 1M+ | Gateways y sharding | Depende de qué base de datos elegiste. Casi nunca toca |
3. ETAPA 1 · No te olvides de crear índices
Si tus tablas no tienen índices, una consulta que filtra por una columna recorre la tabla entera, fila a fila. Con mil filas nadie lo nota. Con doscientas mil, cada petición puede durar minutos, y como llegan a la vez se apilan.
La regla es corta: toda columna por la que filtras, ordenas o unes dos tablas lleva índice, las claves ajenas incluidas, que es el olvido más repetido. Pero solo esas, porque cada índice se paga en cada escritura: en lo que consultas, no en todas las columnas por si acaso.
Lo segundo a tener en cuenta es el N+1. Una consulta para traer la lista y otra por cada elemento de la lista. Veinte pedidos son veintiuna consultas, y se arregla pidiendo los veinte de golpe.
Si no sabes si lo estás haciendo bien, el repaso son diez minutos: activa el registro de consultas lentas, ordena por las cinco más caras de la semana y ponle EXPLAIN delante. Si te contesta que hace un recorrido secuencial sobre una tabla grande, ahí está el índice que falta.
Y una duda que me llega mucho: los índices no son cosa de SQL. Mongo tiene índices, Firestore tiene índices, DynamoDB tiene índices. Cambia el comando y poco más, porque el problema es el mismo en todas: encontrar algo sin mirar fila por fila.
4.1 ETAPA 2 · Hasta 1.000: una máquina, cuanto más aburrida mejor
Hasta aquí no hay nada que escalar: un proceso de Node con Fastify, una Postgres gestionada al lado y un CDN delante de las imágenes y el JavaScript. El error caro de esta fase no es quedarse corto, es pasarse. Montar un orquestador, tres microservicios y una cola para cuarenta usuarios te deja cuatro cosas que mantener y ninguna que enseñar, y cuando llegue el crecimiento de verdad lo vas a rehacer igual.
Aquí debes tener un monorepo, es decir, tener el frontend y el servidor en la misma carpeta y con un solo despliegue, que es lo que te conviene aquí y en las cuatro fases siguientes. Más adelante necesitarás tener microservicios, que es partir la aplicación en trozos con su propia base de datos y su propio despliegue.
Si hay partes o funcionalidades que tienen mucho tráfico, lo mejor es partirlo. Pero no ahora con 1000 usuarios, sino más adelante.
4.2 Añade caché. Y no solo una, tres.
Con diez mil usuarios, casi todo lo que pide la gente es lo mismo que pidió el anterior. Cachear es contestar sin volver a calcular, y hay tres sitios donde hacerlo, del más barato al más caro:
- El CDN: imágenes, CSS, JavaScript y las páginas que no cambian según quién mire. Ni rozan tu servidor.
- Redis: el resultado de una consulta cara, el perfil que se pide en todas las pantallas, el listado de la portada. A un milisegundo de tu servidor.
- La memoria del propio proceso: la configuración, los catálogos, lo pequeño que no cambia. Gratis, pero se duplica en cada copia del servidor, así que solo para cosas pequeñas.
Guardar algo en la caché es la parte fácil. La difícil es tirarlo cuando el dato cambia, porque mientras no lo tires le estás enseñando a la gente un precio viejo o una foto que ya no es la suya. Hay dos maneras y se usan las dos: ponerle fecha de caducidad, treinta segundos y se borra solo, que vale para casi todo; y borrarlo tú a mano cuando cambias el dato, con esa línea pegada a la que guarda el cambio. Si la escribes en otro archivo, el día que alguien toque esa escritura se le va a olvidar.
Y una cosa que solo pasa cuando ya hay tráfico. Mil personas están pidiendo la misma portada y la caché caduca justo en ese segundo: las mil se la encuentran vacía a la vez y las mil se van a la base de datos a calcular lo mismo. Todo lo que la caché llevaba ahorrando le llega de golpe. Se arregla dejando que calcule solo la primera y que las demás esperen su resultado, y no lo programas tú: casi todas las librerías lo traen hecho, buscando «lock» o «single flight».
4.3 El pool de conexiones
Una Postgres no atiende conexiones infinitas: cada una le cuesta memoria, y el límite de una instancia gestionada pequeña anda entre sesenta y doscientas. Parece muchísimo hasta que pones copias del servidor o despliegas en serverless, donde cada instancia que arranca quiere la suya.
El síntoma no se parece a «va lento»: la aplicación devuelve errores de demasiadas conexiones en ráfagas, justo en los picos, y entre pico y pico va perfecta. Si te ha pasado y lo achacaste a la red, era esto.
La solución es un intermediario que mantiene unas pocas conexiones abiertas de verdad y se las va prestando a quien las pida. PgBouncer es el clásico, y Supabase, Neon y compañía ya lo traen puesto: hay que usar la cadena de conexión del pooler en vez de la directa, que es un fallo que se ve mucho.
Lo que lo hace funcionar es el modo transacción: la conexión se devuelve al acabar cada transacción, no al cerrar la aplicación. Ahí es donde veinte conexiones reales dan servicio a quinientos clientes. A cambio dejan de funcionar las sentencias preparadas y LISTEN/NOTIFY; tu ORM suele tener una opción con ese nombre.
5. ETAPA 3 · 50.000: más copias y algo delante que reparte
Llega el día en el que una máquina no da. Hay dos salidas: una máquina más grande, que es el escalado vertical, o más copias de la misma, que es el horizontal. La primera es un botón y funciona hasta que se acaba el catálogo del proveedor. La segunda no se acaba nunca, porque siempre puedes poner otra copia, pero te pide una condición.
La condición es que tu servidor no guarde nada dentro. Nada. Si la sesión vive en la memoria del proceso, la segunda copia no sabe quién eres y te echa. Si el fichero que suben se queda en el disco, media web ve una foto que la otra media no encuentra. Si la tarea programada corre dentro de la aplicación, con tres copias manda el correo tres veces. Sesiones a Redis o a una cookie firmada, ficheros a un bucket y las tareas a algo que las dispare una sola vez.
Lo que reparte no lo montas tú: Vercel, Cloud Run, Railway y Fly traen balanceador y autoescalado de serie. Dos ajustes que casi nadie toca: que suba rápido y baje despacio, porque bajar corriendo justo después del pico te deja desnudo cuando llega el segundo, y que haya un tope de copias, porque un bucle infinito con autoescalado no te tira la web, te tira la tarjeta. Y ya que hay algo delante, ponle un límite de peticiones por IP: mil por segundo hacen el mismo daño vengan de un atacante o del bucle mal escrito de un cliente tuyo.
Hay una versión en la que ni siquiera hay servidor tuyo: las lambdas, o funciones serverless. Subes una función, el proveedor arranca una copia por cada petición y te cobra por milisegundo. Van bien para picos raros, y traen dos peajes: el arranque en frío, que es que la primera petición después de un rato parado tarda más, y que cada copia quiere su conexión a la base de datos, que es el problema del apartado 4.3.
Y aquí es donde por fin entran los microservicios, los que en el apartado 4.1 te dije que dejaras para más adelante. Más adelante es esto. Hasta ahora has escalado copiando la aplicación entera; un microservicio es coger el trozo que va lleno y darle su propio servidor, su propia base de datos y su propio despliegue.
El motivo es dinero. Un bloque único se escala entero o no se escala: si la funcionalidad más usada se lleva casi todo el tráfico, al poner diez copias estás pagando diez veces también por las nueve partes que no toca nadie. Partido en trozos escalas solo el trozo lleno. Y hay un segundo motivo, el equipo: cada grupo lleva sus servicios y despliega cuando quiere, sin esperar a los demás.
Lo que cuesta, para que no te lo vendan solo por la mitad buena: dos trozos que antes se llamaban con una función ahora se llaman por la red, y la red falla. Una consulta que cruzaba dos tablas ahora son dos peticiones y las juntas tú en el código. Y un error que se leía en un log pasa a leerse en cuatro. Por eso la respuesta hasta aquí era el monorepo.
Y Kubernetes, o K8s, el nombre que siempre sale en esta conversación: un programa que reparte tus contenedores entre varias máquinas, los reinicia cuando se caen y pone más copias cuando llega gente. Lo mismo que hacen Vercel o Cloud Run por ti, solo que administrándolo tú. Con dos servicios no lo necesitas, y montarlo antes de tiempo es el error caro de la etapa 2 con otro nombre.
6. ETAPA 4 · 100.000: réplicas de lectura y el retraso que traen
En casi cualquier aplicación se lee mucho más de lo que se escribe: veinte lecturas por escritura es lo corriente, y en algo de contenidos son doscientas. Una réplica es una copia de tu base de datos que se mantiene al día sola y a la que solo se le pregunta. Pones dos y triplicas la capacidad de lectura sin tocar una línea.
El precio es que la réplica va unos milisegundos por detrás. Casi nunca importa, y cuando importa es en el caso más visible de todos: el usuario guarda algo, la pantalla siguiente lee de la réplica y su cambio no aparece. Te lo reportan como «se ha perdido lo que escribí», y no se ha perdido nada.
La regla que lo arregla se llama leer lo tuyo: durante unos segundos después de que alguien escriba, sus lecturas van a la principal. Una marca de tiempo en la sesión y un if. Y el orden importa, porque esto es dinero: réplicas después de la caché, nunca antes. Si la consulta que quieres repartir es la misma dos mil veces por minuto, la réplica te cuesta cada mes lo que Redis hace gratis.
Y a este volumen llega lo que la lista llama index tuning, que suena a magia y es una tarde de revisar lo que ya pusiste. Los índices del apartado 3 los decidiste mirando una tabla de mil filas, y con cien mil usuarios las consultas que te duelen son otras.
Son tres cosas concretas. Una: ordena tus consultas por el tiempo que suman en toda la semana, no por la más lenta de una vez, porque la que se repite diez mil veces te cuesta más que la que tarda dos segundos una vez al día. Dos: mira si alguna necesita un índice de dos columnas en vez de dos índices de una; si filtras por empresa y fecha a la vez, el índice tiene que llevar las dos, y en ese orden, porque uno de dos columnas sirve para la primera columna sola pero no para la segunda sola. Y tres: borra los que no usa nadie, que cada uno se paga en cada escritura. Postgres te dice cuáles no se han usado nunca en una tabla suya que se llama pg_stat_user_indexes.
7. ETAPA 5 · 500.000: fuera de la petición todo lo que pueda esperar
A este volumen el cuello de botella deja de ser la base de datos y pasa a ser el tiempo que tu servidor se pasa haciendo recados: mandar el correo, generar la factura en PDF, avisar a tres servicios externos, recalcular un ranking. Todo eso vive dentro de la petición, y la petición no termina hasta el último recado.
El arreglo es viejo y sigue siendo el mejor: guarda el encargo en una cola, contesta al usuario y deja que otro proceso lo haga por detrás. La petición baja de cuatro segundos a cuarenta milisegundos, y lo lento se puede reintentar sin nadie esperando delante de la pantalla.
Para empezar, BullMQ sobre el Redis que ya tienes montado. Kafka es otra cosa y llega mucho más tarde: no es una cola de tareas, es un registro de eventos que varios consumidores releen a su ritmo. Si no sabes explicar por qué necesitas releerlos, todavía no lo necesitas.
Y entre esos dos está RabbitMQ, el clásico de toda la vida: un servidor dedicado a repartir mensajes, con reglas de reparto mucho más finas que las de BullMQ y sin la complicación de Kafka. Es la opción de quien ya no cabe en Redis pero no tiene nada que releer.
Antes de elegir, la diferencia de fondo. Una cola de tareas guarda encargos: el primero que llega, el primero que se hace, y cuando se hace desaparece. Un registro de eventos guarda lo que ha pasado y no lo borra, así que varios programas distintos pueden leer lo mismo cada uno a su ritmo, y volver atrás si se equivocaron. BullMQ, SQS y RabbitMQ son lo primero. Kafka es lo segundo, y por eso es más caro y más difícil.
Las cuatro que te vas a encontrar, de la más barata a la más cara:
| Herramienta | Qué es | Elígela si | Lo que cuesta |
|---|---|---|---|
| BullMQ | Una librería sobre el Redis que ya tienes | Estás empezando y tus recados son correos, PDF y avisos | Lo más barato: si ya tienes Redis, cero. En Upstash, gratis hasta 500.000 comandos al mes |
| SQS o Cloud Tasks | Una cola gestionada, sin servidor que mantener | No quieres mantener nada y te basta con encolar y reintentar | Céntimos por millón de mensajes. El primer millón al mes sale gratis |
| RabbitMQ | Un servidor dedicado a repartir mensajes | Necesitas prioridades, varias colas y reglas de reparto finas | Una máquina pequeña y alguien que la mire. Gestionado, unos 20 $/mes |
| Kafka | Un registro de eventos que se puede releer | Varios equipos necesitan releer lo mismo a su ritmo | Lo más caro con diferencia: gestionado y en serio se va a tres cifras al mes |
Como en la otra tabla, los precios se mueven y el orden no. Y si dudas entre dos, coge la de arriba: bajar de Kafka a BullMQ no lo hace nadie, y subir de BullMQ a Kafka es una tarde.
Tres cosas que se ponen el mismo día que la cola, porque sin ellas la cola es una forma elegante de perder trabajo:
- Reintentos con espera creciente: 1 s, 4 s, 16 s. Si el servicio de al lado está caído, no le hagas tú de ariete.
- Tareas idempotentes: ejecutarla dos veces tiene que dar el mismo resultado, porque va a pasar. Una clave por encargo y listo.
- Una cola aparte para lo que falla siempre, con alguien mirándola. Si nadie la mira, ahí es donde se va el dinero de tus clientes.
8. ETAPA 6 · +1M: gateways distribuidos y sharding
Al millón aparecen dos nombres, y lo primero es saber que arreglan cosas distintas. Los gateways distribuidos son para que tu web no tarde en el otro lado del mundo. El sharding es para cuando tu base de datos ya no traga más escrituras. Unos cuantos necesitan el primero mucho antes del millón, y casi nadie necesita el segundo nunca.
Un gateway distribuido es el apartado 5 repetido en varios continentes: copias de tu aplicación en Europa, en América y en Asia, y algo delante que manda a cada usuario a la más cercana. Un usuario de Tokio contra un servidor de Madrid pierde unos doscientos milisegundos solo en el viaje de ida y vuelta, y eso no lo arregla ningún índice ni ninguna caché, porque es la distancia. Por eso tampoco se decide por volumen: se decide el día que miras de dónde entra tu gente y ves que está lejos.
Aquí acaba la lista original, y es donde más gente se mete en un lío por seguirla al pie de la letra. Hacer sharding es partir tus datos en varias bases que no se hablan entre ellas: ganas capacidad de escritura y pierdes casi todo lo demás, empezando por los JOIN entre partes y las transacciones que tocan dos.
Y puede que no te toque a ti: hay bases de datos que reparten los datos solas desde la primera fila y otras que no lo van a hacer nunca. Eso lo decidiste en el apartado 1, no al llegar al millón de usuarios.
Y si te toca, antes hay cuatro peldaños. Casi todo el mundo se queda en el primero o en el segundo:
- Una máquina más grande. Aburrido, inmediato y casi siempre suficiente. Es un botón y unos minutos de corte.
- Archivar lo viejo. Nueve de cada diez filas de la tabla que te preocupa no las mira nadie desde hace un año. A una tabla histórica y fuera del camino.
- Particionar por fecha. Misma base de datos y misma aplicación, pero cada consulta toca solo el trozo del mes por el que pregunta.
- Sacar al vecino ruidoso. Esa tabla de eventos que escribe cien veces por segundo no tiene por qué vivir con tus pedidos.
Si después de esos cuatro sigues sin llegar, entonces sí, y por la clave por la que preguntas siempre: en una aplicación con empresas dentro, esa clave es la empresa. Aunque antes hay un atajo, que es pasar por caja: media lista de este artículo se compra hecha, y las de arriba escalan solas mientras las de abajo te hacen entrar en un panel a decidir.
| Herramienta | Cómo escala | Lo que cuesta |
|---|---|---|
| Cloudflare | Sola: CDN, caché y límite por IP de serie | Gratis. El Pro son 20 $/mes y casi nadie lo necesita |
| Vercel | Sola: pone copias de tu app sin pedírselo | Gratis. Después 20 $/mes por persona y el tráfico aparte |
| Neon | Sola: sube y baja con la carga, y se duerme | Gratis. Después pagas lo que consume, sin mínimo mensual |
| Upstash | Sola: no hay servidor que dimensionar | Gratis hasta 500.000 comandos al mes. Luego 0,20 $ por 100.000 |
| Railway | A mano: subes CPU, RAM o número de copias | 5 $/mes con algo de uso incluido. El consumo va aparte |
| Supabase | A mano: subes la máquina; el pooler ya viene | Gratis. El Pro son 25 $/mes y la máquina grande es un extra |
| MongoDB Atlas | Copias de serie; el sharding lo activas tú | Gratis para probar. El sharding empieza en el plan de 57 $/mes |
De ahí sale la regla para elegir: si pagas por uso, escalar no te cuesta nada hasta que la aplicación funciona; si pagas por máquina reservada, escalar es una decisión tuya que se nota el día 1 del mes siguiente. Los precios son los de septiembre de 2026 y se mueven, pero el orden de la tabla no.
Resumen
- Los índices van con la primera tabla, y los tiene Mongo igual que Postgres.
- La base de datos se elige el día uno: Postgres salvo que sepas decir qué hace la otra que ella no.
- Los usuarios no son la unidad: peticiones por segundo, concurrencia y p95.
- Hasta mil, una máquina. Pasarse sale más caro que quedarse corto.
- Caché en tres capas, y quitarla a tiempo es la mitad del trabajo.
- Pool de conexiones antes de pagar una base de datos más grande.
- Horizontal solo si tu servidor no guarda sesiones, ficheros ni tareas dentro.
- Réplicas después de la caché, con «leer lo tuyo» el mismo día.
- Todo lo que pueda esperar, a una cola, con reintentos e idempotencia.
- El sharding casi nunca toca: cuatro peldaños antes, y media lista se compra hecha.
La lista de la que sale todo esto no está mal, está desordenada: mete el día uno en la fase seis y pone en la última algo que ni siquiera depende de ti. Y el desorden se paga, porque cada fase que te adelantas son cosas que mantener sin que nadie te lo haya pedido. Escalar bien es aburrido: haces lo mínimo que devuelve rendimiento, lo mides otra vez y esperas a que se rompa lo siguiente.
Cómo saber en qué fase estás
No lo decidas de memoria. Abre el panel de tu base de datos y mira tres números: peticiones por segundo en la hora punta, p95 de tu ruta más usada y conexiones abiertas contra el límite. Empieza por el primero de los tres que no cuadre, no por lo que más te apetezca montar, que casi siempre lo que toca está varias fases por debajo de lo que uno tenía ganas de tocar.
Y si todavía estás en la fase de tener la aplicación en pie, empieza por el stack completo del vibecoder, y antes de que entre nadie, por las 9 skills de seguridad.
Si escalas algo con esto y funciona, mándame la captura o escríbeme por Instagram. Esos mensajes me alegran el día.