Preparación de Postgres para producción en 48 horas tras un prototipo
Preparación de Postgres para producción en 48 horas: un plan práctico para copias de seguridad, monitoreo, pooling de conexiones, roles, permisos y simulacros de recuperación tras un prototipo.

Qué significa “listo para producción” en Postgres tras un prototipo
“Listo para producción” en Postgres no significa “diseño de base de datos perfecto”. Significa que tu app puede atender usuarios reales, tráfico real y errores reales sin perder datos, colapsar o filtrar accesos.
Un prototipo prioriza la velocidad: un único usuario de base de datos, ajustes por defecto, sin alertas y respaldos que existen más como idea que como práctica. Producción cambia la meta. Quieres comportamiento predecible bajo carga, reglas claras de acceso y una forma de volver atrás cuando algo falla.
La mayoría de los problemas aparecen en tres sitios:
- Pérdida de datos cuando los respaldos faltan, no se prueban o se almacenan junto a la base de datos.
- Caídas cuando el tráfico sube y cada petición abre una conexión nueva (una tormenta de conexiones).
- Brechas de seguridad cuando los secretos se filtran, los roles son demasiado poderosos o las entradas no se manejan con seguridad.
Un empujón de 48 horas para estar listo para producción consiste en añadir una capa ligera de seguridad, no en rediseñar todo. En ese plazo normalmente puedes poner los respaldos en marcha, probar una restauración, añadir monitoreo básico, implementar pooling de conexiones y dejar de ejecutar la app como superusuario. Lo que generalmente no terminas son rediseños importantes de esquema, reescribir consultas complejas o montar una conmutación por error multirregión desde cero.
Si heredaste un prototipo construido por IA (a menudo generado por herramientas como Replit o Cursor), usa un orden basado en riesgo: protege los datos primero, evita las caídas segundo y luego endurece permisos.
Hora 0–2: inventario rápido y triaje de riesgos
Pasa las primeras dos horas recopilando los hechos por escrito. No puedes tomar buenas decisiones si no sabes dónde vive Postgres, quién lo administra y cómo la app lo usa.
Empieza con un inventario simple:
- Dónde está alojado Postgres y qué versión corre
- Cómo se conecta la app (directo, proxy, serverless)
- Qué almacenamiento se usa y si los snapshots están habilitados
- Quién tiene acceso admin y quién puede desplegar cambios en la BD
- Dónde viven las credenciales (env vars, gestor de secretos, repositorio, ajustes de CI)
Luego captura lo que necesitarías para reproducir el estado actual: un volcado de esquema, extensiones instaladas y cualquier tarea programada o migraciones que se ejecuten automáticamente. Anota las 5–10 acciones más importantes que tocan Postgres (registro/inicio de sesión, checkout, búsqueda, ediciones de admin). Si tienes logs, toma una muestra pequeña de consultas lentas.
Finalmente, triajea según lo que más duele ahora: timeouts en picos, “demasiadas conexiones”, un endpoint que siempre es lento o flujos de autenticación que fallan de vez en cuando.
Respaldos: define RPO/RTO e implementa la configuración más simple y fiable
Si solo haces una cosa, que sean respaldos que realmente puedas restaurar. Empieza con dos números:
- RPO (cuánto dato puedes perder, por ejemplo 15 minutos)
- RTO (qué tan rápido debes estar de vuelta, por ejemplo 60 minutos)
Esos objetivos guían todo lo demás. Si puedes tolerar perder un día de datos, respaldos nocturnos podrían bastar. Si no, necesitas respaldos más frecuentes y probablemente recuperación a un punto en el tiempo desde tu hosting de Postgres.
Para una configuración práctica en 48 horas, usa dos capas:
- Snapshots (recuperación rápida)
- Respaldos lógicos (más lentos, pero portables y fáciles de inspeccionar)
Elige una política de retención que puedas explicar en una frase, como “7 diarios y 4 semanales”. Almacena los respaldos fuera de la máquina o del clúster de la base de datos para que una falla no lo deje todo fuera de servicio.
Mantén la prueba de restauración pequeña y repetible. Restaurar una tabla pequeña (o solo esquema más unas pocas filas) en una base limpia y verificar los conteos es suficiente para detectar la mayoría de las malas configuraciones de respaldo.
También decide quién recibe una alerta cuando fallan los respaldos. Debe ser una persona nombrada o una rotación on-call, no “el equipo”.
Preparación para restaurar: demuestra que puedes recuperar datos
Un respaldo que nunca has restaurado es una conjetura. Estar listo para restaurar significa tener una rutina que puedas ejecutar cuando estés cansado, estresado y la app esté caída.
Una prueba rápida de restauración (30–60 minutos)
Restaura en un lugar aislado: una base separada en el mismo servidor, staging o un contenedor temporal. No toques producción mientras pruebas que el respaldo es útil.
# Example: restore into a new database
createdb app_restore_test
# If you have a plain SQL dump
psql -d app_restore_test -f backup.sql
# If you have a custom-format dump
pg_restore -d app_restore_test --clean --if-exists backup.dump
Tras la restauración, comprueba los puntos débiles del prototipo: extensiones faltantes, propietarios equivocados o roles de la app que solo existían en el portátil de alguien.
Valida lo básico:
- El rol de la app puede conectarse y hacer una lectura y escritura simples
- Existen las extensiones necesarias (por ejemplo, uuid-ossp, pgcrypto, PostGIS)
- Los roles y permisos sobrevivieron la restauración (nada quedó propiedad del usuario equivocado)
- Una prueba de humo rápida pasa (iniciar sesión, crear un registro, leerlo)
Documenta como un runbook
Escribe los pasos exactos que seguiste: comandos, de dónde vienen las credenciales y qué significa “éxito”. Cronometra la restauración de principio a fin y compárala con tu RTO. Si la restauración toma 45 minutos y tu RTO es 15, no es solo un problema de tunning; es un desacople entre diseño de recuperación y objetivos.
Monitoreo y alertas: las señales mínimas que detectan caídas reales
No necesitas un gran dashboard. Necesitas un pequeño conjunto de señales que predigan dolor de usuario, más alertas que lleguen a una persona real.
Empieza con unas pocas comprobaciones que realmente revisarás:
- Conexiones activas vs máximo de conexiones
- Presión de CPU y memoria en el host de la base
- Espacio libre en disco y la velocidad a la que disminuye
- Lag de replicación (si tienes réplicas)
- Tasas de error (timeouts, fallos de autenticación)
Luego añade dos comprobaciones de base de datos que detectan fallas sigilosas: consultas lentas y bloqueos. Los prototipos suelen fallar aquí porque un endpoint hace un full table scan o un job en background sostiene un lock y todo se encola detrás.
Mantén las reglas de alerta simples y accionables. Por ejemplo: poco espacio en disco, saturación de conexiones durante varios minutos, un gran salto en latencia p95, bloqueos persistentes o lag de replicación por encima de tu tolerancia.
Ten cuidado con los logs. Registra consultas lentas y errores, pero no registres bodies completos de peticiones, tokens, contraseñas ni SQL completo que contenga datos de usuarios.
Pooling de conexiones: evita tormentas de conexiones antes de que ocurran
La mayoría de los prototipos hace lo más sencillo: abrir una conexión nueva, ejecutar una consulta y seguir. Eso funciona hasta que llega un pico (lanzamiento, campaña de email, tráfico de bots). Postgres tiene un límite estricto de conexiones concurrentes y cada conexión cuesta memoria. Demasiadas conexiones y la base se ralentiza y luego falla.
El pooling soluciona esto al reutilizar y limitar conexiones.
Dónde debería vivir el pooling
El pooling en la capa de la app está bien cuando controlas el código y el runtime se mantiene caliente. Un pooler gestionado es lo más sencillo si tu proveedor lo ofrece. Un pooler dedicado (ejecutándose junto a la base de datos) suele ser lo más predecible cuando tienes varias instancias de app.
Configuraciones seguras para empezar
Empieza pequeño y mide:
- Tamaño del pool: 10–30 por instancia de app (no cientos)
- Timeout de conexión: 2–5 segundos
- Idle timeout: 1–5 minutos
- Statement timeout: 10–30 segundos
- Queue timeout: 5–15 segundos
Los reintentos pueden ayudar con fallos breves de red, pero sé conservador. Reintenta solo en errores claramente transitorios, añade un pequeño retraso aleatorio y limita intentos (a menudo un solo reintento basta). Si no, puedes provocar una tormenta de reintentos.
Confirma también que no haces leak de conexiones: ciérralas, mantén transacciones cortas y no mantengas una transacción abierta mientras esperas APIs externas.
Roles y permisos: menor privilegio sin romper la app
El principio de menor privilegio es una ganancia rápida porque reduce el radio de impacto sin cambiar el esquema. Divide el acceso por tarea, no por persona.
Un patrón simple son tres roles:
- Runtime role para la app (lecturas/escrituras diarias)
- Migrations role para cambios de esquema
- Read-only role para soporte y analítica
El rol de runtime no debería poder crear tablas, cambiar propietarios ni leer todo por defecto. Usa el rol de migrations solo en tu proceso de despliegue, no en la app web.
Después de crear roles, elimina credenciales de admin compartidas de las configuraciones de la app. Un error común en prototipos es publicar la misma contraseña de superusuario usada en desarrollo, copiada en múltiples servicios.
Rota contraseñas y ponlas en una única fuente de verdad (env vars de la plataforma de despliegue o un gestor de secretos). Haz de la rotación un proceso repetible, no una edición heroica de madrugada.
Comprobaciones rápidas de endurecimiento:
- Postgres no es accesible públicamente; el acceso entrante está restringido
- TLS es obligatorio cuando las conexiones cruzan redes no confiables
- La app se conecta con el rol de runtime, no con admin ni migraciones
Comprobaciones de seguridad: evita las trampas más comunes de pérdida de datos y seguridad
Cuando los equipos se apresuran a lanzar, la mayoría de incidentes con Postgres vienen de las mismas fuentes: consultas inseguras, credenciales filtradas y migraciones riesgosas.
Empieza nombrando los riesgos principales que realmente estás protegiendo: inyección SQL, secretos expuestos (contraseñas en código o logs) y cambios de esquema que bloquean o borran tablas.
Para la seguridad de las consultas, trata la concatenación de cadenas SQL con entrada de usuario como un bug. Usa consultas parametrizadas siempre. Una forma rápida de encontrar problemas es buscar helpers de consultas crudas, template strings SQL y patrones de código que construyen SQL con valores proporcionados por el usuario.
Para migraciones, añade guardarraíles simples: haz un respaldo fresco antes de migrar, revisa el diff de la migración y escribe un plan de rollback (aunque sea “restaurar desde backup”).
También elige lo que no vas a arreglar en 48 horas y anótalo para que no se olvide. Ejemplos: security a nivel de fila, cifrado en reposo de los respaldos o trabajo más profundo en consultas e índices.
Simulacro de recuperación ante desastres: practica un escenario realista
Un simulacro de recuperación es una falla pequeña y planificada que ejecutas a propósito. La meta es demostrar que tus pasos de recuperación funcionan, no lucir heroicos.
Elige un escenario que puedas explicar en una frase, como “una mala migración borró la tabla users” o “un script borró filas sin WHERE”. Luego practica restaurar hasta un punto seguro y dejar la app funcionando otra vez.
Mantén el simulacro por debajo de una hora:
- Anuncia una ventana de simulacro y congela escrituras (o pon en modo mantenimiento)
- Simula la falla de forma controlada (idealmente en staging o en una copia)
- Restaura en una base separada y verifica acciones clave
- Decide cómo recuperarías en producción (swap vs copiar de vuelta)
- Anota cuánto tardó y qué te sorprendió
Incluso un incidente pequeño necesita responsabilidad clara: una persona para liderar decisiones, otra para ejecutar la restauración y otra para manejar comunicaciones.
Errores comunes que desperdician tus 48 horas
La forma más fácil de no cumplir el objetivo es gastar tiempo en trabajo que parece productivo pero no reduce riesgo.
Una trampa clásica es asumir que “respaldos automáticos” significa que estás cubierto. Los respaldos solo importan si puedes restaurarlos bajo demanda en un entorno limpio y el equipo conoce los pasos.
Otra trampa es usar un único usuario admin compartido porque “simplemente funciona”. También significa que cualquier bug o credencial filtrada tiene poder total.
Los problemas de conexiones a menudo se “arreglan” subiendo max_connections. Eso normalmente empeora las cosas bajo carga (más uso de memoria, más cambio de contexto, consultas más lentas). Arregla tormentas de conexiones con pooling, no empujando más al servidor.
Las alertas también pueden desperdiciar tiempo. No empieces con 30 alertas ruidosas en las que nadie confía. Un pequeño conjunto que capture dolor real es suficiente: fallos de respaldo, crecimiento de disco, saturación de conexiones, lag de replicación (si existe) y picos en la tasa de errores.
Qué verificar antes de declarar “listo para producción”
Si solo te queda una hora, verifica lo básico de punta a punta. Se trata menos de tunear perfecto y más de evidencia.
Asegúrate de poder responder “sí” con pruebas:
- Los respaldos son reales y recientes. Puedes decir la hora del último backup exitoso, la retención y dónde viven los respaldos.
- Puedes restaurar bajo presión. Hiciste una prueba de restauración en una base separada y registraste cuánto tomó.
- El monitoreo detectará fallos obvios. Una persona real recibe alertas y has probado al menos una alerta.
- Las conexiones están controladas. Hay pooling y timeouts, y una pequeña prueba de ráfaga no derrite la base.
- El acceso está acotado. Existen roles separados para runtime y migraciones, y los secretos no están en código ni logs.
Ejemplo: convertir un prototipo inestable creado por IA en un lanzamiento estable
Un fundador publica un prototipo construido por IA (generado en Cursor y retocado en Replit). Tras un lanzamiento, la app empieza a tener timeouts. Páginas se quedan cargando, inicios de sesión fallan y Postgres muestra cientos de conexiones de corta duración.
El plan de 48 horas se mantiene deliberadamente aburrido: detener la hemorragia, añadir visibilidad y luego hacer que los datos sean recuperables.
Primero, arregla las tormentas de conexiones con pooling y timeouts razonables. Luego añade monitoreo mínimo para conexiones, consultas lentas, uso de disco y estado de los respaldos. Después implementa respaldos automáticos y realiza una restauración real en una base fresca. Finalmente, divide roles para que la app no se ejecute con privilegios de migraciones o admin.
Una vez estable, el tunning de rendimiento es un proyecto más calmado: revisa consultas lentas, añade o corrige índices y verifica patrones donde “una tabla lo hace todo”.
Próximos pasos: mantener la estabilidad a medida que crece el uso
Un empujón de 48 horas te deja por encima de la línea, pero la estabilidad viene de pequeños seguimientos que no se salten.
Elige un backlog corto con responsables y fechas: una comprobación semanal de backup/restauración, una revisión mensual de alertas, una rotación de credenciales programada y una revisión trimestral de accesos. Repite un simulacro de recuperación periódicamente para que la recuperación no dependa de la memoria de una sola persona.
Si heredaste una base de código generada por IA y ves fallos repetidos (auth rota, secretos expuestos, migraciones desordenadas o patrones no escalables), un diagnóstico enfocado del código puede ser más rápido que adivinar. FixMyMess (fixmymess.ai) se especializa en tomar prototipos generados por IA y endurecerlos para producción, empezando por una auditoría priorizada por riesgo para que sepas qué arreglar ahora y qué puede esperar.
Preguntas Frecuentes
¿Cuál es la primera cosa que debo hacer para dejar un prototipo de Postgres listo para producción?
Empieza por asegurarte de que puedas recuperar los datos. Activa respaldos automáticos, guárdalos fuera del servidor de la base de datos y realiza una prueba de restauración en una base limpia. Si no haces otra cosa, haz esto.
¿Cuál es la diferencia entre “tener respaldos” y “estar listo para restaurar”?
Las copias de seguridad son archivos o snapshots que creas para poder recuperar más tarde. La preparación para restaurar significa comprobar que esas copias realmente funcionan restaurándolas en una base aislada y verificando que la aplicación puede leer y escribir. Una copia que nunca has restaurado sigue siendo un riesgo.
¿Cómo elijo un RPO y RTO razonables para una app pequeña?
RPO es cuánto dato puedes permitirte perder, por ejemplo 15 minutos. RTO es qué tan rápido necesitas volver a estar en línea, por ejemplo 60 minutos. Elige números simples con los que puedas vivir y luego ajusta la frecuencia de los respaldos y el método de restauración según ellos.
¿Debo usar snapshots, respaldos lógicos o ambos?
Usa dos capas: snapshots rápidos para recuperaciones rápidas y volcados lógicos para portabilidad e inspección. Mantén una política de retención simple que puedas explicar y guarda los respaldos fuera de la máquina o clúster de la base de datos. El objetivo es fiabilidad, no perfección.
¿Cuál es una prueba rápida de restauración que puedo hacer sin tocar producción?
Crea una base de datos de prueba de restauración (o usa staging) y restaura el último backup allí. Verifica que existen las extensiones necesarias, que los roles y la propiedad no se rompieron, y que el rol de la app puede hacer una lectura y escritura simples. Luego realiza una prueba rápida como iniciar sesión y crear un registro.
¿Por qué los prototipos suelen chocar con el problema de “demasiadas conexiones”?
Una tormenta de conexiones ocurre cuando un pico de tráfico hace que cada petición abra una nueva conexión. Postgres tiene un límite de conexiones concurrentes y cada una consume memoria; la base se ralentiza y luego falla. El pooling y los timeouts evitan esto al limitar y reutilizar conexiones.
¿Cuáles son ajustes seguros de inicio para pooling y timeouts?
Empieza con límites pequeños y mide. Un valor práctico es un pool de 10–30 conexiones por instancia de la app, timeout de conexión de 2–5 segundos y timeout de sentencia de 10–30 segundos. Mantén las transacciones cortas y evita mantenerlas abiertas mientras esperas APIs externas.
¿Cómo debo configurar roles para que mi app no se ejecute como superusuario?
Divide el acceso por función: un rol de ejecución para la app, un rol de migraciones para cambios de esquema y un rol de solo lectura para soporte o analítica. El rol de ejecución no debe poder crear tablas ni ser superusuario. Coloca las credenciales de migraciones solo en tu proceso de deploy, no en la app web.
¿Cuál es el monitoreo y alertado mínimo que necesito para Postgres?
Sigue un pequeño conjunto de señales que predigan dolor de usuario: saturación de conexiones, espacio en disco, tasas de error y latencia de consultas. Añade alertas que lleguen a una persona real y que sean accionables. Evita registrar datos sensibles como tokens, contraseñas o cuerpos completos de petición.
¿Cómo es un simulacro de recuperación ante desastres sencillo para una app con Postgres?
Elige un fallo realista como “una mala migración borró una tabla” y practica restaurar hasta un punto seguro en staging o en una copia. Cronometra el proceso, verifica las acciones clave y escribe los pasos exactos que seguiste. El objetivo es aprender qué se rompe cuando todo está tranquilo, no durante una caída.