Oracle Data Guard Configuration on Oracle Linux 7.8 & Database 19c

Oracle Data Guard Configuration on Oracle Linux 7.8 & Database 19c
Advertisement

Introduction to Oracle Data Guard

Oracle Data Guard provides a comprehensive set of services that create, maintain, manage, and monitor one or more standby databases to enable production Oracle databases to survive disasters and data corruptions. It maintains these standby databases as copies of the production database, and if the production database becomes unavailable, Data Guard can switch any standby database to the production role — minimizing downtime and preventing data loss.

In this guide, we'll configure a Physical Standby Database using RMAN Active Duplication. The primary database ships redo data to the standby, which continuously applies it to stay synchronized. Here's the high-level architecture:

[ PRIMARY DB — dg1 ] ──── Redo Transport ────▶ [ Redo Logs ] ──── Log Apply ────▶ [ STANDBY DB — dg2 ] oradb oradb_stby 192.168.0.2 192.168.0.7

Environment Overview & Prerequisites

ComponentPrimary (dg1)Standby (dg2)
Hostnamedg1.bracecoderdg2.bracecoder
IP Address192.168.0.2192.168.0.7
OSOracle Linux 7.8Oracle Linux 7.8
Database Version19c EE (19.3.0.0)19c EE (19.3.0.0)
DB Nameoradboradb
DB Unique Nameoradboradb_stby
Oracle Home/u01/app/oracle/product/19.0.0/db_1/u01/app/oracle/product/19.0.0/db_1
StorageASM (+DATA, +FRA)ASM (+DATA, +FRA)
CDB?NO (Non-CDB)NO (Non-CDB)

Prerequisites before you begin:

  • Oracle Database 19c Enterprise Edition installed on both servers (software only on standby)
  • Oracle Restart (Grid Infrastructure) configured with ASM on both servers
  • Network connectivity between dg1 and dg2 (ping and SSH working)
  • Sufficient disk space in ASM disk groups on both servers
  • The primary database must be in ARCHIVELOG mode

Let's first verify the infrastructure is ready on both servers.

Verify Oracle Restart Resources — Primary (dg1)

Run crsctl stat res -t as the grid user to confirm Oracle Restart services are running. We need ASM, the Listener, the disk groups, and the database to be ONLINE.

[grid@dg1 ~]$ crsctl stat res -t
--------------------------------------------------------------------------------
Name           Target  State        Server                   State details
--------------------------------------------------------------------------------
Local Resources
--------------------------------------------------------------------------------
ora.DATA.dg
               ONLINE  ONLINE       dg1                      STABLE
ora.FRA.dg
               ONLINE  ONLINE       dg1                      STABLE
ora.LISTENER.lsnr
               ONLINE  ONLINE       dg1                      STABLE
ora.asm
               ONLINE  ONLINE       dg1                      Started,STABLE
ora.ons
               OFFLINE OFFLINE      dg1                      STABLE
--------------------------------------------------------------------------------
Cluster Resources
--------------------------------------------------------------------------------
ora.cssd
      1        ONLINE  ONLINE       dg1                      STABLE
ora.diskmon
      1        OFFLINE OFFLINE                               STABLE
ora.evmd
      1        ONLINE  ONLINE       dg1                      STABLE
ora.oradb.db
      1        ONLINE  ONLINE       dg1                      Open,HOME=/u01/app/o
                                                             racle/product/19.0.0
                                                             /db_1,STABLE
--------------------------------------------------------------------------------shell
✔ Verified
dg1 has ASM (+DATA, +FRA), Listener, and the database oradb all ONLINE and STABLE.

Verify Oracle Restart Resources — Standby (dg2)

On the standby server, we need ASM, the Listener, and disk groups ready. There's no database here yet — we'll create the standby later.

[grid@dg2 ~]$ crsctl stat res -t
--------------------------------------------------------------------------------
Name           Target  State        Server                   State details
--------------------------------------------------------------------------------
Local Resources
--------------------------------------------------------------------------------
ora.DATA.dg
               ONLINE  ONLINE       dg2                      STABLE
ora.FRA.dg
               ONLINE  ONLINE       dg2                      STABLE
ora.LISTENER.lsnr
               ONLINE  ONLINE       dg2                      STABLE
ora.asm
               ONLINE  ONLINE       dg2                      Started,STABLE
ora.ons
               OFFLINE OFFLINE      dg2                      STABLE
--------------------------------------------------------------------------------
Cluster Resources
--------------------------------------------------------------------------------
ora.cssd
      1        ONLINE  ONLINE       dg2                      STABLE
ora.diskmon
      1        OFFLINE OFFLINE                               STABLE
ora.evmd
      1        ONLINE  ONLINE       dg2                      STABLE
--------------------------------------------------------------------------------shell

Verify /etc/hosts on Both Servers

Both servers must resolve each other's hostnames. This is critical for TNS connectivity and RMAN duplication to work.

[grid@dg1 ~]$ cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

192.168.0.2 dg1.bracecoder dg1
192.168.0.7 dg2.bracecoder dg2shell
ℹ Note
The /etc/hosts file must be identical on both servers so that hostname resolution is consistent in both directions.

STEP 1 Verify the Primary Database

Before making any changes, we verify the current state of the primary database — its role, archiving mode, redo log configuration, ASM disk groups, version, datafiles, and log file members. This gives us a clear baseline.

Check Database Role & Open Mode

Confirm the database is open as PRIMARY in READ WRITE mode.

SQL> SELECT name, db_unique_name, database_role, open_mode FROM v$database;

NAME      DB_UNIQUE_NAME                 DATABASE_ROLE    OPEN_MODE
--------- ------------------------------ ---------------- --------------------
ORADB     oradb                          PRIMARY          READ WRITEsql

Verify ARCHIVELOG Mode

Data Guard requires the primary to be in ARCHIVELOG mode so that redo data is archived and can be shipped to the standby.

SQL> archive log list;

Database log mode              Archive Mode
Automatic archival             Enabled
Archive destination            USE_DB_RECOVERY_FILE_DEST
Oldest online log sequence     12
Next log sequence to archive   14
Current log sequence           14sql

Check Online Redo Log Groups

We need to know the number and size of online redo log groups to determine how many standby redo logs to create (rule: standby redo log groups = online redo log groups + 1).

SQL> SELECT group#, thread#, bytes/1024/1024 MB, members, status FROM v$log;

    GROUP#    THREAD#         MB    MEMBERS STATUS
---------- ---------- ---------- ---------- ----------------
         1          1        200          2 INACTIVE
         2          1        200          2 CURRENT
         3          1        200          2 INACTIVEsql
ℹ Key Observation
We have 3 online redo log groups, each 200 MB with 2 members (stored in +DATA and +FRA). So we need 4 standby redo log groups (3 + 1), also sized at 200 MB.

Check ASM Disk Groups

Verify that both +DATA and +FRA have sufficient free space for the standby configuration overhead and archive logs.

SQL> SELECT name, total_mb, free_mb FROM v$asm_diskgroup;

NAME                             TOTAL_MB    FREE_MB
------------------------------ ---------- ----------
DATA                                20472      15508
FRA                                 10236       9500sql

Check Database Version

SQL> SELECT banner FROM v$version WHERE banner LIKE 'Oracle%';

BANNER
--------------------------------------------------------------------------------
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Productionsql

List Datafiles & Log File Members

Document the current datafiles and log file members. These are stored in ASM, which is important for the standby's db_file_name_convert parameter later.

SQL> SELECT name FROM v$datafile;

NAME
--------------------------------------------------------------------------------
+DATA/ORADB/DATAFILE/system.257.1205523331
+DATA/ORADB/DATAFILE/sysaux.258.1205523387
+DATA/ORADB/DATAFILE/undotbs1.259.1205523413
+DATA/ORADB/DATAFILE/soe.267.1205602145
+DATA/ORADB/DATAFILE/users.260.1205523413

SQL> SELECT member FROM v$logfile;

MEMBER
--------------------------------------------------------------------------------
+DATA/ORADB/ONLINELOG/group_3.264.1205523499
+FRA/ORADB/ONLINELOG/group_3.259.1205523501
+DATA/ORADB/ONLINELOG/group_2.263.1205523489
+FRA/ORADB/ONLINELOG/group_2.257.1205523493
+DATA/ORADB/ONLINELOG/group_1.262.1205523489
+FRA/ORADB/ONLINELOG/group_1.258.1205523493sql

STEP 2 Create Standby Redo Logs & Enable Force Logging

Standby redo logs (SRLs) are used by the standby database to receive redo data from the primary in real-time. They must be created on the primary first so that the standby inherits them during duplication. The rule is: number of SRL groups = online redo log groups + 1.

Why +1? The extra group ensures that there's always a standby redo log available for writing while others are being archived — preventing redo transport stalls.

SQL> ALTER DATABASE ADD STANDBY LOGFILE THREAD 1
     GROUP 4 ('+DATA', '+FRA') SIZE 200M;
Database altered.

SQL> ALTER DATABASE ADD STANDBY LOGFILE THREAD 1
     GROUP 5 ('+DATA', '+FRA') SIZE 200M;
Database altered.

SQL> ALTER DATABASE ADD STANDBY LOGFILE THREAD 1
     GROUP 6 ('+DATA', '+FRA') SIZE 200M;
Database altered.

SQL> ALTER DATABASE ADD STANDBY LOGFILE THREAD 1
     GROUP 7 ('+DATA', '+FRA') SIZE 200M;
Database altered.sql
ℹ Why groups 4–7?
Online redo log groups are numbered 1–3. Standby redo log groups must use different group numbers, so we start at 4. Each SRL has members in both +DATA and +FRA for redundancy, matching the online redo log layout.

Verify Standby Redo Logs

SQL> SELECT group#, thread#, bytes/1024/1024 MB, status FROM v$standby_log;

    GROUP#    THREAD#         MB STATUS
---------- ---------- ---------- ----------
         4          1        200 UNASSIGNED
         5          1        200 UNASSIGNED
         6          1        200 UNASSIGNED
         7          1        200 UNASSIGNEDsql

Enable Force Logging

Force logging ensures that all changes are written to the redo logs, even those from operations marked as NOLOGGING. Without this, some data changes could be missing on the standby, causing inconsistencies.

SQL> ALTER DATABASE FORCE LOGGING;
Database altered.

SQL> SELECT force_logging FROM v$database;

FORCE_LOGGING
---------------------------------------
YESsql

STEP 3 Configure Data Guard Parameters on Primary

Now we set the critical Data Guard parameters on the primary database. Each parameter plays a specific role in the redo transport and failover mechanism.

3.1 — Log Archive Config

This tells the primary about all databases in the Data Guard configuration by their db_unique_name. It's required for redo transport to know its targets.

SQL> ALTER SYSTEM SET log_archive_config='DG_CONFIG=(oradb,oradb_stby)' SCOPE=BOTH;
System altered.sql

3.2 — Archive Destinations

log_archive_dest_1 is the local archive destination — where archived logs are stored on the primary itself (using the Fast Recovery Area). log_archive_dest_2 is the remote destination — it ships redo to the standby database asynchronously via the TNS service name oradb_stby.

SQL> ALTER SYSTEM SET log_archive_dest_1='LOCATION=USE_DB_RECOVERY_FILE_DEST
     VALID_FOR=(ALL_LOGFILES,ALL_ROLES) DB_UNIQUE_NAME=oradb' SCOPE=BOTH;
System altered.

SQL> ALTER SYSTEM SET log_archive_dest_2='SERVICE=oradb_stby ASYNC
     VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=oradb_stby' SCOPE=BOTH;
System altered.sql
ℹ Why ASYNC?
ASYNC (asynchronous) means the primary does not wait for the standby to acknowledge receipt before committing transactions. This gives better performance on the primary at the cost of potential minimal data loss during failover. Use SYNC for zero data loss (Maximum Protection / Maximum Availability mode).

3.3 — Enable Archive Destinations

SQL> ALTER SYSTEM SET log_archive_dest_state_1=ENABLE SCOPE=BOTH;
System altered.

SQL> ALTER SYSTEM SET log_archive_dest_state_2=ENABLE SCOPE=BOTH;
System altered.sql

3.4 — Archive Log Format

Defines the naming convention for archived redo logs. %t = thread number, %s = sequence number, %r = resetlogs ID. This requires SCOPE=SPFILE because it's a static parameter.

SQL> ALTER SYSTEM SET log_archive_format='%t_%s_%r.arc' SCOPE=SPFILE;
System altered.sql

3.5 — Standby File Management

Setting this to AUTO tells Oracle to automatically create corresponding datafiles on the standby when new datafiles are added on the primary. Without this, you'd need to manually manage files on the standby.

SQL> ALTER SYSTEM SET standby_file_management=AUTO SCOPE=BOTH;
System altered.sql

3.6 — FAL (Fetch Archive Log) Configuration

fal_server tells the primary where the standby can fetch missing archived logs from. fal_client identifies itself. These are used during gap resolution — if the standby misses some redo, it uses FAL to request the missing logs.

SQL> ALTER SYSTEM SET fal_server='oradb_stby' SCOPE=BOTH;
System altered.

SQL> ALTER SYSTEM SET fal_client='oradb' SCOPE=BOTH;
System altered.sql

3.7 — Enable Flashback Database

Flashback allows you to quickly reinstate a failed primary after a failover, without needing to rebuild it from scratch. This is highly recommended for any Data Guard setup.

SQL> ALTER DATABASE FLASHBACK ON;
Database altered.sql

3.8 — Enable Data Guard Broker

The DG Broker provides a centralized management framework for Data Guard. It simplifies switchover, failover, and monitoring through the dgmgrl command-line tool.

SQL> ALTER SYSTEM SET dg_broker_start=TRUE SCOPE=BOTH;
System altered.sql

Verify All Parameters

SQL> SHOW PARAMETER log_archive_config;
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
log_archive_config                   string      DG_CONFIG=(oradb,oradb_stby)

SQL> SHOW PARAMETER log_archive_dest_2;
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
log_archive_dest_2                   string      SERVICE=oradb_stby ASYNC VALID
                                                 _FOR=(ONLINE_LOGFILES,PRIMARY_
                                                 ROLE) DB_UNIQUE_NAME=oradb_stb
                                                 y

SQL> SHOW PARAMETER fal_server;
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
fal_server                           string      oradb_stby
SQL> SHOW PARAMETER fal_client;
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
fal_client                           string      oradb

SQL> SHOW PARAMETER dg_broker_start;
NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
dg_broker_start                      boolean     TRUEsql

STEP 4 Restart Primary & Verify

Some parameters (like log_archive_format) require a restart to take effect because they were set with SCOPE=SPFILE. Restart the primary database to activate all changes.

SQL> SHUTDOWN IMMEDIATE;
Database closed.
Database dismounted.
ORACLE instance shut down.

SQL> STARTUP;
ORACLE instance started.

Total System Global Area 1996486272 bytes
Fixed Size                  8898176 bytes
Variable Size             452984832 bytes
Database Buffers         1526726656 bytes
Redo Buffers                7876608 bytes
Database mounted.
Database opened.

SQL> SELECT name, db_unique_name, database_role, open_mode FROM v$database;
NAME      DB_UNIQUE_NAME                 DATABASE_ROLE    OPEN_MODE
--------- ------------------------------ ---------------- --------------------
ORADB     oradb                          PRIMARY          READ WRITEsql
✔ Primary Ready
The primary database is back online with all Data Guard parameters active.

STEP 5 Configure Network (TNS) on Both Servers

Data Guard uses Oracle Net Services (TNS) to communicate between primary and standby. We need to configure tnsnames.ora on both servers so each can resolve the other's service name. The listener on the standby also needs a static registration since the standby database won't be fully open to register itself dynamically.

5.1 — Configure tnsnames.ora on Primary (dg1)

As the oracle user on dg1:

[oracle@dg1 ~]$ cd $ORACLE_HOME/network/admin

# Backup existing file
[oracle@dg1 admin]$ cp tnsnames.ora tnsnames.ora.bkp_$(date +%Y%m%d)

[oracle@dg1 admin]$ vi tnsnames.orashell

Add these entries (the same entries go on both servers):

oradb =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.2)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = oradb.bracecoder)
    )
  )

oradb_stby =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.7)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = oradb_stby.bracecoder)
    )
  )tnsnames.ora
ℹ Why .bracecoder in SERVICE_NAME?
The db_domain parameter is set to bracecoder, so the full service name includes the domain suffix. If you use just oradb without the domain, tnsping may succeed but sqlplus connections may fail with ORA-12514.

5.2 — Test TNS from Primary

[oracle@dg1 admin]$ tnsping oradb
[oracle@dg1 admin]$ tnsping oradb_stby

# Test actual database connection
[oracle@dg1 admin]$ sqlplus sys/admin@oradb as sysdba

SQL> SELECT name, db_unique_name FROM v$database;
NAME      DB_UNIQUE_NAME
--------- ------------------------------
ORADB     oradbshell

5.3 — Configure Listener on Standby (dg2)

The standby database won't be running when we first configure Data Guard, so it can't dynamically register with the listener. We need static registration in the listener.ora. As the grid user on dg2:

[grid@dg2 ~]$ cd $ORACLE_HOME/network/admin
[grid@dg2 admin]$ cp listener.ora listener.ora.bkp
[grid@dg2 admin]$ vi listener.orashell

Add the SID_LIST_LISTENER block at the top of the file:

SID_LIST_LISTENER =
  (SID_LIST =
    (SID_DESC =
      (GLOBAL_DBNAME = oradb_stby.bracecoder)
      (ORACLE_HOME = /u01/app/oracle/product/19.0.0/db_1)
      (SID_NAME = oradb)
    )
  )

LISTENER =
  (DESCRIPTION_LIST =
    (DESCRIPTION =
      (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.0.7)(PORT = 1521))
      (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1521))
    )
  )

ENABLE_GLOBAL_DYNAMIC_ENDPOINT_LISTENER=ONlistener.ora
⚠ Important
SID_NAME is oradb (the actual SID/instance name on this server), while GLOBAL_DBNAME is oradb_stby.bracecoder (the service name used for TNS connections). This static entry lets RMAN connect to the standby even when the instance is in NOMOUNT state.

Reload the listener and verify:

[grid@dg2 admin]$ lsnrctl reload
[grid@dg2 admin]$ lsnrctl status

Service "oradb_stby.bracecoder" has 1 instance(s).
  Instance "oradb", status UNKNOWN, has 1 handler(s) for this service...shell

5.4 — Configure tnsnames.ora on Standby (dg2)

Add the same TNS entries as on dg1, for both grid and oracle users:

# As grid user
[grid@dg2 ~]$ cd $ORACLE_HOME/network/admin
[grid@dg2 admin]$ vi tnsnames.ora

# As oracle user
[oracle@dg2 ~]$ cd $ORACLE_HOME/network/admin
[oracle@dg2 admin]$ vi tnsnames.orashell

Add the identical entries (same as dg1's tnsnames.ora shown above).

5.5 — Test TNS from Standby

[oracle@dg2 admin]$ tnsping oradb
[oracle@dg2 admin]$ tnsping oradb_stby

# Test connection to PRIMARY from standby
[oracle@dg2 admin]$ sqlplus sys/admin@oradb as sysdba

SQL> SELECT name FROM v$database;
NAME
---------
ORADBshell

STEP 6 Create Password File & Standby Controlfile on Primary

The standby needs an identical password file (so SYS can authenticate over the network for redo transport) and a standby controlfile (which contains the database structure metadata for the standby).

6.1 — Verify/Create Password File

[oracle@dg1 ~]$ cd $ORACLE_HOME/dbs
[oracle@dg1 dbs]$ ls -l orapworadb

# If not found, create it:
[oracle@dg1 dbs]$ orapwd file=orapworadb password=admin entries=10 format=12shell

6.2 — Create Standby Controlfile

A standby controlfile is a special controlfile that tells Oracle this database is a standby. It's different from a normal backup controlfile.

SQL> ALTER DATABASE CREATE STANDBY CONTROLFILE AS '/tmp/standby_oradb.ctl';
Database altered.

[oracle@dg1 dbs]$ ls -l /tmp/standby_oradb.ctlsql

6.3 — Copy Files to Standby

Use SCP to transfer the password file and standby controlfile to dg2:

# Test SSH first
[oracle@dg1 ~]$ ssh oracle@dg2 hostname
dg2.bracecoder

# Copy password file
[oracle@dg1 ~]$ scp $ORACLE_HOME/dbs/orapworadb oracle@dg2:$ORACLE_HOME/dbs/

# Copy standby controlfile
[oracle@dg1 ~]$ scp /tmp/standby_oradb.ctl oracle@dg2:/tmp/shell

6.4 — Verify on Standby

[oracle@dg2 ~]$ ls -l $ORACLE_HOME/dbs/orapworadb
[oracle@dg2 ~]$ ls -l /tmp/standby_oradb.ctlshell

STEP 7 Prepare Standby Server (DG2) — ASM Directories

Before we can create the standby database, we need to prepare the ASM directory structure on dg2 to receive the datafiles.

[grid@dg2 ~]$ export ORACLE_SID=+ASM
[grid@dg2 ~]$ asmcmd

ASMCMD> mkdir +DATA/ORADB_STBY
ASMCMD> mkdir +FRA/ORADB_STBY
ASMCMD> ls -l +DATA
Type      Redund  Striped  Time         Sys  Name
                                        Y    ASM/
                                        N    ORADB_STBY/
ASMCMD> ls -l +FRA
Type  Redund  Striped  Time  Sys  Name
                             N    ORADB_STBY/
ASMCMD> exitshell
ℹ Why ORADB_STBY?
The ASM directory name matches the db_unique_name of the standby (oradb_stby). Oracle ASM automatically organizes files under this directory when the standby database is created.

STEP 8 Create Standby Initialization Parameter File

The standby needs its own init parameter file with settings that mirror the primary, but with key differences — notably db_unique_name, control_files path, and the db_file_name_convert/log_file_name_convert parameters that map primary paths to standby paths.

[oracle@dg2 ~]$ cd $ORACLE_HOME/dbs

[oracle@dg2 dbs]$ cat > initoradb.ora << 'EOF'
*.audit_file_dest='/u01/app/oracle/admin/oradb/adump'
*.audit_trail='db'
*.compatible='19.0.0.0'
*.control_files='+DATA/ORADB_STBY/CONTROLFILE/control01.ctl','+FRA/ORADB_STBY/CONTROLFILE/control02.ctl'
*.db_block_size=8192
*.db_create_file_dest='+DATA'
*.db_domain='bracecoder'
*.db_file_name_convert='+DATA/ORADB','+DATA/ORADB_STBY','+FRA/ORADB','+FRA/ORADB_STBY'
*.db_name='oradb'
*.db_recovery_file_dest='+FRA'
*.db_recovery_file_dest_size=10G
*.db_unique_name='oradb_stby'
*.diagnostic_dest='/u01/app/oracle'
*.dispatchers='(PROTOCOL=TCP) (SERVICE=oradbXDB)'
*.fal_client='oradb_stby'
*.fal_server='oradb'
*.log_archive_config='DG_CONFIG=(oradb,oradb_stby)'
*.log_archive_dest_1='LOCATION=USE_DB_RECOVERY_FILE_DEST VALID_FOR=(ALL_LOGFILES,ALL_ROLES) DB_UNIQUE_NAME=oradb_stby'
*.log_archive_dest_2='SERVICE=oradb ASYNC VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=oradb'
*.log_archive_dest_state_1='ENABLE'
*.log_archive_dest_state_2='ENABLE'
*.log_archive_format='%t_%s_%r.arc'
*.log_file_name_convert='+DATA/ORADB','+DATA/ORADB_STBY','+FRA/ORADB','+FRA/ORADB_STBY'
*.open_cursors=300
*.pga_aggregate_target=512M
*.processes=300
*.remote_login_passwordfile='EXCLUSIVE'
*.sga_target=1536M
*.standby_file_management='AUTO'
*.undo_tablespace='UNDOTBS1'
EOFshell
⚠ Key Differences from Primary
  • db_unique_name = oradb_stby (not oradb)
  • control_files point to +DATA/ORADB_STBY and +FRA/ORADB_STBY
  • db_file_name_convert and log_file_name_convert map primary paths → standby paths
  • fal_client and fal_server are reversed compared to primary
  • log_archive_dest_2 points back to the primary (for role reversal)

Create Audit Directory & Start NOMOUNT

The audit directory must exist before the instance can start, otherwise you'll get ORA-09925.

[oracle@dg2 dbs]$ mkdir -p /u01/app/oracle/admin/oradb/adump
[oracle@dg2 dbs]$ chmod 755 /u01/app/oracle/admin/oradb/adumpshell
[oracle@dg2 dbs]$ sqlplus / as sysdba

SQL> STARTUP NOMOUNT PFILE='$ORACLE_HOME/dbs/initoradb.ora';
ORACLE instance started.

Total System Global Area 1610609200 bytes
Fixed Size                  8897072 bytes
Variable Size             385875968 bytes
Database Buffers         1207959552 bytes
Redo Buffers                7876608 bytes

SQL> SELECT instance_name, status FROM v$instance;
INSTANCE_NAME    STATUS
---------------- ------------
oradb            STARTEDsql

Create SPFILE from PFILE

Convert the text-based PFILE to a binary SPFILE so the instance uses it by default on subsequent startups.

SQL> CREATE SPFILE FROM PFILE='$ORACLE_HOME/dbs/initoradb.ora';
File created.

SQL> SHUTDOWN IMMEDIATE;
ORACLE instance shut down.

SQL> STARTUP NOMOUNT;
ORACLE instance started.sql

STEP 9 Restore Controlfiles to ASM

Copy the standby controlfile from /tmp into the ASM disk groups using asmcmd. This must be done as the grid user since ASM is owned by grid.

[grid@dg2 ~]$ asmcmd cp /tmp/standby_oradb.ctl +DATA/ORADB_STBY/control01.ctl
copying /tmp/standby_oradb.ctl -> +DATA/ORADB_STBY/control01.ctl

[grid@dg2 ~]$ asmcmd cp /tmp/standby_oradb.ctl +FRA/ORADB_STBY/control02.ctl
copying /tmp/standby_oradb.ctl -> +FRA/ORADB_STBY/control02.ctlshell

Fix Controlfile Path in SPFILE

ASM may create the controlfile at a different internal path than what the SPFILE expects. We need to update the control_files parameter to match the actual ASM alias paths (without the /CONTROLFILE/ subdirectory).

SQL> CREATE PFILE='/tmp/initoradb_new.ora' FROM SPFILE;sql

Edit /tmp/initoradb_new.ora and update the control_files line:

# Change FROM:
*.control_files='+DATA/ORADB_STBY/CONTROLFILE/control01.ctl','+FRA/ORADB_STBY/CONTROLFILE/control02.ctl'

# Change TO:
*.control_files='+DATA/ORADB_STBY/control01.ctl','+FRA/ORADB_STBY/control02.ctl'pfile

Recreate SPFILE and mount the standby:

SQL> STARTUP NOMOUNT PFILE='/tmp/initoradb_new.ora';
ORACLE instance started.

SQL> CREATE SPFILE FROM PFILE='/tmp/initoradb_new.ora';
File created.

SQL> SHUTDOWN IMMEDIATE;
ORACLE instance shut down.

SQL> STARTUP MOUNT;
ORACLE instance started.
...
Database mounted.

SQL> SELECT name, db_unique_name, database_role, open_mode FROM v$database;
NAME      DB_UNIQUE_NAME                 DATABASE_ROLE    OPEN_MODE
--------- ------------------------------ ---------------- --------------------
ORADB     oradb_stby                     PHYSICAL STANDBY MOUNTEDsql
✔ Standby Mounted
The standby database is now in MOUNTED state with the role PHYSICAL STANDBY. This confirms the standby controlfile is properly configured.

STEP 10 Duplicate Database Using RMAN

This is the most critical step — we use RMAN's Active Database Duplication to copy the entire primary database to the standby over the network. The standby must be in NOMOUNT state for this to work.

Prepare Standby for Duplication

The standby was in MOUNT from the previous step. RMAN requires NOMOUNT for DUPLICATE ... FOR STANDBY:

SQL> SHUTDOWN IMMEDIATE;
Database dismounted.
ORACLE instance shut down.

SQL> STARTUP NOMOUNT;
ORACLE instance started.sql

Run RMAN Duplication

Connect RMAN to both the TARGET (primary) and AUXILIARY (standby) databases simultaneously, then run the duplication:

[oracle@dg2 ~]$ rman TARGET sys/admin@oradb AUXILIARY sys/admin@oradb_stby

connected to target database: ORADB (DBID=2958788253)
connected to auxiliary database: ORADB (not mounted)shell
RMAN> DUPLICATE TARGET DATABASE
  FOR STANDBY
  FROM ACTIVE DATABASE
  DORECOVER
  NOFILENAMECHECK;rman
ℹ RMAN Command Breakdown
  • DUPLICATE TARGET DATABASE — Copy the target (primary) database
  • FOR STANDBY — Create it as a standby (not a standalone clone)
  • FROM ACTIVE DATABASE — Copy directly over the network (no backup needed)
  • DORECOVER — Apply any redo generated during the copy to make the standby current
  • NOFILENAMECHECK — Don't verify filenames match between primary and standby (needed since ASM paths differ)

The duplication process takes several minutes. RMAN will:

  1. Copy the password file
  2. Restore a fresh standby controlfile
  3. Mount the standby database
  4. Copy all datafiles over the network
  5. Archive the current log and restore recent archive logs
  6. Recover the standby to make it current

Starting Duplicate Db at 08-FEB-2026 16:00:57
using target database control file instead of recovery catalog
allocated channel: ORA_AUX_DISK_1
channel ORA_AUX_DISK_1: SID=44 device type=DISK
current log archived

contents of Memory Script:
{
   backup as copy reuse
   passwordfile auxiliary format  '/u01/app/oracle/product/19.0.0/db_1/dbs/orapworadb'   ;
}
executing Memory Script

Starting backup at 08-FEB-2026 16:01:01
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=97 device type=DISK
Finished backup at 08-FEB-2026 16:01:05

contents of Memory Script:
{
   restore clone from service  'oradb' standby controlfile;
}
executing Memory Script

Starting restore at 08-FEB-2026 16:01:05
using channel ORA_AUX_DISK_1

channel ORA_AUX_DISK_1: starting datafile backup set restore
channel ORA_AUX_DISK_1: using network backup set from service oradb
channel ORA_AUX_DISK_1: restoring control file
channel ORA_AUX_DISK_1: restore complete, elapsed time: 00:00:04
output file name=+DATA/ORADB_STBY/control01.ctl
output file name=+FRA/ORADB_STBY/control02.ctl
Finished restore at 08-FEB-2026 16:01:12

contents of Memory Script:
{
   sql clone 'alter database mount standby database';
}
executing Memory Script

sql statement: alter database mount standby database
RMAN-05529: warning: DB_FILE_NAME_CONVERT resulted in invalid ASM names; names changed to disk group only.

contents of Memory Script:
{
   set newname for tempfile  1 to
 "+DATA";
   switch clone tempfile all;
   set newname for datafile  1 to
 "+DATA";
   set newname for datafile  3 to
 "+DATA";
   set newname for datafile  4 to
 "+DATA";
   set newname for datafile  5 to
 "+DATA";
   set newname for datafile  7 to
 "+DATA";
   restore
   from  nonsparse   from service
 'oradb'   clone database
   ;
   sql 'alter system archive log current';
}
executing Memory Script

executing command: SET NEWNAME

renamed tempfile 1 to +DATA in control file

executing command: SET NEWNAME

executing command: SET NEWNAME

executing command: SET NEWNAME

executing command: SET NEWNAME

executing command: SET NEWNAME

Starting restore at 08-FEB-2026 16:01:19
using channel ORA_AUX_DISK_1

channel ORA_AUX_DISK_1: starting datafile backup set restore
channel ORA_AUX_DISK_1: using network backup set from service oradb
channel ORA_AUX_DISK_1: specifying datafile(s) to restore from backup set
channel ORA_AUX_DISK_1: restoring datafile 00001 to +DATA
channel ORA_AUX_DISK_1: restore complete, elapsed time: 00:00:45
channel ORA_AUX_DISK_1: starting datafile backup set restore
channel ORA_AUX_DISK_1: using network backup set from service oradb
channel ORA_AUX_DISK_1: specifying datafile(s) to restore from backup set
channel ORA_AUX_DISK_1: restoring datafile 00003 to +DATA
channel ORA_AUX_DISK_1: restore complete, elapsed time: 00:00:26
channel ORA_AUX_DISK_1: starting datafile backup set restore
channel ORA_AUX_DISK_1: using network backup set from service oradb
channel ORA_AUX_DISK_1: specifying datafile(s) to restore from backup set
channel ORA_AUX_DISK_1: restoring datafile 00004 to +DATA
channel ORA_AUX_DISK_1: restore complete, elapsed time: 00:00:07
channel ORA_AUX_DISK_1: starting datafile backup set restore
channel ORA_AUX_DISK_1: using network backup set from service oradb
channel ORA_AUX_DISK_1: specifying datafile(s) to restore from backup set
channel ORA_AUX_DISK_1: restoring datafile 00005 to +DATA
channel ORA_AUX_DISK_1: restore complete, elapsed time: 00:00:35
channel ORA_AUX_DISK_1: starting datafile backup set restore
channel ORA_AUX_DISK_1: using network backup set from service oradb
channel ORA_AUX_DISK_1: specifying datafile(s) to restore from backup set
channel ORA_AUX_DISK_1: restoring datafile 00007 to +DATA
channel ORA_AUX_DISK_1: restore complete, elapsed time: 00:00:02
Finished restore at 08-FEB-2026 16:03:17

sql statement: alter system archive log current
current log archived

contents of Memory Script:
{
   restore clone force from service  'oradb'
           archivelog from scn  2769737;
   switch clone datafile all;
}
executing Memory Script

Starting restore at 08-FEB-2026 16:03:18
using channel ORA_AUX_DISK_1

channel ORA_AUX_DISK_1: starting archived log restore to default destination
channel ORA_AUX_DISK_1: using network backup set from service oradb
channel ORA_AUX_DISK_1: restoring archived log
archived log thread=1 sequence=17
channel ORA_AUX_DISK_1: restore complete, elapsed time: 00:00:01
channel ORA_AUX_DISK_1: starting archived log restore to default destination
channel ORA_AUX_DISK_1: using network backup set from service oradb
channel ORA_AUX_DISK_1: restoring archived log
archived log thread=1 sequence=18
channel ORA_AUX_DISK_1: restore complete, elapsed time: 00:00:02
Finished restore at 08-FEB-2026 16:03:21

datafile 1 switched to datafile copy
input datafile copy RECID=7 STAMP=1224691401 file name=+DATA/ORADB_STBY/DATAFILE/system.262.1224691283
datafile 3 switched to datafile copy
input datafile copy RECID=8 STAMP=1224691401 file name=+DATA/ORADB_STBY/DATAFILE/sysaux.263.1224691327
datafile 4 switched to datafile copy
input datafile copy RECID=9 STAMP=1224691401 file name=+DATA/ORADB_STBY/DATAFILE/undotbs1.264.1224691355
datafile 5 switched to datafile copy
input datafile copy RECID=10 STAMP=1224691401 file name=+DATA/ORADB_STBY/DATAFILE/soe.265.1224691361
datafile 7 switched to datafile copy
input datafile copy RECID=11 STAMP=1224691401 file name=+DATA/ORADB_STBY/DATAFILE/users.266.1224691397

contents of Memory Script:
{
   set until scn  2770069;
   recover
   standby
   clone database
    delete archivelog
   ;
}
executing Memory Script

executing command: SET until clause

Starting recover at 08-FEB-2026 16:03:21
using channel ORA_AUX_DISK_1

starting media recovery

archived log for thread 1 with sequence 17 is already on disk as file +FRA/ORADB_STBY/ARCHIVELOG/2026_02_08/thread_1_seq_17.261.1224691399
archived log for thread 1 with sequence 18 is already on disk as file +FRA/ORADB_STBY/ARCHIVELOG/2026_02_08/thread_1_seq_18.262.1224691401
archived log file name=+FRA/ORADB_STBY/ARCHIVELOG/2026_02_08/thread_1_seq_17.261.1224691399 thread=1 sequence=17
archived log file name=+FRA/ORADB_STBY/ARCHIVELOG/2026_02_08/thread_1_seq_18.262.1224691401 thread=1 sequence=18
media recovery complete, elapsed time: 00:00:01
Finished recover at 08-FEB-2026 16:03:24

contents of Memory Script:
{
   delete clone force archivelog all;
}
executing Memory Script

released channel: ORA_DISK_1
released channel: ORA_AUX_DISK_1
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=97 device type=DISK
deleted archived log
archived log file name=+FRA/ORADB_STBY/ARCHIVELOG/2026_02_08/thread_1_seq_17.261.1224691399 RECID=1 STAMP=1224691398
deleted archived log
archived log file name=+FRA/ORADB_STBY/ARCHIVELOG/2026_02_08/thread_1_seq_18.262.1224691401 RECID=2 STAMP=1224691400
Deleted 2 objects

Finished Duplicate Db at 08-FEB-2026 16:04:02rman output

Verify Standby After Duplication

SQL> SELECT name, db_unique_name, database_role, open_mode, protection_mode
     FROM v$database;

NAME      DB_UNIQUE_NAME    DATABASE_ROLE    OPEN_MODE    PROTECTION_MODE
--------- ----------------- ---------------- ------------ --------------------
ORADB     oradb_stby        PHYSICAL STANDBY MOUNTED      MAXIMUM PERFORMANCE

SQL> SELECT name FROM v$datafile;

NAME
--------------------------------------------------------------------------------
+DATA/ORADB_STBY/DATAFILE/system.262.1224691283
+DATA/ORADB_STBY/DATAFILE/sysaux.263.1224691327
+DATA/ORADB_STBY/DATAFILE/undotbs1.264.1224691355
+DATA/ORADB_STBY/DATAFILE/soe.265.1224691361
+DATA/ORADB_STBY/DATAFILE/users.266.1224691397

SQL> SELECT group#, thread#, bytes/1024/1024 MB, status FROM v$standby_log;

    GROUP#    THREAD#         MB STATUS
---------- ---------- ---------- ----------
         4          1        200 UNASSIGNED
         5          1        200 UNASSIGNED
         6          1        200 UNASSIGNED
         7          1        200 UNASSIGNEDsql
✔ Duplication Complete
All datafiles, standby redo logs, and the controlfile have been successfully duplicated to +DATA/ORADB_STBY. The database role is confirmed as PHYSICAL STANDBY.

STEP 11 Start Managed Recovery & Verify Synchronization

The final step is to start Managed Recovery Process (MRP) on the standby. MRP continuously applies redo data received from the primary, keeping the standby in sync.

Start MRP on Standby (dg2)

SQL> ALTER DATABASE RECOVER MANAGED STANDBY DATABASE
     USING CURRENT LOGFILE DISCONNECT FROM SESSION;
Database altered.sql
ℹ Command Breakdown
  • RECOVER MANAGED STANDBY DATABASE — Start the managed recovery process
  • USING CURRENT LOGFILE — Apply redo in real-time from standby redo logs (real-time apply)
  • DISCONNECT FROM SESSION — Run MRP as a background process so you get your SQL prompt back
dg 1 ====

SQL
SQL> ALTER SYSTEM SET log_archive_dest_2='SERVICE=oradb_stby ASYNC VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=oradb_stby' SCOPE=BOTH;

System altered.

SQL> SELECT dest_id, status, error FROM v$archive_dest_status WHERE dest_id = 2;

   DEST_ID STATUS
---------- ---------
ERROR
-----------------------------------------------------------------
         2 VALID



SQL>

Verify MRP Status on Standby

SQL> SELECT process, status, thread#, sequence#, block#, blocks
     FROM v$managed_standby
     WHERE process LIKE 'MRP%' OR process LIKE 'RFS%';

PROCESS   STATUS          THREAD#  SEQUENCE#     BLOCK#     BLOCKS
--------- ------------ ---------- ---------- ---------- ----------
MRP0      APPLYING_LOG          1         21        197     409600
RFS       IDLE                  1         21        197          1
RFS       IDLE                  0          0          0          0sql
✔ Data Guard Active!
MRP0 is in APPLYING_LOG status — the standby is actively receiving and applying redo from the primary. RFS (Remote File Server) shows the redo transport connection is established. Data Guard is fully operational!

Verify Archive Destination on Primary (dg1)

SQL> SELECT dest_id, status, error FROM v$archive_dest_status WHERE dest_id = 2;

   DEST_ID STATUS    ERROR
---------- --------- --------------------------------------------------
         2 VALIDsql

VALID with no errors means redo transport from primary to standby is working correctly.

Conclusion

Congratulations! You have successfully configured Oracle Data Guard with a Physical Standby Database. Here's a summary of what we accomplished:

ComponentStatus
Primary Database (oradb @ dg1)✔ Running — READ WRITE
Standby Database (oradb_stby @ dg2)✔ Running — MOUNTED (Physical Standby)
Redo Transport✔ Active — ASYNC mode
Log Apply (MRP)✔ APPLYING_LOG
Protection ModeMaximum Performance
Flashback✔ Enabled on Primary
DG Broker✔ Started

In the next part (Day 2), we'll cover configuring the Data Guard Broker for simplified management, performing switchover and failover operations, enabling Active Data Guard (read-only standby), and setting up monitoring and alerts.

⚠ Production Recommendations
  • Use Oracle Secure Backup or RMAN backups on the standby to offload backup workload from the primary
  • Consider SYNC redo transport for zero data loss if your network latency allows it
  • Always test switchover/failover in a non-production environment first
  • Monitor v$dataguard_stats and v$archive_dest_status regularly for transport/apply lag
  • Set up email alerts for Data Guard gaps or transport errors
Share this article:
Advertisement

Comments (0)

No comments yet. Be the first to share your thoughts!