Migrate Oracle 19c Standalone to Exadata 2-Node RAC (Data Guard Switchover Method)
Migrate a standalone single-instance Oracle 19c CDB (datafiles on a flat filesystem) to a 2-node Exadata RAC with ASM by building a physical Data Guard standby on the destination with RMAN active duplication, switching over, converting the new open primary to RAC, then consolidating to BIGFILE tablespaces. The entire operation is driven from the destination. The source database stays open and serving during the multi-day seed; the only outage is the switchover, measured in seconds to minutes.
Length note: This is a 10-phase end-to-end migration documented as a single transactional how-to (the
[3P]/[SYS]/[DEST]audience handoffs and placeholder continuity span all phases). It exceeds the usual instruction length intentionally and with prior approval, so the reader does not have to reassemble context across files.
Audience legend
Every command block carries one of three audience tags on the bold line directly above it. The tag tells you who runs the command and from where.
| Tag | Who runs it | Where |
|---|---|---|
[DEST] |
The experienced Exadata DBA driving the migration | On the Exadata destination (OS shell, SQL*Plus, RMAN, dgmgrl, asmcmd) |
[SYS] |
The same operator, but connected remotely as SYS over SQL*Net |
From the destination, into the source database; no source OS access |
[3P] |
An unskilled third party with shell access to the source | On the source OS; commands are copy-paste ready, no judgment required |
You (the [DEST] operator) have no source OS access and no authority to act on the source OS directly. Source database changes are [SYS] (remote SQL); source OS changes are [3P] (handed off as copy-paste). Hand [3P] blocks to the third party exactly as written after substituting placeholders.
Licensing scope note
Licensed on the destination: Enterprise Edition + Real Application Clusters + Diagnostics and Tuning Pack — and nothing else. This procedure introduces no chargeable option. Specifically:
- RMAN compression is BASIC only.
LOW,MEDIUM, andHIGHrequire the Advanced Compression Option (ACO) and must not be used. - No
SQLNET.COMPRESSION(an ACO feature). - No OLTP / Advanced Row / Hybrid Columnar (HCC) table compression. Existing segment compression attributes are preserved exactly as-is on move; no compression is introduced.
- No Partitioning operations. (Partitioned objects existing at all is a Phase 0 compliance blocker for this license set.)
- No Advanced Security / TDE.
- No GoldenGate.
Diagnostics and Tuning is licensed: AWR, ASH, and SQL Tuning use is permitted (CONTROL_MANAGEMENT_PACK_ACCESS may remain at DIAGNOSTIC+TUNING).
Placeholder legend
Placeholders use <UPPER_SNAKE_CASE> angle brackets. Substitute every placeholder before running a command. The set below is fixed and used consistently throughout the document.
| Placeholder | Meaning / example |
|---|---|
<SOURCE_DB_NAME> |
Source DB_NAME (e.g., PRODCDB) |
<SOURCE_DB_UNIQUE_NAME> |
Source DB_UNIQUE_NAME (e.g., prodcdb) |
<SOURCE_SID> |
Source ORACLE_SID on the source host |
<SOURCE_SCAN_HOST> |
Source listener/SCAN hostname or VIP |
<SOURCE_DB_SERVICE> |
Source database service registered with the source listener |
<SOURCE_DB_BLOCK_SIZE> |
Source DB_BLOCK_SIZE (e.g., 8192) — destination auxiliary must match |
<SOURCE_DATAFILE_PATH> |
Source datafile directory, for the Phase 4 optional explicit datafile mapping (e.g., /u02/oradata/PRODCDB/); add one convert pair per source mount point |
<SOURCE_REDO_LOG_PATH> |
Source online redo log directory, for the Phase 4 optional explicit log mapping (e.g., /u02/oradata/PRODCDB/) |
<DEST_DB_UNIQUE_NAME> |
Destination DB_UNIQUE_NAME (e.g., prodexa) |
<DEST_SCAN_HOST> |
Exadata SCAN name |
<AUX_INSTANCE> |
Auxiliary instance ORACLE_SID on the destination during the build |
<AUX_AUDIT_DIR> |
Auxiliary audit_file_dest, created on disk before STARTUP NOMOUNT (e.g., /u01/app/oracle/admin/<AUX_INSTANCE>/adump) |
<AUX_STATIC_SERVICE> |
Static service name registered for the auxiliary (e.g., prodexa_DGMGRL or a dedicated <DEST_DB_UNIQUE_NAME>_aux) |
<LISTENER_PORT> |
Listener port, typically 1521 |
<DEST_ORACLE_HOME> |
Destination 19c RDBMS home (e.g., /u01/app/oracle/product/19.0.0/dbhome_1) |
<NODE1> / <NODE2> |
Exadata RAC node hostnames |
<INST1> / <INST2> |
RAC instance names (e.g., prodexa1, prodexa2) |
<DATA_DG> |
Data disk group with leading + (e.g., +DATA) |
<RECO_DG> |
Recovery disk group with leading + (e.g., +RECO) |
<DATA_DG_NAME> / <RECO_DG_NAME> |
Disk group names without the leading + (e.g., DATA, RECO) for srvctl -diskgroup |
<DG_NAME> |
ASM disk group hosting the ACFS volume (e.g., DATA) |
<VOLUME_NAME> |
ADVM volume name (e.g., XFER) |
<VOLUME_DEVICE> |
ADVM volume device path from volinfo (e.g., /dev/asm/xfer-123) |
<VOLUME_SIZE> |
ACFS volume size (small — e.g., 10G; staging holds only small files) |
<ACFS_MOUNT> |
ACFS mount point (= /acfs/transfer) |
<TRANSFER_USER> / <TRANSFER_GROUP> |
Locked-down SFTP transfer account / group |
<CHROOT_HOME> |
Writable drop directory inside the chroot (<ACFS_MOUNT>/<TRANSFER_USER>) |
<SYS_PASSWORD> |
The SYS password (identical on source and destination auxiliary) |
<DB_ACCOUNT> |
The privileged DB account used for the duplicate connection (e.g., SYS) |
<PROFILE_NAME> |
The profile assigned to <DB_ACCOUNT> |
<NEW_PASSWORD> |
The re-set password value (same value, to bump the expiry clock) |
<ORIGINAL_VALUE> |
The profile's original PASSWORD_LIFE_TIME to restore on revert (e.g., the default 180) |
<REDO_SIZE> |
Standby redo log size — ≥ the largest online redo log |
<SGA_TARGET> |
SGA_TARGET value sized for the destination instance (e.g., 48G) |
<PGA_TARGET> |
PGA_AGGREGATE_TARGET value for the destination instance (e.g., 16G) |
<RECO_SIZE> |
DB_RECOVERY_FILE_DEST_SIZE value for the destination FRA (e.g., 2T) |
<OLD_TS> |
Existing smallfile user tablespace being consolidated |
<NEW_TS> |
New BIGFILE tablespace target |
<PDB_NAME> |
A pluggable database name |
<SCHEMA> / <TABLE> / <INDEX> |
Object owner / table / index being relocated |
Assumptions
This instruction assumes:
- The source is a standalone single-instance Oracle 19c CDB (multitenant) with one or more PDBs, on Linux x86-64, datafiles on a flat filesystem, hundreds of datafiles, 200 GB–15 TB, no encryption/TDE.
- The destination is an Exadata 2-node RAC with Grid Infrastructure running, ASM disk groups
<DATA_DG>and<RECO_DG>present, and a 19c RDBMS home installed at the same Release Update as the source. - Source and destination are the same endianness (Linux x86-64 both); no cross-platform conversion.
DB_BLOCK_SIZEon the destination auxiliary will be set to match the source.- The operator is an experienced Exadata/RAC/Data Guard DBA working on the destination, with
SYS-over-SQL*Net access to the source on<LISTENER_PORT>, but no source OS access. - Oracle Net connectivity exists from the destination to the source SCAN/listener, and back from the source to the destination (for redo transport and FAL after the duplicate).
- The source has spare archived-redo capacity (or the third party can add it) to retain redo for the entire multi-day seed window plus margin.
- The source database uses an SPFILE (server parameter file) — mandatory for active database duplication, since
DUPLICATE … FROM ACTIVE DATABASE … SPFILEcopies it. Confirm on the source withSHOW PARAMETER spfile(the value must be non-empty); if the source runs on a text pfile, create an SPFILE first (CREATE SPFILE FROM PFILE;then restart) — a[3P]/[SYS]action. - A third party with shell access to the source is available to run copy-paste
[3P]commands. - Licensing is Enterprise Edition + RAC + Diagnostics and Tuning only.
Prerequisites
Automatic setup
No automatic or RPM package configures this migration. It is a manual DBA procedure end to end. The Exadata image already ships ASM, ACFS/ADVM, and OpenSSH; no package installation is required, only configuration.
Manual setup
The manual environment preparation is performed inside the phases below, because the setup is staged across three machines and three audiences and depends on values that surface during discovery:
- Source (
[SYS]):FORCE LOGGING, ARCHIVELOG confirmation, standby redo logs,REMOTE_LOGIN_PASSWORDFILE, migration-account non-expiry — Phase 2. - Source (
[3P]): real password file (orapwd format=12), archived-redo retention sizing, OS account aging, destination name resolution intnsnames.ora— Phase 2. - Destination auxiliary (
[DEST]): minimal pfile (matching block size,ENABLE_PLUGGABLE_DATABASE=TRUE), password file (sameSYSpassword), static listener registration,STARTUP NOMOUNT— Phase 3. - Destination staging (
[DEST]): temporary ACFS volume for small files plus a locked-down SFTP chroot account on both RAC nodes — Phase 1.
Additional setup
- Enterprise Edition + RAC + Diagnostics and Tuning licenses on the destination.
- Sufficient
<DATA_DG>/<RECO_DG>capacity for the full database plus the Fast Recovery Area. - Set destination environment variables before each
[DEST]block:
[DEST]
export ORACLE_HOME=<DEST_ORACLE_HOME>
export ORACLE_SID=<AUX_INSTANCE>
export PATH=$ORACLE_HOME/bin:$PATH
Phase 0 — Discovery and compliance go/no-go
Independently confirm, before touching anything, that the source uses no chargeable option and no encryption. This phase is container-aware: run option/pack and encryption checks across CDB$ROOT and every PDB, using CDB_* views for container-wide coverage. Any usage of Partitioning, non-BASIC compression, Advanced Security, In-Memory, Spatial, OLAP, Label Security, or Database Vault is a hard compliance blocker — stop and escalate. AWR/ASH/SQL Tuning usage is fine (Diagnostics and Tuning is licensed).
Step 1: Run the option/pack usage script (MOS 1317265.1)
Obtain options_packs_usage_statistics.sql from My Oracle Support Doc ID 1317265.1 and run its contents in a local SQLPlus session connected as SYS to the source. The script's SPOOL writes its report locally on the destination where you run SQLPlus.
[SYS]
sqlplus sys/<SYS_PASSWORD>@//<SOURCE_SCAN_HOST>:<LISTENER_PORT>/<SOURCE_DB_SERVICE> as sysdba
[SYS]
@options_packs_usage_statistics.sql
Review the generated report. Any line flagging Partitioning, Advanced Compression (non-BASIC), Advanced Security, In-Memory, Spatial, OLAP, Label Security, or Database Vault is a blocker.
Step 2: Check feature usage container-wide
[SYS]
SELECT name, version, detected_usages, currently_used, first_usage_date, last_usage_date
FROM cdb_feature_usage_statistics
WHERE detected_usages > 0
ORDER BY currently_used DESC;
Expected output:
NAME VERSION DETECTED_USAGES CURRENTLY_USED ...
Automatic SQL Tuning Advisor 19.0.0.0.0 1234 TRUE ...
Real Application Clusters (RAC) 19.0.0.0.0 0 FALSE ...
No row for Partitioning, Advanced Compression, Advanced Security, In-Memory, Spatial/Spatial and Graph, OLAP, Label Security, or Database Vault with CURRENTLY_USED = TRUE.
Step 3: Check linked-in options
[SYS]
SELECT parameter, value
FROM v$option
WHERE parameter IN ('Partitioning','Advanced Compression','Real Application Clusters',
'Advanced Security','Oracle Database Vault','Oracle Label Security',
'In-Memory Column Store','Spatial','OLAP');
Real Application Clusters may read FALSE on a standalone source (expected). Partitioning, Advanced Compression, Advanced Security, Oracle Database Vault, Oracle Label Security, In-Memory Column Store, Spatial, and OLAP being TRUE only means the option is linked into the binary, not that it is used; combine this with the usage checks. Actual usage of any of those is a blocker.
Step 4: Detect non-BASIC table compression (CDB-wide)
[SYS]
SELECT compression, compress_for, COUNT(*)
FROM cdb_tables
WHERE compression = 'ENABLED'
GROUP BY compression, compress_for;
Expected output:
COMPRESSION COMPRESS_FOR COUNT(*)
----------- -------------- --------
ENABLED BASIC 42
Any COMPRESS_FOR other than BASIC (for example ADVANCED, QUERY LOW, QUERY HIGH, ARCHIVE LOW, ARCHIVE HIGH) is an Advanced Compression / HCC usage and is a blocker.
Step 5: Detect partitioned objects (CDB-wide)
[SYS]
SELECT con_id, COUNT(*) AS partitioned_tables
FROM cdb_part_tables
GROUP BY con_id;
Expected output:
no rows selected
Any partitioned tables are a Partitioning-option blocker for this license set. If found, stop and escalate.
Step 6: Verify encryption absence in every container
Run the encryption checks in CDB$ROOT and in each PDB (or use CON_ID from the V$ views as below). All must show no encryption.
[SYS]
SELECT con_id, wrl_type, status, wallet_type FROM v$encryption_wallet;
SELECT * FROM v$encryption_keys;
SELECT con_id, masterkey_activated FROM v$database_key_info;
SELECT * FROM v$encrypted_tablespaces;
SELECT tablespace_name, encrypted FROM cdb_tablespaces WHERE encrypted = 'YES';
SELECT * FROM cdb_encrypted_columns;
Expected output:
-- v$encryption_wallet: STATUS = NOT_AVAILABLE for each container
-- v$encryption_keys: no rows selected
-- v$database_key_info: MASTERKEY_ACTIVATED = NO
-- v$encrypted_tablespaces: no rows selected
-- cdb_tablespaces WHERE encrypted='YES': no rows selected
-- cdb_encrypted_columns: no rows selected
Note:
TABLESPACE_ENCRYPTIONand similar parameters/views vary by Release Update. Rely on whichever encryption views exist in the installed RU; treat the absence of a newer view gracefully rather than as a failure.
Step 7: Confirm no network encryption or SQLNET compression on the source
Hand this to the third party to inspect the source sqlnet.ora.
[3P]
grep -Ei 'SQLNET\.ENCRYPTION_|SQLNET\.CRYPTO_CHECKSUM_|SQLNET\.COMPRESSION' \
$ORACLE_HOME/network/admin/sqlnet.ora
Expected output:
(no matching lines — the file has none of these parameters set)
Any SQLNET.ENCRYPTION_*, SQLNET.CRYPTO_CHECKSUM_*, or SQLNET.COMPRESSION present must be evaluated: SQLNET.COMPRESSION is an Advanced Compression feature and must remain off for this migration.
Phase 1 — Destination temporary ACFS staging and SFTP chroot account (both nodes)
Create a small ACFS volume on the destination to receive small files (password file, tnsnames.ora, pfile/spfile) from the source, plus a locked-down SFTP-based file-transfer chroot account. Staging is for small files only — a 15 TB backup over a ~1 Gbps link is infeasible to ship this way; the bulk data moves over SQL*Net via RMAN active duplication in Phase 4. General-purpose ACFS staging is supported on Exadata.
Step 1: Create and mount the ACFS volume
[DEST]
asmcmd volcreate -G <DG_NAME> -s <VOLUME_SIZE> <VOLUME_NAME>
asmcmd volinfo -G <DG_NAME> <VOLUME_NAME>
Expected output:
Diskgroup Name: DATA
Volume Name: XFER
Volume Device: /dev/asm/xfer-123
State: ENABLED
...
Record the Volume Device as <VOLUME_DEVICE>. Then, as root:
[DEST]
/sbin/mkfs -t acfs <VOLUME_DEVICE>
mkdir -p <ACFS_MOUNT>
/bin/mount -t acfs <VOLUME_DEVICE> <ACFS_MOUNT>
/sbin/acfsutil registry -a <VOLUME_DEVICE> <ACFS_MOUNT>
Registering with acfsutil registry (or srvctl add filesystem) makes the mount cluster-managed across both nodes.
Expected output:
acfsutil registry: mount point <ACFS_MOUNT> successfully added to Oracle Registry
Step 2: Create the chroot directory tree with correct ownership
Warning: OpenSSH
ChrootDirectoryrefuses to operate if the chroot directory or any of its parents is owned by a non-root user or is group/world-writable. A login will fail withbad ownership or modes for chroot directory. The chroot path itself must beroot:rootandchmod 755; only a subdirectory inside it may be writable by the transfer user.
As root on both nodes:
[DEST]
chown root:root <ACFS_MOUNT>
chmod 755 <ACFS_MOUNT>
groupadd <TRANSFER_GROUP>
useradd -g <TRANSFER_GROUP> -d <CHROOT_HOME> -s /sbin/nologin <TRANSFER_USER>
mkdir -p <CHROOT_HOME>
chown <TRANSFER_USER>:<TRANSFER_GROUP> <CHROOT_HOME>
chmod 750 <CHROOT_HOME>
<CHROOT_HOME> is <ACFS_MOUNT>/<TRANSFER_USER> — the only writable drop directory, inside the root-owned chroot.
Step 3: Configure the SFTP-based chroot in sshd
As root on both nodes, append a Match block to /etc/ssh/sshd_config:
[DEST]
Match User <TRANSFER_USER>
ChrootDirectory <ACFS_MOUNT>
ForceCommand internal-sftp
AllowTcpForwarding no
X11Forwarding no
Validate and reload on both nodes:
[DEST]
sshd -t
systemctl reload sshd
sshd -t produces no output on success. Modern scp uses the SFTP subsystem and therefore works against this account; legacy scp -O (the old SCP protocol) is blocked.
Step 4: Stage small files from the source
After the source password file and pfile exist (Phase 2/3), the third party copies the small files into the chroot drop directory.
[3P]
scp ./tnsnames.ora ./spfile<SOURCE_SID>.ora ./orapw<SOURCE_SID> \
<TRANSFER_USER>@<DEST_SCAN_HOST>:/
Expected output:
tnsnames.ora 100% ...
spfilePRODCDB.ora 100% ...
orapwPRODCDB 100% ...
The / destination resolves to the writable drop directory inside the chroot.
Phase 2 — Source-side prerequisites
Prepare the source for redo transport and remote duplication. Database changes are [SYS]; OS-level actions and any restart are [3P]. Set the migration-account non-expiry now, before the multi-day seed, so the password cannot expire mid-build.
Step 1: Enable FORCE LOGGING and confirm ARCHIVELOG mode
[SYS]
ALTER DATABASE FORCE LOGGING;
SELECT log_mode, force_logging FROM v$database;
Expected output:
LOG_MODE FORCE_LOGGING
------------ -------------
ARCHIVELOG YES
If LOG_MODE is NOARCHIVELOG, hand the third party Step 6 to place the source in ARCHIVELOG mode first.
Step 2: Add standby redo logs on the source
Standby redo logs are required on the source so it can act as a standby after switchover. Count per thread = (online log groups per thread) + 1; size ≥ the largest online log. Repeat the statement for each required group.
[SYS]
ALTER DATABASE ADD STANDBY LOGFILE THREAD 1 SIZE <REDO_SIZE>;
Confirm:
[SYS]
SELECT group#, thread#, bytes/1024/1024 AS mb, status FROM v$standby_log;
Expected output:
GROUP# THREAD# MB STATUS
---------- ---------- ---------- ----------
11 1 1024 UNASSIGNED
12 1 1024 UNASSIGNED
...
Step 3: Make the migration account non-expiring
Do not assume SYS is exempt from ORA-28001. Set the profile to unlimited password life and re-set the password (same value) to reset the expiry clock.
[SYS]
SELECT username, profile, account_status, expiry_date
FROM dba_users WHERE username = '<DB_ACCOUNT>';
[SYS]
ALTER PROFILE <PROFILE_NAME> LIMIT PASSWORD_LIFE_TIME UNLIMITED;
ALTER USER <DB_ACCOUNT> IDENTIFIED BY <NEW_PASSWORD>;
Expected output:
Profile altered.
User altered.
Step 4: Create a real password file and set REMOTE_LOGIN_PASSWORDFILE
Hand this to the third party on the source host. A real (orapwd) password file is required for the remote SYS connection used by RMAN.
[3P]
orapwd file=$ORACLE_HOME/dbs/orapw$ORACLE_SID password=<SYS_PASSWORD> format=12 force=y
[SYS]
SHOW PARAMETER remote_login_passwordfile
If not already EXCLUSIVE:
[SYS]
ALTER SYSTEM SET REMOTE_LOGIN_PASSWORDFILE='EXCLUSIVE' SCOPE=SPFILE;
A change here requires a restart (Step 6).
Step 5: Remove OS password aging on the source oracle account
This step always runs, regardless of whether a restart is pending. If the source enforces OS account password aging, the oracle account password could expire mid-seed. Remove aging for the oracle account only while the migration runs.
[3P]
chage -M -1 oracle
Step 6: Place the source in ARCHIVELOG / set REMOTE_LOGIN_PASSWORDFILE and size redo retention (if needed)
Warning: This restarts the source instance. Schedule a maintenance window. Skip this step entirely if Step 1 already confirmed
ARCHIVELOGmode and noREMOTE_LOGIN_PASSWORDFILEchange is pending.
[3P]
sqlplus / as sysdba <<'EOF'
SHUTDOWN IMMEDIATE;
STARTUP MOUNT;
ALTER DATABASE ARCHIVELOG;
ALTER DATABASE OPEN;
EOF
Size the Fast Recovery Area / archive destination to retain redo for the entire multi-day seed window plus margin.
Step 7: Stage destination name resolution on the source for redo transport
After switchover the source becomes a standby and must ship redo to the destination via LOG_ARCHIVE_DEST_2='SERVICE=<DEST_DB_UNIQUE_NAME> ...' (Phase 5 Step 3). The SERVICE name must be resolvable on the source through an Oracle Net naming method to a connect descriptor that matches the destination listener. Hand the third party a tnsnames.ora stanza on the source that resolves <DEST_DB_UNIQUE_NAME> to the Exadata SCAN.
[3P]
# Append to $ORACLE_HOME/network/admin/tnsnames.ora on the source
<DEST_DB_UNIQUE_NAME> =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = <DEST_SCAN_HOST>)(PORT = <LISTENER_PORT>))
(CONNECT_DATA = (SERVER = DEDICATED)(SERVICE_NAME = <DEST_DB_UNIQUE_NAME>))
)
The destination must likewise resolve the source service for FAL (FAL_SERVER=<SOURCE_DB_UNIQUE_NAME>, Phase 5 Step 2). Ensure a matching <SOURCE_DB_UNIQUE_NAME> stanza exists in the destination tnsnames.ora pointing at <SOURCE_SCAN_HOST>:<LISTENER_PORT>/<SOURCE_DB_SERVICE>.
Phase 3 — Destination auxiliary instance preparation
Build a minimal NOMOUNT auxiliary on the destination to receive the duplicate. A NOMOUNT instance does not self-register with the listener, so a static listener entry is mandatory before DUPLICATE.
Step 1: Create a minimal auxiliary pfile
The
DB_BLOCK_SIZEMUST equal<SOURCE_DB_BLOCK_SIZE>. Because the source is a CDB, setENABLE_PLUGGABLE_DATABASE=TRUE.
[DEST]
*.db_name='<SOURCE_DB_NAME>'
*.db_unique_name='<DEST_DB_UNIQUE_NAME>'
*.db_block_size=<SOURCE_DB_BLOCK_SIZE>
*.enable_pluggable_database=TRUE
*.audit_file_dest='<AUX_AUDIT_DIR>'
*.db_create_file_dest='<DATA_DG>'
*.db_recovery_file_dest='<RECO_DG>'
*.db_recovery_file_dest_size=<RECO_SIZE>
*.compatible='19.0.0'
*.sga_target=<SGA_TARGET>
*.pga_aggregate_target=<PGA_TARGET>
Step 2: Create the auxiliary password file with the same SYS password
[DEST]
orapwd file=$ORACLE_HOME/dbs/orapw<AUX_INSTANCE> password=<SYS_PASSWORD> format=12 force=y
Step 3: Add a static listener entry for the auxiliary service
Add a SID_LIST entry to the destination listener for <AUX_STATIC_SERVICE>, then reload the listener.
[DEST]
SID_LIST_LISTENER =
(SID_LIST =
(SID_DESC =
(GLOBAL_DBNAME = <AUX_STATIC_SERVICE>)
(ORACLE_HOME = <DEST_ORACLE_HOME>)
(SID_NAME = <AUX_INSTANCE>)
)
)
[DEST]
lsnrctl reload
Step 4: Create the audit directory, then start the auxiliary in NOMOUNT
The
audit_file_destdirectory must exist on disk before startup, orSTARTUP NOMOUNTfails withORA-09925: Unable to create audit trail file. RMAN cannot create it for you.
[DEST]
mkdir -p <AUX_AUDIT_DIR>
sqlplus / as sysdba <<EOF
STARTUP NOMOUNT PFILE='$ORACLE_HOME/dbs/init<AUX_INSTANCE>.ora';
EOF
Expected output:
ORACLE instance started.
Total System Global Area ...
Step 5: Verify the static service is registered before duplicating
Do not issue
DUPLICATEuntil the static service shows inlsnrctl status. A NOMOUNT instance only appears via the static entry.
[DEST]
lsnrctl status
Expected output:
Service "<AUX_STATIC_SERVICE>" has 1 instance(s).
Instance "<AUX_INSTANCE>", status UNKNOWN, has 1 handler(s) for this service...
The command completed successfully
status UNKNOWN is normal for a statically registered NOMOUNT instance.
Phase 4 — RMAN active duplication for standby
Build the standby directly from the running source over SQL*Net. With the target connected by a net service name, RMAN uses the bandwidth-efficient backup-set (pull) method when the command includes USING [COMPRESSED] BACKUPSET or SECTION SIZE, or when the number of auxiliary channels is greater than or equal to the number of target channels — these are independent triggers, and this command satisfies both. The whole CDB (all PDBs) is duplicated. Because this is a standby duplication, RMAN automatically copies the source password file to the standby (overwriting the temporary one created in Phase 3) — no PASSWORD FILE clause is needed. The result is a mounted standby; redo apply is not yet started.
Step 1: Connect RMAN to target and auxiliary
[DEST]
rman
[DEST]
RMAN> CONNECT TARGET sys/<SYS_PASSWORD>@<SOURCE_SCAN_HOST>:<LISTENER_PORT>/<SOURCE_DB_SERVICE>;
RMAN> CONNECT AUXILIARY sys/<SYS_PASSWORD>@<DEST_SCAN_HOST>:<LISTENER_PORT>/<AUX_STATIC_SERVICE>;
Expected output:
connected to target database: PRODCDB (DBID=...)
connected to auxiliary database: PRODCDB (not mounted)
Step 2: Set BASIC compression
Use
BASIConly.LOW,MEDIUM, andHIGHrequire the Advanced Compression Option and are out of license scope.
[DEST]
RMAN> CONFIGURE COMPRESSION ALGORITHM 'BASIC';
Step 3: Run the duplicate
NOFILENAMECHECK is required because the source and destination are different hosts. CLUSTER_DATABASE='FALSE' is correct here and stays FALSE through switchover — the database remains single-instance until the RAC conversion in Phase 8, which runs on the open primary. Use USING COMPRESSED BACKUPSET (BASIC) by default; use USING BACKUPSET (no compression) instead if source CPU is constrained.
Relocating the flat-file datafiles into ASM. Because the source is on a non-OMF filesystem, always set DB_CREATE_FILE_DEST, DB_CREATE_ONLINE_LOG_DEST_1, and DB_RECOVERY_FILE_DEST (as in the command below). They place the control file, the online/standby redo logs, and every datafile into ASM as Oracle-Managed Files and auto-name them — no per-path mapping — which is the recommended default for hundreds of files. Oracle's guidance is explicit: "Set DB_CREATE_FILE_DEST explicitly… Do not rely solely on DB_FILE_NAME_CONVERT."
If you additionally need to map specific source datafile/redo paths explicitly, add DB_FILE_NAME_CONVERT / LOG_FILE_NAME_CONVERT on top (see the optional variant after the command). They take precedence for the files they match, while DB_CREATE_FILE_DEST still places the control file and anything unmatched — the *_CONVERT parameters do not relocate the control file. Combining them is valid here because the source is non-OMF: the "do not set DB_FILE_NAME_CONVERT if you set DB_CREATE_FILE_DEST" caution in the reference applies only to OMF sources, and Oracle's own ASM duplication example sets both together.
[DEST] — the duplicate command:
RMAN> RUN {
ALLOCATE CHANNEL t1 TYPE DISK;
ALLOCATE CHANNEL t2 TYPE DISK;
ALLOCATE CHANNEL t3 TYPE DISK;
ALLOCATE CHANNEL t4 TYPE DISK;
ALLOCATE AUXILIARY CHANNEL a1 TYPE DISK;
ALLOCATE AUXILIARY CHANNEL a2 TYPE DISK;
ALLOCATE AUXILIARY CHANNEL a3 TYPE DISK;
ALLOCATE AUXILIARY CHANNEL a4 TYPE DISK;
DUPLICATE TARGET DATABASE FOR STANDBY FROM ACTIVE DATABASE
USING COMPRESSED BACKUPSET
SECTION SIZE 8G
SPFILE
SET DB_UNIQUE_NAME='<DEST_DB_UNIQUE_NAME>'
SET DB_CREATE_FILE_DEST='<DATA_DG>'
SET DB_CREATE_ONLINE_LOG_DEST_1='<DATA_DG>'
SET DB_RECOVERY_FILE_DEST='<RECO_DG>'
SET CLUSTER_DATABASE='FALSE'
SET STANDBY_FILE_MANAGEMENT='AUTO'
NOFILENAMECHECK
DORECOVER;
}
[DEST] — optional explicit path mapping: to map specific source datafile/redo paths instead of letting OMF auto-name them, add these two lines to the SPFILE SET block above (keep DB_CREATE_FILE_DEST/DB_RECOVERY_FILE_DEST — they place the control file and any unmatched files):
SET DB_FILE_NAME_CONVERT='<SOURCE_DATAFILE_PATH>','<DATA_DG>'
SET LOG_FILE_NAME_CONVERT='<SOURCE_REDO_LOG_PATH>','<DATA_DG>'
If datafiles span multiple source mount points, add one
'source','+DATA'pair per path, e.g.SET DB_FILE_NAME_CONVERT='/u02/oradata/<SOURCE_DB_NAME>/','<DATA_DG>','/u03/oradata/<SOURCE_DB_NAME>/','<DATA_DG>'.
Expected output:
Finished Duplicate Db at ...
Step 4: Confirm the standby is mounted
[DEST]
SELECT db_unique_name, database_role, open_mode FROM v$database;
Expected output:
DB_UNIQUE_NAME DATABASE_ROLE OPEN_MODE
---------------- ----------------- --------------------
prodexa PHYSICAL STANDBY MOUNTED
Phase 5 — Data Guard configuration, redo apply, and gap catch-up
Add standby redo logs on the destination standby (required for redo transport and real-time apply), wire up redo transport and FAL on both sides, start managed recovery, then optionally enable the broker. The standby stays single-instance (CLUSTER_DATABASE=FALSE) throughout — RAC conversion happens after switchover in Phase 8.
Step 1: Add standby redo logs on the destination standby
[DEST]
ALTER DATABASE ADD STANDBY LOGFILE THREAD 1 SIZE <REDO_SIZE>;
Repeat for the required count (online groups per thread + 1). Confirm with SELECT group#, thread#, bytes FROM v$standby_log;.
Step 2: Set redo transport and FAL parameters on the standby
[DEST]
ALTER SYSTEM SET LOG_ARCHIVE_CONFIG='DG_CONFIG=(<SOURCE_DB_UNIQUE_NAME>,<DEST_DB_UNIQUE_NAME>)';
ALTER SYSTEM SET LOG_ARCHIVE_DEST_2='SERVICE=<SOURCE_DB_UNIQUE_NAME> ASYNC VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=<SOURCE_DB_UNIQUE_NAME>';
ALTER SYSTEM SET FAL_SERVER='<SOURCE_DB_UNIQUE_NAME>';
ALTER SYSTEM SET FAL_CLIENT='<DEST_DB_UNIQUE_NAME>';
ALTER SYSTEM SET STANDBY_FILE_MANAGEMENT='AUTO';
SERVICE=<SOURCE_DB_UNIQUE_NAME> and FAL_SERVER='<SOURCE_DB_UNIQUE_NAME>' must resolve on the destination through an Oracle Net naming method to the source listener (the destination tnsnames.ora stanza referenced in Phase 2 Step 7).
Step 3: Set the matching transport parameters on the source
SERVICE=<DEST_DB_UNIQUE_NAME> and FAL_SERVER='<DEST_DB_UNIQUE_NAME>' must resolve on the source — staged in Phase 2 Step 7.
[SYS]
ALTER SYSTEM SET LOG_ARCHIVE_CONFIG='DG_CONFIG=(<SOURCE_DB_UNIQUE_NAME>,<DEST_DB_UNIQUE_NAME>)';
ALTER SYSTEM SET LOG_ARCHIVE_DEST_2='SERVICE=<DEST_DB_UNIQUE_NAME> ASYNC VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=<DEST_DB_UNIQUE_NAME>';
ALTER SYSTEM SET FAL_SERVER='<DEST_DB_UNIQUE_NAME>';
ALTER SYSTEM SET FAL_CLIENT='<SOURCE_DB_UNIQUE_NAME>';
Step 4: Start managed recovery with real-time apply
[DEST]
ALTER DATABASE RECOVER MANAGED STANDBY DATABASE DISCONNECT FROM SESSION;
Expected output:
Database altered.
Step 5: Confirm apply is running and the gap is closing
[DEST]
SELECT process, status, thread#, sequence# FROM v$managed_standby WHERE process LIKE 'MRP%';
SELECT name, value FROM v$dataguard_stats WHERE name IN ('transport lag','apply lag');
SELECT * FROM v$archive_gap;
Expected output:
PROCESS STATUS THREAD# SEQUENCE#
--------- ------------ -------- ---------
MRP0 APPLYING_LOG 1 4567
NAME VALUE
-------------- -------------
transport lag +00 00:00:00
apply lag +00 00:00:00
(v$archive_gap: no rows selected)
Step 6 (optional): Enable the broker
The broker is optional for this migration — Phase 7 provides a SQL*Plus switchover path that does not require it. If you choose to use the broker, it requires static _DGMGRL listener entries on both sides. After adding those entries:
[DEST]
DGMGRL> CONNECT sys/<SYS_PASSWORD>
DGMGRL> CREATE CONFIGURATION 'dg_config' AS PRIMARY DATABASE IS '<SOURCE_DB_UNIQUE_NAME>' CONNECT IDENTIFIER IS '<SOURCE_DB_SERVICE>';
DGMGRL> ADD DATABASE '<DEST_DB_UNIQUE_NAME>' AS CONNECT IDENTIFIER IS '<DEST_DB_UNIQUE_NAME>';
DGMGRL> ENABLE CONFIGURATION;
Optional convenience note: DBCA can wrap the duplicate with
-createDuplicateDB -createAsStandby. These flags are documented in the 19c Data Guard Concepts and Administration guide (section 3.4) and produce only a mounted standby — no standby redo logs, no managed apply, no broker. They are not a substitute for the configuration work in this phase. The primary path here uses explicit RMAN duplication plus the steps above.
Phase 6 — Pre-switchover gap verification
Quiesce the apply pipeline state to a clean, verifiable point immediately before cutover. The standby is still single-instance (CLUSTER_DATABASE=FALSE); no structural RAC work happens here. Stop managed recovery, confirm the gap is closed, then proceed to switchover.
Step 1: Stop managed recovery
[DEST]
ALTER DATABASE RECOVER MANAGED STANDBY DATABASE CANCEL;
Step 2: Verify the apply gap before switchover
[DEST]
SELECT name, value FROM v$dataguard_stats WHERE name IN ('transport lag','apply lag');
SELECT * FROM v$archive_gap;
Expected output:
NAME VALUE
-------------- -------------
transport lag +00 00:00:00
apply lag +00 00:00:00
(v$archive_gap: no rows selected)
Both lags at or near zero and no archive gap is the green light for switchover. The VERIFY option of the switchover statement (Phase 7) re-checks readiness, but confirm here first.
Note: Switchover requires Redo Apply to be active on the target standby. Re-enable managed recovery before issuing the switchover so the target is in the required state:
[DEST]
ALTER DATABASE RECOVER MANAGED STANDBY DATABASE DISCONNECT FROM SESSION;
Phase 7 — Switchover (cutover)
Warning: Switchover transfers the primary role. Confirm apply lag is ~0 and there is no archive gap (Phase 6 Step 2) before proceeding. The application outage begins now and lasts seconds to minutes.
The SQL*Plus path (Step 1) is the primary, broker-independent method and works whether or not the broker is configured. If you enabled the broker in Phase 5 Step 6, use the DGMGRL alternative (Step 2) instead — do not run both.
Step 1: Switch over using SQL*Plus (primary method)
The single-command switchover prerequisites are: the primary is open and Redo Apply is active on the target standby. Run VERIFY first, then the switchover. Both statements run on the current primary (the source), so they are [SYS].
[SYS]
ALTER DATABASE SWITCHOVER TO <DEST_DB_UNIQUE_NAME> VERIFY;
Expected output:
Database altered.
Database altered. means the target is ready. ORA-16470: Redo Apply is not running on switchover target means managed recovery is not active on the destination — start it (Phase 6 final note) and re-run VERIFY. After a clean VERIFY, perform the switchover:
[SYS]
ALTER DATABASE SWITCHOVER TO <DEST_DB_UNIQUE_NAME>;
Expected output:
Database altered.
After this statement the original primary (source) transitions to the physical standby role with no shutdown/restart. The destination becomes the primary but is still mounted (it was a managed-recovery standby, not Active Data Guard). Open it on the destination:
[DEST]
ALTER DATABASE OPEN;
Expected output:
Database altered.
The former source is now a mounted physical standby; redo apply does not auto-start with the SQL path. Re-start managed recovery on the new standby (remote SYS to the former source) so it applies redo from the new primary:
[SYS]
ALTER DATABASE RECOVER MANAGED STANDBY DATABASE DISCONNECT FROM SESSION;
Expected output:
Database altered.
Skip Step 2 — the switchover is complete. Proceed to Phase 8.
Step 2: Switch over using DGMGRL (broker alternative)
Use this only if you enabled the broker in Phase 5 Step 6. DGMGRL opens the new primary automatically.
[DEST]
DGMGRL> SWITCHOVER TO <DEST_DB_UNIQUE_NAME>;
Expected output:
Performing switchover NOW, please wait...
Switchover succeeded, new primary is "<DEST_DB_UNIQUE_NAME>"
Step 3: Verify the destination is now an open primary
[DEST]
SELECT db_unique_name, database_role, open_mode FROM v$database;
Expected output:
DB_UNIQUE_NAME DATABASE_ROLE OPEN_MODE
---------------- -------------- --------------------
prodexa PRIMARY READ WRITE
Phase 8 — RAC conversion on the open primary
Convert the now-open single-instance primary into a 2-node RAC database. Structural changes — adding a second redo thread, its standby redo logs, and a second undo tablespace — run against the open database. Set the cluster and per-instance parameters in the SPFILE, register the database and both instances with CRS as a PRIMARY started OPEN, then bounce under Clusterware so the second instance comes up.
Step 1: Add the second redo thread, its standby redo logs, and a second UNDO
Run on the open primary. Each RAC instance requires its own redo thread and its own undo tablespace.
[DEST]
ALTER DATABASE ADD LOGFILE THREAD 2 SIZE <REDO_SIZE>;
ALTER DATABASE ADD STANDBY LOGFILE THREAD 2 SIZE <REDO_SIZE>;
ALTER DATABASE ENABLE PUBLIC THREAD 2;
CREATE UNDO TABLESPACE UNDOTBS2 DATAFILE '<DATA_DG>' SIZE 4G AUTOEXTEND ON;
Repeat the redo/standby-redo statements to reach the required group count per thread.
Step 2: Move the SPFILE to ASM and set the cluster and per-instance parameters
[DEST]
CREATE SPFILE='<DATA_DG>/<DEST_DB_UNIQUE_NAME>/spfile<DEST_DB_UNIQUE_NAME>.ora' FROM PFILE;
Set CLUSTER_DATABASE=TRUE and the unique per-instance values (INSTANCE_NUMBER, THREAD, UNDO_TABLESPACE must be unique per instance):
[DEST]
ALTER SYSTEM SET CLUSTER_DATABASE=TRUE SCOPE=SPFILE;
ALTER SYSTEM SET INSTANCE_NUMBER=1 SID='<INST1>' SCOPE=SPFILE;
ALTER SYSTEM SET INSTANCE_NUMBER=2 SID='<INST2>' SCOPE=SPFILE;
ALTER SYSTEM SET THREAD=1 SID='<INST1>' SCOPE=SPFILE;
ALTER SYSTEM SET THREAD=2 SID='<INST2>' SCOPE=SPFILE;
ALTER SYSTEM SET UNDO_TABLESPACE='UNDOTBS1' SID='<INST1>' SCOPE=SPFILE;
ALTER SYSTEM SET UNDO_TABLESPACE='UNDOTBS2' SID='<INST2>' SCOPE=SPFILE;
Step 3: Register the database and instances with CRS
The database is now a primary, so register it with -role PRIMARY and -startoption OPEN (both are the srvctl add database defaults). -diskgroup names omit the leading +.
[DEST]
srvctl add database -db <DEST_DB_UNIQUE_NAME> -oraclehome <DEST_ORACLE_HOME> \
-spfile <DATA_DG>/<DEST_DB_UNIQUE_NAME>/spfile<DEST_DB_UNIQUE_NAME>.ora \
-pwfile $ORACLE_HOME/dbs/orapw<AUX_INSTANCE> \
-dbtype RAC -role PRIMARY -startoption OPEN \
-diskgroup "<DATA_DG_NAME>,<RECO_DG_NAME>"
srvctl add instance -db <DEST_DB_UNIQUE_NAME> -instance <INST1> -node <NODE1>
srvctl add instance -db <DEST_DB_UNIQUE_NAME> -instance <INST2> -node <NODE2>
Step 4: Shut down the manually-started instance before Clusterware takes over
Warning: Shut down the instance that was started manually (the single instance running since the duplicate) before starting the database under Clusterware. Starting under CRS while a manually-started instance is up will collide on the same instance.
[DEST]
SHUTDOWN IMMEDIATE;
Step 5: Start the database under CRS
[DEST]
srvctl start database -db <DEST_DB_UNIQUE_NAME>
srvctl status database -db <DEST_DB_UNIQUE_NAME>
Expected output:
Instance <INST1> is running on node <NODE1>
Instance <INST2> is running on node <NODE2>
Step 6: Verify roles on both instances
[DEST]
SELECT inst_id, database_role, open_mode FROM gv$database;
Expected output:
INST_ID DATABASE_ROLE OPEN_MODE
---------- ---------------- --------------------
1 PRIMARY READ WRITE
2 PRIMARY READ WRITE
Phase 9 — Post-cutover memory parameters and BIGFILE consolidation
Set memory parameters on the CDB root, then consolidate user tablespaces into BIGFILE tablespaces per-PDB using online segment relocation. Introduce no compression: preserve every segment's existing compression attributes on move.
Step 1: Set memory parameters at the CDB root
Run in CDB$ROOT.
[DEST]
ALTER SYSTEM SET SGA_TARGET=<SGA_TARGET> SCOPE=SPFILE SID='*';
ALTER SYSTEM SET PGA_AGGREGATE_TARGET=<PGA_TARGET> SCOPE=SPFILE SID='*';
Bounce per instance with srvctl if the values require a restart.
Step 2: Generate the relocation list per PDB (with an exclusion list)
Switch into the target PDB first. State the container context before each divergent command.
[DEST]
ALTER SESSION SET CONTAINER = <PDB_NAME>;
Build the list of user segments to move, excluding SYSTEM, SYSAUX, UNDO, and TEMP (those are handled separately or skipped) so no MOVE is generated for dictionary segments. Exclude partitioned objects (none should exist — Phase 0 flags them).
[DEST]
SELECT 'ALTER TABLE "'||owner||'"."'||table_name||'" MOVE ONLINE TABLESPACE <NEW_TS>;'
FROM dba_tables
WHERE owner NOT IN ('SYS','SYSTEM')
AND tablespace_name NOT IN ('SYSTEM','SYSAUX')
AND tablespace_name NOT LIKE 'UNDO%'
AND tablespace_name NOT LIKE 'TEMP%'
AND (partitioned = 'NO' OR partitioned IS NULL)
AND nested = 'NO'
AND iot_type IS NULL
UNION ALL
SELECT 'ALTER INDEX "'||owner||'"."'||index_name||'" REBUILD ONLINE TABLESPACE <NEW_TS>;'
FROM dba_indexes
WHERE owner NOT IN ('SYS','SYSTEM')
AND tablespace_name NOT IN ('SYSTEM','SYSAUX')
AND partitioned = 'NO';
Step 3: Create the BIGFILE tablespace and relocate segments online
For each smallfile user tablespace <OLD_TS>:
[DEST]
CREATE BIGFILE TABLESPACE <NEW_TS> DATAFILE '<DATA_DG>' SIZE 100G AUTOEXTEND ON NEXT 8G MAXSIZE UNLIMITED;
Run the generated relocation statements (Step 2 output):
[DEST]
ALTER TABLE "<SCHEMA>"."<TABLE>" MOVE ONLINE TABLESPACE <NEW_TS>;
ALTER INDEX "<SCHEMA>"."<INDEX>" REBUILD ONLINE TABLESPACE <NEW_TS>;
MOVE ONLINE and REBUILD ONLINE are core Enterprise Edition and introduce no chargeable option. Compression attributes carried by the segments are preserved unchanged.
Warning: Segments that online MOVE rejects — clusters, IOTs with LOBs, online LOB relocation, materialized views, and any partitioned tables found in Phase 0 — must be relocated with
DBMS_REDEFINITION, notALTER TABLE ... MOVE PARTITION.MOVE PARTITIONinvokes the Partitioning option, which is out of license scope.
For rejected segments:
[DEST]
BEGIN
DBMS_REDEFINITION.START_REDEF_TABLE(
uname => '<SCHEMA>',
orig_table => '<TABLE>',
int_table => '<TABLE>_INT',
options_flag => DBMS_REDEFINITION.CONS_USE_ROWID);
END;
/
Complete with COPY_TABLE_DEPENDENTS and FINISH_REDEF_TABLE per the package reference, targeting <NEW_TS>.
Step 4: Rename and drop the old tablespace
Warning:
DROP TABLESPACE ... INCLUDING CONTENTS AND DATAFILESpermanently deletes the datafiles. Verify all segments moved out of<OLD_TS>before dropping.
[DEST]
ALTER TABLESPACE <OLD_TS> RENAME TO <OLD_TS>_OLD;
ALTER TABLESPACE <NEW_TS> RENAME TO <OLD_TS>;
DROP TABLESPACE <OLD_TS>_OLD INCLUDING CONTENTS AND DATAFILES;
Step 5: Leave SYSTEM and SYSAUX as-is
SYSTEM and SYSAUX cannot be renamed, dropped, or taken offline. Leave them unchanged.
Step 6: Migrate UNDO to BIGFILE (per RAC instance, LOCAL_UNDO aware)
Check whether local undo is enabled:
[DEST]
SELECT property_value FROM database_properties WHERE property_name = 'LOCAL_UNDO_ENABLED';
With local undo on (TRUE), each PDB/instance has its own undo tablespace; set the container accordingly before each statement. Create a bigfile undo, switch the instance to it, then drop the old. Repeat per instance/thread.
Warning:
DROP TABLESPACE ... INCLUDING CONTENTS AND DATAFILESpermanently deletes the undo datafiles. Switch the instance to the new undo tablespace (ALTER SYSTEM SET UNDO_TABLESPACE) and confirm no active transactions reference the old one before dropping.
[DEST]
CREATE BIGFILE UNDO TABLESPACE UNDOTBS1_BF DATAFILE '<DATA_DG>' SIZE 8G AUTOEXTEND ON;
ALTER SYSTEM SET UNDO_TABLESPACE='UNDOTBS1_BF' SID='<INST1>';
DROP TABLESPACE UNDOTBS1 INCLUDING CONTENTS AND DATAFILES;
Step 7: Migrate TEMP to BIGFILE
Warning:
DROP TABLESPACE ... INCLUDING CONTENTS AND DATAFILESpermanently deletes the tempfiles. Set the new tablespace as the database default temporary tablespace before dropping the old one.
[DEST]
CREATE BIGFILE TEMPORARY TABLESPACE TEMP_BF TEMPFILE '<DATA_DG>' SIZE 32G AUTOEXTEND ON;
ALTER DATABASE DEFAULT TEMPORARY TABLESPACE TEMP_BF;
DROP TABLESPACE TEMP INCLUDING CONTENTS AND DATAFILES;
Phase 10 — Migration account revert and staging teardown
Optionally revert the password-expiry change, then tear down the chroot account and ACFS volume on both nodes.
Step 1 (optional): Revert password expiry on the new primary
The profile change made in Phase 2 lives in the database that was replicated and switched over, so the migration account's profile is now on the new primary (the destination). The revert therefore runs on the destination as [DEST], not against the old source. This is optional hardening — the profile change persists harmlessly through the migration. Restore the original PASSWORD_LIFE_TIME (e.g., the default 180):
[DEST]
ALTER PROFILE <PROFILE_NAME> LIMIT PASSWORD_LIFE_TIME <ORIGINAL_VALUE>;
Step 2: Remove the sshd Match block and reload (both nodes)
As root on both nodes, delete the Match User <TRANSFER_USER> block from /etc/ssh/sshd_config, then:
[DEST]
sshd -t
systemctl reload sshd
Step 3: Remove the transfer account and group (both nodes)
[DEST]
userdel -r <TRANSFER_USER>
groupdel <TRANSFER_GROUP>
Step 4: Shred staged secrets and dismount the ACFS volume
Warning:
acfsutil registry -dandvoldeletepermanently remove the staging filesystem and its ADVM volume.
[DEST]
shred -u <CHROOT_HOME>/orapw* <CHROOT_HOME>/spfile* <CHROOT_HOME>/tnsnames.ora
acfsutil snap info <ACFS_MOUNT>
umount <ACFS_MOUNT>
acfsutil registry -d <ACFS_MOUNT>
asmcmd voldisable -G <DG_NAME> <VOLUME_NAME>
asmcmd voldelete -G <DG_NAME> <VOLUME_NAME>
rmdir <ACFS_MOUNT>
Validation
Quick check
If you enabled the broker (Phase 5 Step 6), confirm the configuration:
[DEST]
DGMGRL> SHOW CONFIGURATION;
Expected output:
Configuration - dg_config
Protection Mode: MaxPerformance
Members:
prodexa - Primary database
prodcdb - Physical standby database
Configuration Status:
SUCCESS (status updated ...)
If the broker is not in use, confirm roles directly instead:
[DEST]
SELECT database_role, open_mode FROM v$database;
Expected output:
DATABASE_ROLE OPEN_MODE
-------------- ------------
PRIMARY READ WRITE
The new Exadata RAC database as primary confirms the migration completed.
Full validation
- Both instances open as primary
[DEST]
srvctl status database -db <DEST_DB_UNIQUE_NAME>
Expected output:
Instance <INST1> is running on node <NODE1>
Instance <INST2> is running on node <NODE2>
- Roles and cluster status
[DEST]
SELECT database_role, open_mode, cluster_database FROM v$database;
SELECT thread#, status, enabled FROM v$thread;
Expected output:
DATABASE_ROLE OPEN_MODE CLUSTER_DATABASE
-------------- ------------ ----------------
PRIMARY READ WRITE TRUE
THREAD# STATUS ENABLED
---------- ------ --------
1 OPEN PUBLIC
2 OPEN PUBLIC
- BIGFILE consolidation, per PDB
[DEST]
SELECT con_id, tablespace_name, bigfile FROM cdb_tablespaces WHERE bigfile = 'YES';
SELECT con_id, COUNT(*) AS datafiles FROM cdb_data_files GROUP BY con_id;
SELECT tablespace_name FROM dba_tablespaces WHERE tablespace_name IN ('SYSTEM','SYSAUX');
Expected output:
-- consolidated user tablespaces show BIGFILE = YES
-- cdb_data_files count is sharply lower than the original hundreds
-- SYSTEM and SYSAUX still present (left intact)
- Compliance re-check (no chargeable option introduced)
[DEST]
SELECT con_id, wrl_type, status FROM v$encryption_wallet;
SELECT compression, compress_for, COUNT(*) FROM cdb_tables WHERE compression='ENABLED' GROUP BY compression, compress_for;
Re-run options_packs_usage_statistics.sql (MOS 1317265.1) on the new primary and confirm no new chargeable option appears. Encryption views still show none; compression remains BASIC only.
- No archive gap on the new standby (the former source)
[SYS]
SELECT * FROM v$archive_gap;
SELECT process, status FROM v$managed_standby WHERE process LIKE 'MRP%';
Expected output:
(v$archive_gap: no rows selected)
PROCESS STATUS
--------- ------------
MRP0 APPLYING_LOG
Troubleshooting
| Problem | Cause | Solution |
|---|---|---|
RMAN-04006: error from auxiliary database / ORA-01017: invalid username/password on CONNECT AUXILIARY |
Password file missing or SYS password differs between source and destination auxiliary |
Recreate both password files with the same <SYS_PASSWORD>: orapwd file=$ORACLE_HOME/dbs/orapw<SID> password=<SYS_PASSWORD> format=12 force=y; set REMOTE_LOGIN_PASSWORDFILE=EXCLUSIVE on the source |
ORA-12514 / TNS:listener does not currently know of service when connecting to the auxiliary |
NOMOUNT instance did not self-register; no static listener entry | Add the SID_LIST static entry for <AUX_STATIC_SERVICE>, run lsnrctl reload, and confirm lsnrctl status shows the service before DUPLICATE |
ORA-19505: failed to identify file / file-naming errors during DUPLICATE |
DB_CREATE_FILE_DEST/DB_RECOVERY_FILE_DEST not set, NOFILENAMECHECK omitted with different hosts, or (with explicit mapping) a DB_FILE_NAME_CONVERT source path doesn't match the real file location |
Set DB_CREATE_FILE_DEST='<DATA_DG>' and DB_RECOVERY_FILE_DEST='<RECO_DG>' in the SPFILE SET clause and include NOFILENAMECHECK — these place the control file/logs even when explicit conversion is used. If using explicit DB_FILE_NAME_CONVERT, verify each 'source' path matches the real source datafile/redo directories (one pair per mount point); keep DB_CREATE_FILE_DEST set so the control file is still placed |
ORA-09925: Unable to create audit trail file on STARTUP NOMOUNT of the auxiliary |
audit_file_dest directory does not exist on disk |
mkdir -p <AUX_AUDIT_DIR> (matching audit_file_dest in the auxiliary pfile) before STARTUP NOMOUNT |
ORA-28001: the password has expired mid-build |
Migration account password aged out during the multi-day seed | Set ALTER PROFILE <PROFILE_NAME> LIMIT PASSWORD_LIFE_TIME UNLIMITED and re-set the password before starting (Phase 2 Step 3) |
Redo transport fails after switchover; ORA-12154/TNS:could not resolve the connect identifier in the standby alert log |
SERVICE/FAL_SERVER name not resolvable on the database where LOG_ARCHIVE_DEST_n is set |
Add the matching tnsnames.ora stanza (<DEST_DB_UNIQUE_NAME> on the source, <SOURCE_DB_UNIQUE_NAME> on the destination) pointing at the correct SCAN/service (Phase 2 Step 7) |
bad ownership or modes for chroot directory on SFTP login |
<ACFS_MOUNT> or a parent is not root:root or is group/world-writable |
chown root:root <ACFS_MOUNT>; chmod 755 <ACFS_MOUNT>; keep the writable drop dir as a user-owned subdir inside the chroot |
Standby falls behind; V$ARCHIVE_GAP shows missing sequences |
Source archived-redo retention too small for the seed window | Increase the source FRA / archive destination size; re-ship the gap via FAL_SERVER, or roll forward with RECOVER STANDBY DATABASE FROM SERVICE |
MRP not applying; V$MANAGED_STANDBY shows no MRP0 |
Managed recovery not started, or no standby redo logs on the destination | Add standby redo logs on the destination (Phase 5 Step 1), then ALTER DATABASE RECOVER MANAGED STANDBY DATABASE DISCONNECT FROM SESSION |
ORA-16470: Redo Apply is not running on switchover target on SWITCHOVER TO ... VERIFY |
Managed recovery is not active on the destination standby | Start managed recovery on the destination (Phase 6 final note), then re-run the VERIFY and switchover statements |
SWITCHOVER fails — ORA-16775 / target not ready |
Apply gap not zero, or standby redo logs missing on one side | Resolve the gap (lags ~0, no V$ARCHIVE_GAP) and ensure standby redo logs exist on both databases and both threads, then retry |
Second instance fails to start under srvctl start database |
A manually-started instance still holds the database, colliding with Clusterware | SHUTDOWN IMMEDIATE the manually-started instance (Phase 8 Step 4) before srvctl start database |
ALTER TABLE ... MOVE ONLINE reports that the operation is not supported for the segment |
Segment type (cluster, IOT-with-LOB, MV) not eligible for online move | Relocate with DBMS_REDEFINITION instead; never use MOVE PARTITION (Partitioning option is unlicensed) |
srvctl add database rejects the disk group argument |
Leading + included in -diskgroup |
Use disk group names without +: -diskgroup "<DATA_DG_NAME>,<RECO_DG_NAME>" |
References
- Duplicating a Database (Backup and Recovery User's Guide)
- DUPLICATE (RMAN Command Reference)
- CONFIGURE (RMAN Command Reference)
- RECOVER (RMAN Command Reference)
- Creating a Standby Database with RMAN (Data Guard Concepts and Administration)
- Creating a Physical Standby Database (Data Guard Concepts and Administration)
- Managing Oracle Data Guard Role Transitions
- Oracle Data Guard Redo Transport Services
- Using RMAN in an Oracle Data Guard Configuration
- Administering Database Instances and Cluster Databases (Real Application Clusters Administration)
- Server Control Utility Reference (Real Application Clusters Administration)
- Parameters for the sqlnet.ora File
- Managing Tablespaces (Administrator's Guide)
- CREATE TABLESPACE (SQL Language Reference)
- ALTER TABLE (SQL Language Reference)
- ALTER INDEX (SQL Language Reference)
- DROP TABLESPACE (SQL Language Reference)
- DBMS_REDEFINITION (PL/SQL Packages and Types Reference)
- Managing Undo (Administrator's Guide)
- UNDO_TABLESPACE (Database Reference)
- ALTER PROFILE (SQL Language Reference)
- Oracle Database Licensing Information User Manual (19c)
- Maximum Availability with Oracle ASM (Exadata)
- About Oracle ACFS Administration
- Steps to Manage Oracle ACFS
- Managing Oracle ADVM with ASMCMD
- Server Control (SRVCTL) Command Reference — add/remove filesystem
- OpenSSH sshd_config — ChrootDirectory, ForceCommand, Match
- How to Determine if an Option or Pack is in Use (MOS Doc ID 1317265.1)
- Oracle ACFS Support on Oracle Exadata (MOS Doc ID 1929629.1)