--- title: "Replicación en streaming de PostgreSQL 16 en Ubuntu 24.04" description: "Configura replicación en streaming entre dos servidores PostgreSQL 16 en Ubuntu 24.04 con pg_basebackup y un slot de replicación, y promociona el standby." category: "Gestión de Bases de Datos" language: es source: https://cubepath.com/es/docs/gestión-de-bases-de-datos/configuracion-de-replicacion-postgresql updated: 2026-09-25 --- # Replicación en streaming de PostgreSQL 16 en Ubuntu 24.04 La replicación en streaming de PostgreSQL envía el registro de escritura anticipada (WAL) del servidor primario a uno o varios servidores standby, que lo aplican de forma continua y quedan como copia casi exacta en tiempo real. El standby admite consultas de solo lectura (hot standby) y puede promocionarse a primario si el principal falla. En este tutorial configurarás un primario y un standby con PostgreSQL 16 en Ubuntu 24.04, con un slot de replicación para que el primario no borre WAL que el standby todavía necesita. ## Requisitos previos Para seguir esta guía necesitas: - Dos servidores con Ubuntu 24.04 LTS, por ejemplo dos VPS de CubePath, idealmente conectados por una red privada. En los ejemplos: - Primario: `pg1`, IP privada `10.0.0.2`. - Standby: `pg2`, IP privada `10.0.0.3`. - Un usuario no root con privilegios `sudo` en ambos. - PostgreSQL 16 instalado en los dos con `sudo apt install postgresql`. La versión mayor debe ser la misma en ambos servidores. - UFW activo en ambos servidores. Sustituye las IP por las tuyas en todos los comandos. Los datos del standby se sustituirán por completo por los del primario, así que no uses un servidor con datos que quieras conservar. ## Paso 1: Configurar el servidor primario En Ubuntu, la configuración de PostgreSQL 16 está en `/etc/postgresql/16/main/`. En `pg1`, abre `postgresql.conf`: ```bash sudo nano /etc/postgresql/16/main/postgresql.conf ``` Busca y ajusta estas opciones (descomenta las líneas que empiecen por `#`): ```ini listen_addresses = 'localhost,10.0.0.2' wal_level = replica max_wal_senders = 10 max_replication_slots = 10 max_slot_wal_keep_size = 10GB ``` - `listen_addresses`: por defecto PostgreSQL solo escucha en `localhost`; añade la IP privada para que el standby pueda conectarse. - `wal_level = replica`, `max_wal_senders` y `max_replication_slots`: son los valores por defecto en PostgreSQL 16, pero conviene dejarlos explícitos. - `max_slot_wal_keep_size`: límite de WAL que un slot puede retener. Sin él, un standby caído durante días podría llenar el disco del primario. ## Paso 2: Crear el rol de replicación y permitir la conexión Crea un rol con el atributo `REPLICATION`. El comando te pedirá la contraseña dos veces; usa una contraseña larga y aleatoria: ```bash sudo -u postgres createuser --replication --pwprompt replicador ``` Ahora autoriza al standby en `pg_hba.conf`, que controla quién puede conectarse y cómo: ```bash sudo nano /etc/postgresql/16/main/pg_hba.conf ``` Añade esta línea al final del archivo: ```text host replication replicador 10.0.0.3/32 scram-sha-256 ``` La palabra `replication` en la columna de base de datos se refiere a las conexiones de replicación física, no a una base de datos con ese nombre. `scram-sha-256` es el método de contraseña por defecto de PostgreSQL 16. Reinicia PostgreSQL (el cambio de `listen_addresses` requiere reinicio) y abre el puerto solo para el standby: ```bash sudo systemctl restart postgresql sudo ufw allow from 10.0.0.3 to any port 5432 proto tcp ``` Comprueba que PostgreSQL escucha en la IP privada: ```bash sudo ss -tlnp | grep 5432 ``` ```output LISTEN 0 200 10.0.0.2:5432 0.0.0.0:* users:(("postgres",pid=5210,fd=7)) LISTEN 0 200 127.0.0.1:5432 0.0.0.0:* users:(("postgres",pid=5210,fd=6)) ``` ## Paso 3: Preparar el standby En `pg2`, detén PostgreSQL. El clúster creado al instalar el paquete está vacío y se sustituirá por una copia del primario: ```bash sudo systemctl stop postgresql ``` Guarda la contraseña del rol de replicación en el archivo `.pgpass` del usuario `postgres`, para que el standby pueda reconectarse sin intervención: ```bash sudo -u postgres nano /var/lib/postgresql/.pgpass ``` ```text 10.0.0.2:5432:replication:replicador:your_strong_password ``` ```bash sudo chmod 600 /var/lib/postgresql/.pgpass ``` El campo `replication` hace que la línea se aplique a las conexiones de replicación. PostgreSQL ignora el archivo si sus permisos son más abiertos que `600`. Comprueba la conexión desde el standby al primario: ```bash sudo -u postgres psql "host=10.0.0.2 user=replicador dbname=replication replication=true" -c "IDENTIFY_SYSTEM;" ``` ```output systemid | timeline | xlogpos | dbname ---------------------+----------+-----------+-------- 7419876543210987654 | 1 | 0/3000148 | (1 row) ``` Mueve el directorio de datos vacío a un lado; `pg_basebackup` necesita un directorio de destino vacío o inexistente: ```bash sudo mv /var/lib/postgresql/16/main /var/lib/postgresql/16/main.orig ``` ## Paso 4: Clonar el primario con pg_basebackup `pg_basebackup` copia todo el clúster del primario mientras sigue en funcionamiento. Ejecútalo en `pg2`: ```bash sudo -u postgres pg_basebackup -h 10.0.0.2 -U replicador \ -D /var/lib/postgresql/16/main \ -X stream -R -C -S standby_pg2 -P ``` Qué hace cada opción: - `-X stream`: transmite también el WAL generado durante la copia, así la copia es consistente por sí sola. - `-R`: crea el archivo `standby.signal` y escribe `primary_conninfo` en `postgresql.auto.conf`, de modo que el servidor arrancará como standby conectado al primario. - `-C -S standby_pg2`: crea en el primario un slot de replicación llamado `standby_pg2` y lo configura como `primary_slot_name`. El slot garantiza que el primario conserve el WAL hasta que el standby lo haya recibido. - `-P`: muestra el progreso. ```output 30452/30452 kB (100%), 1/1 tablespace ``` Comprueba que se creó la configuración de standby: ```bash sudo ls /var/lib/postgresql/16/main/standby.signal sudo cat /var/lib/postgresql/16/main/postgresql.auto.conf ``` ```output /var/lib/postgresql/16/main/standby.signal # Do not edit this file manually! # It will be overwritten by the ALTER SYSTEM command. primary_conninfo = 'user=replicador passfile=''/var/lib/postgresql/.pgpass'' channel_binding=prefer host=10.0.0.2 port=5432 sslmode=prefer ...' primary_slot_name = 'standby_pg2' ``` ## Paso 5: Arrancar el standby En Ubuntu, `postgresql.conf` vive en `/etc`, no en el directorio de datos, así que la configuración del standby es la que ya tenía `pg2`. La opción `hot_standby` está activada por defecto y permite consultas de solo lectura. Arranca PostgreSQL: ```bash sudo systemctl start postgresql ``` Revisa el registro del clúster: ```bash sudo tail -n 5 /var/log/postgresql/postgresql-16-main.log ``` ```output LOG: entering standby mode LOG: redo starts at 0/2000028 LOG: consistent recovery state reached at 0/2000100 LOG: database system is ready to accept read-only connections LOG: started streaming WAL from primary at 0/3000000 on timeline 1 ``` Cuando compruebes que todo funciona, puedes borrar el directorio antiguo con `sudo rm -rf /var/lib/postgresql/16/main.orig`. ## Paso 6: Verificar la replicación En el primario (`pg1`), consulta los standby conectados: ```bash sudo -u postgres psql -x -c "SELECT client_addr, state, sync_state, replay_lag FROM pg_stat_replication;" ``` ```output -[ RECORD 1 ]--------- client_addr | 10.0.0.3 state | streaming sync_state | async replay_lag | 00:00:00.000812 ``` Y el estado del slot: ```bash sudo -u postgres psql -c "SELECT slot_name, active, wal_status FROM pg_replication_slots;" ``` ```output slot_name | active | wal_status -------------+--------+------------ standby_pg2 | t | reserved (1 row) ``` En el standby (`pg2`), confirma que está en modo recuperación: ```bash sudo -u postgres psql -c "SELECT pg_is_in_recovery(), status, sender_host FROM pg_stat_wal_receiver;" ``` ```output pg_is_in_recovery | status | sender_host -------------------+-----------+------------- t | streaming | 10.0.0.2 (1 row) ``` Ahora prueba con datos reales. En el primario: ```bash sudo -u postgres psql -c "CREATE TABLE prueba_replica (id int PRIMARY KEY, nota text);" sudo -u postgres psql -c "INSERT INTO prueba_replica VALUES (1, 'hola desde pg1');" ``` En el standby: ```bash sudo -u postgres psql -c "SELECT * FROM prueba_replica;" ``` ```output id | nota ----+---------------- 1 | hola desde pg1 (1 row) ``` Un intento de escritura en el standby falla, como debe: ```bash sudo -u postgres psql -c "INSERT INTO prueba_replica VALUES (2, 'desde pg2');" ``` ```output ERROR: cannot execute INSERT in a read-only transaction ``` Borra la tabla en el primario con `sudo -u postgres psql -c "DROP TABLE prueba_replica;"`. ## Paso 7: Promocionar el standby a primario Si el primario falla, promociona el standby para que acepte escrituras. En `pg2`: ```bash sudo -u postgres psql -c "SELECT pg_promote();" ``` ```output pg_promote ------------ t (1 row) ``` `pg_is_in_recovery()` devolverá ahora `f`, y el archivo `standby.signal` desaparece. Apunta tus aplicaciones a `10.0.0.3`. > Importante: el primario antiguo no debe volver a arrancar como primario, porque tendrías dos servidores aceptando escrituras. Para reincorporarlo, conviértelo en standby del nuevo primario repitiendo los pasos 2 a 5 en la dirección contraria, y elimina en él el slot antiguo si ya no se usa. Si alguna vez retiras el standby de forma definitiva sin promocionarlo, borra su slot en el primario para que no retenga WAL: ```bash sudo -u postgres psql -c "SELECT pg_drop_replication_slot('standby_pg2');" ``` ## Solución de problemas **`FATAL: no pg_hba.conf entry for replication connection from host "10.0.0.3"`**: falta la línea `host replication` en `pg_hba.conf` del primario, o la IP no coincide. Corrígela y recarga con `sudo systemctl reload postgresql`. **`FATAL: password authentication failed for user "replicador"`**: la contraseña de `.pgpass` no coincide o el archivo tiene permisos distintos de `600`. Puedes cambiar la contraseña en el primario con `sudo -u postgres psql -c "\password replicador"`. **`pg_basebackup: error: directory "/var/lib/postgresql/16/main" exists but is not empty`**: no se movió el directorio de datos en el paso 3. Comprueba que PostgreSQL está detenido y muévelo. **`wal_status` pasa a `lost` en `pg_replication_slots`**: el standby estuvo desconectado y superó `max_slot_wal_keep_size`, por lo que el primario descartó WAL que necesitaba. Borra el slot, detén el standby y repite los pasos 3 a 5. **El retraso (`replay_lag`) crece de forma continua**: el standby no da abasto aplicando cambios o la red está saturada. Revisa la carga de disco del standby con `iostat -x 1` (paquete `sysstat`) y las consultas largas en él, que pueden retrasar la aplicación del WAL. ## Conclusión Tienes un standby de PostgreSQL 16 que recibe el WAL del primario en streaming, protegido con un slot de replicación con límite de retención y listo para promocionarse. Como siguientes pasos, puedes enviar las consultas de solo lectura al standby, activar la replicación síncrona con `synchronous_standby_names` si no puedes permitirte perder ninguna transacción, o automatizar la conmutación por error con una herramienta como Patroni.