Instruction

Oracle 19c: Migrate a PDB Using Unplug/Plug

Introduction

This instruction walks through the complete procedure for migrating a Pluggable Database (PDB) from one Container Database (CDB) to another using the unplug/plug method. The PDB is unplugged from the source CDB, generating an XML metadata file, and then plugged into the target CDB using CREATE PLUGGABLE DATABASE ... USING.

When to use unplug/plug:

  • Consolidating PDBs from multiple CDBs into a single CDB
  • Decommissioning a server and moving PDBs to a new host
  • Rebalancing workloads across CDBs
  • Migrating a PDB to a CDB with a different storage layout

When to use a different method:

Method Use Instead When
PDB Relocate (CREATE PLUGGABLE DATABASE ... RELOCATE) You need hot migration with minimal PDB downtime -- the PDB remains open during the copy phase
Remote Clone (CREATE PLUGGABLE DATABASE ... FROM pdb@dblink) You want to duplicate a PDB (not move it) -- the source PDB stays in the source CDB
Data Pump Full Transportable (expdp TRANSPORTABLE=ALWAYS FULL=Y) Cross-platform migration with different endianness, or when unplug/plug is blocked by version/platform incompatibility

Scope: This instruction covers same-version migration only (19c to 19c). It does not cover combined unplug-plug-upgrade scenarios (e.g., plugging a 12c PDB into a 19c CDB), which require additional upgrade steps.

Naming Conventions

This instruction uses the following placeholder names consistently:

Placeholder Meaning
SOURCE_CDB The CDB that currently contains the PDB
TARGET_CDB The CDB that will receive the PDB
MIGRATE_PDB The PDB being moved

Replace these with your actual database names throughout.

Assumptions

This instruction assumes:

  • Oracle Linux 8 (x86_64) on all servers involved
  • Oracle Database 19c (19.3 or later with any Release Update) on both source and target CDBs
  • Both CDBs are single-instance (not RAC)
  • The reader has SYSDBA administrative privilege on both CDBs
  • The PDB to be migrated (MIGRATE_PDB) is currently open and operational in SOURCE_CDB
  • Both CDBs run on the same platform and endianness (both x86_64 Linux)
  • The target CDB character set is AL32UTF8 (which accepts PDBs with any character set), or the target CDB character set is compatible with the PDB's character set
  • The target CDB has the same or a superset of the database options installed in the source CDB
  • The COMPATIBLE initialization parameter on the target CDB is equal to or greater than the source CDB
  • Datafiles are stored on a filesystem (not ASM). An ASM note is included where relevant
  • Transparent Data Encryption (TDE) is not in use on the PDB being migrated. A TDE note is included for reference, but full TDE key export/import is out of scope
  • The reader understands basic Oracle administration (SQL*Plus, environment variables, startup/shutdown)

Prerequisites

Automatic

Oracle handles these automatically during the unplug/plug process -- no manual action required:

  • PDB metadata cleanup: When the PDB is unplugged, Oracle records the unplug in the data dictionary and marks the PDB as UNPLUGGED in the source CDB
  • Datafile header update: Oracle modifies datafile headers during unplug to record the successful unplug operation
  • Default service creation: After the PDB is plugged into the target CDB and opened, Oracle automatically creates a default service with the PDB name
  • Temp file creation: Oracle creates new temp files for the PDB in the target CDB when the PDB is opened

Manual

Complete all of the following before beginning the migration. All commands run as the oracle OS user.

1. Set environment variables for the source CDB

export ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1
export ORACLE_SID=SOURCE_CDB
export PATH=$ORACLE_HOME/bin:$PATH

2. Verify PDB health -- component status

-- Connected to SOURCE_CDB as SYSDBA
ALTER SESSION SET CONTAINER = MIGRATE_PDB;
SELECT comp_name, version, status FROM dba_registry;

Expected output:

COMP_NAME                      VERSION        STATUS
------------------------------ -------------- ------
Oracle Database Catalog Views  19.0.0.0.0     VALID
Oracle Database Packages ...   19.0.0.0.0     VALID
...

All components must show VALID. If any show INVALID, recompile before proceeding:

-- Connected to SOURCE_CDB as SYSDBA, container = MIGRATE_PDB
@$ORACLE_HOME/rdbms/admin/utlrp.sql

3. Verify PDB health -- invalid objects baseline

-- Connected to SOURCE_CDB as SYSDBA, container = MIGRATE_PDB
SELECT COUNT(*) AS invalid_count FROM dba_objects WHERE status = 'INVALID';

Expected output:

INVALID_COUNT
-------------
            0

Record this count. After migration, compare to confirm no new invalid objects appeared.

4. Record PDB configuration

Record the PDB character set:

-- Connected to SOURCE_CDB as SYSDBA, container = MIGRATE_PDB
SELECT value FROM nls_database_parameters WHERE parameter = 'NLS_CHARACTERSET';

Expected output:

VALUE
----------
AL32UTF8

Record datafile locations:

-- Connected to SOURCE_CDB as SYSDBA, container = MIGRATE_PDB
SELECT file_id, tablespace_name, file_name, bytes/1024/1024 AS size_mb
FROM dba_data_files
ORDER BY file_id;

Expected output:

FILE_ID TABLESPACE_NAME  FILE_NAME                                              SIZE_MB
------- ---------------- ----------------------------------------------------- --------
      9 SYSTEM           /u01/app/oracle/oradata/SOURCE_CDB/MIGRATE_PDB/system01.dbf    280
     10 SYSAUX           /u01/app/oracle/oradata/SOURCE_CDB/MIGRATE_PDB/sysaux01.dbf    350
     11 UNDOTBS1         /u01/app/oracle/oradata/SOURCE_CDB/MIGRATE_PDB/undotbs01.dbf    100
     12 USERS            /u01/app/oracle/oradata/SOURCE_CDB/MIGRATE_PDB/users01.dbf       5

Record temp file locations:

-- Connected to SOURCE_CDB as SYSDBA, container = MIGRATE_PDB
SELECT file_id, tablespace_name, file_name FROM dba_temp_files;

Save this information. The datafile paths are needed for FILE_NAME_CONVERT if you use the COPY or MOVE option.

5. Verify target CDB compatibility

export ORACLE_SID=TARGET_CDB
sqlplus / as sysdba

Check the character set:

-- Connected to TARGET_CDB as SYSDBA
SELECT value FROM nls_database_parameters WHERE parameter = 'NLS_CHARACTERSET';

If the target CDB character set is AL32UTF8, it accepts PDBs with any character set. If it is not AL32UTF8, it must be compatible with the PDB's character set recorded in step 4.

Check the COMPATIBLE parameter:

-- Connected to TARGET_CDB as SYSDBA
SHOW PARAMETER compatible

Expected output:

NAME        TYPE   VALUE
----------- ------ ----------
compatible  string 19.0.0

This value must be equal to or greater than the source CDB's COMPATIBLE setting.

Check installed database options:

-- Connected to TARGET_CDB as SYSDBA
SELECT comp_name, status FROM dba_registry WHERE status = 'VALID';

The target CDB must have the same or a superset of the components listed in the source PDB (from step 2).

6. Compare patch levels (if using different Oracle homes)

If the source and target CDBs use different Oracle homes, compare patch levels:

# On source server
$ORACLE_HOME/OPatch/opatch lspatches

# On target server
$ORACLE_HOME/OPatch/opatch lspatches

SQL-level patch differences are resolved by datapatch after plug-in. Binary-level incompatibilities block plug-in and are reported by DBMS_PDB.CHECK_PLUG_COMPATIBILITY.

7. Plan datafile placement strategy

Decide which file handling option to use:

Option Behavior When to Use
NOCOPY Datafiles remain in their current location. No file copying occurs. Same server. The source CDB no longer needs the files.
COPY Datafiles are copied to a new location. Originals are preserved. This is the default when no option is specified. Cross-server migration, or when you want to keep source files as a fallback.
MOVE Datafiles are relocated to a new location. Originals are removed. Same server. You want to change the storage path and reclaim the original space.

8. Create target directories (COPY or MOVE only)

If using COPY or MOVE with FILE_NAME_CONVERT, create the target directory and set ownership:

# On the target server
sudo mkdir -p /u01/app/oracle/oradata/TARGET_CDB/MIGRATE_PDB
sudo chown oracle:oinstall /u01/app/oracle/oradata/TARGET_CDB/MIGRATE_PDB

9. Check for active sessions in the PDB

Before closing the PDB, verify no active sessions or uncommitted transactions exist:

-- Connected to SOURCE_CDB as SYSDBA
ALTER SESSION SET CONTAINER = MIGRATE_PDB;
SELECT sid, serial#, username, status, program
FROM v$session
WHERE type = 'USER';

Notify application teams and disconnect users before proceeding.

10. Back up the PDB

Take an RMAN backup of the PDB before unplugging. After the PDB is dropped from the source CDB, it cannot be recovered from the source CDB's backups.

export ORACLE_SID=SOURCE_CDB
rman target /
BACKUP PLUGGABLE DATABASE MIGRATE_PDB PLUS ARCHIVELOG;

Expected output:

...
channel ORA_DISK_1: backup set complete, elapsed time: 00:01:15
Finished backup at ...

Migration Procedure

Step 1: Close the PDB on the source CDB

The PDB must be closed before it can be unplugged.

-- Connected to SOURCE_CDB as SYSDBA
ALTER PLUGGABLE DATABASE MIGRATE_PDB CLOSE IMMEDIATE;

Expected output:

Pluggable database altered.

Verify the PDB is in MOUNTED state:

-- Connected to SOURCE_CDB as SYSDBA
SHOW PDBS

Expected output:

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 MIGRATE_PDB                    MOUNTED

Step 2: Unplug the PDB

Generate the XML metadata file that describes the PDB. Oracle records the PDB's datafile locations, tablespace definitions, and configuration in this file.

Primary method -- XML manifest:

-- Connected to SOURCE_CDB as SYSDBA
ALTER PLUGGABLE DATABASE MIGRATE_PDB UNPLUG INTO '/u01/app/oracle/oradata/MIGRATE_PDB.xml';

Expected output:

Pluggable database altered.

Verify the XML file was created:

ls -l /u01/app/oracle/oradata/MIGRATE_PDB.xml

Expected output:

-rw-r--r-- 1 oracle oinstall 8523 Feb 24 10:15 /u01/app/oracle/oradata/MIGRATE_PDB.xml

Alternative -- .pdb archive file:

Instead of an XML file, you can generate a compressed archive that bundles the XML metadata and all PDB datafiles into a single file. This is convenient for cross-server transfers because you only move one file.

-- Connected to SOURCE_CDB as SYSDBA
ALTER PLUGGABLE DATABASE MIGRATE_PDB UNPLUG INTO '/u01/app/oracle/oradata/MIGRATE_PDB.pdb';

The file extension determines the format: .xml produces a metadata-only file, .pdb produces a compressed archive. The .pdb archive takes longer to generate because Oracle must compress and package all datafiles.

Note: When using the .pdb archive, you cannot use SOURCE_FILE_NAME_CONVERT or SOURCE_FILE_DIRECTORY during plug-in. The archive contains the files internally, so Oracle extracts them during CREATE PLUGGABLE DATABASE.

Step 3: Drop the PDB from the source CDB

After unplug, the PDB remains in the source CDB's data dictionary with a status of UNPLUGGED. The only operation you can perform on an unplugged PDB is to drop it.

-- Connected to SOURCE_CDB as SYSDBA
DROP PLUGGABLE DATABASE MIGRATE_PDB KEEP DATAFILES;

Expected output:

Pluggable database dropped.

KEEP DATAFILES retains the datafiles on disk. This is critical -- the target CDB needs these files for the plug-in operation. KEEP DATAFILES is the default behavior, but specifying it explicitly makes the intent clear.

Warning: Do not use INCLUDING DATAFILES unless you used the .pdb archive method (where the datafiles are already inside the archive) or you have already copied the files to the target server.

Verify the PDB no longer appears in the source CDB:

-- Connected to SOURCE_CDB as SYSDBA
SHOW PDBS

Expected output:

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO

Note: Temp files are always removed during DROP PLUGGABLE DATABASE, regardless of the KEEP DATAFILES clause. Oracle creates new temp files when the PDB is opened in the target CDB.

Step 4: Transfer files to the target server (cross-server only)

If the source and target CDBs are on different servers, copy the XML file and all PDB datafiles to the target server. Skip this step if both CDBs are on the same server.

XML manifest method -- copy the XML file and all datafiles:

# From the source server
scp /u01/app/oracle/oradata/MIGRATE_PDB.xml oracle@target-server:/u01/app/oracle/oradata/

scp /u01/app/oracle/oradata/SOURCE_CDB/MIGRATE_PDB/*.dbf oracle@target-server:/u01/app/oracle/oradata/TARGET_CDB/MIGRATE_PDB/

.pdb archive method -- copy the single archive file:

scp /u01/app/oracle/oradata/MIGRATE_PDB.pdb oracle@target-server:/u01/app/oracle/oradata/

After copying, verify file ownership on the target server:

# On the target server
ls -l /u01/app/oracle/oradata/TARGET_CDB/MIGRATE_PDB/

All files must be owned by oracle:oinstall and readable by the oracle user. Fix ownership if needed:

sudo chown -R oracle:oinstall /u01/app/oracle/oradata/TARGET_CDB/MIGRATE_PDB/

Step 5: Check plug compatibility on the target CDB

Before plugging in, verify the PDB is compatible with the target CDB. This step catches version mismatches, missing database options, character set conflicts, and other incompatibilities before the actual plug-in attempt.

export ORACLE_SID=TARGET_CDB
sqlplus / as sysdba
-- Connected to TARGET_CDB as SYSDBA
SET SERVEROUTPUT ON
DECLARE
  compatible CONSTANT VARCHAR2(3) :=
    CASE DBMS_PDB.CHECK_PLUG_COMPATIBILITY(
           pdb_descr_file => '/u01/app/oracle/oradata/MIGRATE_PDB.xml',
           pdb_name       => 'MIGRATE_PDB')
    WHEN TRUE THEN 'YES'
    ELSE 'NO'
  END;
BEGIN
  DBMS_OUTPUT.PUT_LINE('Compatible: ' || compatible);
END;
/

Expected output:

Compatible: YES

PL/SQL procedure successfully completed.

If the output is NO, query the violations view to identify the issues:

-- Connected to TARGET_CDB as SYSDBA
SELECT name, cause, type, message, status
FROM pdb_plug_in_violations
WHERE name = 'MIGRATE_PDB'
ORDER BY type, cause;

Resolve all rows where type = 'ERROR' before proceeding. Rows where type = 'WARNING' do not block plug-in and are typically resolved by datapatch after the PDB is opened. See the Troubleshooting section for common violations and their resolutions.

Note: For .pdb archive files, pass the .pdb file path instead of the .xml path in the pdb_descr_file parameter.

Step 6: Plug the PDB into the target CDB

Choose the appropriate variant based on your file placement strategy.

Option A -- NOCOPY (same server, datafiles remain in place):

Use this when the target CDB can access the datafiles at their current location. No file copying occurs, so this is the fastest option.

-- Connected to TARGET_CDB as SYSDBA
CREATE PLUGGABLE DATABASE MIGRATE_PDB
  USING '/u01/app/oracle/oradata/MIGRATE_PDB.xml'
  NOCOPY
  TEMPFILE REUSE;

Expected output:

Pluggable database created.

Option B -- COPY with FILE_NAME_CONVERT (files copied to a new location):

Use this when the datafiles need to be in a different directory (cross-server migration after file transfer, or reorganizing storage paths). COPY is the default behavior when neither NOCOPY nor MOVE is specified.

-- Connected to TARGET_CDB as SYSDBA
CREATE PLUGGABLE DATABASE MIGRATE_PDB
  USING '/u01/app/oracle/oradata/MIGRATE_PDB.xml'
  COPY
  FILE_NAME_CONVERT = (
    '/u01/app/oracle/oradata/SOURCE_CDB/MIGRATE_PDB/',
    '/u01/app/oracle/oradata/TARGET_CDB/MIGRATE_PDB/'
  )
  TEMPFILE REUSE;

Expected output:

Pluggable database created.

The FILE_NAME_CONVERT clause maps the source datafile directory (as recorded in the XML) to the target directory. Oracle copies each datafile to the new location.

Option C -- MOVE with FILE_NAME_CONVERT (files relocated, originals removed):

Use this when you want to change the storage path and reclaim space from the original location.

-- Connected to TARGET_CDB as SYSDBA
CREATE PLUGGABLE DATABASE MIGRATE_PDB
  USING '/u01/app/oracle/oradata/MIGRATE_PDB.xml'
  MOVE
  FILE_NAME_CONVERT = (
    '/u01/app/oracle/oradata/SOURCE_CDB/MIGRATE_PDB/',
    '/u01/app/oracle/oradata/TARGET_CDB/MIGRATE_PDB/'
  )
  TEMPFILE REUSE;

Expected output:

Pluggable database created.

Option D -- .pdb archive file:

When you unplugged to a .pdb archive, pass the archive path. Oracle extracts the XML and datafiles from the archive. Files are extracted to the same directory where the .pdb file resides.

-- Connected to TARGET_CDB as SYSDBA
CREATE PLUGGABLE DATABASE MIGRATE_PDB
  USING '/u01/app/oracle/oradata/MIGRATE_PDB.pdb'
  TEMPFILE REUSE;

Expected output:

Pluggable database created.

Note on SOURCE_FILE_NAME_CONVERT: If you transferred files to the target server and placed them in a different directory than where the XML file references, use SOURCE_FILE_NAME_CONVERT to tell Oracle where the files actually are:

CREATE PLUGGABLE DATABASE MIGRATE_PDB
  USING '/u01/app/oracle/oradata/MIGRATE_PDB.xml'
  SOURCE_FILE_NAME_CONVERT = (
    '/u01/app/oracle/oradata/SOURCE_CDB/MIGRATE_PDB/',
    '/u01/staging/MIGRATE_PDB/'
  )
  NOCOPY
  TEMPFILE REUSE;

SOURCE_FILE_NAME_CONVERT maps the paths in the XML to the actual file locations before any COPY/MOVE/NOCOPY processing. It is not available with .pdb archive files.

Note on SOURCE_FILE_DIRECTORY: If all source datafiles are in a single directory, you can use SOURCE_FILE_DIRECTORY instead of SOURCE_FILE_NAME_CONVERT:

CREATE PLUGGABLE DATABASE MIGRATE_PDB
  USING '/u01/app/oracle/oradata/MIGRATE_PDB.xml'
  SOURCE_FILE_DIRECTORY = '/u01/staging/MIGRATE_PDB/'
  NOCOPY
  TEMPFILE REUSE;

Verify the PDB was created in MOUNTED state:

-- Connected to TARGET_CDB as SYSDBA
SHOW PDBS

Expected output:

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 MIGRATE_PDB                    MOUNTED

Step 7: Open the PDB

Open the PDB in read/write mode. This step completes the integration -- Oracle changes the PDB status from NEW to NORMAL on the first read/write open.

-- Connected to TARGET_CDB as SYSDBA
ALTER PLUGGABLE DATABASE MIGRATE_PDB OPEN;

Expected output:

Pluggable database altered.

Verify the PDB is open in read/write mode:

-- Connected to TARGET_CDB as SYSDBA
SHOW PDBS

Expected output:

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 MIGRATE_PDB                    READ WRITE NO

If the PDB opens in RESTRICTED mode, check PDB_PLUG_IN_VIOLATIONS for unresolved errors. See the Troubleshooting section.

Step 8: Run datapatch (if patch levels differ)

If the source and target CDBs have different Release Update (RU) levels, run datapatch to apply SQL-level patch changes to the PDB. If both CDBs are at the same patch level, you can skip this step.

Exit SQL*Plus first:

EXIT
export ORACLE_SID=TARGET_CDB
$ORACLE_HOME/OPatch/datapatch -verbose

Expected output:

...
Patch installation complete. Total patches installed: 1
...

Datapatch only patches PDBs that are open. If the PDB is open in RESTRICTED mode due to patch violations, datapatch can still apply patches. After datapatch completes, close and reopen the PDB:

-- Connected to TARGET_CDB as SYSDBA
ALTER PLUGGABLE DATABASE MIGRATE_PDB CLOSE;
ALTER PLUGGABLE DATABASE MIGRATE_PDB OPEN;

Step 9: Recompile invalid objects (if needed)

sqlplus / as sysdba
-- Connected to TARGET_CDB as SYSDBA
ALTER SESSION SET CONTAINER = MIGRATE_PDB;
SELECT COUNT(*) AS invalid_count FROM dba_objects WHERE status = 'INVALID';

If the invalid count is higher than the baseline recorded in the prerequisites, recompile:

-- Connected to TARGET_CDB as SYSDBA, container = MIGRATE_PDB
@$ORACLE_HOME/rdbms/admin/utlrp.sql

Step 10: Save the PDB state

Save the PDB state so it opens automatically when the target CDB restarts.

-- Connected to TARGET_CDB as SYSDBA
ALTER SESSION SET CONTAINER = CDB$ROOT;
ALTER PLUGGABLE DATABASE MIGRATE_PDB SAVE STATE;

Expected output:

Pluggable database altered.

Validation

Quick check

Connect to the target CDB and verify the PDB is open:

-- Connected to TARGET_CDB as SYSDBA
SHOW PDBS

Expected output:

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 MIGRATE_PDB                    READ WRITE NO

The migrated PDB appears in READ WRITE mode with RESTRICTED = NO.

Full validation

1. PDB status via V$PDBS

-- Connected to TARGET_CDB as SYSDBA
SELECT con_id, name, open_mode, restricted FROM v$pdbs WHERE name = 'MIGRATE_PDB';

Expected output:

CON_ID NAME         OPEN_MODE  RES
------ ------------ ---------- ---
     3 MIGRATE_PDB  READ WRITE NO

2. PDB status via CDB_PDBS

-- Connected to TARGET_CDB as SYSDBA
SELECT pdb_name, status FROM cdb_pdbs WHERE pdb_name = 'MIGRATE_PDB';

Expected output:

PDB_NAME        STATUS
--------------- ------
MIGRATE_PDB     NORMAL

Status must be NORMAL. A status of NEW means the PDB has not been opened in read/write mode yet. A status of UNUSABLE means the creation failed.

3. Plug-in violations check

-- Connected to TARGET_CDB as SYSDBA
SELECT name, cause, type, message, status
FROM pdb_plug_in_violations
WHERE name = 'MIGRATE_PDB' AND type = 'ERROR' AND status <> 'RESOLVED';

Expected output:

no rows selected

No unresolved ERROR rows should exist. WARNING rows with status RESOLVED are acceptable.

4. Component status inside the PDB

-- Connected to TARGET_CDB as SYSDBA
ALTER SESSION SET CONTAINER = MIGRATE_PDB;
SELECT comp_name, version, status FROM dba_registry;

Expected output:

COMP_NAME                      VERSION        STATUS
------------------------------ -------------- ------
Oracle Database Catalog Views  19.0.0.0.0     VALID
Oracle Database Packages ...   19.0.0.0.0     VALID
...

All components must show VALID.

5. Invalid objects check

-- Connected to TARGET_CDB as SYSDBA, container = MIGRATE_PDB
SELECT COUNT(*) AS invalid_count FROM dba_objects WHERE status = 'INVALID';

The count should be zero or match the pre-migration baseline recorded in the prerequisites.

6. Verify PDB save state

-- Connected to TARGET_CDB as SYSDBA
ALTER SESSION SET CONTAINER = CDB$ROOT;
SELECT con_name, instance_name, state FROM dba_pdb_saved_states WHERE con_name = 'MIGRATE_PDB';

Expected output:

CON_NAME     INSTANCE_NAME  STATE
------------ -------------- -----
MIGRATE_PDB  TARGET_CDB     OPEN

7. Verify source CDB cleanup

-- Connected to SOURCE_CDB as SYSDBA
SHOW PDBS

Expected output:

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO

The migrated PDB no longer appears in the source CDB.

Post-Migration Steps

1. Back up the PDB in the target CDB

The PDB cannot be recovered unless it is backed up in the target CDB. Previous RMAN backups from the source CDB do not apply after plug-in.

export ORACLE_SID=TARGET_CDB
rman target /
BACKUP PLUGGABLE DATABASE MIGRATE_PDB PLUS ARCHIVELOG;

2. Configure the listener for the PDB service

Oracle automatically registers a default service named MIGRATE_PDB with the listener when the PDB opens. Verify the service is registered:

lsnrctl status

Look for a line containing the PDB service name:

Service "MIGRATE_PDB" has 1 instance(s).
  Instance "TARGET_CDB", status READY, has 1 handler(s) for this service...

If you use static listener registration, add a SID_DESC entry for the PDB in listener.ora:

SID_LIST_LISTENER =
  (SID_LIST =
    (SID_DESC =
      (GLOBAL_DBNAME = MIGRATE_PDB)
      (ORACLE_HOME = /u01/app/oracle/product/19.0.0/dbhome_1)
      (SID_NAME = TARGET_CDB)
    )
  )

3. Update tnsnames.ora

Add a TNS entry for the PDB so clients can connect directly. Add this to $ORACLE_HOME/network/admin/tnsnames.ora (or the location specified by $TNS_ADMIN):

MIGRATE_PDB =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = target-server)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = MIGRATE_PDB)
    )
  )

Test the connection:

sqlplus system@MIGRATE_PDB
SELECT sys_context('USERENV', 'CON_NAME') FROM dual;

Expected output:

SYS_CONTEXT('USERENV','CON_NAME')
----------------------------------
MIGRATE_PDB

4. Update application connection strings

Update all application connection strings that previously connected to MIGRATE_PDB through the source CDB. Change the hostname and service name to point to the target server and PDB service.

5. Clean up source server (optional)

After confirming the migration is successful and the target PDB backup is complete, you can remove the PDB datafiles from the source server if they are no longer needed:

# Only after confirming successful migration and backup
rm -rf /u01/app/oracle/oradata/SOURCE_CDB/MIGRATE_PDB/
rm /u01/app/oracle/oradata/MIGRATE_PDB.xml

Notes

PDB renaming during plug-in

You can assign a different name to the PDB during plug-in by specifying a different name in the CREATE PLUGGABLE DATABASE statement:

CREATE PLUGGABLE DATABASE NEW_PDB_NAME USING '/u01/app/oracle/oradata/MIGRATE_PDB.xml' NOCOPY TEMPFILE REUSE;

The PDB's default service name changes to NEW_PDB_NAME. Update all listener configurations, tnsnames.ora entries, and application connection strings accordingly.

AS CLONE

Specify AS CLONE when the target CDB already contains a PDB that was created from the same source (e.g., plugging in a copy of a PDB that still exists in the target CDB). AS CLONE forces Oracle to generate a new DBID and GUID to avoid identifier conflicts:

CREATE PLUGGABLE DATABASE MIGRATE_PDB USING '/u01/app/oracle/oradata/MIGRATE_PDB.xml' AS CLONE NOCOPY TEMPFILE REUSE;

ASM storage

If your environment uses ASM instead of a filesystem, adjust the file paths accordingly:

  • Use +DISKGROUP/cdb_name/pdb_guid/datafile/ format for ASM paths
  • Use asmcmd to verify disk group free space before plug-in
  • Use FILE_NAME_CONVERT to map filesystem paths to ASM disk group paths if the source PDB was on a filesystem

TDE considerations

If the PDB being migrated uses Transparent Data Encryption, the unplug command must include the ENCRYPT clause:

ALTER PLUGGABLE DATABASE MIGRATE_PDB UNPLUG INTO '/path/MIGRATE_PDB.xml' ENCRYPT USING transport_secret;

And the plug-in command must include the DECRYPT clause:

-- United-mode TDE (keystore at CDB level):
CREATE PLUGGABLE DATABASE MIGRATE_PDB USING '/path/MIGRATE_PDB.xml'
  KEYSTORE IDENTIFIED BY keystore_password DECRYPT USING transport_secret
  NOCOPY TEMPFILE REUSE;

For united-mode TDE PDBs, KEYSTORE IDENTIFIED BY keystore_password is required alongside DECRYPT USING. Omitting the ENCRYPT/DECRYPT clauses for a TDE-enabled PDB results in ORA-46680. Full TDE migration (including keystore management in united vs. isolated mode) is beyond the scope of this instruction. See the Oracle Database Advanced Security Guide for complete TDE procedures.

Common user handling

Common user accounts (e.g., C##CUSTOM_USER) from the source CDB are handled as follows when the PDB is plugged into the target CDB:

  • If the common user exists in the target CDB: the PDB is linked to the target CDB's common user and receives the target CDB's privileges
  • If the common user does not exist in the target CDB and owns objects in the PDB: the account is locked
  • If the common user does not exist in the target CDB and does not own objects in the PDB: the account is dropped

To resolve locked common user accounts, either create a matching common user in the target CDB root, or create a local user in the PDB and use Data Pump to transfer ownership.

Troubleshooting

Problem Cause Solution
DBMS_PDB.CHECK_PLUG_COMPATIBILITY returns NO -- version mismatch Source and target CDBs are at different patch levels Query pdb_plug_in_violations for details. If the violation type is WARNING, proceed with plug-in and run datapatch after opening the PDB. If the violation type is ERROR, apply matching patches to both CDBs.
DBMS_PDB.CHECK_PLUG_COMPATIBILITY returns NO -- component mismatch Target CDB is missing database options that exist in the source PDB Install the missing options on the target CDB, or remove the extra components from the source PDB before unplugging.
ORA-65005: missing or invalid file name pattern for file Source or replacement file name pattern is missing or invalid in SOURCE_FILE_NAME_CONVERT or FILE_NAME_CONVERT Open the XML file and check the actual <path> entries. Ensure the patterns in FILE_NAME_CONVERT or SOURCE_FILE_NAME_CONVERT match the paths in the XML exactly.
ORA-65116: incompatible database character set Target CDB character set is not AL32UTF8 and is not a superset of the PDB's character set Recreate the target CDB with AL32UTF8 as the character set, or use a CDB with a compatible character set.
PDB opens in restricted mode Pending datapatch operations or unresolved plug-in violations (check PDB_PLUG_IN_VIOLATIONS for entries with status <> 'RESOLVED') Run datapatch -verbose from the OS shell, then close and reopen the PDB: ALTER PLUGGABLE DATABASE MIGRATE_PDB CLOSE; ALTER PLUGGABLE DATABASE MIGRATE_PDB OPEN;
PDB in UNUSABLE state after failed creation An error occurred during CREATE PLUGGABLE DATABASE (query CDB_PDBS — status shows UNUSABLE) Check the alert log at $ORACLE_BASE/diag/rdbms/target_cdb/TARGET_CDB/trace/alert_TARGET_CDB.log for details. Drop the unusable PDB (DROP PLUGGABLE DATABASE MIGRATE_PDB INCLUDING DATAFILES;), fix the issue, and retry from Step 6.
PDB_PLUG_IN_VIOLATIONS shows ERROR entries after opening Incompatibilities discovered during PDB open Query the violations view: SELECT name, cause, type, message FROM pdb_plug_in_violations WHERE name = 'MIGRATE_PDB' AND type = 'ERROR' AND status <> 'RESOLVED';. Address each violation per the message text.
FILE_NAME_CONVERT errors -- file not found Source datafile paths in the XML do not match the patterns in FILE_NAME_CONVERT Open the XML file and check the actual <path> entries. Ensure the first pattern in FILE_NAME_CONVERT matches the paths in the XML exactly. Use SOURCE_FILE_NAME_CONVERT if files were moved after unplug.
Insufficient disk space during COPY or MOVE Target filesystem does not have enough free space Check available space with df -h. The target needs at least as much space as the PDB datafiles total (recorded in prerequisites step 4). Free space or choose a different mount point.
ORA-46680: master keys must be exported The PDB uses TDE but the unplug did not include ENCRYPT USING Re-plug the PDB into the source CDB (if possible) or use a backup. Unplug again with ENCRYPT USING transport_secret, then plug in with DECRYPT USING transport_secret.
Common user account locked after plug-in A common user from the source CDB does not exist in the target CDB and owns objects in the PDB Create the common user in the target CDB root: CREATE USER C##username IDENTIFIED BY password CONTAINER=ALL;, then open the PDB -- the account is automatically unlocked.
PDB opens but services are not registered with the listener Listener registration has not completed, or static registration is required Wait 60 seconds for automatic registration, or run ALTER SYSTEM REGISTER; in the CDB. For static registration, add a SID_DESC entry in listener.ora and reload: lsnrctl reload.

References