RDS Custom for Oracle end of support
Important
End of support notice: On March 31, 2027, Amazon will end support for Amazon RDS Custom for Oracle. After March 31, 2027, you will no longer be able to access the RDS Custom for Oracle console or RDS Custom for Oracle resources. For more information, see RDS Custom for Oracle end of support.
Overview
After careful consideration, Amazon has made the decision to discontinue the Amazon RDS Custom for Oracle service. The service will be deprecated effective March 31, 2027. This prescriptive guidance provides detailed migration strategies to help you migrate from RDS Custom for Oracle to self-managed Oracle databases on Amazon Elastic Compute Cloud (Amazon EC2).
Key timelimes
-
From March 31, 2026 to March 31, 2027: We recommend that you migrate from RDS Custom for Oracle to running Oracle on EC2. During this period, you can continue using RDS Custom for Oracle with existing features and support.
-
After March 31, 2027: You will no longer be able to use the RDS Custom for Oracle service.
Target audience
This guidance is intended for:
-
Database administrators responsible for Oracle database migrations
-
Cloud architects planning migration strategies
-
DevOps engineers managing database infrastructure
-
IT managers overseeing the migration process
Prerequisites
Before you begin, ensure you have:
-
An active Amazon RDS Custom for Oracle instance running Oracle 19c Enterprise Edition
-
Appropriate Amazon Identity and Access Management (IAM) permissions to create and manage EC2 instances
-
Understanding of your database architecture (non-CDB or multitenant CDB with PDBs)
-
Network connectivity planning between source and target instances
-
Backup and recovery strategy for your migration
Migration options
As part of the migration process, you can choose one of two migration options based on your business requirements and use case:
Option 1: RMAN Active Duplication (Online/Offline Migration)
Best suited for
-
Workloads that can afford planned downtime during final cutover
-
Simpler migration requirements with fewer moving parts
-
Databases where you want a straightforward, one-time migration
-
Scenarios where you don't need continuous synchronization before cutover
Key characteristics
-
Downtime: Minimal downtime during final cutover (database remains online during duplication, brief downtime for final cutover)
-
Complexity: Lower complexity with direct database duplication
-
Duration: Migration time depends on database size and network bandwidth
-
Fallback: Requires maintaining the source database until validation is complete
-
Online capability: Source database remains online and accessible during the duplication process
Migration approach: RMAN active duplication creates an exact copy of the source database
on the target by copying database files over the network from the live, running source database. The source database remains
online and accessible to applications during the duplication process. For multitenant databases, RMAN automatically duplicates
the entire CDB including CDB$ROOT, PDB$SEED, and all pluggable databases in a single operation. Only a
brief cutover window is needed to redirect applications to the new EC2 instance.
Option 2: Oracle Data Guard (Online Migration)
Best suited for
-
Production workloads requiring minimal downtime
-
Mission-critical databases that must remain available
-
Scenarios where you need continuous synchronization before cutover
-
Migrations requiring built-in fallback capability
Key characteristics
-
Downtime: Near-zero downtime (seconds to minutes for switchover)
-
Complexity: Higher complexity with Data Guard configuration
-
Duration: Initial setup time plus continuous synchronization until switchover
-
Fallback: Built-in fallback capability by keeping the source as a standby
Migration approach: Oracle Data Guard maintains a synchronized standby database by continuously shipping and applying redo logs from the primary database. When you're ready to complete the migration, you perform a switchover that promotes the EC2 standby to primary with minimal downtime. For multitenant databases, Data Guard automatically protects the entire CDB including all PDBs.
Decision matrix
Use the following matrix to help choose the appropriate migration option:
|
Aspect |
RMAN Active Duplication |
Oracle Data Guard |
|---|---|---|
|
Source database availability |
Online during duplication | Online during entire process |
|
Acceptable downtime |
Minutes to hours (final cutover) | Seconds to minutes (switchover) |
|
Migration complexity |
Lower | Higher |
|
Continuous synchronization |
No | Yes |
|
Fallback capability |
Manual (keep source) | Built-in (automatic) |
|
Testing before cutover |
Limited | Full testing possible |
|
Network bandwidth requirement |
High during duplication | Moderate (continuous) |
|
Best for |
Most migrations, dev/test, planned cutover | Production, mission-critical, near-zero downtime |
Architecture considerations
Both migration options support two Oracle database architectures:
Non-CDB
Traditional single-instance Oracle databases without the multitenant architecture. These databases:
-
Have a single database instance
-
Do not use pluggable databases (PDBs)
-
Are simpler to manage and migrate
-
Typically named ORCL in RDS Custom for Oracle
Multitenant (CDB with PDBs)
Container databases (CDB) hosting multiple pluggable databases (PDBs), introduced in Oracle 12c. These databases:
-
Have a container database (CDB) with
CDB$ROOTandPDB$SEED -
Host one or more pluggable databases (PDBs)
-
Provide database consolidation and resource isolation
-
Typically named RDSCDB in RDS Custom for Oracle
-
Use Oracle Managed Files (OMF) with GUID-based subdirectories for PDB data files
Important note for multitenant migrations: The target database will be renamed to ORCL during the migration process (source: RDSCDB → target: ORCL). This simplifies the target configuration and aligns with standard naming conventions.
Key differences in migration approach
|
Aspect |
Non-CDB |
Multitenant (CDB with PDBs) |
|---|---|---|
|
Migration scope |
Single database | Entire CDB with all PDBs |
|
Post-migration state |
Database open READ WRITE | CDB open READ WRITE; PDBs in MOUNTED state |
|
PDB management |
N/A | Must open PDBs and configure auto-open |
|
Cleanup |
Single database | CDB$ROOT (cascades to PDBs); handle C## common users |
|
Application connections |
Database service | PDB services (not CDB) |
|
Parameter file |
Standard parameters | Requires enable_pluggable_database=TRUE |
|
Complexity |
Lower | Higher due to multiple containers |
Common prerequisites for both migration options
Before starting either migration option, complete the following prerequisite steps:
-
Launch and configure EC2 instance
Launch an EC2 instance with the following considerations:
-
Instance type: Choose an EC2 instance type that meets the resource requirements of your workload. Using the same instance class as your RDS Custom instance is a good starting point. Consider memory, CPU, and network bandwidth requirements.
-
Operating system: Oracle Linux or Red Hat Enterprise Linux (matching or compatible with your RDS Custom OS version)
-
Oracle software: Install Oracle Database software (same major version, minor version, Release Update, and ideally the same one-off patches as RDS Custom). Ensure the Oracle software is installed in /u01/app/oracle/ or your preferred location.
-
Storage: Configure Amazon EBS volumes with appropriate size and IOPS to meet your workload requirements. Consider using gp3 volumes for cost-effective performance or io2 Block Express for high-performance workloads.
-
-
Configure storage architecture
-
File system storage (recommended for most scenarios)
-
Use standard file system directories for Oracle data files
-
Simpler to manage and suitable for most workloads
-
This guidance uses file system storage examples
-
-
Oracle Automatic Storage Management (ASM)
-
If your workload requires ASM, install and configure standalone ASM on the EC2 instance
-
Adjust all path parameters in the init file accordingly to use ASM disk groups (e.g., +DATA, +FRA)
-
The migration process is similar for ASM, with path adjustments
-
-
-
Set up file transfer mechanism
Create a mechanism to transfer files between RDS Custom and EC2 instances. You have several options:
-
Option A: Amazon S3 (recommended for most scenarios)
-
Create an Amazon S3 bucket or use an existing one
-
Install and configure Amazon CLI on both instances
-
For instructions, see Getting started with the Amazon CLI.
-
-
Option B: Direct SCP/SFTP
-
If SSH ports are open between instances, you can transfer files directly
-
Suitable for small files like parameter files and password files
-
-
Option C: Amazon EFS
-
If you already have Amazon EFS mounted on both instances, you can use it as a shared file system
-
Suitable for environments with existing EFS infrastructure
This guidance uses Amazon S3 for examples, but you can adapt the commands for your chosen method.
-
-
-
Configure network connectivity
Ensure network connectivity between the RDS Custom and EC2 instances:
-
Same VPC: Security groups must allow bidirectional traffic on the Oracle listener port (default 1521, or your custom port)
-
Different VPCs (same account): Set up VPC peering and configure route tables and security groups
-
Different accounts: Set up VPC peering across accounts or use Amazon Transit Gateway
-
Verify connectivity: Use ping and telnet to test connectivity on the database port
-
-
Create directory structure on EC2
The directory structure depends on your database architecture:
Example For Non-CDB:
# Non-CDB directories mkdir -p /u01/app/oracle/oradata/ORCL/controlfile mkdir -p /u01/app/oracle/oradata/ORCL/datafile mkdir -p /u01/app/oracle/oradata/ORCL/onlinelog mkdir -p /u01/app/oracle/oradata/ORCL/arch mkdir -p /u01/app/oracle/admin/ORCL/adump mkdir -p /u01/app/oracle/backup # Set ownership chown -R oracle:oinstall /u01/app/oracle/oradata/ORCL chown -R oracle:oinstall /u01/app/oracle/admin/ORCL chown -R oracle:oinstall /u01/app/oracle/backupExample For Multitenant (CDB with PDBs)
# CDB directories mkdir -p /u01/app/oracle/oradata/ORCL/controlfile mkdir -p /u01/app/oracle/oradata/ORCL/cdb/datafile mkdir -p /u01/app/oracle/oradata/ORCL/pdbseed/datafile mkdir -p /u01/app/oracle/oradata/ORCL/onlinelog mkdir -p /u01/app/oracle/oradata/ORCL/arch mkdir -p /u01/app/oracle/admin/ORCL/adump mkdir -p /u01/app/oracle/backup # PDB directories (RDS Custom uses OMF with GUID-based paths) # Create a generic pdb directory - migration will create subdirectories as needed mkdir -p /u01/app/oracle/oradata/ORCL/pdb/datafile # Set ownership chown -R oracle:oinstall /u01/app/oracle/oradata/ORCL chown -R oracle:oinstall /u01/app/oracle/admin/ORCL chown -R oracle:oinstall /u01/app/oracle/backupNote
RDS Custom for Oracle uses Oracle Managed Files (OMF) for PDB data files with GUID-based subdirectories (e.g.,
/rdsdbdata/db/pdb/RDSCDB_A/). The migration process will automatically create the necessary subdirectory structure on the target. You only need to create the parent directories.{GUID}/datafile/Storage strategy: Consider using a separate EBS volume for /u01/app/oracle/backup to easily detach and remove it after migration completes, freeing up storage costs.
-
Verify source database configuration
Before starting the migration, verify your source database configuration:
-
Log in to the RDS Custom database host as the rdsdb user and set the environment:
Example
# For non-CDB export ORACLE_HOME=/rdsdbbin/oracle.19.custom.r1.EE.1 export ORACLE_SID=ORCL export PATH=$ORACLE_HOME/bin:$PATH # For multitenant CDB export ORACLE_HOME=/rdsdbbin/oracle.19.custom.r1.EE-CDB.1 export ORACLE_SID=RDSCDB export PATH=$ORACLE_HOME/bin:$PATH -
Connect to the database and check the architecture:
Example
sqlplus / as sysdba SQL> SELECT name, cdb, open_mode, log_mode FROM v$database;Example For Non-CDB, expected output
NAME CDB OPEN_MODE LOG_MODE --------- --- -------------------- ------------ ORCL NO READ WRITE ARCHIVELOGExample For Multitenant (CDB), expected output:
NAME CDB OPEN_MODE LOG_MODE --------- --- -------------------- ------------ RDSCDB YES READ WRITE ARCHIVELOG -
If you have a multitenant CDB, list all PDBs and their status:
Example
SQL> SELECT con_id, name, open_mode, restricted FROM v$pdbs;Expected output (example with 1 PDB named ORCLDB):
Example
CON_ID NAME OPEN_MODE RES ---------- ------------------------------ ---------- --- 2 PDB$SEED READ ONLY NO 3 ORCLDB READ WRITE NO -
Check the total database size:
Example For Non-CDB:
SQL> SELECT SUM(bytes)/1024/1024/1024 AS total_size_gb FROM dba_data_files;Example For Multitenant:
-- Total CDB size (all containers) SQL> SELECT SUM(bytes)/1024/1024/1024 AS total_size_gb FROM cdb_data_files; -- Size per PDB SQL> SELECT p.name AS pdb_name, ROUND(SUM(d.bytes)/1024/1024/1024, 2) AS size_gb FROM v$pdbs p JOIN cdb_data_files d ON p.con_id = d.con_id GROUP BY p.name, p.con_id ORDER BY p.con_id;
Option 1: Physical Migration Using RMAN Active Duplication
Topics
This section provides detailed steps for migrating your Oracle database from RDS Custom for Oracle to EC2 using RMAN active duplication. This method duplicates from a live, running database, keeping the source online and accessible during the migration process.
When to use RMAN active duplication
Choose RMAN active duplication when:
-
You want to keep the source database online and accessible during migration
-
You can afford a brief cutover window for final application redirection
-
You want a straightforward migration process with fewer moving parts
-
Your database size and network bandwidth allow for reasonable duplication time
-
You don't need continuous synchronization before cutover
-
You're migrating production, development, or test databases
RMAN active duplication overview
RMAN active duplication doesn't require a backup of the source database or taking the source database offline. It duplicates the live, running source database to the destination by copying database files over the network while the source remains online and accessible to applications.
Key advantages:
-
Source remains online: Applications can continue accessing the source database during duplication
-
No backup required: Eliminates the need for intermediate backup storage
-
Direct network transfer: Database files are copied directly from source to target
-
Automatic consistency: RMAN ensures the duplicate database is consistent
-
Single operation: For multitenant, duplicates entire CDB including all PDBs in one operation
The duplication process creates an exact copy of the source database on the target, including all data files, control files, and redo logs. The source database continues to serve application requests throughout the duplication process. Only a brief cutover window is needed at the end to redirect applications from the source to the target.
Typical timeline
-
Duplication phase (source remains online): 30 minutes to several hours depending on database size
-
Validation phase (source remains online): Hours to days as needed
-
Cutover phase (brief downtime): Minutes to redirect applications to EC2
Migration workflow for RMAN active duplication
RDS Custom DB instance (source) steps:
-
Pause Amazon RDS Custom automation
-
Verify database architecture (non-CDB or CDB with PDBs)
-
Create a password file and parameter file from the source database
-
Copy the password file and parameter file to the target EC2 instance
-
Verify the source database is running in archive log mode
-
Configure tnsnames.ora on the RDS Custom DB server
EC2 DB instance (target) steps:
-
Edit the parameter file for EC2 (architecture-specific)
-
Configure tnsnames.ora on EC2
-
Configure the environment for the EC2 instance
-
Configure Oracle Listener on EC2
-
Start the database on EC2 in NOMOUNT state
RMAN duplication steps:
-
Perform RMAN active duplication
-
Open the database (and PDBs for multitenant)
-
Configure PDB auto-open (multitenant only)
-
Remove RDS Custom specific users and objects
-
Create SPFILE and configure automatic startup
-
Resume Amazon RDS Custom automation on source (if keeping it active)
Step 1: Pause Amazon RDS Custom automation
Pause the automation mode on your RDS Custom instance before proceeding with the migration steps to ensure the automation doesn't interfere with the RMAN activity. The --resume-full-automation-mode-minute parameter (240 minutes = 4 hours in this example) should be adjusted based on your database size and expected duplication time.
Important: Pausing automation does not affect database availability. The database remains online and accessible to applications during the duplication process.
Example
aws rds modify-db-instance \ --db-instance-identifier my-custom-instance \ --automation-mode all-paused \ --resume-full-automation-mode-minute 240 \ --region us-east-1 --query 'DBInstances[0].AutomationMode'
Validate the automation status:
Example
aws ds describe-db-instances \ --db-instance-identifier my-custom-instance \ --region us-east-1 \ --query 'DBInstances[0].AutomationMode'
Expected output: "all-paused"
Step 2: Create password and parameter files
Create a password file using orapwd. Retrieve the SYS password from Amazon Secrets Manager (stored during RDS
Custom instance creation).
Example
# Non-CDB $ORACLE_HOME/bin/orapwd file=/rdsdbdata/config/orapwORCL password=<SYS_PASSWORD>entries=10 # Multitenant $ORACLE_HOME/bin/orapwd file=/rdsdbdata/config/orapwRDSCDB password=<SYS_PASSWORD>entries=10
Create a parameter file from the source database:
Example
sqlplus / as sysdba SQL> CREATE PFILE='/tmp/initORCL.ora' FROM SPFILE; -- Non-CDB SQL> CREATE PFILE='/tmp/initRDSCDB.ora' FROM SPFILE; -- Multitenant
The generated parameter file will contain RDS Custom-specific paths and parameters. For multitenant, key parameters include:
-
enable_pluggable_database=TRUE(critical for multitenant) -
noncdb_compatible=TRUE(for backward compatibility) -
Data file paths for
CDB$ROOT,PDB$SEED, and all PDBs -
db_create_file_destanddb_create_online_log_dest_1for OMF
Step 3: Transfer files to EC2
Choose your preferred file transfer method. This guidance uses Amazon S3 for examples.
Using Amazon S3:
Using Amazon S3:
Example For Non-CDB:
# Copy files to Amazon S3 from the RDS Custom instance aws s3 cp /tmp/initORCL.ora s3://<YOUR_BUCKET>/ aws s3 cp /rdsdbdata/config/orapwORCL s3://<YOUR_BUCKET>/ # On EC2, download files aws s3 cp s3://<YOUR_BUCKET>/initORCL.ora $ORACLE_HOME/dbs/ aws s3 cp s3://<YOUR_BUCKET>/orapwORCL $ORACLE_HOME/dbs/
Example For Multitenant
# Copy files to Amazon S3 from the RDS Custom instance aws s3 cp /tmp/initRDSCDB.ora s3://<YOUR_BUCKET>/ aws s3 cp /rdsdbdata/config/orapwRDSCDB s3://<YOUR_BUCKET>/ # On EC2, download and rename files to use ORCL naming aws s3 cp s3://<YOUR_BUCKET>/initRDSCDB.ora $ORACLE_HOME/dbs/initORCL.ora aws s3 cp s3://<YOUR_BUCKET>/orapwRDSCDB $ORACLE_HOME/dbs/orapwORCL
Validate the files on EC2:
Example
ls -l $ORACLE_HOME/dbs/initORCL.ora ls -l $ORACLE_HOME/dbs/orapwORCL
Step 4: Edit parameter file on EC2
The parameter file requires careful editing to ensure successful migration. This is one of the most critical steps in the migration process.
Edit $ORACLE_HOME/dbs/initORCL.ora on the EC2 instance and make these critical changes:
-
Update database name: For multitenant, change all occurrences of RDSCDB to ORCL
-
Convert RDS Custom paths to EC2 paths: Replace /rdsdbdata/ with /u01/app/oracle/
-
Remove RDS Custom-specific parameters
-
dg_broker_config_file1anddg_broker_config_file2(if present - indicates a replica existed) -
standby_file_management(if present) -
spfile(we'll create a new SPFILE later) -
Any
log_archive_destparameters pointing to standby destinations (keep onlylog_archive_dest_1for local archiving)
-
-
Add file name conversion parameters (see below)
-
Adjust memory parameters based on your EC2 instance size (optional but recommended)
Understanding file name conversion parameters:
The DB_FILE_NAME_CONVERT and LOG_FILE_NAME_CONVERT parameters are critical for RMAN duplication.
They tell RMAN how to map source file paths to target file paths during the duplication process. Without these parameters, RMAN
will attempt to create files in the same paths as the source, which will fail on EC2.
Key considerations:
-
Each parameter accepts pairs of strings: source path followed by target path
-
Multiple pairs can be specified in a single parameter
-
For multitenant, you must map paths for
CDB$ROOT,PDB$SEED, and all PDBs -
The order of pairs matters - RMAN processes them in the order specified
-
Paths are case-sensitive and must match exactly
Path mappings:
|
RDS Custom path |
EC2 path |
Description |
|---|---|---|
| /rdsdbbin | /u01/app/oracle | Oracle base |
| /rdsdbdata/db/ORCL_A/datafile/ | /u01/app/oracle/oradata/ORCL/datafile/ | Data files |
| /rdsdbdata/db/ORCL_A/controlfile/ | /u01/app/oracle/oradata/ORCL/controlfile/ | Control files |
| /rdsdbdata/db/ORCL_A/onlinelog/ | /u01/app/oracle/oradata/ORCL/onlinelog/ | Online redo logs |
| /rdsdbdata/db/ORCL_A/arch/ | /u01/app/oracle/oradata/ORCL/arch/ | Archive logs |
| /rdsdbdata/admin/ORCL/adump | /u01/app/oracle/admin/ORCL/adump | Audit dump |
| /rdsdbdata/log | /u01/app/oracle | Diagnostic destination |
|
RDS Custom path |
EC2 path |
Description |
|---|---|---|
/rdsdbbin |
/u01/app/oracle |
Oracle base |
/rdsdbdata/db/cdb/RDSCDB/datafile/ |
/u01/app/oracle/oradata/ORCL/cdb/datafile/ |
CDB root data files |
/rdsdbdata/db/cdb/pdbseed/ |
/u01/app/oracle/oradata/ORCL/pdbseed/datafile/ |
PDB$SEED data files |
/rdsdbdata/db/pdb/RDSCDB_A/ |
/u01/app/oracle/oradata/ORCL/pdb/datafile/ |
PDB data files (OMF with GUID) |
/rdsdbdata/db/cdb/RDSCDB_A/controlfile/ |
/u01/app/oracle/oradata/ORCL/controlfile/ |
Control files |
/rdsdbdata/db/cdb/RDSCDB_A/onlinelog/ |
/u01/app/oracle/oradata/ORCL/onlinelog/ |
Online redo logs |
/rdsdbdata/db/cdb/RDSCDB_A/arch/redolog |
/u01/app/oracle/oradata/ORCL/arch/ |
Archive logs |
/rdsdbdata/admin/RDSCDB/adump |
/u01/app/oracle/admin/ORCL/adump |
Audit dump |
/rdsdbdata/log |
/u01/app/oracle |
Diagnostic destination |
Add conversion parameters:
Example Non-CDB:
*.DB_FILE_NAME_CONVERT='/rdsdbdata/db/ORCL_A/datafile/','/u01/app/oracle/oradata/ORCL/datafile/' *.LOG_FILE_NAME_CONVERT='/rdsdbdata/db/ORCL_A/onlinelog/','/u01/app/oracle/oradata/ORCL/onlinelog/'
Example Multitenant (must include mappings for CDB root, PDB$SEED, and all PDB paths):
*.DB_FILE_NAME_CONVERT='/rdsdbdata/db/cdb/RDSCDB/datafile/','/u01/app/oracle/oradata/ORCL/cdb/datafile/','/rdsdbdata/db/cdb/pdbseed/','/u01/app/oracle/oradata/ORCL/pdbseed/datafile/','/rdsdbdata/db/pdb/RDSCDB_A/','/u01/app/oracle/oradata/ORCL/pdb/datafile/' *.LOG_FILE_NAME_CONVERT='/rdsdbdata/db/cdb/RDSCDB_A/onlinelog/','/u01/app/oracle/oradata/ORCL/onlinelog/'
Important: For multitenant, ensure
enable_pluggable_database=TRUE is present in the parameter file. RDS Custom uses OMF for PDB data
files with GUID-based subdirectories - the path mapping handles this automatically.
Complete example parameter files:
Example Non-CDB parameter file (initORCL.ora):
ORCL.__data_transfer_cache_size=0 ORCL.__db_cache_size=6039797760 ORCL.__inmemory_ext_roarea=0 ORCL.__inmemory_ext_rwarea=0 ORCL.__java_pool_size=0 ORCL.__large_pool_size=33554432 ORCL.__oracle_base='/u01/app/oracle' ORCL.__pga_aggregate_target=4966055936 ORCL.__sga_target=7449083904 ORCL.__shared_io_pool_size=134217728 ORCL.__shared_pool_size=1207959552 ORCL.__streams_pool_size=0 ORCL.__unified_pga_pool_size=0 *.archive_lag_target=300 *.audit_file_dest='/u01/app/oracle/admin/ORCL/adump' *.compatible='19.0.0' *.control_files='/u01/app/oracle/oradata/ORCL/controlfile/control-01.ctl' *.db_block_checking='MEDIUM' *.db_create_file_dest='/u01/app/oracle/oradata/ORCL' *.db_name='ORCL' *.db_recovery_file_dest_size=1073741824 *.db_unique_name='ORCL' *.dbfips_140=FALSE *.diagnostic_dest='/u01/app/oracle' *.filesystemio_options='setall' *.heat_map='OFF' *.job_queue_processes=50 *.local_listener='(address=(protocol=tcp)(host=)(port=1521))' *.log_archive_dest_1='location="/u01/app/oracle/oradata/ORCL/arch", valid_for=(ALL_LOGFILES,ALL_ROLES)' *.log_archive_format='-%s-%t-%r.arc' *.max_string_size='STANDARD' *.memory_max_target=12385852416 *.memory_target=12385852416 *.open_cursors=300 *.pga_aggregate_target=0 *.processes=1673 *.recyclebin='OFF' *.sga_target=0 *.undo_tablespace='UNDO_T1' *.use_large_pages='FALSE' *.DB_FILE_NAME_CONVERT='/rdsdbdata/db/ORCL_A/datafile/','/u01/app/oracle/oradata/ORCL/datafile/' *.LOG_FILE_NAME_CONVERT='/rdsdbdata/db/ORCL_A/onlinelog/','/u01/app/oracle/oradata/ORCL/onlinelog/'
Example Multitenant parameter file (initORCL.ora):
ORCL.__data_transfer_cache_size=0 ORCL.__db_cache_size=6006243328 ORCL.__inmemory_ext_roarea=0 ORCL.__inmemory_ext_rwarea=0 ORCL.__java_pool_size=0 ORCL.__large_pool_size=33554432 ORCL.__oracle_base='/u01/app/oracle' ORCL.__pga_aggregate_target=4966055936 ORCL.__sga_target=7415529472 ORCL.__shared_io_pool_size=134217728 ORCL.__shared_pool_size=1207959552 ORCL.__streams_pool_size=0 ORCL.__unified_pga_pool_size=0 *.archive_lag_target=300 *.audit_file_dest='/u01/app/oracle/admin/ORCL/adump' *.compatible='19.0.0' *.control_files='/u01/app/oracle/oradata/ORCL/controlfile/control-01.ctl' *.db_block_checking='MEDIUM' *.db_create_file_dest='/u01/app/oracle/oradata/ORCL/pdb/datafile' *.db_create_online_log_dest_1='/u01/app/oracle/oradata/ORCL' *.db_name='ORCL' *.db_recovery_file_dest_size=1073741824 *.db_unique_name='ORCL' *.dbfips_140=FALSE *.diagnostic_dest='/u01/app/oracle' *.enable_pluggable_database=TRUE *.filesystemio_options='setall' *.heat_map='OFF' *.job_queue_processes=50 *.local_listener='(address=(protocol=tcp)(host=)(port=1521))' *.log_archive_dest_1='location="/u01/app/oracle/oradata/ORCL/arch", valid_for=(ALL_LOGFILES,ALL_ROLES)' *.log_archive_format='-%s-%t-%r.arc' *.max_string_size='STANDARD' *.memory_max_target=12361688064 *.memory_target=12361688064 *.noncdb_compatible=TRUE *.open_cursors=300 *.pga_aggregate_target=0 *.processes=1670 *.recyclebin='OFF' *.sga_target=0 *.undo_tablespace='UNDO_T1' *.use_large_pages='FALSE' *.DB_FILE_NAME_CONVERT='/rdsdbdata/db/cdb/RDSCDB/datafile/','/u01/app/oracle/oradata/ORCL/cdb/datafile/','/rdsdbdata/db/cdb/pdbseed/','/u01/app/oracle/oradata/ORCL/pdbseed/datafile/','/rdsdbdata/db/pdb/RDSCDB_A/','/u01/app/oracle/oradata/ORCL/pdb/datafile/' *.LOG_FILE_NAME_CONVERT='/rdsdbdata/db/cdb/RDSCDB_A/onlinelog/','/u01/app/oracle/oradata/ORCL/onlinelog/'
Key parameter explanations:
-
enable_pluggable_database=TRUE: Required for multitenant CDB. This parameter enables the pluggable database feature. -
noncdb_compatible=TRUE: Set by RDS Custom for backward compatibility. Can be kept or removed based on your requirements. -
db_create_file_dest: Specifies the default location for Oracle Managed Files (OMF). For multitenant, this points to the PDB datafile directory. -
db_create_online_log_dest_1: Specifies the location for online redo logs when using OMF. -
DB_FILE_NAME_CONVERT: Critical for RMAN duplication. Maps source data file paths to target paths. -
LOG_FILE_NAME_CONVERT: Maps source redo log paths to target paths. -
memory_targetandmemory_max_target: Adjust these based on your EC2 instance memory. The values shown are examples. -
processes: Maximum number of operating system processes that can connect to Oracle. Adjust based on your workload.
Memory sizing guidelines:
When adjusting memory parameters for your EC2 instance:
|
EC2 Instance Memory |
Recommended memory_target |
Recommended memory_max_target |
|---|---|---|
| 16 GB | 12 GB (12884901888 bytes) | 14 GB (15032385536 bytes) |
| 32 GB | 24 GB (25769803776 bytes) | 28 GB (30064771072 bytes) |
| 64 GB | 48 GB (51539607552 bytes) | 56 GB (60129542144 bytes) |
| 128 GB | 96 GB (103079215104 bytes) | 112 GB (120259084288 bytes) |
Leave approximately 20-25% of system memory for the operating system and other processes.
Step 5: Configure TNS and listener
On both instances, add TNS entries to tnsnames.ora:
Example Non-CDB:
DB_SOURCE = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST =<RDS_CUSTOM_IP>)(PORT = 1521)) (CONNECT_DATA = (SID = ORCL))) DB_TARGET = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST =<EC2_IP>)(PORT = 1521)) (CONNECT_DATA = (SID = ORCL)))
Example Multitenant:
DB_SOURCE = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST =<RDS_CUSTOM_IP>)(PORT = 1521)) (CONNECT_DATA = (SID = RDSCDB))) DB_TARGET = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST =<EC2_IP>)(PORT = 1521)) (CONNECT_DATA = (SID = ORCL)))
Example Configure listener on EC2 ($ORACLE_HOME/network/admin/listener.ora):
LISTENER = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST =<EC2_IP>)(PORT = 1521))) SID_LIST_LISTENER = (SID_LIST = (SID_DESC = (GLOBAL_DBNAME = ORCL) (ORACLE_HOME = /u01/app/oracle/product/19.0.0/dbhome_1) (SID_NAME = ORCL)))
Example Start listener:
lsnrctl start
Note
For RMAN active duplication, the TNS entries connect to the database instance using SID (not SERVICE_NAME). For multitenant, this connects to the CDB, and RMAN automatically duplicates all PDBs.
Step 6: Start database in NOMOUNT on EC2
Example
export ORACLE_SID=ORCL export ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1 export PATH=$ORACLE_HOME/bin:$PATH sqlplus / as sysdba SQL> STARTUP NOMOUNT PFILE='$ORACLE_HOME/dbs/initORCL.ora';
Example Verify parameters:
SQL> SHOW PARAMETER db_name SQL> SHOW PARAMETER control_files SQL> SHOW PARAMETER enable_pluggable_database -- For multitenant
Step 7: Perform RMAN active duplication
RMAN active duplication copies the database from the live, running source to the target. The source database remains online and accessible to applications throughout this process.
Connect RMAN to both the source and target instances:
Example
rman target sys/<password>@DB_SOURCE auxiliary sys/<password>@DB_TARGET
Example Expected output for non-CDB:
Recovery Manager: Release 19.0.0.0.0 - Production connected to target database: ORCL (DBID=4089929259) connected to auxiliary database: ORCL (not mounted)
Example Expected output for multitenant:
Recovery Manager: Release 19.0.0.0.0 - Production connected to target database: RDSCDB (DBID=4089929259) connected to auxiliary database: ORCL (not mounted)
Example Run the active duplication command:
RMAN> DUPLICATE DATABASE TO 'ORCL' FROM ACTIVE DATABASE NOFILENAMECHECK;
Note
-
Source remains online: The source database continues to serve application requests during duplication
-
For non-CDB: This duplicates the entire database while it remains online
-
For multitenant: This duplicates the entire CDB including
CDB$ROOT,PDB$SEED, and all PDBs in a single operation while the source remains online -
NOFILENAMECHECK: Allows RMAN to proceed even if file names differ between source and target
-
Duration: Depends on database size and network bandwidth. For a 100GB database, expect 30-60 minutes
-
Network impact: High network bandwidth usage during duplication, but source database remains accessible
The RMAN active duplication process includes several phases:
-
Connecting to source and target databases
-
Creating an
SPFILEfrom memory on the target -
Restoring the control file to the target
-
Mounting the target database
-
Copying all data files from source to target over the network (source remains online)
-
Recovering the target database
-
Opening the target database with
RESETLOGS
During duplication, the source database:
-
Remains in
READ WRITEmode -
Continues to accept connections
-
Processes transactions normally
-
Generates redo logs normally
-
Is fully accessible to applications
Example Monitor progress in another session:
SQL> SELECT sid, serial#, sofar, totalwork, ROUND(sofar/totalwork*100,2) pct_complete FROM v$session_longops WHERE totalwork > 0 AND sofar <> totalwork;
Detailed monitoring and troubleshooting during RMAN duplication:
While RMAN duplication is running, you can monitor its progress using several methods:
-
Monitor RMAN output:
The RMAN session will display detailed progress information including:
-
Channel allocation
-
Datafile copy progress
-
Estimated completion time
-
Bytes processed
channel ORA_AUX_DISK_1: starting datafile copy input datafile file number=00001 name=/rdsdbdata/db/ORCL_A/datafile/system01.dbf output file name=/u01/app/oracle/oradata/ORCL/datafile/system01.dbf tag=TAG20260303T120000 channel ORA_AUX_DISK_1: datafile copy complete, elapsed time: 00:05:23 channel ORA_AUX_DISK_1: starting datafile copy input datafile file number=00003 name=/rdsdbdata/db/ORCL_A/datafile/sysaux01.dbf output file name=/u01/app/oracle/oradata/ORCL/datafile/sysaux01.dbf tag=TAG20260303T120000Example output:
channel ORA_AUX_DISK_1: starting datafile copy input datafile file number=00001 name=/rdsdbdata/db/ORCL_A/datafile/system01.dbf output file name=/u01/app/oracle/oradata/ORCL/datafile/system01.dbf tag=TAG20260303T120000 channel ORA_AUX_DISK_1: datafile copy complete, elapsed time: 00:05:23 channel ORA_AUX_DISK_1: starting datafile copy input datafile file number=00003 name=/rdsdbdata/db/ORCL_A/datafile/sysaux01.dbf output file name=/u01/app/oracle/oradata/ORCL/datafile/sysaux01.dbf tag=TAG20260303T120000 -
-
Monitor long-running operations:
Example In a separate SQL*Plus session on the target EC2 instance:
SQL> SELECT sid, serial#, opname, sofar, totalwork, ROUND(sofar/totalwork*100,2) pct_complete, time_remaining, elapsed_seconds FROM v$session_longops WHERE totalwork > 0 AND sofar <> totalwork ORDER BY start_time;This query shows:
-
opname: Operation name (e.g., "RMAN: full datafile restore") -
sofar: Blocks processed so far -
totalwork: Total blocks to process -
pct_complete: Percentage complete -
time_remaining: Estimated seconds remaining -
elapsed_seconds: Time elapsed so far
-
-
Monitor RMAN channels:
Example
SQL> SELECT sid, serial#, context, sofar, totalwork, ROUND(sofar/totalwork*100,2) pct_complete FROM v$session_longops WHERE opname LIKE 'RMAN%' AND totalwork > 0 AND sofar <> totalwork; -
Check alert log:
Example On the target EC2 instance, monitor the alert log for any errors or warnings:
tail -f $ORACLE_BASE/diag/rdbms/orcl/ORCL/trace/alert_ORCL.logCommon issues during RMAN duplication:
-
Network timeout
RMAN-03009: failure of duplicate command on ORA_AUX_DISK_1 channel ORA-03135: connection lost contactSolution: Increase network timeout values in
sqlnet.oraon both instances:SQLNET.RECV_TIMEOUT=600 SQLNET.SEND_TIMEOUT=600 -
Insufficient space on target
RMAN-03009: failure of duplicate command ORA-19504: failed to create file "/u01/app/oracle/oradata/ORCL/datafile/users01.dbf" ORA-27040: file create error, unable to create file Linux-x86_64 Error: 28: No space left on deviceSolution: Check available space and add more EBS volume capacity:
df -h /u01/app/oracle/oradata -
Parameter file errors
RMAN-05021: this configuration cannot be used RMAN-06004: ORACLE error from auxiliary database: ORA-01261: Parameter db_create_file_dest destination string cannot be translatedSolution: Verify all directories in the parameter file exist and have proper permissions.
-
Password file mismatch
RMAN-00571: =========================================================== RMAN-00569: =============== ERROR MESSAGE STACK FOLLOWS =============== RMAN-00571: =========================================================== RMAN-03002: failure of duplicate command at 03/03/2026 12:00:00 RMAN-06136: ORACLE error from auxiliary database: ORA-01017: invalid username/password; logon deniedSolution: Ensure the password file on the target matches the source and has the correct name (
orapwORCL).
-
Step 8: Open PDBs (multitenant only)
After RMAN duplication completes, the CDB on EC2 is open in READ WRITE mode, but all PDBs are in
MOUNTED state. This is expected behavior - you must open them manually.
Check the current PDB status:
SQL> SELECT con_id, name, open_mode FROM v$pdbs;
Expected output (example with one PDB named ORCLDB):
CON_ID NAME OPEN_MODE ---------- ------------------------------ ---------- 2 PDB$SEED READ ONLY 3 ORCLDB MOUNTED
Open all PDBs:
SQL> ALTER PLUGGABLE DATABASE ALL OPEN; Pluggable database altered.
Verify all PDBs are now open in READ WRITE mode:
SQL> SELECT con_id, name, open_mode, restricted FROM v$pdbs;
Expected output:
CON_ID NAME OPEN_MODE RES ---------- ------------------------------ ---------- --- 2 PDB$SEED READ ONLY NO 3 ORCLDB READ WRITE NO
Configure auto-open on startup using the save state method (recommended for Oracle 19c):
SQL> ALTER PLUGGABLE DATABASE ALL SAVE STATE; Pluggable database altered.
This tells Oracle to remember the current open state of PDBs and restore it on CDB startup.
Verify the saved state:
SQL> SELECT con_name, instance_name, state FROM dba_pdb_saved_states;
Verify PDB services are registered with the listener:
lsnrctl services
Expected output should show services for the CDB and each PDB. If services aren't showing:
SQL> ALTER SYSTEM REGISTER;
Then check again with lsnrctl services.
Step 9: Remove RDS Custom objects
Because this is now a self-managed database on EC2, you should remove RDS Custom-specific users and objects. The cleanup process differs between non-CDB and multitenant architectures.
Important
Before dropping RDS-specific users and tablespaces, verify that no application objects exist under these schemas:
SQL> SELECT owner, object_type, COUNT(*) FROM dba_objects WHERE owner IN ('RDSADMIN', 'RDS_DATAGUARD') AND object_name NOT LIKE 'RDS%' AND object_name NOT LIKE 'SYS_%' GROUP BY owner, object_type;
If any application objects are found, migrate them to appropriate application schemas before proceeding.
Example Non-CDB:
SQL> DROP USER RDSADMIN CASCADE; SQL> DROP USER RDS_DATAGUARD CASCADE; SQL> DROP DIRECTORY OPATCH_INST_DIR; SQL> DROP DIRECTORY OPATCH_LOG_DIR; SQL> DROP DIRECTORY OPATCH_SCRIPT_DIR; SQL> DROP TABLESPACE RDSADMIN INCLUDING CONTENTS AND DATAFILES; -- Verify removal SQL> SELECT username FROM dba_users WHERE username LIKE '%RDS%';
Expected output: no rows selected
Multitenant:
In a multitenant environment, RDS Custom creates common users in CDB$ROOT that are visible across all PDBs.
You must clean up from CDB$ROOT.
-- Connect to CDB$ROOT SQL> SHOW CON_NAME; -- Check for RDS-specific common users (including C## prefixed users) SQL> SELECT username, common, con_id FROM cdb_users WHERE username LIKE '%RDS%' OR username LIKE 'C##RDS%' ORDER BY username; -- Drop non-common users SQL> DROP USER RDSADMIN CASCADE; SQL> DROP USER RDS_DATAGUARD CASCADE; -- If any C## common users exist -- SQL> DROP USER C##RDSADMIN CASCADE ; -- Drop directories and tablespace SQL> DROP DIRECTORY OPATCH_INST_DIR; SQL> DROP DIRECTORY OPATCH_LOG_DIR; SQL> DROP DIRECTORY OPATCH_SCRIPT_DIR; SQL> DROP TABLESPACE RDSADMIN INCLUDING CONTENTS AND DATAFILES; -- Verify removal from CDB$ROOT SQL> SELECT username FROM dba_users WHERE username LIKE '%RDS%'; -- Verify removal from each PDB SQL> ALTER SESSION SET CONTAINER =<PDB_NAME>; SQL> SELECT username FROM dba_users WHERE username LIKE '%RDS%';
Expected output for all queries: no rows selected
Step 10: Configure automatic startup
Create SPFILE:
SQL> CREATE SPFILE FROM PFILE='$ORACLE_HOME/dbs/initORCL.ora'; SQL> SHUTDOWN IMMEDIATE; SQL> STARTUP;
For multitenant, ensure PDBs open:
SQL> ALTER PLUGGABLE DATABASE ALL OPEN;
Edit /etc/oratab:
ORCL:/u01/app/oracle/product/19.0.0/dbhome_1:Y
Step 11: Final validation
Example Non-CDB:
SQL> SELECT name, open_mode, log_mode FROM v$database; SQL> SELECT SUM(bytes)/1024/1024/1024 AS size_gb FROM dba_data_files; SQL> SELECT COUNT(*) FROM dba_objects WHERE status = 'INVALID';
Example Multitenant:
SQL> SELECT name, open_mode, log_mode, cdb FROM v$database; SQL> SELECT con_id, name, open_mode FROM v$pdbs; SQL> SELECT SUM(bytes)/1024/1024/1024 AS total_size_gb FROM cdb_data_files; SQL> SELECT con_id, COUNT(*) FROM cdb_objects WHERE status = 'INVALID' GROUP BY con_id;
Test application connectivity: # Non-CDB sqlplus<app_user>/<password>@<EC2_IP>:1521/ORCL # Multitenant (connect to PDB) sqlplus<app_user>/<password>@<EC2_IP>:1521/<PDB_NAME>
Step 12: Resume RDS Custom automation
aws rds modify-db-instance \ --db-instance-identifier my-custom-instance \ --automation-mode full \ --region us-east-1
Option 2: Physical Migration Using Oracle Data Guard
This section provides detailed steps for migrating your Oracle database from RDS Custom for Oracle to EC2 using Oracle Data Guard. This method is suitable for migrations requiring minimal downtime.
When to use Oracle Data Guard
Choose Oracle Data Guard when:
-
You require minimal downtime (seconds to minutes for switchover)
-
You're migrating production or mission-critical databases
-
You need continuous synchronization before cutover
-
You want built-in fallback capability
-
You need to test the target database before committing to the migration
Oracle Data Guard overview
Oracle Data Guard maintains a synchronized standby database by continuously shipping and applying redo logs from the primary database. When you're ready to complete the migration, you perform a switchover that promotes the EC2 standby to primary with minimal downtime (seconds to minutes). For multitenant databases, Data Guard automatically protects the entire CDB including all PDBs. Redo generated by any PDB is shipped to the standby and applied to the corresponding PDB on the standby.
Migration workflow for Oracle Data Guard
RDS Custom DB instance (primary) steps:
-
Pause Amazon RDS Custom automation
-
Verify database architecture (non-CDB or CDB with PDBs)
-
Confirm the primary database is running in archive log mode and
FORCE_LOGGINGis enabled -
Create a password file
-
Perform an RMAN online backup of the primary database (or use active duplication)
-
Create a parameter file from the source database
-
Copy the backup sets, parameter file, and password file to the target EC2 instance
EC2 DB instance (standby) steps:
-
Copy all files from the source to the EC2 instance
-
Copy the password file to the EC2 instance
-
Edit the parameter file for EC2 (architecture-specific)
-
Create the server parameter file from the parameter file
-
Restore standby control file and database
Data Guard configuration steps:
-
Configure Oracle listeners on both instances
-
Configure tnsnames.ora on both instances
-
Start the Oracle Data Guard broker on both instances (optional but recommended)
-
Enable Oracle Data Guard configuration
-
Configure fal_server and fal_client on the EC2 standby instance
-
Configure the standby redo log files on both instances
-
Recover the standby instance
-
Perform the manual switchover
-
Open the database (and PDBs for multitenant)
-
Configure PDB auto-open (multitenant only)
-
Remove RDS Custom specific users and objects
-
Create SPFILE and configure automatic startup
Step 1: Pause Amazon RDS Custom automation
Pause the automation mode on your RDS Custom instance. The --resume-full-automation-mode-minute parameter
should be adjusted based on your database size and expected Data Guard setup time.
aws rds modify-db-instance \ --db-instance-identifier my-custom-instance \ --automation-mode all-paused \ --resume-full-automation-mode-minute 480 \ --region us-east-1
Validate the automation status:
aws rds describe-db-instances \ --db-instance-identifier my-custom-instance \ --region us-east-1 \ --query 'DBInstances[0].AutomationMode'
Expected output: "all-paused"
Step 2: Confirm archive log mode and FORCE_LOGGING
Oracle Data Guard requires the primary database to be in archive log mode with force logging enabled:
sqlplus / as sysdba SQL> SELECT log_mode, force_logging FROM v$database;
Expected output:
LOG_MODE FORCE_LOGGING ------------ ------------- ARCHIVELOG YES
If force logging is not enabled, run:
SQL> ALTER DATABASE FORCE LOGGING;
Step 3: Create password and parameter files
Create a password file using orapwd. Retrieve the SYS password from Amazon Secrets Manager.
# Non-CDB $ORACLE_HOME/bin/orapwd file=/rdsdbdata/config/orapwORCL password=<SYS_PASSWORD>entries=10 # Multitenant $ORACLE_HOME/bin/orapwd file=/rdsdbdata/config/orapwRDSCDB password=<SYS_PASSWORD>entries=10
Create a parameter file from the source database:
sqlplus / as sysdba SQL> CREATE PFILE='/tmp/initORCL.ora' FROM SPFILE; -- Non-CDB SQL> CREATE PFILE='/tmp/initRDSCDB.ora' FROM SPFILE; -- Multitenant
Step 4: Perform RMAN backup or use active duplication
You have two options for creating the standby database:
Option A: RMAN online backup (recommended for most scenarios)
Create a backup directory and backup the database:
mkdir -p /rdsdbdata/backup rman target / RMAN> run { backup as compressed backupset filesperset 2 format '/rdsdbdata/backup/db_%U' database; sql 'alter system archive log current'; backup as compressed backupset filesperset 50 format '/rdsdbdata/backup/arch_%U' archivelog all; } RMAN> backup current controlfile for standby format '/rdsdbdata/backup/standby.ctl';
Note
For multitenant, the database keyword backs up the entire CDB including all PDBs automatically.
Option B: Active duplication (alternative method)
Skip the backup step and use RMAN active duplication to build the standby directly over the network. This eliminates the need for backup storage and file transfers. After configuring TNS and listeners (see below), run:
RMAN> DUPLICATE TARGET DATABASE FOR STANDBY FROM ACTIVE DATABASE DORECOVER;
This guidance focuses on Option A (backup-based), but Option B is a valid alternative.
Step 5: Transfer files to EC2
Choose your preferred file transfer method. This guidance uses Amazon S3 for examples.
Using Amazon S3:
Example For Non-CDB:
# Copy files to Amazon S3 from the RDS Custom instance aws s3 cp /rdsdbdata/backup s3://<YOUR_BUCKET>/ --recursive aws s3 cp /tmp/initORCL.ora s3://<YOUR_BUCKET>/ aws s3 cp /rdsdbdata/config/orapwORCL s3://<YOUR_BUCKET>/ # On EC2, download files aws s3 cp s3://<YOUR_BUCKET>/backup /u01/app/oracle/backup/ --recursive aws s3 cp s3://<YOUR_BUCKET>/initORCL.ora $ORACLE_HOME/dbs/ aws s3 cp s3://<YOUR_BUCKET>/orapwORCL $ORACLE_HOME/dbs/
Example For Multitenant:
# Copy files to Amazon S3 from the RDS Custom instance aws s3 cp /rdsdbdata/backup s3://<YOUR_BUCKET>/ --recursive aws s3 cp /tmp/initRDSCDB.ora s3://<YOUR_BUCKET>/ aws s3 cp /rdsdbdata/config/orapwRDSCDB s3://<YOUR_BUCKET>/ # On EC2, download and rename files to use ORCL naming aws s3 cp s3://<YOUR_BUCKET>/backup /u01/app/oracle/backup/ --recursive aws s3 cp s3://<YOUR_BUCKET>/initRDSCDB.ora $ORACLE_HOME/dbs/initORCL.ora aws s3 cp s3://<YOUR_BUCKET>/orapwRDSCDB $ORACLE_HOME/dbs/orapwORCL
Step 6: Edit parameter file on EC2
Edit $ORACLE_HOME/dbs/initORCL.ora on the EC2 instance and make these critical changes:
-
Update database name: For multitenant, change all occurrences of
RDSCDBtoORCL -
Change db_unique_name: From
ORCL_A(orRDSCDB_A) toORCL_B -
Convert RDS Custom paths to EC2 paths: Replace
/rdsdbdata/with/u01/app/oracle/ -
Remove RDS Custom-specific parameters
-
dg_broker_config_file1anddg_broker_config_file2(if present) -
standby_file_management(if present) -
spfile(we'll create a newSPFILElater) -
Any
log_archive_destparameters pointing to standby destinations
-
-
Adjust memory parameters based on your EC2 instance size (optional but recommended)
Path mappings:
Non-CDB:
-
/rdsdbdata/db/ORCL_A/datafile/→/u01/app/oracle/oradata/ORCL/datafile/ -
/rdsdbdata/db/ORCL_A/controlfile/→/u01/app/oracle/oradata/ORCL/controlfile/ -
/rdsdbdata/db/ORCL_A/onlinelog/→/u01/app/oracle/oradata/ORCL/onlinelog/ -
/rdsdbdata/admin/ORCL/adump→/u01/app/oracle/admin/ORCL/adump
Multitenant:
-
/rdsdbdata/db/cdb/RDSCDB/datafile/→/u01/app/oracle/oradata/ORCL/cdb/datafile/ -
/rdsdbdata/db/cdb/pdbseed/→/u01/app/oracle/oradata/ORCL/pdbseed/datafile/ -
/rdsdbdata/db/pdb/RDSCDB_A/→/u01/app/oracle/oradata/ORCL/pdb/datafile/ -
/rdsdbdata/db/cdb/RDSCDB_A/controlfile/→/u01/app/oracle/oradata/ORCL/controlfile/ -
/rdsdbdata/admin/RDSCDB/adump→/u01/app/oracle/admin/ORCL/adump
Important: For multitenant, ensure
enable_pluggable_database=TRUE is present in the parameter file.
Step 7: Create SPFILE and restore standby database
Start the instance and create SPFILE:
sqlplus / as sysdba SQL> STARTUP NOMOUNT PFILE='$ORACLE_HOME/dbs/initORCL.ora'; SQL> CREATE SPFILE='/u01/app/oracle/admin/ORCL/pfile/spfileORCL.ora' FROM PFILE='$ORACLE_HOME/dbs/initORCL.ora'; SQL> SHUTDOWN IMMEDIATE;
Create symbolic link:
ln -sfn /u01/app/oracle/admin/ORCL/pfile/spfileORCL.ora $ORACLE_HOME/dbs/spfileORCL.ora
Start the instance and restore:
SQL> STARTUP NOMOUNT; rman target /
If backup files are in a different path than the source, catalog them first:
RMAN> catalog start with '/u01/app/oracle/backup/';
Restore standby control file and mount:
RMAN> restore standby controlfile from '/u01/app/oracle/backup/standby.ctl'; RMAN> alter database mount;
If data file paths differ (e.g., using ASM), use SET NEWNAME:
RMAN> run { set newname for database to '+DATA/%b'; restore database; switch datafile all; }
Otherwise, simply restore:
RMAN> restore database;
Recover the database to the last available sequence:
RMAN> list backup of archivelog all; RMAN> recover database until sequence <LAST_SEQ + 1>;
Note
For multitenant, RMAN automatically restores and recovers all PDBs. You do not need to restore each PDB separately.
Step 8: Configure TNS and listeners
On both instances, add TNS entries to tnsnames.ora:
Example Non-CDB:
ORCL_A = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST =<RDS_CUSTOM_IP>)(PORT = 1521)) (CONNECT_DATA = (SID = ORCL))) ORCL_B = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST =<EC2_IP>)(PORT = 1521)) (CONNECT_DATA = (SID = ORCL)))
Example Multitenant:
RDSCDB_A = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST =<RDS_CUSTOM_IP>)(PORT = 1521)) (CONNECT_DATA = (SID = RDSCDB))) ORCL_B = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST =<EC2_IP>)(PORT = 1521)) (CONNECT_DATA = (SID = ORCL)))
Configure listeners on both instances. On RDS Custom, append to listener.ora:
Example For Non-CDB:
SID_LIST_L_ORCL_DG=(SID_LIST = (SID_DESC = (SID_NAME = ORCL)(GLOBAL_DBNAME = ORCL) (ORACLE_HOME = /rdsdbbin/oracle.19.custom.r1.EE.1))) L_ORCL_DG=(DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(PORT = 1521)(HOST =<RDS_CUSTOM_IP>)))
Example For Multitenant:
SID_LIST_L_RDSCDB_DG=(SID_LIST = (SID_DESC = (SID_NAME = RDSCDB)(GLOBAL_DBNAME = RDSCDB) (ORACLE_HOME = /rdsdbbin/oracle.19.custom.r1.EE-CDB.1))) L_RDSCDB_DG=(DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(PORT = 1521)(HOST =<RDS_CUSTOM_IP>)))
Start the listener:
$ORACLE_HOME/bin/lsnrctl start L_ORCL_DG # or L_RDSCDB_DG for multitenant
On EC2, create $ORACLE_HOME/network/admin/listener.ora:
SID_LIST_L_ORCL_DG=(SID_LIST = (SID_DESC = (SID_NAME = ORCL)(GLOBAL_DBNAME = ORCL) (ORACLE_HOME = /u01/app/oracle/product/19.0.0/dbhome_1))) L_ORCL_DG=(DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(PORT = 1521)(HOST =<EC2_IP>)))
Start the listener:
$ORACLE_HOME/bin/lsnrctl start L_ORCL_DG
Note
You can use the existing listener on RDS Custom if preferred, but creating a separate Data Guard listener provides better isolation.
Important
If tnsping or listener connectivity fails, check iptables rules on EC2. Many EC2
Linux instances have default iptables rules that block port 1521. Add a rule: sudo iptables -I INPUT 5 -p
tcp --dport 1521 -j ACCEPT
Step 9: Enable Data Guard broker and configuration
On both instances, enable the Data Guard broker:
sqlplus / as sysdba SQL> ALTER SYSTEM SET dg_broker_start=true;
On the RDS Custom primary, connect to the Data Guard broker and create the configuration:
dgmgrl /
Example For Non-CDB:
DGMGRL> CREATE CONFIGURATION my_dg_config AS PRIMARY DATABASE IS ORCL_A CONNECT IDENTIFIER IS ORCL_A; DGMGRL> ADD DATABASE ORCL_B AS CONNECT IDENTIFIER IS ORCL_B MAINTAINED AS PHYSICAL;
Example For Multitenant:
DGMGRL> CREATE CONFIGURATION my_dg_config AS PRIMARY DATABASE IS RDSCDB_A CONNECT IDENTIFIER IS RDSCDB_A; DGMGRL> ADD DATABASE ORCL_B AS CONNECT IDENTIFIER IS ORCL_B MAINTAINED AS PHYSICAL;
Set static connect identifiers and enable:
Example For Non-CDB:
DGMGRL> edit database orcl_a set property StaticConnectIdentifier='(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(PORT=1521)(HOST=<RDS_CUSTOM_IP>))(CONNECT_DATA=(SID=ORCL)(SERVER=DEDICATED)))'; DGMGRL> edit database orcl_b set property StaticConnectIdentifier='(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(PORT=1521)(HOST=<EC2_IP>))(CONNECT_DATA=(SID=ORCL)(SERVER=DEDICATED)))'; DGMGRL> ENABLE CONFIGURATION;
Example For Multitenant:
DGMGRL> edit database rdscdb_a set property StaticConnectIdentifier='(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(PORT=1521)(HOST=<RDS_CUSTOM_IP>))(CONNECT_DATA=(SID=RDSCDB)(SERVER=DEDICATED)))'; DGMGRL> edit database orcl_b set property StaticConnectIdentifier='(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(PORT=1521)(HOST=<EC2_IP>))(CONNECT_DATA=(SID=ORCL)(SERVER=DEDICATED)))'; DGMGRL> ENABLE CONFIGURATION;
Note
Data Guard broker is optional but recommended for easier management. For simple migrations, you can configure Data Guard manually without the broker.
Note
When you enable Data Guard for a CDB, it automatically protects all PDBs. Redo generated by any PDB is shipped to the standby and applied to the corresponding PDB on the standby.
Step 10: Configure standby redo logs and start recovery
On the EC2 standby instance, add standby redo log files (n+1 where n is the number of online redo log groups):
ALTER DATABASE ADD STANDBY LOGFILE ('slog1.rdo') SIZE 128M; ALTER DATABASE ADD STANDBY LOGFILE ('slog2.rdo') SIZE 128M; ALTER DATABASE ADD STANDBY LOGFILE ('slog3.rdo') SIZE 128M; ALTER DATABASE ADD STANDBY LOGFILE ('slog4.rdo') SIZE 128M; ALTER DATABASE ADD STANDBY LOGFILE ('slog5.rdo') SIZE 128M;
Note
For multitenant, standby redo logs are created at the CDB level and are shared by all PDBs.
Configure FAL parameters on the standby:
Example For Non-CDB:
SQL> alter system set fal_server = 'ORCL_A'; SQL> alter system set fal_client = 'ORCL_B';
Example For Multitenant:
SQL> alter system set fal_server = 'RDSCDB_A'; SQL> alter system set fal_client = 'ORCL_B';
Start managed recovery:
SQL> recover managed standby database disconnect from session;
Monitor the apply lag:
SQL> SELECT name, value FROM v$dataguard_stats WHERE name = 'apply lag';
Detailed monitoring and management of Data Guard synchronization:
Monitoring Data Guard is critical to ensure successful migration. Here are comprehensive monitoring techniques:
-
Monitor Data Guard statistics:
-- Comprehensive Data Guard statistics SQL> SELECT name, value, unit, time_computed, datum_time FROM v$dataguard_stats ORDER BY name;Key metrics to monitor:
-
transport lag: Time difference between when redo was generated on primary and received on standby
-
apply lag: Time difference between when redo was generated and applied on standby
-
apply rate: Rate at which redo is being applied (MB/sec)
-
redo received: Total redo received by standby
-
redo applied: Total redo applied by standby
-
-
Monitor archive log shipping:
On the primary (RDS Custom):
-- Check archive log generation rate SQL> SELECT TO_CHAR(first_time, 'YYYY-MM-DD HH24') AS hour, COUNT(*) AS log_count, ROUND(SUM(blocks * block_size)/1024/1024/1024, 2) AS size_gb FROM v$archived_log WHERE first_time > SYSDATE - 1 GROUP BY TO_CHAR(first_time, 'YYYY-MM-DD HH24') ORDER BY hour; -- Check archive log destination status SQL> SELECT dest_id, status, error, destination FROM v$archive_dest WHERE status != 'INACTIVE';On the standby (EC2):
-- Check archive log apply status SQL> SELECT sequence#, first_time, next_time, applied FROM v$archived_log WHERE applied = 'NO' ORDER BY sequence#; -- Check archive log gap SQL> SELECT thread#, low_sequence#, high_sequence# FROM v$archive_gap; -
Monitor managed recovery process:
-- Check if managed recovery is running SQL> SELECT process, status, thread#, sequence#, block#, blocks FROM v$managed_standby WHERE process LIKE 'MRP%' OR process LIKE 'RFS%'; -- Check recovery progress SQL> SELECT process, status, sequence#, TO_CHAR(timestamp, 'YYYY-MM-DD HH24:MI:SS') AS timestamp FROM v$managed_standby ORDER BY process; -
Monitor redo apply rate for multitenant:
For multitenant databases, monitor apply rate per PDB:
-- Check redo apply rate per container SQL> SELECT con_id, name, ROUND(SUM(value)/1024/1024, 2) AS redo_applied_mb FROM v$con_sysstat cs, v$containers c WHERE cs.con_id = c.con_id AND cs.name = 'redo size' GROUP BY con_id, name ORDER BY con_id; -
Monitor standby redo logs:
-- Check standby redo log status SQL> SELECT group#, thread#, sequence#, bytes/1024/1024 AS size_mb, status FROM v$standby_log ORDER BY group#; -- Check if standby redo logs are being used SQL> SELECT group#, thread#, sequence#, status, archived FROM v$standby_log WHERE status = 'ACTIVE'; -
Estimate synchronization completion:
Calculate the remaining time based on apply rate:
-- Calculate estimated time to catch up SQL> SELECT ROUND((SELECT value FROM v$dataguard_stats WHERE name = 'apply lag')/60, 2) AS lag_minutes, ROUND((SELECT value FROM v$dataguard_stats WHERE name = 'apply rate')/1024, 2) AS apply_rate_mbps, ROUND( (SELECT value FROM v$dataguard_stats WHERE name = 'apply lag') / NULLIF((SELECT value FROM v$dataguard_stats WHERE name = 'apply rate'), 0) / 60, 2 ) AS estimated_catchup_minutes FROM dual;
Common Data Guard synchronization issues:
Issue 1: High apply lag
Symptoms:
SQL> SELECT name, value FROM v$dataguard_stats WHERE name = 'apply lag'; NAME VALUE -------------------------------- ----- apply lag +00 01:30:00
Causes and solutions:
-
Insufficient CPU/IO on standby: Upgrade EC2 instance type or increase EBS IOPS
-
Network bandwidth limitation: Use enhanced networking or higher bandwidth instance types
-
Multiple PDBs with high transaction rate: Consider increasing redo apply parallelism (requires Active Data Guard license)
-- Increase apply parallelism (requires Active Data Guard) SQL> ALTER DATABASE RECOVER MANAGED STANDBY DATABASE CANCEL; SQL> ALTER DATABASE RECOVER MANAGED STANDBY DATABASE USING CURRENT LOGFILE PARALLEL 4 DISCONNECT FROM SESSION;
Issue 2: Archive log gap
Symptoms:
SQL> SELECT * FROM v$archive_gap; THREAD# LOW_SEQUENCE# HIGH_SEQUENCE# ------- ------------- -------------- 1 1234 1240
Solution:
-- FAL (Fetch Archive Log) will automatically fetch missing logs -- Verify FAL parameters are set correctly SQL> SHOW PARAMETER fal_server SQL> SHOW PARAMETER fal_client -- Manually register missing archive logs if needed -- On primary, check if logs still exist SQL> SELECT name FROM v$archived_log WHERE sequence# BETWEEN 1234 AND 1240; -- If logs are missing on primary, you may need to rebuild the standby
Issue 3: Redo transport failure
Symptoms:
SQL> SELECT dest_id, status, error FROM v$archive_dest WHERE dest_id = 2; DEST_ID STATUS ERROR ------- --------- ---------------------------------------- 2 ERROR ORA-16191: Primary log shipping client not logged on standby
Solution:
-- Check network connectivity -- Verify TNS configuration -- Check listener status on standby -- Restart log transport SQL> ALTER SYSTEM SET log_archive_dest_state_2 = 'DEFER'; SQL> ALTER SYSTEM SET log_archive_dest_state_2 = 'ENABLE';
Issue 4: Managed recovery not applying redo
Symptoms:
SQL> SELECT process, status FROM v$managed_standby WHERE process = 'MRP0'; PROCESS STATUS --------- ------------ MRP0 WAIT_FOR_LOG
Solution:
# Check if archive logs are arriving ls -ltr /u01/app/oracle/oradata/ORCL/arch/ # Check alert log for errors tail -100 $ORACLE_BASE/diag/rdbms/orcl_b/ORCL/trace/alert_ORCL.log # Restart managed recovery sqlplus / as sysdba SQL> ALTER DATABASE RECOVER MANAGED STANDBY DATABASE CANCEL; SQL> ALTER DATABASE RECOVER MANAGED STANDBY DATABASE DISCONNECT FROM SESSION;
For Multitenant, you can also check the status of each PDB on the standby:
SQL> SELECT con_id, name, open_mode, restricted FROM v$pdbs;
Expected output (PDBs in MOUNTED state on standby):
CON_ID NAME OPEN_MODE RES ---------- ------------------------------ ---------- --- 2 PDB$SEED MOUNTED 3 ORCLDB MOUNTED
Note
On a physical standby, PDBs remain in MOUNTED state during managed recovery.
Step 11: Perform the switchover
Once you are satisfied that the standby is fully synchronized and ready, perform the switchover. For multitenant, the switchover will switch the entire CDB (all PDBs) from the RDS Custom primary to the EC2 standby.
On the RDS Custom primary instance, connect to the Data Guard broker and validate both databases are ready for switchover:
Example For Non-CDB:
DGMGRL> VALIDATE DATABASE ORCL_A; DGMGRL> VALIDATE DATABASE ORCL_B;
Example For Multitenant:
DGMGRL> VALIDATE DATABASE RDSCDB_A; DGMGRL> VALIDATE DATABASE ORCL_B;
Both should show Ready for Switchover: Yes
Switch over from the RDS Custom primary to the EC2 standby:
DGMGRL> SWITCHOVER TO ORCL_B;
Verify the switchover is successful:
DGMGRL> SHOW CONFIGURATION VERBOSE;
The EC2 instance (ORCL_B) is now the primary database, and the RDS Custom instance is the physical
standby.
Step 12: Open PDBs (multitenant only)
After the switchover, the CDB on EC2 is open in READ WRITE mode, but all PDBs are in MOUNTED state. You must open them manually.
Connect to the new primary on EC2:
sqlplus / as sysdba SQL> SELECT name, open_mode, database_role, cdb FROM v$database;
Expected output:
NAME OPEN_MODE DATABASE_ROLE CDB --------- -------------------- ---------------- --- ORCL READ WRITE PRIMARY YES
Check the current PDB status:
SQL> SELECT con_id, name, open_mode, restricted FROM v$pdbs;
Expected output (PDBs in MOUNTED state - example with one PDB named ORCLDB):
CON_ID NAME OPEN_MODE RES ---------- ------------------------------ ---------- --- 2 PDB$SEED READ ONLY NO 3 ORCLDB MOUNTED
Open all PDBs:
SQL> ALTER PLUGGABLE DATABASE ALL OPEN;
Pluggable database altered.
Verify all PDBs are now open in READ WRITE mode:
SQL> SELECT con_id, name, open_mode, restricted FROM v$pdbs;
Expected output:
CON_ID NAME OPEN_MODE RES ---------- ------------------------------ ---------- --- 2 PDB$SEED READ ONLY NO 3 ORCLDB READ WRITE NO
Step 13: Configure PDB auto-open on startup (multitenant only)
Configure PDBs to automatically open when the CDB starts using the save state method (recommended for Oracle 19c):
SQL> ALTER PLUGGABLE DATABASE ALL SAVE STATE; Pluggable database altered.
Verify the saved state:
SQL> SELECT con_name, instance_name, state FROM dba_pdb_saved_states;
Verify PDB services are registered with the listener:
lsnrctl services
Expected output should show services for the CDB and each PDB. If services aren't showing:
SQL> ALTER SYSTEM REGISTER;
Then check again with lsnrctl services.
Step 14: Remove RDS Custom objects
Because this is now a self-managed database on EC2, you should remove RDS Custom specific users and objects. The cleanup process differs slightly between non-CDB and multitenant architectures.
Important
Before dropping RDS-specific users and tablespaces, verify that no application objects exist under these schemas:
SQL> SELECT owner, object_type, COUNT(*) FROM dba_objects WHERE owner IN ('RDSADMIN', 'RDS_DATAGUARD') AND object_name NOT LIKE 'RDS%' AND object_name NOT LIKE 'SYS_%' GROUP BY owner, object_type;
If any application objects are found, migrate them to appropriate application schemas before proceeding.
Non-CDB cleanup:
sqlplus / as sysdba -- Drop RDS-specific users SQL> DROP USER RDSADMIN CASCADE; SQL> DROP USER RDS_DATAGUARD CASCADE; -- Drop RDS-specific directories SQL> DROP DIRECTORY OPATCH_INST_DIR; SQL> DROP DIRECTORY OPATCH_LOG_DIR; SQL> DROP DIRECTORY OPATCH_SCRIPT_DIR; -- Drop the RDSADMIN tablespace SQL> DROP TABLESPACE RDSADMIN INCLUDING CONTENTS AND DATAFILES; -- Verify removal SQL> SELECT username FROM dba_users WHERE username LIKE '%RDS%';
Expected output: no rows selected
Multitenant cleanup:
In a multitenant environment, RDS Custom creates common users in CDB$ROOT that are visible across all PDBs.
You must clean up from CDB$ROOT.
sqlplus / as sysdba -- Verify you are in CDB$ROOT SQL> SHOW CON_NAME; -- Check for RDS-specific common users (including C## prefixed users) SQL> SELECT username, common, con_id FROM cdb_users WHERE username LIKE 'RDS%' OR username LIKE 'C##RDS%' ORDER BY username; -- Drop non-common users SQL> DROP USER RDSADMIN CASCADE; SQL> DROP USER RDS_DATAGUARD CASCADE; -- If any C## common users exist -- Example (adjust based on your findings): -- SQL> DROP USER C##RDS_DATAGUARD CASCADE; -- Drop RDS-specific directories SQL> DROP DIRECTORY OPATCH_INST_DIR; SQL> DROP DIRECTORY OPATCH_LOG_DIR; SQL> DROP DIRECTORY OPATCH_SCRIPT_DIR; -- Drop the RDSADMIN tablespace SQL> DROP TABLESPACE RDSADMIN INCLUDING CONTENTS AND DATAFILES; -- Verify removal from CDB$ROOT SQL> SELECT username FROM dba_users WHERE username LIKE '%RDS%'; -- Verify removal from each PDB (example with one PDB named ORCLDB) SQL> ALTER SESSION SET CONTAINER = ORCLDB; SQL> SELECT username FROM dba_users WHERE username LIKE '%RDS%';
Expected output for all queries: no rows selected
Step 15: Configure automatic startup
Verify the SPFILE is being used:
sqlplus / as sysdba SQL> SHOW PARAMETER spfile;
If the spfile path is correct, no action is needed. If not, create one:
SQL> CREATE SPFILE FROM MEMORY;
Restart the database:
SQL> SHUTDOWN IMMEDIATE; SQL> STARTUP;
For multitenant, open all PDBs (they should auto-open if you saved state earlier):
SQL> SELECT con_id, name, open_mode FROM v$pdbs;
If PDBs are not open, open them manually:
SQL> ALTER PLUGGABLE DATABASE ALL OPEN;
Edit /etc/oratab:
vi /etc/oratab
Change the line for ORCL from N to Y:
ORCL:/u01/app/oracle/product/19.0.0/dbhome_1:Y
Step 16: Final validation
Perform comprehensive validation on the migrated database.
Example For Non-CDB:
sqlplus / as sysdba -- Verify database role and status SQL> SELECT name, open_mode, log_mode, database_role FROM v$database; -- Check database size SQL> SELECT SUM(bytes)/1024/1024/1024 AS size_gb FROM dba_data_files; -- Verify all objects are valid SQL> SELECT owner, object_type, COUNT(*) FROM dba_objects WHERE status = 'INVALID' GROUP BY owner, object_type; -- Verify data files SQL> SELECT name, status FROM v$datafile; -- Test application connectivity SQL> SELECT username, machine, program FROM v$session WHERE username IS NOT NULL;
Example For Multitenant:
sqlplus / as sysdba -- Verify CDB status SQL> SELECT name, open_mode, log_mode, cdb, database_role FROM v$database; -- Verify all PDBs are open SQL> SELECT con_id, name, open_mode, restricted FROM v$pdbs; -- Check total CDB size SQL> SELECT SUM(bytes)/1024/1024/1024 AS total_size_gb FROM cdb_data_files; -- Check size per PDB SQL> SELECT p.name AS pdb_name, ROUND(SUM(d.bytes)/1024/1024/1024, 2) AS size_gb FROM v$pdbs p JOIN cdb_data_files d ON p.con_id = d.con_id GROUP BY p.name,p.con_id ORDER BY p.con_id; -- Verify all objects are valid across all PDBs SQL> SELECT con_id, owner, object_type, COUNT(*) FROM cdb_objects WHERE status = 'INVALID' GROUP BY con_id, owner, object_type; -- Verify PDB services are registered SQL> SELECT name FROM v$services ORDER BY name; Test application connectivity: # Non-CDB sqlplus<app_user>/<password>@<EC2_IP>:1521/ORCL # Multitenant (connect to PDB) sqlplus<app_user>/<password>@<EC2_IP>:1521/<PDB_NAME>
Test application connectivity:
# Non-CDB sqlplus<app_user>/<password>@<EC2_IP>:1521/ORCL # Multitenant (connect to PDB) sqlplus<app_user>/<password>@<EC2_IP>:1521/<PDB_NAME>
Step 17: Clean up backup files
After successful validation, remove backup files and detach the backup volume if using a separate EBS volume:
rm -rf /u01/app/oracle/backup/*
If using a separate EBS volume for backups:
# Unmount the volume sudo umount /u01/app/oracle/backup # Detach and delete the EBS volume from AWS Console or CLI aws ec2 detach-volume --volume-id<volume-id>aws ec2 delete-volume --volume-id<volume-id>
Step 18: Resume RDS Custom automation
If you plan to keep the RDS Custom instance running as a fallback during a validation period, resume the automation:
aws rds modify-db-instance \ --db-instance-identifier my-custom-instance \ --automation-mode full \ --region us-east-1
Troubleshooting common issues
This section covers common issues you may encounter during migration for both RMAN duplication and Oracle Data Guard approaches, covering both non-CDB and multitenant architectures.
ORA-09925: Unable to create audit trail file
Cause: The audit directory specified in audit_file_dest parameter doesn't
exist on the target EC2 instance.
Solution:
Ensure the audit directory exists and has proper permissions:
mkdir -p /u01/app/oracle/admin/ORCL/adump chown -R oracle:oinstall /u01/app/oracle/admin/ORCL chmod -R 755 /u01/app/oracle/admin/ORCL
ORA-01261: Parameter db_create_file_dest destination string cannot be translated
Cause: The directory specified in db_create_file_dest parameter doesn't exist
on the target EC2 instance.
Solution:
For non-CDB:
mkdir -p /u01/app/oracle/oradata/ORCL chown -R oracle:oinstall /u01/app/oracle/oradata/ORCL chmod -R 755 /u01/app/oracle/oradata/ORCL
For multitenant:
mkdir -p /u01/app/oracle/oradata/ORCL/pdb/datafile chown -R oracle:oinstall /u01/app/oracle/oradata/ORCL chmod -R 755 /u01/app/oracle/oradata/ORCL
ORA-01804: Failure to initialize timezone information
This error can occur when dropping the RDSADMIN user if the RDS source has a higher timezone version than
what's installed in your EC2 $ORACLE_HOME.
Solution:
-
Check the timezone versions:
SELECT * FROM v$timezone_file; SELECT PROPERTY_NAME, PROPERTY_VALUE FROM database_properties WHERE PROPERTY_NAME LIKE '%DST%'; -
As a workaround, set the timezone file environment variable to match what your $ORACLE_HOME has available:
ls $ORACLE_HOME/oracore/zoneinfo/timezlrg_*.dat export ORA_TZFILE=$ORACLE_HOME/oracore/zoneinfo/timezone_40.datAdjust the number to match the version available in your installation.
-
Retry the drop:
sqlplus / as sysdba SQL> DROP USER RDSADMIN CASCADE;
Cross-RU migration issues (different Release Updates)
Cause: The target EC2 instance has a different Oracle Release Update (RU) or one-off patches than the source RDS Custom instance, causing compatibility errors during or after migration.
Common errors:
-
ORA-00600 internal errors during redo apply (Data Guard)
-
ORA-39700 database must be opened with
UPGRADEoption -
Dictionary inconsistencies after migration
-
Invalid objects in
DBA_REGISTRYorDBA_OBJECTS
Solution:
Best practice - Match RU versions and one-off patches exactly:
-
Check the exact RU version on both source and target:
-- On both source and target SQL> SELECT * FROM v$version; SQL> SELECT patch_id, patch_uid, version, action, status, description FROM dba_registry_sqlpatch ORDER BY action_time DESC; -
Verify the $ORACLE_HOME patch level:
# On both instances $ORACLE_HOME/OPatch/opatch lsinventory -
If versions don't match, align them before migration by applying or rolling back patches as needed.
-
If you must proceed with mismatched RUs, run datapatch after migration:
cd $ORACLE_HOME/OPatch ./datapatch -verbose -
Check for invalid objects and recompile:
SQL> @?/rdbms/admin/utlrp.sql SQL> SELECT owner, object_type, COUNT(*) FROM dba_objects WHERE status = 'INVALID' GROUP BY owner, object_type;
Network connectivity issues
Cause: Security groups, network ACLs, or iptables blocking the Oracle
listener port.
Solution:
-
Verify security groups allow the port bidirectionally
-
Check iptables on EC2:
sudo iptables -L INPUT -n -v -
Add rule if needed:
# Insert rule before the REJECT rule (typically position 5) sudo iptables -I INPUT 5 -p tcp --dport 1521 -j ACCEPT # For enhanced security, allow only from specific source IPs sudo iptables -I INPUT 5 -p tcp -s<RDS_Custom_IP>--dport 1521 -j ACCEPT # Save rules permanently sudo service iptables save -
Test connectivity:
telnet<EC2_instance_IP>1521 tnsping DB_SOURCE tnsping DB_TARGET
PDBs not opening after migration (multitenant only)
Cause: This is expected behavior. After RMAN duplication or Data Guard switchover, the CDB
is open but PDBs are in MOUNTED state.
Solution:
Open them manually:
SQL> ALTER PLUGGABLE DATABASE ALL OPEN;
If a specific PDB fails to open, check the alert log for errors:
tail -100 $ORACLE_BASE/diag/rdbms/orcl/ORCL/trace/alert_ORCL.log
Common causes include missing data files or path mapping issues.
PDB data files not found or path mismatch (multitenant only)
Cause: The migration didn't map all source paths correctly, especially for OMF-based PDB data files.
Solution:
-
Check which data files are missing:
SQL> SELECT name, status FROM v$datafile WHERE status != 'ONLINE'; -
If the files were placed in the wrong directory, rename them in the control file:
SQL> ALTER DATABASE RENAME FILE '/wrong/path/datafile.dbf' TO '/correct/path/datafile.dbf'; -
To prevent this, always verify the source data file paths with
SELECT con_id, name FROM v$datafile ORDER BY con_id;before configuring the parameter file.
PDB services not registering with listener (multitenant only)
Cause: The listener is not aware of the PDB services after opening PDBs.
Solution:
-
Force service registration:
SQL> ALTER SYSTEM REGISTER; -
If services still don't appear, check the
local_listenerparameter:SQL> SHOW PARAMETER local_listener;Ensure it points to the correct listener address. If needed, update it:
SQL> ALTER SYSTEM SET local_listener='(ADDRESS=(PROTOCOL=TCP)(HOST=<EC2_instance_IP>)(PORT=1521))'; SQL> ALTER SYSTEM REGISTER; -
Verify with:
lsnrctl services
PDBs not auto-opening after CDB restart (multitenant only)
Cause: PDB save state was not configured.
Solution:
Verify that PDB save state was configured:
SQL> SELECT con_name, instance_name, state FROM dba_pdb_saved_states;
If no rows are returned, save the state:
SQL> ALTER PLUGGABLE DATABASE ALL OPEN; SQL> ALTER PLUGGABLE DATABASE ALL SAVE STATE;
Data Guard redo transport not working
Cause: Network connectivity issues, incorrect TNS configuration, or FAL parameters not set.
Solution:
-
Verify the standby is in MOUNT mode:
SQL> SELECT status FROM v$instance; -
Check fal_server and fal_client are set correctly on the standby:
SQL> SHOW PARAMETER fal_server SQL> SHOW PARAMETER fal_client -
Verify network connectivity:
tnsping ORCL_A # or RDSCDB_A for multitenant -
Check the log_archive_dest_2 parameter on the primary points to the standby (if configured manually without broker).
Data Guard apply lag increasing with multiple PDBs (multitenant only)
Cause: For CDBs with multiple PDBs, redo apply can be slower due to the volume of changes across all containers.
Solution:
-
Check the apply rate:
SQL> SELECT name, value, unit FROM v$dataguard_stats WHERE name IN ('apply rate', 'apply lag'); -
Consider increasing parallelism for redo apply (requires Active Data Guard license):
SQL> ALTER DATABASE RECOVER MANAGED STANDBY DATABASE CANCEL; SQL> ALTER DATABASE RECOVER MANAGED STANDBY DATABASE USING CURRENT LOGFILE PARALLEL 4 DISCONNECT FROM SESSION; -
Verify there are no resource constraints (CPU, I/O) on the standby instance.
RMAN archive log backup fails with ORA-19625
Cause: RDS Custom automation has purged older archive logs from disk, but RMAN's control file still has records of them.
Solution:
-
Crosscheck and clean up stale archive log entries:
RMAN> CROSSCHECK ARCHIVELOG ALL; RMAN> DELETE NOPROMPT EXPIRED ARCHIVELOG ALL; -
Re-run just the archive log backup:
RMAN> RUN { SQL 'ALTER SYSTEM ARCHIVE LOG CURRENT'; BACKUP AS COMPRESSED BACKUPSET FILESPERSET 50 FORMAT '/rdsdbdata/backup/arch_%U' ARCHIVELOG ALL; }
Common user drop fails in multitenant (multitenant only)
Cause: Common users (prefixed with C##) must be dropped with the
CONTAINER=ALL clause.
Solution:
-- For common users SQL> DROP USER C##RDS_DATAGUARD CASCADE CONTAINER=ALL; -- For non-common users in CDB$ROOT SQL> DROP USER RDSADMIN CASCADE;
Verify you're connected to CDB$ROOT:
SQL> SHOW CON_NAME;
Post-migration tasks
After successful migration, complete these additional tasks to ensure your self-managed Oracle database on EC2 is production-ready.
Update application connection strings
For Non-CDB:
-
Point your applications to the new EC2 instance endpoint
-
Update connection strings to use the EC2 instance IP or hostname
-
Test all application functionality thoroughly
For Multitenant:
-
Point your applications to the new EC2 instance PDB service names (e.g., ORCLDB or your specific PDB names)
-
Ensure applications connect to the correct PDB, not the CDB
-
Update connection strings to use PDB service names
-
Test all application functionality for each PDB
Example connection strings:
# Non-CDB jdbc:oracle:thin:@<EC2_IP>:1521/ORCL # Multitenant (connect to PDB) jdbc:oracle:thin:@<EC2_IP>:1521/ORCLDB
Configure backup strategy
Set up a comprehensive backup strategy for your self-managed database:
RMAN backups:
-
Configure automated RMAN backup scripts for full, incremental, and archive log backups
-
Set up backup retention policies based on your recovery point objectives (RPO)
-
Store backups on Amazon S3 for durability and cost-effectiveness
-
Regularly test backup restoration procedures
Amazon Backup:
-
Use Amazon Backup
for EBS volume snapshots -
Configure backup schedules and retention policies
-
Enable cross-region backup copies for disaster recovery
For Multitenant:
-
RMAN backups of the CDB automatically include all PDBs
-
You can also back up individual PDBs if needed
-
Consider PDB-specific backup schedules based on business requirements
Example RMAN backup script:
#!/bin/bash export ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1 export ORACLE_SID=ORCL export PATH=$ORACLE_HOME/bin:$PATH rman target / << EOF run { backup as compressed backupset database plus archivelog; delete noprompt obsolete; } exit; EOF
Set up monitoring
Implement comprehensive monitoring for your EC2-hosted Oracle database:
Amazon CloudWatch
-
Set up CloudWatch metrics for EC2 instance health, disk usage, and custom Oracle metrics
-
Create CloudWatch alarms for critical thresholds
-
Use CloudWatch Logs for database alert log monitoring
Oracle Enterprise Manager (OEM):
-
If available, configure OEM for database monitoring
-
Set up performance monitoring and diagnostics
-
Configure automated alerts for critical events
Third-party tools:
-
Consider tools like Datadog, New Relic, or Prometheus for database monitoring
-
Integrate with your existing monitoring infrastructure
Key metrics to monitor:
-
Tablespace usage
-
Archive log space
-
Invalid objects
-
Session counts
-
Wait events
-
CPU and memory utilization
-
I/O performance
For Multitenant:
-
Monitor both CDB-level and PDB-level metrics
-
Set up alerts for PDB resource usage and quotas
-
Track PDB-specific performance metrics
Configure security groups and network ACLs
Review and tighten security for the EC2 instance:
Security groups:
-
Restrict database port access to only authorized application servers and bastion hosts
-
Remove any overly permissive rules created during migration
-
Document security group rules and their purposes
Network ACLs:
-
Configure VPC network ACLs for additional security layers
-
Implement defense-in-depth security strategy
SSH access:
-
Limit SSH access to specific IP ranges or use Amazon Systems Manager Session Manager
-
Disable password authentication and use key-based authentication only
-
Implement multi-factor authentication (MFA) for privileged access
Encryption:
-
Enable encryption at rest for EBS volumes
-
Enable encryption in transit for database connections using Oracle Native Network Encryption or TLS
-
Rotate encryption keys regularly
Implement high availability
If your workload requires high availability, consider implementing:
Oracle Data Guard:
-
Configure a new standby database on another EC2 instance for disaster recovery
-
For multitenant, Data Guard protects the entire CDB including all PDBs
-
The standby can be in a different Availability Zone or Region
-
Implement automated failover mechanisms using scripts or third-party tools
Amazon Multi-AZ deployment:
-
Deploy standby instances in different Availability Zones
-
Use Amazon Route 53 for DNS failover
-
Implement application-level connection pooling with failover support
Backup and recovery:
-
Maintain regular backups with tested restore procedures
-
Document recovery time objectives (RTO) and recovery point objectives (RPO)
-
Conduct regular disaster recovery drills
Perform thorough application testing
Before decommissioning the RDS Custom instance:
Functional testing:
-
Verify all application features work correctly
-
Test all database-dependent functionality
-
Validate data integrity and consistency
Performance testing:
-
Compare performance metrics with the RDS Custom baseline
-
Identify and address any performance regressions
-
Optimize queries and indexes as needed
Load testing:
-
Test the database under expected peak loads
-
Verify resource utilization stays within acceptable limits
-
Identify and address any bottlenecks
Failover testing (if HA is configured):
-
Test failover scenarios
-
Verify application reconnection logic
-
Measure actual RTO and RPO
Backup and restore testing:
-
Verify backup and restore procedures work correctly
-
Test point-in-time recovery
-
Validate backup integrity
For Multitenant:
-
Test each PDB independently
-
Verify PDB isolation and resource allocation
-
Test PDB-specific operations (clone, unplug/plug, etc.)
Decommission RDS Custom instance
After a successful validation period (typically 1-2 weeks):
-
Final backup: Take a final backup of the RDS Custom instance for archival purposes
# Create final snapshot aws rds create-db-snapshot \ --db-instance-identifier my-custom-instance \ --db-snapshot-identifier my-custom-instance-final-snapshot \ --region us-east-1 -
Document the migration: Update runbooks and documentation with the new EC2 configuration
-
Delete the RDS Custom instance: Use the Amazon Console or CLI to delete the instance
# Delete RDS Custom instance without final snapshot (if already created above) aws rds delete-db-instance \ --db-instance-identifier my-custom-instance \ --skip-final-snapshot \ --region us-east-1 # Or create a final snapshot before deletion aws rds delete-db-instance \ --db-instance-identifier my-custom-instance \ --final-db-snapshot-identifier my-custom-instance-final-snapshot \ --region us-east-1 -
Clean up resources: Remove associated snapshots, parameter groups, and option groups if no longer needed
-
Update documentation: Ensure all operational documentation reflects the new EC2-based architecture
Comparison: RMAN Active Duplication vs Oracle Data Guard
The following table summarizes the key differences between the two migration options:
|
Aspect |
RMAN Active Duplication |
Oracle Data Guard |
|---|---|---|
|
Source database availability |
Online during entire duplication | Online during entire process |
|
Downtime |
Minutes (final cutover only) | Seconds to minutes (switchover) |
|
Complexity |
Lower | Higher |
|
Migration duration |
Single duplication operation | Initial setup + continuous sync |
|
Continuous synchronization |
No | Yes |
|
Fallback capability |
Manual (keep source running) | Built-in (automatic) |
|
Testing before cutover |
Limited (test after duplication) | Full testing possible (standby can be tested) |
|
Network bandwidth |
High during duplication | Moderate (continuous) |
|
Source database impact |
Minimal (read operations) | Minimal (redo shipping) |
|
Best for |
Most migrations, straightforward cutover | Mission-critical, near-zero downtime required |
|
Non-CDB support |
Yes | Yes |
|
Multitenant support |
Yes (entire CDB) | Yes (entire CDB) |
|
Post-migration PDB state |
CDB open, PDBs MOUNTED | CDB open, PDBs MOUNTED |
|
Requires RMAN |
Yes | Yes (for initial backup in backup-based approach) |
|
Requires Data Guard |
No | Yes |
|
Skill level required |
Intermediate | Advanced |
|
Cutover process |
Redirect applications to EC2 | Switchover command + redirect applications |
Comparison: Non-CDB vs Multitenant migration
The following table summarizes the key differences between migrating non-CDB and multitenant databases:
|
Aspect |
Non-CDB migration |
Multitenant (CDB with PDBs) migration |
|---|---|---|
|
Database type |
Single-instance non-CDB (e.g., ORCL) |
CDB (source: RDSCDB, target: ORCL) with CDB$ROOT + PDB$SEED + one
or more PDBs |
|
Migration scope |
Single database | Entire CDB (all PDBs included automatically) |
|
RMAN duplication scope |
Duplicates single database | Duplicates entire CDB (all containers) |
|
Data Guard scope |
Protects single database | Protects entire CDB (all PDBs included automatically) |
|
Parameter file |
Standard init parameters | Must include enable_pluggable_database=TRUE |
|
Post-migration database state |
Database opens in READ WRITE mode | CDB opens in READ WRITE mode; PDBs remain in MOUNTED state |
|
PDB opening |
N/A | Must manually open all PDBs with ALTER PLUGGABLE DATABASE ALL OPEN |
|
PDB auto-open on startup |
N/A | Must configure PDB save state or startup trigger |
|
Validation |
Single database checks | Must validate CDB and each PDB individually |
|
RDS-specific cleanup |
Drop users/objects from single database | Drop common users from CDB$ROOT (cascades to PDBs); handle C## users |
|
TNS/Listener configuration |
Single service for database | CDB service + individual PDB services dynamically registered |
|
Application connection strings |
Connect to single database | Connect to individual PDB services (not CDB) |
|
Backup strategy |
Backup single database | Backup entire CDB (includes all PDBs) or individual PDBs |
|
Resource management |
Database-level resource management | CDB-level and PDB-level resource management with Resource Manager |
|
Complexity |
Lower complexity | Higher complexity due to multiple containers and OMF paths |
Best practices and recommendations
This section provides comprehensive best practices for successful migration from RDS Custom for Oracle to EC2.
Pre-migration planning
-
Conduct a thorough assessment:
Before starting the migration, perform a comprehensive assessment of your environment:
-
Database inventory: Document all databases, their sizes, architectures (non-CDB vs multitenant), and dependencies
-
Application dependencies: Identify all applications connecting to the database and their connection methods
-
Performance baseline: Capture performance metrics (CPU, memory, I/O, network) for comparison post-migration
-
Backup and recovery requirements: Document RPO (Recovery Point Objective) and RTO (Recovery Time Objective)
-
Compliance requirements: Identify any regulatory or compliance requirements that may affect the migration
-
-
Choose the right EC2 instance type:
Select an EC2 instance type based on your workload characteristics:
Workload Type
Recommended Instance Family
Key Characteristics
General purpose OLTP M6i, M6a, M7i Balanced compute, memory, and network Memory-intensive R6i, R6a, R7i, X2idn High memory-to-CPU ratio Compute-intensive C6i, C6a, C7i High CPU performance I/O-intensive I4i, Im4gn High local NVMe SSD storage Mixed workloads M5, M5a, M5n Cost-effective balanced performance Instance sizing guidelines:
-
Start with the same instance class as your RDS Custom instance
-
Monitor resource utilization during a test migration
-
Consider using Amazon Compute Optimizer for recommendations
-
Plan for 20-30% headroom for growth and peak loads
-
-
Design your storage architecture:
EBS volume types:
Volume Type
Use Case
Performance
Cost
gp3 General purpose, most workloads Up to 16,000 IOPS, 1,000 MB/s Low io2 Block Express Mission-critical, high-performance Up to 256,000 IOPS, 4,000 MB/s High Io1 High-performance databases Up to 64,000 IOPS, 1,000 MB/s Medium-High gp2 Legacy general purpose Up to 16,000 IOPS Low Storage layout recommendations:
# Recommended layout for production databases /u01/app/oracle # Oracle software (50-100 GB, gp3) /u01/app/oracle/oradata # Data files (sized for database, gp3 or io2) /u01/app/oracle/arch # Archive logs (separate volume, gp3) /u01/app/oracle/backup # Backups (separate volume, gp3, can be detached post-migration)Benefits of separate volumes:
-
Independent IOPS allocation
-
Easier capacity management
-
Simplified backup and snapshot strategies
-
Better performance isolation
-
-
Establish a rollback plan:
Always have a rollback strategy:
-
Keep RDS Custom instance running during validation period (1-2 weeks recommended)
-
Maintain regular backups of both source and target
-
Document rollback procedures including connection string changes
-
Test rollback process in a non-production environment
-
Define rollback criteria (performance degradation, data inconsistency, application errors)
-
Migration execution best practices
-
Timing your migration:
Choose the optimal time window:
-
Low-traffic periods: Weekends, holidays, or off-peak hours
-
Maintenance windows: Align with scheduled maintenance if possible
-
Avoid month-end/quarter-end: These periods typically have high transaction volumes
-
Consider time zones: For global applications, choose a time that minimizes impact across regions
-
-
Communication plan:
Establish clear communication:
-
Stakeholder notification: Inform all stakeholders at least 2 weeks in advance
-
Application teams: Coordinate with application teams for connection string updates
-
Status updates: Provide regular updates during migration
-
Escalation path: Define clear escalation procedures for issues
-
Post-migration communication: Confirm successful completion and any follow-up actions
-
-
Validation checkpoints:
Implement validation at each stage:
Pre-migration validation:
-- Capture object counts SQL> SELECT object_type, COUNT(*) FROM dba_objects GROUP BY object_type ORDER BY object_type; -- Capture tablespace usage SQL> SELECT tablespace_name, ROUND(SUM(bytes)/1024/1024/1024, 2) AS size_gb FROM dba_data_files GROUP BY tablespace_name; -- Capture user counts SQL> SELECT COUNT(*) FROM dba_users; -- For multitenant, capture PDB information SQL> SELECT con_id, name, open_mode FROM v$pdbs;Post-migration validation:
-- Verify object counts match SQL> SELECT object_type, COUNT(*) FROM dba_objects GROUP BY object_type ORDER BY object_type; -- Verify no invalid objects SQL> SELECT owner, object_type, object_name FROM dba_objects WHERE status = 'INVALID'; -- Verify tablespace usage SQL> SELECT tablespace_name, ROUND(SUM(bytes)/1024/1024/1024, 2) AS size_gb FROM dba_data_files GROUP BY tablespace_name; -- Verify database size matches SQL> SELECT SUM(bytes)/1024/1024/1024 AS total_size_gb FROM dba_data_files; -- For multitenant, verify all PDBs are open SQL> SELECT con_id, name, open_mode FROM v$pdbs; -
Performance validation:
Compare performance metrics before and after migration:
-- Capture AWR snapshots before migration (on RDS Custom) SQL> EXEC DBMS_WORKLOAD_REPOSITORY.CREATE_SNAPSHOT; -- After migration (on EC2), compare metrics SQL> SELECT snap_id, begin_interval_time, end_interval_time FROM dba_hist_snapshot ORDER BY snap_id DESC FETCH FIRST 10 ROWS ONLY; -- Generate AWR report for comparison SQL> @?/rdbms/admin/awrrpt.sqlKey metrics to compare:
-
Average active sessions
-
DB time per transaction
-
Physical reads per second
-
Logical reads per second
-
Redo size per second
-
User calls per second
-
Parse time per execute
-
Post-migration optimization
-
After migration, optimize database performance:
-
Database performance tuning:
Gather statistics:
-- Gather dictionary statistics SQL> EXEC DBMS_STATS.GATHER_DICTIONARY_STATS; -- Gather fixed object statistics SQL> EXEC DBMS_STATS.GATHER_FIXED_OBJECTS_STATS; -- Gather schema statistics SQL> EXEC DBMS_STATS.GATHER_SCHEMA_STATS('SCHEMA_NAME', cascade=>TRUE); -- For multitenant, gather statistics for each PDB SQL> ALTER SESSION SET CONTAINER = PDB_NAME; SQL> EXEC DBMS_STATS.GATHER_DATABASE_STATS(cascade=>TRUE);Optimize memory parameters:
-- Enable Automatic Memory Management (if not already enabled) SQL> ALTER SYSTEM SET MEMORY_TARGET = 24G SCOPE=SPFILE; SQL> ALTER SYSTEM SET MEMORY_MAX_TARGET = 28G SCOPE=SPFILE; SQL> SHUTDOWN IMMEDIATE; SQL> STARTUP; -- Or use Automatic Shared Memory Management SQL> ALTER SYSTEM SET SGA_TARGET = 16G SCOPE=SPFILE; SQL> ALTER SYSTEM SET PGA_AGGREGATE_TARGET = 8G SCOPE=SPFILE;Configure result cache:
-- Enable result cache for frequently accessed queries SQL> ALTER SYSTEM SET RESULT_CACHE_MAX_SIZE = 1G; SQL> ALTER SYSTEM SET RESULT_CACHE_MODE = MANUAL; -
Storage optimization:
Enable compression:
-- For tables with infrequent updates ALTER TABLE large_table MOVE COMPRESS FOR OLTP; -- For archive/historical data ALTER TABLE archive_table MOVE COMPRESS FOR ARCHIVE HIGH; -- Rebuild indexes after compression ALTER INDEX index_name REBUILD ONLINE;Implement partitioning:
-- For large tables, consider partitioning -- Example: Range partitioning by date CREATE TABLE sales_partitioned ( sale_id NUMBER, sale_date DATE, amount NUMBER ) PARTITION BY RANGE (sale_date) ( PARTITION sales_2024 VALUES LESS THAN (TO_DATE('2025-01-01', 'YYYY-MM-DD')), PARTITION sales_2025 VALUES LESS THAN (TO_DATE('2026-01-01', 'YYYY-MM-DD')), PARTITION sales_2026 VALUES LESS THAN (MAXVALUE) ); -
Implement monitoring and alerting:
CloudWatch custom metrics:
Create a script to publish Oracle metrics to CloudWatch:
#!/bin/bash # publish_oracle_metrics.sh INSTANCE_ID=$(ec2-metadata --instance-id | cut -d " " -f 2) REGION=$(ec2-metadata --availability-zone | cut -d " " -f 2 | sed 's/[a-z]$//') # Get tablespace usage TABLESPACE_USAGE=$(sqlplus -s / as sysdba << EOF SET PAGESIZE 0 FEEDBACK OFF VERIFY OFF HEADING OFF ECHO OFF SELECT ROUND(MAX(percent_used), 2) FROM ( SELECT tablespace_name, ROUND((used_space/tablespace_size)*100, 2) AS percent_used FROM dba_tablespace_usage_metrics ); EXIT; EOF ) # Publish to CloudWatch aws cloudwatch put-metric-data \ --region $REGION \ --namespace "Oracle/Database" \ --metric-name "TablespaceUsage" \ --value $TABLESPACE_USAGE \ --unit Percent \ --dimensions InstanceId=$INSTANCE_ID,Database=ORCL # Add more metrics as needed (sessions, wait events, etc.)Set up CloudWatch alarms:
# Create alarm for high tablespace usage aws cloudwatch put-metric-alarm \ --alarm-name oracle-high-tablespace-usage \ --alarm-description "Alert when tablespace usage exceeds 85%" \ --metric-name TablespaceUsage \ --namespace Oracle/Database \ --statistic Maximum \ --period 300 \ --evaluation-periods 2 \ --threshold 85 \ --comparison-operator GreaterThanThreshold \ --alarm-actions arn:aws:sns:region:account-id:topic-name -
Security hardening:
Database security:
-- Enforce password complexity ALTER PROFILE DEFAULT LIMIT PASSWORD_LIFE_TIME 90 PASSWORD_GRACE_TIME 7 PASSWORD_REUSE_TIME 365 PASSWORD_REUSE_MAX 5 FAILED_LOGIN_ATTEMPTS 5 PASSWORD_LOCK_TIME 1; -- Enable audit ALTER SYSTEM SET AUDIT_TRAIL = DB, EXTENDED SCOPE=SPFILE; SHUTDOWN IMMEDIATE; STARTUP; -- Audit critical operations AUDIT ALL ON SYS.AUD$ BY ACCESS; AUDIT CREATE USER BY ACCESS; AUDIT DROP USER BY ACCESS; AUDIT ALTER USER BY ACCESS; AUDIT CREATE SESSION BY ACCESS WHENEVER NOT SUCCESSFUL;Network security:
# Restrict SSH access sudo vi /etc/ssh/sshd_config # Set: PermitRootLogin no # Set: PasswordAuthentication no # Configure firewall sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.0/8" port port="1521" protocol="tcp" accept' sudo firewall-cmd --reload # Enable Oracle Native Network Encryption # Edit sqlnet.ora SQLNET.ENCRYPTION_SERVER = REQUIRED SQLNET.ENCRYPTION_TYPES_SERVER = (AES256, AES192, AES128) SQLNET.CRYPTO_CHECKSUM_SERVER = REQUIRED SQLNET.CRYPTO_CHECKSUM_TYPES_SERVER = (SHA256, SHA384, SHA512)
-
-
Backup and recovery strategy:
Implement comprehensive backup strategy:
#!/bin/bash # rman_backup.sh - Daily incremental backup script export ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1 export ORACLE_SID=ORCL export PATH=$ORACLE_HOME/bin:$PATH # Backup to local disk rman target / << EOF RUN { ALLOCATE CHANNEL ch1 DEVICE TYPE DISK FORMAT '/u01/app/oracle/backup/inc_%U'; BACKUP INCREMENTAL LEVEL 1 DATABASE PLUS ARCHIVELOG; DELETE NOPROMPT OBSOLETE; CROSSCHECK BACKUP; DELETE NOPROMPT EXPIRED BACKUP; } EXIT; EOF # Copy backups to S3 aws s3 sync /u01/app/oracle/backup/ s3://my-oracle-backups/$(date +%Y%m%d)/ \ --storage-class STANDARD_IA \ --exclude "*" \ --include "inc_*" \ --include "arch_*" # Clean up local backups older than 7 days find /u01/app/oracle/backup/ -name "inc_*" -mtime +7 -delete find /u01/app/oracle/backup/ -name "arch_*" -mtime +7 -deleteSchedule backups with cron:
# Edit crontab crontab -e # Add backup schedule # Full backup on Sunday at 2 AM 0 2 * * 0 /home/oracle/scripts/rman_full_backup.sh >> /home/oracle/logs/backup_full.log 2>&1 # Incremental backup Monday-Saturday at 2 AM 0 2 * * 1-6 /home/oracle/scripts/rman_incremental_backup.sh >> /home/oracle/logs/backup_inc.log 2>&1 # Archive log backup every 4 hours 0 */4 * * * /home/oracle/scripts/rman_archivelog_backup.sh >> /home/oracle/logs/backup_arch.log 2>&1
Cost optimization
1. Right-sizing:
After migration, monitor and optimize costs:
-
Use Amazon Cost Explorer to analyze EC2 and EBS costs
-
Enable Amazon Compute Optimizer for instance type recommendations
-
Review CloudWatch metrics to identify underutilized resources
-
Consider Reserved Instances or Savings Plans for predictable workloads
2. Storage optimization:
-
Implement lifecycle policies for S3 backups (move to Glacier after 30 days)
-
Delete unused EBS snapshots regularly
-
Use gp3 instead of gp2 for cost savings with same performance
-
Detach and delete backup volumes after migration completes
3. Automation:
-
Automate start/stop of non-production databases during off-hours
-
Use Amazon Systems Manager for patch management
-
Implement auto-scaling for read replicas if using Data Guard
Conclusion
This prescriptive guidance provided detailed migration strategies for moving your Oracle databases from Amazon RDS Custom for Oracle to self-managed Oracle databases on Amazon EC2. With the RDS Custom for Oracle service deprecation effective March 31, 2027, it's important to plan and execute your migration well in advance.
Key takeaways
Migration options:
-
RMAN Active Duplication: Best for most migrations, keeps source database online during duplication, requires only brief cutover window for application redirection
-
Oracle Data Guard: Best for mission-critical workloads requiring near-zero downtime, providing continuous synchronization and built-in switchover capability
Architecture support:
-
Both migration options support non-CDB (traditional single-instance) and multitenant (CDB with PDBs) architectures
-
For multitenant, both methods automatically handle the entire CDB including all PDBs in a single operation
-
PDBs require manual opening and auto-open configuration post-migration
Critical success factors:
-
Proper network configuration and connectivity between source and target
-
Exact version compatibility (major version, minor version, Release Update, and one-off patches)
-
Adequate network bandwidth for data transfer (RMAN) or redo shipping (Data Guard)
-
Understanding that RMAN active duplication keeps source online - only brief cutover needed
-
Thorough testing and validation before decommissioning the source
-
Comprehensive post-migration tasks including backup, monitoring, and security configuration
Next steps:
-
Assess your database architecture (non-CDB or multitenant)
-
Choose the appropriate migration option based on your downtime tolerance and complexity requirements
-
Complete all prerequisite steps including EC2 instance setup and network configuration
-
Follow the detailed migration steps for your chosen option
-
Perform thorough validation and testing
-
Complete post-migration tasks to ensure production readiness
-
Decommission the RDS Custom instance after successful validation
Additional resources
Support
For assistance with your migration:
-
Contact Amazon Support through the Amazon Management Console
-
Consult with Oracle support for database-specific questions
Document information
Last updated: March 2026
Contributors:
-
Sharath Chandra Kampili, Database Specialist Solutions Architect, Amazon Web Services
-
Ibrahim Emara, Database Specialist Solution Architect, Amazon Web Services
-
Vetrivel Subramani, Database Specialist Solution Architect, Amazon Web Services