DB_CLUSTER_NAME: the name of this database cluster—for
example, subscriber-cluster.
ENCODED_PASSWORD: the database login password for the
default postgres user role, encoded as a base64 string—for example,
Q2hhbmdlTWUxMjM= for ChangeMe123.
CPU_COUNT: the number of CPUs available to each
database instance in this database cluster.
MEMORY_SIZE: the amount of memory per database instance of this
database cluster. We recommend setting this to 8 gigabytes per CPU. For
example, if you set cpu to 2 earlier in this manifest, then we recommend
setting memory to 16Gi.
DISK_SIZE: the disk size per database instance—for example, 10Gi.
Find the pod that you need.
$ kubectl get pod -l "alloydbomni.internal.dbadmin.goog/dbcluster=DB_CLUSTER_NAME, alloydbomni.internal.dbadmin.goog/task-type=database, dbs.internal.dbadmin.goog/ha-role=Primary"
Log into the subscriber cluster database pod.
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
al-2bce-publisher-0 3/3 Running 0 20h
$ kubectl exec -ti SUBSCRIBER_POD_NAME -- /bin/bash
Defaulted container "database" out of: database, logrotate-agent, memoryagent, dbinit (init)
postgres@al-3513-subscriber-0:/$
Replace SUBSCRIBER_POD_NAME with the name of the subscriber pod.
Find the IP address of the load balancer on publisher DBcluster, like 10.116.14.190
$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
al-publisher-rw-ilb ClusterIP 10.116.14.190 <none> 5432/TCP 21h
Take a schema backup from the publisher as an initial copy of the
published data in the publisher database. Logical replication does not
support DDL replication.
A schema or table that you plan to replicate must exist in the
destination (subscriber cluster) before logical replication starts.
Replace the TABLE_NAME with the name of the table in the publisher DBCluster that the subscriber is subscribed to.
On the subscriber cluster, verify that the row added to the table in
the publisher cluster has been replicated to table in the subscriber cluster.
# On the subscriber database, data is synced.
postgres@al-3513-subscriber-0:/$ psql -h localhost -U postgres DATABASE_NAME
customer=# select * from TABLE_NAME;
id | name | age | salary
----+-------+-----+--------
1 | Quinn | 25 | 65000
2 | Kim | 22 | 72250
3 | Bola | 31 | 53000
4 | Sasha | 33 | 105000
5 | Yuri | 27 | 85000
6 | Alex | 39 | 100000
(6 rows)
Manually create additional tables
Logical replication doesn't automatically synchronize DDL changes, unlike the
replicate_ddl_command in pglogical. While the open-source tool
pgl_ddl_deploy
offers a solution, you can also execute DDL commands manually on the
subscriber.
To illustrate this, create a new table called finance in the customer
database on the publisher cluster.
# On the publisher database
$ kubectl exec -ti al-2bce-publisher-0 -- /bin/bash
Defaulted container "database" out of: database, logrotate-agent, memoryagent, dbinit (init)
postgres@al-2bce-publisher-0:/$ psql -h localhost -U postgres customer
customer=# create table finance (row text);
CREATE TABLE
customer=# insert into finance values ('critical data');
INSERT 0 1
customer=# ALTER PUBLICATION pub_customer ADD TABLE finance;
ALTER PUBLICATION
When a new table is added to the publisher cluster, you manually apply the DDL
(table creation) in the subscriber, and then verify replication by running the
following on the subscriber cluster.
postgres@al-3513-subscriber-0:/$ psql -h localhost -U postgres customer
customer=# create table finance (row text);
CREATE TABLE
customer=# ALTER SUBSCRIPTION sub_customer REFRESH PUBLICATION;
ALTER SUBSCRIPTION
customer=# select * from finance;
row
---------------
critical data
(1 row)
[[["Easy to understand","easyToUnderstand","thumb-up"],["Solved my problem","solvedMyProblem","thumb-up"],["Other","otherUp","thumb-up"]],[["Hard to understand","hardToUnderstand","thumb-down"],["Incorrect information or sample code","incorrectInformationOrSampleCode","thumb-down"],["Missing the information/samples I need","missingTheInformationSamplesINeed","thumb-down"],["Other","otherDown","thumb-down"]],["Last updated 2025-08-28 UTC."],[],[],null,["# Configure subscriber cluster for logical replication\n\nSelect a documentation version: 16.8.0keyboard_arrow_down\n\n- [Current (16.8.0)](/alloydb/omni/current/docs/logical-replication)\n- [16.8.0](/alloydb/omni/16.8.0/docs/logical-replication)\n- [16.3.0](/alloydb/omni/16.3.0/docs/logical-replication)\n- [15.12.0](/alloydb/omni/15.12.0/docs/logical-replication)\n- [15.7.1](/alloydb/omni/15.7.1/docs/logical-replication)\n- [15.7.0](/alloydb/omni/15.7.0/docs/logical-replication)\n- [15.5.5](/alloydb/omni/15.5.5/docs/logical-replication)\n- [15.5.4](/alloydb/omni/15.5.4/docs/logical-replication)\n- [15.5.2](/alloydb/omni/15.5.2/docs/logical-replication)\n\n\u003cbr /\u003e\n\nThis document provides examples that show you how to manually create and configure a *subscriber cluster* . A subscriber cluster is a database cluster that receives data replicated from a publisher cluster.\n\n\u003cbr /\u003e\n\nThe code snippets on this page are examples that you can use as models,\nreplacing the values with values for your AlloyDB Omni\nresources.\n\nBefore you begin\n----------------\n\n- [Install Omni Operator on Kubernetes](/alloydb/omni/16.8.0/docs/deploy-kubernetes).\n- Ensure that you create a replication slot, publisher cluster, and publication. For more information, see [Create a replication slot and publication](/alloydb/omni/16.8.0/docs/create-replication-slots).\n\nCreate and configure the subscriber cluster\n-------------------------------------------\n\n1. Create a subscriber cluster.\n\n $ cat \u003c\u003c EOF | kubectl apply -f -\n apiVersion: v1\n kind: Secret\n metadata:\n name: db-pw-\u003cvar translate=\"no\"\u003eDB_CLUSTER_NAME\u003c/var\u003e\n type: Opaque\n data:\n \u003cvar translate=\"no\"\u003eDB_CLUSTER_NAME\u003c/var\u003e: \"\u003cvar translate=\"no\"\u003eENCODED_PASSWORD\u003c/var\u003e\" # Password is odspassword\n ---\n apiVersion: alloydbomni.dbadmin.goog/v1\n kind: DBCluster\n metadata:\n name: subscriber\n spec:\n databaseVersion: \"16.8.0\"\n primarySpec:\n adminUser:\n passwordRef:\n name: db-pw-\u003cvar translate=\"no\"\u003eDB_CLUSTER_NAME\u003c/var\u003e\n resources:\n memory: \u003cvar translate=\"no\"\u003eMEMORY_SIZE\u003c/var\u003e\n cpu: \u003cvar translate=\"no\"\u003eCPU_COUNT\u003c/var\u003e\n disks:\n - name: DataDisk\n size: \u003cvar translate=\"no\"\u003eDISK_SIZE\u003c/var\u003e\n EOF\n\n Replace the following:\n - \u003cvar translate=\"no\"\u003eDB_CLUSTER_NAME\u003c/var\u003e: the name of this database cluster---for example, `subscriber-cluster`.\n - \u003cvar translate=\"no\"\u003eENCODED_PASSWORD\u003c/var\u003e: the database login password for the\n default `postgres` user role, encoded as a base64 string---for example,\n `Q2hhbmdlTWUxMjM=` for `ChangeMe123`.\n\n - \u003cvar translate=\"no\"\u003eCPU_COUNT\u003c/var\u003e: the number of CPUs available to each\n database instance in this database cluster.\n\n - \u003cvar translate=\"no\"\u003eMEMORY_SIZE\u003c/var\u003e: the amount of memory per database instance of this\n database cluster. We recommend setting this to 8 gigabytes per CPU. For\n example, if you set `cpu` to `2` earlier in this manifest, then we recommend\n setting `memory` to `16Gi`.\n\n - \u003cvar translate=\"no\"\u003eDISK_SIZE\u003c/var\u003e: the disk size per database instance---for example, `10Gi`.\n\n2. Find the pod that you need.\n\n $ kubectl get pod -l \"alloydbomni.internal.dbadmin.goog/dbcluster=\u003cvar translate=\"no\"\u003eDB_CLUSTER_NAME\u003c/var\u003e, alloydbomni.internal.dbadmin.goog/task-type=database, dbs.internal.dbadmin.goog/ha-role=Primary\"\n\n3. Log into the subscriber cluster database pod.\n\n $ kubectl get pod\n NAME READY STATUS RESTARTS AGE\n al-2bce-publisher-0 3/3 Running 0 20h\n\n $ kubectl exec -ti \u003cvar translate=\"no\"\u003eSUBSCRIBER_POD_NAME\u003c/var\u003e -- /bin/bash\n Defaulted container \"database\" out of: database, logrotate-agent, memoryagent, dbinit (init)\n postgres@al-3513-subscriber-0:/$\n\n Replace \u003cvar translate=\"no\"\u003eSUBSCRIBER_POD_NAME\u003c/var\u003e with the name of the subscriber pod.\n4. Find the IP address of the load balancer on publisher DBcluster, like `10.116.14.190`\n\n $ kubectl get service\n NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE\n al-publisher-rw-ilb ClusterIP 10.116.14.190 \u003cnone\u003e 5432/TCP 21h\n\n5. Take a schema backup from the publisher as an initial copy of the\n published data in the publisher database. Logical replication does not\n support DDL replication.\n A schema or table that you plan to replicate must exist in the\n destination (subscriber cluster) before logical replication starts.\n\n postgres@al-3513-subscriber-0:/$ pg_dump -h \u003cvar translate=\"no\"\u003ePUBLISHER_IP_ADDRESS\u003c/var\u003e -U postgres --create --schema-only customer \u003e /tmp/customer.schema-only.sql\n\n Replace \u003cvar translate=\"no\"\u003ePUBLISHER_IP_ADDRESS\u003c/var\u003e with the IP address of the load balancer on publisher DBcluster.\n6. Apply the backup in the subscriber database.\n\n postgres@al-3513-subscriber-0:/$ psql -h localhost -U postgres \u003c /tmp/customer.schema-only.sql\n\n7. Optional: Verify that no data is in the table.\n\n # There is no data in table company\n customer=# select * from company;\n id | name | age | salary\n ----+------+-----+--------\n (0 rows)\n\n8. Create a subscription for the database. Ensure that the publication is already created on the publisher DBCluster.\n\n postgres@al-3513-subscriber-0:/$ CREATE SUBSCRIPTION sub_customer CONNECTION 'host=\u003cvar translate=\"no\"\u003ePUBLISHER_IP_ADDRESS\u003c/var\u003e port=5432 user=\u003cvar translate=\"no\"\u003eREPLICATION_USER\u003c/var\u003e dbname=\u003cvar translate=\"no\"\u003eDATABASE_NAME\u003c/var\u003e password=\u003cvar translate=\"no\"\u003ePUBLISHER_CLUSTER_PASSWORD\u003c/var\u003e sslmode=require' PUBLICATION \u003cvar translate=\"no\"\u003ePUBLICATION_NAME\u003c/var\u003e WITH (slot_name='\u003cvar translate=\"no\"\u003eREPLICATION_SLOT_NAME\u003c/var\u003e');\n\n Replace the following:\n - \u003cvar translate=\"no\"\u003eREPLICATION_USER\u003c/var\u003e: the name of the user that connects to the replication slot.\n - \u003cvar translate=\"no\"\u003eDATABASE_NAME\u003c/var\u003e: set to the name of the database whose changes you want to stream from the replication slot.\n - \u003cvar translate=\"no\"\u003ePUBLISHER_CLUSTER_PASSWORD\u003c/var\u003e: the database login password for the `postgres` user of publisher DBCluster.\n - \u003cvar translate=\"no\"\u003ePUBLICATION_NAME\u003c/var\u003e: the publication name that subscriber subscribes to.\n - \u003cvar translate=\"no\"\u003eREPLICATION_SLOT_NAME\u003c/var\u003e: the name of the replication slot created on publisher DBCluster.\n9. Optional: Verify replication on the subscriber cluster.\n\n postgres@al-3513-subscriber-0:/$ psql -h localhost -U postgres \u003cvar translate=\"no\"\u003eDATABASE_NAME\u003c/var\u003e\n customer=# select * from public.company;\n id | name | age | salary\n ----+-------+-----+--------\n 1 | Quinn | 25 | 65000\n 2 | Kim | 22 | 72250\n 3 | Bola | 31 | 53000\n 4 | Sasha | 33 | 105000\n 5 | Yuri | 27 | 85000\n (5 rows)\n\n10. On the publisher cluster, add a row to the table.\n\n # On the publisher database\n $ kubectl exec -ti al-2bce-publisher-0 -- /bin/bash\n Defaulted container \"database\" out of: database, logrotate-agent, memoryagent, dbinit (init)\n postgres@al-2bce-publisher-0:/$ psql -h localhost -U postgres \u003cvar translate=\"no\"\u003eDATABASE_NAME\u003c/var\u003e\n customer=# insert into \u003cvar translate=\"no\"\u003eTABLE_NAME\u003c/var\u003e (id, name, age, salary) values (6, 'Alex', 39, 100000);\n\n Replace the \u003cvar translate=\"no\"\u003eTABLE_NAME\u003c/var\u003e with the name of the table in the publisher DBCluster that the subscriber is subscribed to.\n11. On the subscriber cluster, verify that the row added to the table in\n the publisher cluster has been replicated to table in the subscriber cluster.\n\n # On the subscriber database, data is synced.\n postgres@al-3513-subscriber-0:/$ psql -h localhost -U postgres \u003cvar translate=\"no\"\u003eDATABASE_NAME\u003c/var\u003e\n customer=# select * from \u003cvar translate=\"no\"\u003eTABLE_NAME\u003c/var\u003e;\n id | name | age | salary\n ----+-------+-----+--------\n 1 | Quinn | 25 | 65000\n 2 | Kim | 22 | 72250\n 3 | Bola | 31 | 53000\n 4 | Sasha | 33 | 105000\n 5 | Yuri | 27 | 85000\n 6 | Alex | 39 | 100000\n (6 rows)\n\nManually create additional tables\n---------------------------------\n\nLogical replication doesn't automatically synchronize DDL changes, unlike the\n`replicate_ddl_command` in `pglogical`. While the open-source tool\n[`pgl_ddl_deploy`](https://github.com/enova/pgl_ddl_deploy)\noffers a solution, you can also execute DDL commands manually on the\nsubscriber.\n\n1. To illustrate this, create a new table called `finance` in the `customer`\n database on the publisher cluster.\n\n # On the publisher database\n $ kubectl exec -ti al-2bce-publisher-0 -- /bin/bash\n Defaulted container \"database\" out of: database, logrotate-agent, memoryagent, dbinit (init)\n postgres@al-2bce-publisher-0:/$ psql -h localhost -U postgres customer\n customer=# create table finance (row text);\n CREATE TABLE\n customer=# insert into finance values ('critical data');\n INSERT 0 1\n customer=# ALTER PUBLICATION pub_customer ADD TABLE finance;\n ALTER PUBLICATION\n\n2. When a new table is added to the publisher cluster, you manually apply the DDL\n (table creation) in the subscriber, and then verify replication by running the\n following on the subscriber cluster.\n\n postgres@al-3513-subscriber-0:/$ psql -h localhost -U postgres customer\n customer=# create table finance (row text);\n CREATE TABLE\n customer=# ALTER SUBSCRIPTION sub_customer REFRESH PUBLICATION;\n ALTER SUBSCRIPTION\n customer=# select * from finance;\n row\n ---------------\n critical data\n (1 row)\n\nWhat's next\n-----------\n\n- [Manage high availability (HA) in Kubernetes](/alloydb/omni/16.8.0/docs/kubernetes-ha)"]]