[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"blog-/blog/tutorial/replicacion-postgresql-alta-disponibilidad":3,"prev-/blog/tutorial/replicacion-postgresql-alta-disponibilidad":1462,"next-/blog/tutorial/replicacion-postgresql-alta-disponibilidad":1465,"related-/blog/tutorial/replicacion-postgresql-alta-disponibilidad":1468},{"id":4,"title":5,"author":6,"authorUrl":7,"body":8,"category":1428,"cta":1429,"date":1432,"dateModified":1432,"description":1433,"draft":1434,"extension":1435,"faq":1436,"featured":1434,"image":1449,"imageAlt":1450,"meta":1451,"navigation":160,"path":1452,"readingTime":415,"seo":1453,"stem":1454,"tags":1455,"__hash__":1461},"blog/blog/tutorial/replicacion-postgresql-alta-disponibilidad.md","Replicación master-slave en PostgreSQL — alta disponibilidad paso a paso","Syswork México","/nosotros",{"type":9,"value":10,"toc":1398},"minimark",[11,30,38,43,54,57,61,69,86,94,198,202,207,210,229,272,276,279,328,332,335,350,353,419,422,452,455,473,476,480,484,500,504,507,529,537,541,603,606,700,703,707,713,732,735,773,776,782,786,793,805,814,816,820,836,839,857,860,866,870,874,902,908,914,917,921,945,952,956,959,1035,1074,1076,1080,1083,1086,1150,1153,1195,1203,1207,1210,1240,1243,1268,1271,1284,1290,1294,1297,1317,1327,1336,1339,1343,1346,1385,1394],[12,13,14,15,20,21,20,25,29],"p",{},"Una base de datos sin réplica es un punto único de falla. Si el disco muere, si el servidor se cae, si una actualización sale mal — tu ",[16,17,19],"a",{"href":18},"/soluciones/erp","ERP",", tu ",[16,22,24],{"href":23},"/soluciones/crm","CRM",[16,26,28],{"href":27},"/soluciones/e-commerce","e-commerce"," se detienen. Con replicación, una segunda copia de tu base de datos se mantiene sincronizada en otro servidor, lista para tomar el control en minutos.",[12,31,32,33,37],{},"En esta guía vas a configurar replicación streaming en ",[16,34,36],{"href":35},"/tecnologias/postgresql","PostgreSQL"," con un servidor primario y una réplica de solo lectura — el esquema de alta disponibilidad más común y práctico para la mayoría de las empresas.",[39,40,42],"h2",{"id":41},"arquitectura","Arquitectura",[44,45,50],"pre",{"className":46,"code":48,"language":49},[47],"language-text","┌─────────────────┐         ┌─────────────────┐\n│    PRIMARIO      │  WAL    │    RÉPLICA       │\n│  10.0.1.10       │────────▶│  10.0.1.11       │\n│                  │ stream  │                  │\n│  Lecturas ✅     │         │  Lecturas ✅     │\n│  Escrituras ✅   │         │  Escrituras ❌   │\n│                  │         │  (solo lectura)  │\n└─────────────────┘         └─────────────────┘\n     ▲                           ▲\n     │                           │\n  App (ERP, CRM)          Reportes, BI\n  Escrituras              Consultas pesadas\n","text",[51,52,48],"code",{"__ignoreMap":53},"",[12,55,56],{},"El primario acepta todas las escrituras y envía los cambios (WAL — Write Ahead Log) a la réplica en tiempo real. La réplica aplica los cambios y está disponible para consultas de solo lectura. Si el primario falla, la réplica puede promoverse a primario.",[39,58,60],{"id":59},"requisitos-previos","Requisitos previos",[12,62,63,64,68],{},"Dos servidores Ubuntu 24.04 con ",[16,65,67],{"href":66},"/blog/instalar-postgresql-en-ubuntu-24","PostgreSQL 16 instalado"," en ambos:",[70,71,72,80],"ul",{},[73,74,75,79],"li",{},[76,77,78],"strong",{},"Primario:"," 10.0.1.10",[73,81,82,85],{},[76,83,84],{},"Réplica:"," 10.0.1.11",[12,87,88,89,93],{},"Ambos servidores deben poder comunicarse por el puerto 5432. Configura tu ",[16,90,92],{"href":91},"/blog/configurar-firewall-ufw-linux","firewall UFW",":",[44,95,99],{"className":96,"code":97,"language":98,"meta":53,"style":53},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark","# En el primario: permitir conexión desde la réplica\nsudo ufw allow from 10.0.1.11 to any port 5432 comment 'PostgreSQL replicación'\n\n# En la réplica: permitir conexión desde el primario (para pg_basebackup)\nsudo ufw allow from 10.0.1.10 to any port 5432 comment 'PostgreSQL primario'\n","bash",[51,100,101,110,155,162,168],{"__ignoreMap":53},[102,103,106],"span",{"class":104,"line":105},"line",1,[102,107,109],{"class":108},"sutJx","# En el primario: permitir conexión desde la réplica\n",[102,111,113,117,121,124,127,130,133,136,139,142,145,149,152],{"class":104,"line":112},2,[102,114,116],{"class":115},"sbgvK","sudo",[102,118,120],{"class":119},"s_sjI"," ufw",[102,122,123],{"class":119}," allow",[102,125,126],{"class":119}," from",[102,128,85],{"class":129},"srdBf",[102,131,132],{"class":119}," to",[102,134,135],{"class":119}," any",[102,137,138],{"class":119}," port",[102,140,141],{"class":129}," 5432",[102,143,144],{"class":119}," comment",[102,146,148],{"class":147},"sjJ54"," '",[102,150,151],{"class":119},"PostgreSQL replicación",[102,153,154],{"class":147},"'\n",[102,156,158],{"class":104,"line":157},3,[102,159,161],{"emptyLinePlaceholder":160},true,"\n",[102,163,165],{"class":104,"line":164},4,[102,166,167],{"class":108},"# En la réplica: permitir conexión desde el primario (para pg_basebackup)\n",[102,169,171,173,175,177,179,181,183,185,187,189,191,193,196],{"class":104,"line":170},5,[102,172,116],{"class":115},[102,174,120],{"class":119},[102,176,123],{"class":119},[102,178,126],{"class":119},[102,180,79],{"class":129},[102,182,132],{"class":119},[102,184,135],{"class":119},[102,186,138],{"class":119},[102,188,141],{"class":129},[102,190,144],{"class":119},[102,192,148],{"class":147},[102,194,195],{"class":119},"PostgreSQL primario",[102,197,154],{"class":147},[39,199,201],{"id":200},"paso-1-configurar-el-servidor-primario","Paso 1: Configurar el servidor primario",[203,204,206],"h3",{"id":205},"crear-usuario-de-replicación","Crear usuario de replicación",[12,208,209],{},"En el primario, crea un usuario dedicado para la replicación:",[44,211,213],{"className":96,"code":212,"language":98,"meta":53,"style":53},"sudo -u postgres psql\n",[51,214,215],{"__ignoreMap":53},[102,216,217,219,223,226],{"class":104,"line":105},[102,218,116],{"class":115},[102,220,222],{"class":221},"stzsN"," -u",[102,224,225],{"class":119}," postgres",[102,227,228],{"class":119}," psql\n",[44,230,234],{"className":231,"code":232,"language":233,"meta":53,"style":53},"language-sql shiki shiki-themes material-theme-lighter github-light github-dark","CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'contraseña_replica_segura';\n","sql",[51,235,236],{"__ignoreMap":53},[102,237,238,242,245,249,252,255,258,261,263,266,269],{"class":104,"line":105},[102,239,241],{"class":240},"sw1J6","CREATE",[102,243,244],{"class":240}," ROLE",[102,246,248],{"class":247},"su5hD"," replicator ",[102,250,251],{"class":240},"WITH",[102,253,254],{"class":247}," REPLICATION ",[102,256,257],{"class":240},"LOGIN",[102,259,260],{"class":240}," PASSWORD",[102,262,148],{"class":147},[102,264,265],{"class":119},"contraseña_replica_segura",[102,267,268],{"class":147},"'",[102,270,271],{"class":247},";\n",[203,273,275],{"id":274},"configurar-pg_hbaconf","Configurar pg_hba.conf",[12,277,278],{},"Permite la conexión de replicación desde la réplica:",[44,280,282],{"className":96,"code":281,"language":98,"meta":53,"style":53},"sudo tee -a /etc/postgresql/16/main/pg_hba.conf > /dev/null \u003C\u003CEOF\n\n# Replicación\nhost    replication    replicator    10.0.1.11/32    scram-sha-256\nEOF\n",[51,283,284,310,314,319,324],{"__ignoreMap":53},[102,285,286,288,291,294,297,301,304,307],{"class":104,"line":105},[102,287,116],{"class":115},[102,289,290],{"class":119}," tee",[102,292,293],{"class":221}," -a",[102,295,296],{"class":119}," /etc/postgresql/16/main/pg_hba.conf",[102,298,300],{"class":299},"smGrS"," >",[102,302,303],{"class":119}," /dev/null",[102,305,306],{"class":299}," \u003C\u003C",[102,308,309],{"class":147},"EOF\n",[102,311,312],{"class":104,"line":112},[102,313,161],{"emptyLinePlaceholder":160},[102,315,316],{"class":104,"line":157},[102,317,318],{"class":119},"# Replicación\n",[102,320,321],{"class":104,"line":164},[102,322,323],{"class":119},"host    replication    replicator    10.0.1.11/32    scram-sha-256\n",[102,325,326],{"class":104,"line":170},[102,327,309],{"class":147},[203,329,331],{"id":330},"configurar-postgresqlconf","Configurar postgresql.conf",[12,333,334],{},"Edita la configuración del primario para habilitar la replicación:",[44,336,338],{"className":96,"code":337,"language":98,"meta":53,"style":53},"sudo nano /etc/postgresql/16/main/postgresql.conf\n",[51,339,340],{"__ignoreMap":53},[102,341,342,344,347],{"class":104,"line":105},[102,343,116],{"class":115},[102,345,346],{"class":119}," nano",[102,348,349],{"class":119}," /etc/postgresql/16/main/postgresql.conf\n",[12,351,352],{},"Modifica o agrega estas líneas:",[44,354,358],{"className":355,"code":356,"language":357,"meta":53,"style":53},"language-ini shiki shiki-themes material-theme-lighter github-light github-dark","# Escuchar en todas las interfaces\nlisten_addresses = '*'\n\n# WAL (Write Ahead Log)\nwal_level = replica\nmax_wal_senders = 5\nwal_keep_size = 1GB\n\n# Archivado de WAL (recomendado para point-in-time recovery)\narchive_mode = on\narchive_command = 'cp %p /var/lib/postgresql/16/wal_archive/%f'\n","ini",[51,359,360,365,370,374,379,384,390,396,401,407,413],{"__ignoreMap":53},[102,361,362],{"class":104,"line":105},[102,363,364],{},"# Escuchar en todas las interfaces\n",[102,366,367],{"class":104,"line":112},[102,368,369],{},"listen_addresses = '*'\n",[102,371,372],{"class":104,"line":157},[102,373,161],{"emptyLinePlaceholder":160},[102,375,376],{"class":104,"line":164},[102,377,378],{},"# WAL (Write Ahead Log)\n",[102,380,381],{"class":104,"line":170},[102,382,383],{},"wal_level = replica\n",[102,385,387],{"class":104,"line":386},6,[102,388,389],{},"max_wal_senders = 5\n",[102,391,393],{"class":104,"line":392},7,[102,394,395],{},"wal_keep_size = 1GB\n",[102,397,399],{"class":104,"line":398},8,[102,400,161],{"emptyLinePlaceholder":160},[102,402,404],{"class":104,"line":403},9,[102,405,406],{},"# Archivado de WAL (recomendado para point-in-time recovery)\n",[102,408,410],{"class":104,"line":409},10,[102,411,412],{},"archive_mode = on\n",[102,414,416],{"class":104,"line":415},11,[102,417,418],{},"archive_command = 'cp %p /var/lib/postgresql/16/wal_archive/%f'\n",[12,420,421],{},"Crea el directorio de archivo WAL:",[44,423,425],{"className":96,"code":424,"language":98,"meta":53,"style":53},"sudo mkdir -p /var/lib/postgresql/16/wal_archive\nsudo chown postgres:postgres /var/lib/postgresql/16/wal_archive\n",[51,426,427,440],{"__ignoreMap":53},[102,428,429,431,434,437],{"class":104,"line":105},[102,430,116],{"class":115},[102,432,433],{"class":119}," mkdir",[102,435,436],{"class":221}," -p",[102,438,439],{"class":119}," /var/lib/postgresql/16/wal_archive\n",[102,441,442,444,447,450],{"class":104,"line":112},[102,443,116],{"class":115},[102,445,446],{"class":119}," chown",[102,448,449],{"class":119}," postgres:postgres",[102,451,439],{"class":119},[12,453,454],{},"Reinicia PostgreSQL:",[44,456,458],{"className":96,"code":457,"language":98,"meta":53,"style":53},"sudo systemctl restart postgresql\n",[51,459,460],{"__ignoreMap":53},[102,461,462,464,467,470],{"class":104,"line":105},[102,463,116],{"class":115},[102,465,466],{"class":119}," systemctl",[102,468,469],{"class":119}," restart",[102,471,472],{"class":119}," postgresql\n",[474,475],"ad-banner",{},[39,477,479],{"id":478},"paso-2-preparar-la-réplica","Paso 2: Preparar la réplica",[203,481,483],{"id":482},"detener-postgresql-en-la-réplica","Detener PostgreSQL en la réplica",[44,485,487],{"className":96,"code":486,"language":98,"meta":53,"style":53},"sudo systemctl stop postgresql\n",[51,488,489],{"__ignoreMap":53},[102,490,491,493,495,498],{"class":104,"line":105},[102,492,116],{"class":115},[102,494,466],{"class":119},[102,496,497],{"class":119}," stop",[102,499,472],{"class":119},[203,501,503],{"id":502},"limpiar-el-directorio-de-datos","Limpiar el directorio de datos",[12,505,506],{},"La réplica necesita empezar con una copia exacta del primario. Borra los datos actuales:",[44,508,510],{"className":96,"code":509,"language":98,"meta":53,"style":53},"sudo rm -rf /var/lib/postgresql/16/main/*\n",[51,511,512],{"__ignoreMap":53},[102,513,514,516,519,522,525],{"class":104,"line":105},[102,515,116],{"class":115},[102,517,518],{"class":119}," rm",[102,520,521],{"class":221}," -rf",[102,523,524],{"class":119}," /var/lib/postgresql/16/main/",[102,526,528],{"class":527},"s_hVV","*\n",[530,531,534],"alert",{"title":532,"type":533},"Cuidado","warning",[12,535,536],{},"Este comando borra todos los datos de PostgreSQL en la réplica. Si la réplica tiene datos que necesitas conservar, haz un backup antes. En una instalación nueva, el directorio solo contiene la configuración por defecto.",[203,538,540],{"id":539},"copiar-los-datos-del-primario-con-pg_basebackup","Copiar los datos del primario con pg_basebackup",[44,542,544],{"className":96,"code":543,"language":98,"meta":53,"style":53},"sudo -u postgres pg_basebackup \\\n  -h 10.0.1.10 \\\n  -U replicator \\\n  -D /var/lib/postgresql/16/main \\\n  -Fp -Xs -P -R\n",[51,545,546,560,569,579,589],{"__ignoreMap":53},[102,547,548,550,552,554,557],{"class":104,"line":105},[102,549,116],{"class":115},[102,551,222],{"class":221},[102,553,225],{"class":119},[102,555,556],{"class":119}," pg_basebackup",[102,558,559],{"class":527}," \\\n",[102,561,562,565,567],{"class":104,"line":112},[102,563,564],{"class":221},"  -h",[102,566,79],{"class":129},[102,568,559],{"class":527},[102,570,571,574,577],{"class":104,"line":157},[102,572,573],{"class":221},"  -U",[102,575,576],{"class":119}," replicator",[102,578,559],{"class":527},[102,580,581,584,587],{"class":104,"line":164},[102,582,583],{"class":221},"  -D",[102,585,586],{"class":119}," /var/lib/postgresql/16/main",[102,588,559],{"class":527},[102,590,591,594,597,600],{"class":104,"line":170},[102,592,593],{"class":221},"  -Fp",[102,595,596],{"class":221}," -Xs",[102,598,599],{"class":221}," -P",[102,601,602],{"class":221}," -R\n",[12,604,605],{},"Desglose de los parámetros:",[607,608,609,622],"table",{},[610,611,612],"thead",{},[613,614,615,619],"tr",{},[616,617,618],"th",{},"Parámetro",[616,620,621],{},"Función",[623,624,625,636,646,656,666,676,686],"tbody",{},[613,626,627,633],{},[628,629,630],"td",{},[51,631,632],{},"-h 10.0.1.10",[628,634,635],{},"IP del servidor primario",[613,637,638,643],{},[628,639,640],{},[51,641,642],{},"-U replicator",[628,644,645],{},"Usuario de replicación",[613,647,648,653],{},[628,649,650],{},[51,651,652],{},"-D ...",[628,654,655],{},"Directorio destino (datos de la réplica)",[613,657,658,663],{},[628,659,660],{},[51,661,662],{},"-Fp",[628,664,665],{},"Formato plain (archivos tal cual)",[613,667,668,673],{},[628,669,670],{},[51,671,672],{},"-Xs",[628,674,675],{},"Incluir WAL por streaming",[613,677,678,683],{},[628,679,680],{},[51,681,682],{},"-P",[628,684,685],{},"Mostrar progreso",[613,687,688,693],{},[628,689,690],{},[51,691,692],{},"-R",[628,694,695,696,699],{},"Crear automáticamente el archivo ",[51,697,698],{},"standby.signal"," y configurar la conexión",[12,701,702],{},"El proceso tomará desde segundos hasta minutos dependiendo del tamaño de tu base de datos. Verás el progreso en porcentaje.",[39,704,706],{"id":705},"paso-3-verificar-la-configuración-de-la-réplica","Paso 3: Verificar la configuración de la réplica",[12,708,709,710,712],{},"El flag ",[51,711,692],{}," de pg_basebackup creó automáticamente dos cosas:",[714,715,716,723],"ol",{},[73,717,718,722],{},[76,719,720],{},[51,721,698],{}," — archivo vacío que le dice a PostgreSQL que arranque como réplica",[73,724,725,731],{},[76,726,727,728],{},"Conexión primaria en ",[51,729,730],{},"postgresql.auto.conf"," — la cadena de conexión al primario",[12,733,734],{},"Verifica:",[44,736,738],{"className":96,"code":737,"language":98,"meta":53,"style":53},"# Debe existir el archivo standby.signal\nls -la /var/lib/postgresql/16/main/standby.signal\n\n# Debe tener la conexión al primario\ncat /var/lib/postgresql/16/main/postgresql.auto.conf\n",[51,739,740,745,756,760,765],{"__ignoreMap":53},[102,741,742],{"class":104,"line":105},[102,743,744],{"class":108},"# Debe existir el archivo standby.signal\n",[102,746,747,750,753],{"class":104,"line":112},[102,748,749],{"class":115},"ls",[102,751,752],{"class":221}," -la",[102,754,755],{"class":119}," /var/lib/postgresql/16/main/standby.signal\n",[102,757,758],{"class":104,"line":157},[102,759,161],{"emptyLinePlaceholder":160},[102,761,762],{"class":104,"line":164},[102,763,764],{"class":108},"# Debe tener la conexión al primario\n",[102,766,767,770],{"class":104,"line":170},[102,768,769],{"class":115},"cat",[102,771,772],{"class":119}," /var/lib/postgresql/16/main/postgresql.auto.conf\n",[12,774,775],{},"Deberías ver algo como:",[44,777,780],{"className":778,"code":779,"language":49},[47],"primary_conninfo = 'user=replicator password=contraseña_replica_segura host=10.0.1.10 port=5432'\n",[51,781,779],{"__ignoreMap":53},[203,783,785],{"id":784},"configurar-la-réplica-como-hot-standby","Configurar la réplica como hot standby",[12,787,788,789,792],{},"Edita ",[51,790,791],{},"postgresql.conf"," de la réplica para permitir consultas de lectura:",[44,794,795],{"className":96,"code":337,"language":98,"meta":53,"style":53},[51,796,797],{"__ignoreMap":53},[102,798,799,801,803],{"class":104,"line":105},[102,800,116],{"class":115},[102,802,346],{"class":119},[102,804,349],{"class":119},[44,806,808],{"className":355,"code":807,"language":357,"meta":53,"style":53},"hot_standby = on\n",[51,809,810],{"__ignoreMap":53},[102,811,812],{"class":104,"line":105},[102,813,807],{},[474,815],{},[39,817,819],{"id":818},"paso-4-arrancar-la-réplica","Paso 4: Arrancar la réplica",[44,821,823],{"className":96,"code":822,"language":98,"meta":53,"style":53},"sudo systemctl start postgresql\n",[51,824,825],{"__ignoreMap":53},[102,826,827,829,831,834],{"class":104,"line":105},[102,828,116],{"class":115},[102,830,466],{"class":119},[102,832,833],{"class":119}," start",[102,835,472],{"class":119},[12,837,838],{},"Verifica en los logs que la replicación está funcionando:",[44,840,842],{"className":96,"code":841,"language":98,"meta":53,"style":53},"sudo tail -20 /var/log/postgresql/postgresql-16-main.log\n",[51,843,844],{"__ignoreMap":53},[102,845,846,848,851,854],{"class":104,"line":105},[102,847,116],{"class":115},[102,849,850],{"class":119}," tail",[102,852,853],{"class":221}," -20",[102,855,856],{"class":119}," /var/log/postgresql/postgresql-16-main.log\n",[12,858,859],{},"Deberías ver mensajes como:",[44,861,864],{"className":862,"code":863,"language":49},[47],"LOG:  entering standby mode\nLOG:  redo starts at 0/3000028\nLOG:  started streaming WAL from primary at 0/4000000 on timeline 1\nLOG:  consistent recovery state reached at 0/4000110\n",[51,865,863],{"__ignoreMap":53},[39,867,869],{"id":868},"paso-5-verificar-la-replicación","Paso 5: Verificar la replicación",[203,871,873],{"id":872},"desde-el-primario","Desde el primario",[44,875,877],{"className":96,"code":876,"language":98,"meta":53,"style":53},"sudo -u postgres psql -c \"SELECT client_addr, state, sent_lsn, write_lsn, flush_lsn, replay_lsn FROM pg_stat_replication;\"\n",[51,878,879],{"__ignoreMap":53},[102,880,881,883,885,887,890,893,896,899],{"class":104,"line":105},[102,882,116],{"class":115},[102,884,222],{"class":221},[102,886,225],{"class":119},[102,888,889],{"class":119}," psql",[102,891,892],{"class":221}," -c",[102,894,895],{"class":147}," \"",[102,897,898],{"class":119},"SELECT client_addr, state, sent_lsn, write_lsn, flush_lsn, replay_lsn FROM pg_stat_replication;",[102,900,901],{"class":147},"\"\n",[12,903,904,905,93],{},"Deberías ver la IP de la réplica con estado ",[51,906,907],{},"streaming",[44,909,912],{"className":910,"code":911,"language":49},[47]," client_addr | state     | sent_lsn   | write_lsn  | flush_lsn  | replay_lsn\n-------------+-----------+------------+------------+------------+------------\n 10.0.1.11   | streaming | 0/4000110  | 0/4000110  | 0/4000110  | 0/4000110\n",[51,913,911],{"__ignoreMap":53},[12,915,916],{},"Si todos los LSN coinciden, la réplica está sincronizada — cero retraso.",[203,918,920],{"id":919},"desde-la-réplica","Desde la réplica",[44,922,924],{"className":96,"code":923,"language":98,"meta":53,"style":53},"sudo -u postgres psql -c \"SELECT pg_is_in_recovery();\"\n",[51,925,926],{"__ignoreMap":53},[102,927,928,930,932,934,936,938,940,943],{"class":104,"line":105},[102,929,116],{"class":115},[102,931,222],{"class":221},[102,933,225],{"class":119},[102,935,889],{"class":119},[102,937,892],{"class":221},[102,939,895],{"class":147},[102,941,942],{"class":119},"SELECT pg_is_in_recovery();",[102,944,901],{"class":147},[12,946,947,948,951],{},"Debe devolver ",[51,949,950],{},"t"," (true) — confirmando que es una réplica.",[203,953,955],{"id":954},"prueba-funcional","Prueba funcional",[12,957,958],{},"Crea datos en el primario y verifica que aparecen en la réplica:",[44,960,962],{"className":96,"code":961,"language":98,"meta":53,"style":53},"# En el primario\nsudo -u postgres psql -c \"CREATE TABLE test_replica (id serial, msg text, created_at timestamptz DEFAULT now());\"\nsudo -u postgres psql -c \"INSERT INTO test_replica (msg) VALUES ('Hola desde el primario');\"\n\n# En la réplica (debería aparecer en menos de 1 segundo)\nsudo -u postgres psql -c \"SELECT * FROM test_replica;\"\n",[51,963,964,969,988,1007,1011,1016],{"__ignoreMap":53},[102,965,966],{"class":104,"line":105},[102,967,968],{"class":108},"# En el primario\n",[102,970,971,973,975,977,979,981,983,986],{"class":104,"line":112},[102,972,116],{"class":115},[102,974,222],{"class":221},[102,976,225],{"class":119},[102,978,889],{"class":119},[102,980,892],{"class":221},[102,982,895],{"class":147},[102,984,985],{"class":119},"CREATE TABLE test_replica (id serial, msg text, created_at timestamptz DEFAULT now());",[102,987,901],{"class":147},[102,989,990,992,994,996,998,1000,1002,1005],{"class":104,"line":157},[102,991,116],{"class":115},[102,993,222],{"class":221},[102,995,225],{"class":119},[102,997,889],{"class":119},[102,999,892],{"class":221},[102,1001,895],{"class":147},[102,1003,1004],{"class":119},"INSERT INTO test_replica (msg) VALUES ('Hola desde el primario');",[102,1006,901],{"class":147},[102,1008,1009],{"class":104,"line":164},[102,1010,161],{"emptyLinePlaceholder":160},[102,1012,1013],{"class":104,"line":170},[102,1014,1015],{"class":108},"# En la réplica (debería aparecer en menos de 1 segundo)\n",[102,1017,1018,1020,1022,1024,1026,1028,1030,1033],{"class":104,"line":386},[102,1019,116],{"class":115},[102,1021,222],{"class":221},[102,1023,225],{"class":119},[102,1025,889],{"class":119},[102,1027,892],{"class":221},[102,1029,895],{"class":147},[102,1031,1032],{"class":119},"SELECT * FROM test_replica;",[102,1034,901],{"class":147},[44,1036,1038],{"className":96,"code":1037,"language":98,"meta":53,"style":53},"# Verificar que la réplica no acepta escrituras\nsudo -u postgres psql -h 10.0.1.11 -c \"INSERT INTO test_replica (msg) VALUES ('Intento escribir');\"\n# ERROR: cannot execute INSERT in a read-only transaction\n",[51,1039,1040,1045,1069],{"__ignoreMap":53},[102,1041,1042],{"class":104,"line":105},[102,1043,1044],{"class":108},"# Verificar que la réplica no acepta escrituras\n",[102,1046,1047,1049,1051,1053,1055,1058,1060,1062,1064,1067],{"class":104,"line":112},[102,1048,116],{"class":115},[102,1050,222],{"class":221},[102,1052,225],{"class":119},[102,1054,889],{"class":119},[102,1056,1057],{"class":221}," -h",[102,1059,85],{"class":129},[102,1061,892],{"class":221},[102,1063,895],{"class":147},[102,1065,1066],{"class":119},"INSERT INTO test_replica (msg) VALUES ('Intento escribir');",[102,1068,901],{"class":147},[102,1070,1071],{"class":104,"line":157},[102,1072,1073],{"class":108},"# ERROR: cannot execute INSERT in a read-only transaction\n",[474,1075],{},[39,1077,1079],{"id":1078},"paso-6-monitorear-el-retraso-de-replicación","Paso 6: Monitorear el retraso de replicación",[12,1081,1082],{},"El retraso de replicación (replication lag) es la métrica más importante. Si crece, significa que la réplica se está quedando atrás.",[203,1084,873],{"id":1085},"desde-el-primario-1",[44,1087,1089],{"className":231,"code":1088,"language":233,"meta":53,"style":53},"SELECT\n  client_addr,\n  state,\n  pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS lag_bytes,\n  pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn)) AS lag_pretty\nFROM pg_stat_replication;\n",[51,1090,1091,1096,1101,1109,1127,1142],{"__ignoreMap":53},[102,1092,1093],{"class":104,"line":105},[102,1094,1095],{"class":240},"SELECT\n",[102,1097,1098],{"class":104,"line":112},[102,1099,1100],{"class":247},"  client_addr,\n",[102,1102,1103,1106],{"class":104,"line":157},[102,1104,1105],{"class":240},"  state",[102,1107,1108],{"class":247},",\n",[102,1110,1111,1114,1118,1121,1124],{"class":104,"line":164},[102,1112,1113],{"class":247},"  pg_wal_lsn_diff(pg_current_wal_lsn",[102,1115,1117],{"class":1116},"sP7_E","()",[102,1119,1120],{"class":247},", replay_lsn) ",[102,1122,1123],{"class":240},"AS",[102,1125,1126],{"class":247}," lag_bytes,\n",[102,1128,1129,1132,1134,1137,1139],{"class":104,"line":170},[102,1130,1131],{"class":247},"  pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn",[102,1133,1117],{"class":1116},[102,1135,1136],{"class":247},", replay_lsn)) ",[102,1138,1123],{"class":240},[102,1140,1141],{"class":247}," lag_pretty\n",[102,1143,1144,1147],{"class":104,"line":386},[102,1145,1146],{"class":240},"FROM",[102,1148,1149],{"class":247}," pg_stat_replication;\n",[203,1151,920],{"id":1152},"desde-la-réplica-1",[44,1154,1156],{"className":231,"code":1155,"language":233,"meta":53,"style":53},"SELECT\n  now() - pg_last_xact_replay_timestamp() AS lag_time,\n  pg_is_in_recovery() AS is_replica;\n",[51,1157,1158,1162,1183],{"__ignoreMap":53},[102,1159,1160],{"class":104,"line":105},[102,1161,1095],{"class":240},[102,1163,1164,1167,1169,1172,1175,1177,1180],{"class":104,"line":112},[102,1165,1166],{"class":240},"  now",[102,1168,1117],{"class":1116},[102,1170,1171],{"class":299}," -",[102,1173,1174],{"class":247}," pg_last_xact_replay_timestamp",[102,1176,1117],{"class":1116},[102,1178,1179],{"class":240}," AS",[102,1181,1182],{"class":247}," lag_time,\n",[102,1184,1185,1188,1190,1192],{"class":104,"line":157},[102,1186,1187],{"class":247},"  pg_is_in_recovery",[102,1189,1117],{"class":1116},[102,1191,1179],{"class":240},[102,1193,1194],{"class":247}," is_replica;\n",[12,1196,1197,1198,1202],{},"Para monitoreo continuo, configura ",[16,1199,1201],{"href":1200},"/blog/monitoreo-grafana-prometheus","Prometheus con postgres_exporter"," y crea una alerta cuando el lag supere un umbral (por ejemplo, 30 segundos para replicación asíncrona).",[39,1204,1206],{"id":1205},"paso-7-promover-la-réplica-a-primario-failover-manual","Paso 7: Promover la réplica a primario (failover manual)",[12,1208,1209],{},"Si el primario falla y necesitas que la réplica tome el control:",[44,1211,1213],{"className":96,"code":1212,"language":98,"meta":53,"style":53},"# En la réplica\nsudo -u postgres pg_ctlcluster 16 main promote\n",[51,1214,1215,1220],{"__ignoreMap":53},[102,1216,1217],{"class":104,"line":105},[102,1218,1219],{"class":108},"# En la réplica\n",[102,1221,1222,1224,1226,1228,1231,1234,1237],{"class":104,"line":112},[102,1223,116],{"class":115},[102,1225,222],{"class":221},[102,1227,225],{"class":119},[102,1229,1230],{"class":119}," pg_ctlcluster",[102,1232,1233],{"class":129}," 16",[102,1235,1236],{"class":119}," main",[102,1238,1239],{"class":119}," promote\n",[12,1241,1242],{},"O con pg_ctl:",[44,1244,1246],{"className":96,"code":1245,"language":98,"meta":53,"style":53},"sudo -u postgres pg_ctl promote -D /var/lib/postgresql/16/main\n",[51,1247,1248],{"__ignoreMap":53},[102,1249,1250,1252,1254,1256,1259,1262,1265],{"class":104,"line":105},[102,1251,116],{"class":115},[102,1253,222],{"class":221},[102,1255,225],{"class":119},[102,1257,1258],{"class":119}," pg_ctl",[102,1260,1261],{"class":119}," promote",[102,1263,1264],{"class":221}," -D",[102,1266,1267],{"class":119}," /var/lib/postgresql/16/main\n",[12,1269,1270],{},"Después de la promoción:",[714,1272,1273,1278,1281],{},[73,1274,1275,1276],{},"La réplica elimina el archivo ",[51,1277,698],{},[73,1279,1280],{},"Acepta escrituras",[73,1282,1283],{},"Se convierte en el nuevo primario",[530,1285,1287],{"title":1286,"type":533},"El failover manual tiene riesgo",[12,1288,1289],{},"Promover la réplica manualmente es un proceso de emergencia. Si el primario original sigue vivo, puedes terminar con dos servidores que aceptan escrituras (split-brain). Para failover automático y seguro, usa Patroni — el estándar de la industria para alta disponibilidad de PostgreSQL.",[39,1291,1293],{"id":1292},"replicación-síncrona-opcional","Replicación síncrona (opcional)",[12,1295,1296],{},"Si no puedes perder ni una sola transacción (finanzas, facturación), puedes cambiar a replicación síncrona. En el primario:",[44,1298,1300],{"className":355,"code":1299,"language":357,"meta":53,"style":53},"# postgresql.conf del primario\nsynchronous_commit = on\nsynchronous_standby_names = 'replica1'\n",[51,1301,1302,1307,1312],{"__ignoreMap":53},[102,1303,1304],{"class":104,"line":105},[102,1305,1306],{},"# postgresql.conf del primario\n",[102,1308,1309],{"class":104,"line":112},[102,1310,1311],{},"synchronous_commit = on\n",[102,1313,1314],{"class":104,"line":157},[102,1315,1316],{},"synchronous_standby_names = 'replica1'\n",[12,1318,1319,1320,1323,1324,93],{},"En la réplica, configura su ",[51,1321,1322],{},"application_name"," en el ",[51,1325,1326],{},"primary_conninfo",[44,1328,1330],{"className":355,"code":1329,"language":357,"meta":53,"style":53},"primary_conninfo = '... application_name=replica1'\n",[51,1331,1332],{"__ignoreMap":53},[102,1333,1334],{"class":104,"line":105},[102,1335,1329],{},[12,1337,1338],{},"Con replicación síncrona, el primario no confirma una escritura al cliente hasta que la réplica la haya recibido. Esto garantiza cero pérdida de datos pero agrega latencia a cada escritura — úsala solo cuando la integridad de datos es más importante que la velocidad.",[39,1340,1342],{"id":1341},"siguientes-pasos","Siguientes pasos",[12,1344,1345],{},"Con la replicación funcionando, el siguiente nivel es:",[70,1347,1348,1354,1360,1366,1376],{},[73,1349,1350,1353],{},[76,1351,1352],{},"Patroni"," — failover automático con consenso distribuido (etcd/ZooKeeper) para que la réplica se promueva sola si el primario falla",[73,1355,1356,1359],{},[76,1357,1358],{},"PgBouncer"," — connection pooling para manejar cientos de conexiones sin saturar PostgreSQL",[73,1361,1362,1365],{},[76,1363,1364],{},"pgBackRest"," — backups incrementales con verificación de integridad y point-in-time recovery",[73,1367,1368,1371,1372],{},[76,1369,1370],{},"Múltiples réplicas"," — una local para HA, otra remota para DR, otra para ",[16,1373,1375],{"href":1374},"/soluciones/bi","BI",[73,1377,1378,1384],{},[76,1379,1380],{},[16,1381,1383],{"href":1382},"/servicios/bases-de-datos","Bases de datos administradas"," — nuestro servicio DBA as a Service con monitoreo, mantenimiento y soporte 24/7",[1386,1387],"call-to-action",{"description":1388,"eyebrow":1389,"icon":1390,"label":1391,"title":1392,"to":1393},"Implementamos replicación con failover automático, monitoreo de lag y pruebas de failover periódicas para tu PostgreSQL de producción.","Alta disponibilidad","i-lucide-database","Solicitar evaluación","¿Necesitas que tu base de datos nunca se detenga?","/contacto",[1395,1396,1397],"style",{},"html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sw1J6, html code.shiki .sw1J6{--shiki-light:#F76D47;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":53,"searchDepth":112,"depth":157,"links":1399},[1400,1401,1402,1407,1412,1415,1416,1421,1425,1426,1427],{"id":41,"depth":112,"text":42},{"id":59,"depth":112,"text":60},{"id":200,"depth":112,"text":201,"children":1403},[1404,1405,1406],{"id":205,"depth":157,"text":206},{"id":274,"depth":157,"text":275},{"id":330,"depth":157,"text":331},{"id":478,"depth":112,"text":479,"children":1408},[1409,1410,1411],{"id":482,"depth":157,"text":483},{"id":502,"depth":157,"text":503},{"id":539,"depth":157,"text":540},{"id":705,"depth":112,"text":706,"children":1413},[1414],{"id":784,"depth":157,"text":785},{"id":818,"depth":112,"text":819},{"id":868,"depth":112,"text":869,"children":1417},[1418,1419,1420],{"id":872,"depth":157,"text":873},{"id":919,"depth":157,"text":920},{"id":954,"depth":157,"text":955},{"id":1078,"depth":112,"text":1079,"children":1422},[1423,1424],{"id":1085,"depth":157,"text":873},{"id":1152,"depth":157,"text":920},{"id":1205,"depth":112,"text":1206},{"id":1292,"depth":112,"text":1293},{"id":1341,"depth":112,"text":1342},"tutorial",{"title":1430,"description":1431,"label":1391,"to":1393,"icon":1390},"¿Necesitas alta disponibilidad para tu base de datos?","Implementamos replicación, failover automático y monitoreo de PostgreSQL para que tus datos nunca se detengan.","2026-01-10","Guía completa para configurar replicación streaming en PostgreSQL con un servidor primario y una réplica de solo lectura para alta disponibilidad y distribución de carga.",false,"md",[1437,1440,1443,1446],{"question":1438,"answer":1439},"¿Cuál es la diferencia entre replicación síncrona y asíncrona?","En replicación asíncrona, el primario confirma la escritura al cliente sin esperar a que la réplica la reciba — es más rápido pero hay un pequeño riesgo de perder transacciones recientes si el primario falla antes de replicar. En replicación síncrona, el primario espera la confirmación de la réplica antes de responder al cliente — garantiza cero pérdida de datos pero agrega latencia a cada escritura.",{"question":1441,"answer":1442},"¿La réplica puede atender consultas mientras replica?","Sí. Por defecto las réplicas de PostgreSQL son 'hot standby' — aceptan consultas de solo lectura mientras reciben y aplican las escrituras del primario. Esto permite distribuir lecturas pesadas (reportes, dashboards de BI) a la réplica sin afectar el rendimiento del primario.",{"question":1444,"answer":1445},"¿Qué pasa si el servidor primario falla?","La réplica tiene una copia completa y actualizada de los datos. Puedes promoverla a primario manualmente con pg_ctl promote o automáticamente con herramientas como Patroni. Después de la promoción, la réplica acepta escrituras y se convierte en el nuevo primario.",{"question":1447,"answer":1448},"¿Puedo tener más de una réplica?","Sí. PostgreSQL soporta múltiples réplicas conectadas al mismo primario. Puedes tener una réplica local para alta disponibilidad, otra remota para disaster recovery y otra dedicada a consultas de BI — todas recibiendo datos del mismo primario.","/images/blog/postgresql-replicacion.jpg","Diagrama de replicación PostgreSQL mostrando servidor primario sincronizando datos con réplica de solo lectura",{},"/blog/tutorial/replicacion-postgresql-alta-disponibilidad",{"title":5,"description":1433},"blog/tutorial/replicacion-postgresql-alta-disponibilidad",[1456,1457,1458,1459,1460,1428],"postgresql","replicacion","alta-disponibilidad","bases-de-datos","linux","DsvL7uD5ex4brSaOdbwESgKt9_C0fJ7ggqI35dB7Cv0",{"path":1463,"title":1464},"/blog/tutorial/respaldos-incrementales-rsync-bash","Respaldos incrementales con rsync y scripts Bash",{"path":1466,"title":1467},"/blog/tutorial/instalar-postgresql-en-ubuntu-24","Cómo instalar PostgreSQL en Ubuntu 24.04",[1469,1477,1484],{"path":1470,"title":1471,"description":1472,"date":1473,"category":1428,"image":1474,"imageAlt":1475,"readingTime":1476},"/blog/tutorial/apis-rest-python-fastapi","Crear APIs REST con Python y FastAPI para integraciones empresariales","Guía paso a paso para construir una API REST profesional con Python y FastAPI que conecte tu ERP, CRM o cualquier sistema con validación, autenticación y documentación automática.","2026-03-04","/images/blog/fastapi-api-rest.jpg","Editor de código mostrando una API FastAPI con documentación Swagger generada automáticamente",12,{"path":1478,"title":1479,"description":1480,"date":1481,"category":1428,"image":1482,"imageAlt":1483,"readingTime":398},"/blog/tutorial/configurar-firewall-ufw-linux","Configurar firewall en Linux con UFW — reglas esenciales","Guía paso a paso para configurar UFW (Uncomplicated Firewall) en Ubuntu y Debian con las reglas esenciales para proteger servidores de producción.","2026-03-01","/images/blog/ufw-firewall-linux.jpg","Terminal de Linux mostrando reglas de firewall UFW activas protegiendo un servidor de producción",{"path":1485,"title":1486,"description":1487,"date":1488,"category":1428,"image":1489,"imageAlt":1490,"readingTime":409},"/blog/tutorial/traefik-reverse-proxy-docker","Configurar Traefik como reverse proxy para contenedores Docker","Guía paso a paso para instalar Traefik como reverse proxy con descubrimiento automático de contenedores Docker, SSL con Let's Encrypt y dashboard de monitoreo.","2026-02-28","/images/blog/traefik-docker.jpg","Dashboard de Traefik mostrando rutas automáticas hacia múltiples contenedores Docker con SSL activo"]