Este artículo brinda la fórmula publicada por la wiki de PostgreSQL para calcular la concurrencia ideal de su hardware (la fórmula de tamaño del grupo de conexiones más citada en el ecosistema), explica por qué cada conexión cuesta más que un subproceso de aplicación y luego detalla el procedimiento para configurar max_connections sin adivinar. Nuestra metodología de referencia documenta el protocolo de medición utilizado para cualquier afirmación de rendimiento en este blog.
Lo esencial
- Sin agrupación, max_connections debería cubrir todas las conexiones de clientes simultáneas, no solo aquellas que Postgres puede procesar eficientemente en paralelo.
- Fórmula de referencia de la wiki de PostgreSQL: concurrencia activa ideal = (núcleos físicos × 2) + discos eficientes. Un punto de partida que debe ser validado mediante mediciones, no un límite estricto.
- max_connections es un parámetro de contexto
postmaster: cambiarlo requiere un reinicio completo del servidor, no una simple recarga. - Cada conexión de Postgres es un proceso de sistema separado, no un hilo liviano: esto es lo que hace que la sobrecarga sea real tan pronto como aumenta el número de conexiones.
- Verificado en el código: en sus clústeres de Postgres dedicados, Aurabase varía max_connections de 50 (nivel gratuito) a 400 (nivel empresarial) según el tamaño del clúster.
Por qué una conexión Postgres cuesta más que un hilo de aplicación
Postgres no utiliza un grupo de subprocesos ligero para sus conexiones. Cada conexión de cliente desencadena un proceso de sistema completo.
El proceso postmaster crea uno nuevo ("fork") para cada intento de conexión, dedicado a esta sesión única hasta que se cierra. La documentación oficial del proyecto describe precisamente este mecanismo en su capítulo sobre fundamentos arquitectónicos (postgresql.org/docs/current/connect-estab.html, sección “Semántica de conexión”, consultado el 24 de agosto de 2026).
Esta elección tiene una verdadera ventaja: la caída de una conexión no afecta a las demás, ya que cada proceso queda aislado del resto del servidor. También tiene un costo directo: cada conexión adicional agrega un proceso completo del sistema operativo para programar, con su propio espacio de memoria y su propia sobrecarga de cambio de contexto para el kernel.
Una aplicación que abre 500 conexiones directas a Postgres sin agrupación obliga al servidor a gestionar 500 procesos del sistema simultáneos, incluso si la gran mayoría de ellos permanecen inactivos entre dos solicitudes.
Lo que realmente consume una conexión: memoria compartida y work_mem
Dos mecanismos distintos afectan la memoria y confundirlos casi siempre conduce a un diagnóstico erróneo.
El primero está arreglado. Al inicio, Postgres reserva estructuras de memoria compartida (bloqueos, tabla de procesos) dimensionadas según el valor de max_connections, independientemente de si estas conexiones se abren posteriormente o no. La documentación oficial para la configuración señala explícitamente: aumentarla puede requerir más memoria compartida del sistema de la que permite la configuración predeterminada de su sistema operativo (postgresql.org/docs/current/runtime-config-connection.html, consultado el 24 de agosto de 2026).
El segundo es variable y mucho más peligroso a escala: work_mem no se asigna una vez por conexión, sino una vez por operación de clasificación o hash en el plan de consulta. La documentación oficial es explícita en este punto: una consulta compleja puede lanzar varias de estas operaciones en paralelo, y varias sesiones pueden hacer lo mismo simultáneamente, de modo que la memoria realmente utilizada puede valer varias veces work_mem (postgresql.org/docs/current/runtime-config-resource.html, consultado el 24 de agosto de 2026).
No es solo max_connections × work_mem lo que amenaza la memoria de un servidor. Es max_connections × work_mem × número de operaciones simultáneas por consulta. Es este producto el que explica un servidor que intercambia, o que se queda sin memoria tras un aumento de max_connections considerado inofensivo.
La fórmula de tamaño del wiki de PostgreSQL
La wiki oficial del proyecto PostgreSQL documenta una fórmula de referencia para calcular cuántas conexiones activas su hardware puede procesar eficientemente en paralelo, no cuántas conexiones se abren en total (wiki.postgresql.org/wiki/Number_Of_Database_Connections, consultado el 24 de agosto de 2026).
concurrencia activa ideal = (núcleos físicos × 2) + discos eficientes. El número de núcleos excluye el hyperthreading. La cantidad de discos efectivos sigue siendo cercana a 1 en el almacenamiento SSD moderno, donde la noción de un disco físico separado (“eje”) pierde gran parte de su significado original.
En un servidor con 8 núcleos físicos y almacenamiento SSD, la fórmula da (8 × 2) + 1 = 17 conexiones activas antes de que el rendimiento comience a degradarse. Esta cifra suele sorprender: parece ínfima en comparación con los cientos de conexiones que abre una aplicación en la práctica. Éste es precisamente el tema del siguiente párrafo.
El número calculado por la fórmula mide la simultaneidad que la CPU y el disco pueden absorber, no la cantidad de conexiones de cliente que su aplicación necesita abrir. Una flota de 20 procesos de aplicación, cada uno con su propio grupo de 10 conexiones, abre 200 conexiones simultáneas a Postgres incluso si sólo 17 de ellas están trabajando activamente en un momento dado. Sin un pooler, max_connections debe cubrir los 200, no los 17. Es esta brecha la que empuja a la mayoría de las arquitecturas a agregar un pooler en modo de transacción, incluso si eso significa elegir cuál (consulte nuestra comparación de PgBouncer, Supavisor y PgCat).
Cómo cambiar max_connections (y por qué es necesario reiniciar)
max_connections no se intercambia en caliente. Este es un parámetro de contexto postmaster: Postgres lo lee una vez, al inicio, para dimensionar su memoria compartida. Una recarga de configuración (pg_reload_conf() o SIGHUP) no es suficiente, debe reiniciar el servidor.
Primero verifique el valor actual y su contexto, para confirmar que será necesario reiniciar:
Luego aplique el nuevo valor, luego reinicie:
max_connections incluye por defecto superuser_reserved_connections (3 por defecto): estas conexiones están reservadas para un superusuario en caso de saturación, nunca están disponibles para su aplicación, incluso si aún no se ha alcanzado el contador global.
Cómo Aurabase presupuesta max_connections en sus clústeres de Postgres
Dimensionar max_connections no es solo un ejercicio teórico. Así es como Aurabase lo presupuesta en sus clústeres de Postgres administrados:
Clústeres dedicados: un clúster de Postgres por proyecto
En este nivel (vea nuestra comparación base dedicada vs compartida), cada proyecto recibe su propio clúster CloudNativePG y su propio presupuesto max_connections, dimensionado con el tamaño de la instancia:
| gratis (dedicado) | conexiones_max 50 | 1 instancia · 500 m vCPU · 512 Mi |
|---|---|---|
| profesional (predeterminado) | conexiones_max 200 | 2 instancias · 1 vCPU · 2Gi |
| equipo | conexiones_max 300 | 3 instancias · 2 vCPU · 3Gi |
| negocio | conexiones_max 400 | 3 instancias · 2 vCPU · 4Gi |
Clústeres compartidos: varios proyectos de una organización, un presupuesto compartido
En este segundo camino, todos los proyectos de la misma organización se conectan a través de un pooler CNPG (PgBouncer, modo transaction) frente a un primario compartido:
| gratis | conexiones_max 50 | max_client_conn 100 | conexiones_max_usuario 20 |
|---|---|---|---|
| profesional | conexiones_max 100 | max_client_conn 200 | conexiones_max_usuario 60 |
| equipo | conexiones_max 200 | max_client_conn 400 | conexiones_max_usuario 150 |
Todos los proyectos de una organización se conectan a través de una función de aplicación compartida. Por lo tanto, max_user_connections por sí solo limita el total de conexiones de servidor que esta función puede abrir en todo el clúster: esta es la verdadera protección global del clúster, no max_client_conn, que solo limita las conexiones de clientes al propio pooler.
Sin embargo, este agrupador solo atiende el tráfico de aplicaciones SDK. PostgREST, por su parte, permanece conectado directamente al primario (servicio-rw): la agrupación en modo de transacción rompería su mecanismo de recarga de esquema, que escucha un canal LISTEN dedicado llamado pgrst. Por lo tanto, sus propias conexiones (2 por réplica en el nivel compartido, 10 por réplica en el nivel dedicado) cuentan directamente en el presupuesto max_connections del principal, fuera de cualquier agrupador, exactamente el tipo de conexión "olvidada" que debe incluir el paso 1 del procedimiento siguiente.
El código documenta explícitamente estos presupuestos de pool compartido como valores iniciales que se calibrarán en condiciones reales, midiendo pg_stat_activity bajo carga, no como cifras fijas de un punto de referencia publicado. Esta es la misma disciplina que se describe en nuestra metodología de referencia : medir antes de ajustar, no adivinar y luego esperar. Estos clústeres se ejecutan en PostgreSQL 16, una elección documentada en nuestra comparación Postgres 16 vs 17 vs 18.
El procedimiento de 5 pasos para dimensionar max_connections sin agrupar
Este procedimiento no depende de ninguna herramienta en particular: se aplica a cualquier servidor Postgres, administrado o autohospedado.
- Cuente sus conexiones de clientes reales. Número de procesos de aplicaciones multiplicados por el tamaño de su grupo interno, además de herramientas de administración, replicación y monitoreo. Es este número, no la fórmula, el que establece el límite máximo de conexiones.
- Calcule la concurrencia ideal de su hardware con la fórmula de la wiki de PostgreSQL: (núcleos físicos × 2) + discos eficientes. Esta figura indica cuántas de estas conexiones pueden funcionar realmente en paralelo sin degradar el rendimiento.
- Establezca max_connections por encima de la necesidad real para el paso 1, con margen para
superuser_reserved_connectionsy para cualquier herramienta de administración que abra sus propias conexiones fuera de la aplicación. - Aplique el cambio con ALTER SYSTEM SET, luego reinicie el servidor. Este es un parámetro de postmaster: una simple recarga no es suficiente, como se detalló anteriormente.
- Supervisar pg_stat_activity a lo largo del tiempo. Si el número de conexiones inactivas excede en gran medida el número de conexiones activas, esto no es un problema de max_connections: es la señal de que necesita un agrupador frente al servidor, no un número mayor.
La solicitud de seguimiento del paso 5, utilizable directamente:
Cuando la fórmula ya no es suficiente: las señales de que necesitas un pooler
Tres señales regresan sistemáticamente cuando max_connections por sí solo ya no es suficiente, sea cual sea su valor.
- El error
FATAL: sorry, too many clients alreadyaparece durante la carga máxima, mientras que la mayoría de las conexiones mostradas porpg_stat_activityestán en estado inactivo. - La aplicación se ejecuta en un entorno sin servidor o con trabajadores efímeros (funciones de borde, trabajos cortos), que abren y cierran conexiones mucho más rápido de lo que el modelo de proceso por conexión de Postgres fue diseñado para acomodar.
- La fórmula y el procedimiento anteriores ya se han aplicado y la necesidad real de conexiones de clientes continúa excediendo la memoria disponible que se puede asignar sin poner en peligro work_mem o Shared_buffers.
En estos tres casos, la respuesta correcta casi siempre es un pooler ubicado entre la aplicación y Postgres, no un max_connections superior. Nuestra comparación PgBouncer, Supavisor y PgCat detalla las tres opciones, y nuestra guía para el modo de transacción explica el compromiso más común una vez que el pooler está implementado. Para conocer todos los ajustes de Postgres más allá de las conexiones, consulte nuestra lista de verificación de ajuste de Postgres de producción .