Wednesday, September 16, 2026

Oracle 19c: Cloning Encrypted PDBs and Moving TDE Keys

Oracle TDE master key in a wallet or Oracle Key Vault protects keys for encrypted tablespaces and columns
Oracle TDE key hierarchy. This walkthrough uses file-based wallets in Oracle Database 19c.

When cloning or refreshing an encrypted pluggable database (PDB) in Oracle Database 19c, it is easy to focus on the datafiles. Restore the backup, recover the database, and plug the PDB into the destination container database (CDB).

But don't forget the keys. With Transparent Data Encryption (TDE), the destination also needs access to the master encryption keys stored in the source wallet.

In this post I will walk through two ways to bring the keys with an encrypted PDB, and then show where those steps fit when the PDB is restored from backup. I am using Oracle Database 19c, local file-based TDE wallets, and united mode. In united mode the PDBs use the CDB's wallet, while keeping their own master encryption keys.

This builds on my posts about encrypting an Oracle PDB with TDE and Oracle wallet files for TDE and authentication.

NOTE: These are command examples to adapt and validate on your 19c Release Update. Database names, paths, passwords, and the recovery SCN are placeholders. This is a walkthrough of the key handling, not output from a completed lab.

For unplug/plug, I can bring the keys with ENCRYPT USING and DECRYPT USING, or export and import them separately. A refresh from RMAN backups also needs the historical keys during recovery into the auxiliary CDB.

In this post: Check the wallet · Unplug/plug with keys · Export and import keys · Refresh from RMAN backups · Rekey and verify · Merge wallets

TDE wallet keys: what needs to move with a PDB?

TDE has two levels of keys. The tablespace key encrypts the data, and the TDE master encryption key protects that key. The master key is stored in the keystore. Copying encrypted datafiles does not, by itself, give a different CDB access to that master key. Oracle's TDE overview

Moving an encrypted PDB and its TDE wallet keysThe encrypted PDB datafiles move to the destination PDB. Master keys are transported or exported and imported into the destination wallet, retaining its existing PDB keys.An encrypted PDB has two things to carryOracle Database 19c • Local TDE wallet • United modeSource PDBEncrypted datafilesProtected tablespace keysDestination PDBCopied datafilesStill encryptedCopy / restore / plugSource CDB walletPDB master encryption keysCurrent + historical keysDestination CDB walletImported PDB keysRetain existing PDB keysTransport with the PDBOR export / importAfter import: rekey the new PDB, verify encrypted data, and back up the updated wallet.A new master key does not remove the need for keys used by older backups.Figure 1. Move both the encrypted PDB datafiles and the required master keys into the destination environment.

For these examples I am using the following names.

Name Purpose
PRODCDB / APPPDB Source CDB and encrypted PDB
AUXCDB / APPPDB Temporary CDB used for a restore from backup
TESTCDB / APP_REFRESH Existing destination CDB and the new refresh PDB
SourceWalletPassword Password for the source wallet
TargetWalletPassword Password for the destination wallet
TransportSecret Separate secret protecting keys during transport

The transport secret and the wallet passwords have different jobs. I do not need to make the source and destination wallet passwords match.

STEP #1 - Check the TDE wallet configuration

On each CDB, I first check the configuration and wallet status.

-- SQL*Plus, connected to the appropriate CDB root
SHOW CON_NAME
SHOW PARAMETER wallet_root
SHOW PARAMETER tde_configuration

SELECT con_id, status, wallet_type, keystore_mode,
       wrl_parameter
FROM   v$encryption_wallet
ORDER BY con_id;

The examples assume TDE_CONFIGURATION='KEYSTORE_CONFIGURATION=FILE', with the united wallet under WALLET_ROOT/tde. A PDB row should show UNITED; the root can show NONE. An OPEN wallet is a useful first check, but it does not prove that it contains every key needed for this refresh. Wallet status reference

For a password wallet that is closed, open it from the root. Use the password belonging to that CDB.

ADMINISTER KEY MANAGEMENT SET KEYSTORE OPEN
  IDENTIFIED BY "SourceWalletPassword"
  CONTAINER = ALL;

If auto-login already has the wallet open, check its state rather than blindly repeating the open. The key-management examples below use FORCE KEYSTORE where supported to allow access to the password wallet. Have the ewallet.p12 password available even when normal database startup uses cwallet.sso. Configuring TDE

I also record the source PDB's keys before the move.

ALTER SESSION SET CONTAINER = APPPDB;

SELECT key_id, creation_time, activation_time
FROM   v$encryption_keys
ORDER BY activation_time;

This gives me key identifiers to compare later. I am recording metadata here, not displaying the secret key material. Encryption key view

Run the key operations as an appropriately privileged key administrator (SYSKM or ADMINISTER KEY MANAGEMENT), and the PDB operations with the necessary database privileges. The examples assume both are available to the administrative session.

METHOD #1 - Unplug and plug an encrypted PDB with its TDE keys

For a planned unplug/plug, I can protect the PDB's keys with a transport secret as part of the unplug operation.

1) Unplug on the source

-- PRODCDB root; source wallet must be open
ALTER SESSION SET CONTAINER = CDB$ROOT;

ALTER PLUGGABLE DATABASE APPPDB CLOSE IMMEDIATE;

ALTER PLUGGABLE DATABASE APPPDB
  UNPLUG INTO '/secure_stage/apppdb.xml'
  ENCRYPT USING "TransportSecret";

ENCRYPT USING protects the transported keys. The XML file is a manifest; the PDB's datafiles still have to be made available on the destination host. A .pdb archive is an alternative packaging format. Unplugging PDBs

NOTE: Unplugging takes this source PDB out of service. For a refresh that must leave production running, use the backup/auxiliary workflow below and unplug the restored copy.

2) Check compatibility at the destination

For this example, the XML and datafiles are accessible at the paths recorded in the XML. If the files were staged at different paths, account for that with SOURCE_FILE_NAME_CONVERT; FILE_NAME_CONVERT controls the destination copies.

-- TESTCDB root
SET SERVEROUTPUT ON
DECLARE
  can_plug BOOLEAN;
BEGIN
  can_plug := DBMS_PDB.CHECK_PLUG_COMPATIBILITY(
    pdb_descr_file => '/secure_stage/apppdb.xml',
    pdb_name       => 'APP_REFRESH');
  IF can_plug THEN
    DBMS_OUTPUT.PUT_LINE('Compatible');
  ELSE
    RAISE_APPLICATION_ERROR(-20001,
      'Review PDB_PLUG_IN_VIOLATIONS before continuing');
  END IF;
END;
/

Resolve compatibility errors before creating the PDB, including applicable patch, component, and character-set issues. Plug compatibility check

3) Plug in the copy

The destination wallet must already exist and be open. APP_REFRESH must be a new PDB name in TESTCDB.

-- TESTCDB root; filesystem paths used for this example
CREATE PLUGGABLE DATABASE APP_REFRESH AS CLONE
  USING '/secure_stage/apppdb.xml'
  COPY
  FILE_NAME_CONVERT =
    ('/u02/oradata/PRODCDB/APPPDB/',
     '/u02/oradata/TESTCDB/APP_REFRESH/')
  KEYSTORE IDENTIFIED BY "TargetWalletPassword"
  DECRYPT USING "TransportSecret";

ALTER PLUGGABLE DATABASE APP_REFRESH OPEN;

Here, AS CLONE gives the plugged copy a new identity, and COPY creates its datafiles at the destination. DECRYPT USING supplies the transport secret, while KEYSTORE IDENTIFIED BY supplies the destination wallet password. Adapt file placement for OMF or ASM. CREATE PLUGGABLE DATABASE reference

Finish with the rekey and verification steps below. An initial restricted open is not the end of the process.

METHOD #2 - Export and import TDE keys for an Oracle 19c PDB

This is an alternative to Method #1. I use a separate export file when I want the key transfer to be an explicit step in the refresh process.

1) Export from inside the source PDB

-- PRODCDB, or AUXCDB after restoring the PDB
ALTER SESSION SET CONTAINER = APPPDB;

ADMINISTER KEY MANAGEMENT EXPORT ENCRYPTION KEYS
  WITH SECRET "TransportSecret"
  TO '/secure_stage/apppdb_keys.exp'
  FORCE KEYSTORE
  IDENTIFIED BY "SourceWalletPassword";

ALTER SESSION SET CONTAINER = CDB$ROOT;
ALTER PLUGGABLE DATABASE APPPDB CLOSE IMMEDIATE;
ALTER PLUGGABLE DATABASE APPPDB
  UNPLUG INTO '/secure_stage/apppdb.xml';

Export inside the PDB. Do not add a WITH IDENTIFIER IN filter: a PDB export carries its keys and the metadata identifying the active key. Protect the export file and deliver its secret separately. Exporting keys leaves the source keys in place. Key export syntax

2) Import into the destination root before plugging, when required

If SYSTEM, SYSAUX, UNDO, or TEMP is encrypted, first import into TESTCDB's root. Then import again inside the new PDB to associate the keys with it. For a PDB without those encrypted system tablespaces, the pre-plug root import can be skipped. Oracle's united-mode PDB procedure

-- TESTCDB root: conditional pre-plug import
ALTER SESSION SET CONTAINER = CDB$ROOT;

ADMINISTER KEY MANAGEMENT IMPORT ENCRYPTION KEYS
  WITH SECRET "TransportSecret"
  FROM '/secure_stage/apppdb_keys.exp'
  FORCE KEYSTORE
  IDENTIFIED BY "TargetWalletPassword"
  WITH BACKUP USING 'before_refresh_root_import';

3) Plug in, open the PDB wallet, and import inside the PDB

Run the compatibility check from Method #1 first. This manifest was created without ENCRYPT USING, so this branch does not use DECRYPT USING.

-- TESTCDB root
CREATE PLUGGABLE DATABASE APP_REFRESH AS CLONE
  USING '/secure_stage/apppdb.xml'
  COPY
  FILE_NAME_CONVERT =
    ('/u02/oradata/PRODCDB/APPPDB/',
     '/u02/oradata/TESTCDB/APP_REFRESH/')
  KEYSTORE IDENTIFIED BY "TargetWalletPassword";

ALTER SESSION SET CONTAINER = APP_REFRESH;

-- If the PDB's password wallet is not already open:
ADMINISTER KEY MANAGEMENT SET KEYSTORE OPEN
  FORCE KEYSTORE
  IDENTIFIED BY "TargetWalletPassword";

ALTER SESSION SET CONTAINER = CDB$ROOT;
ALTER PLUGGABLE DATABASE APP_REFRESH OPEN;

ALTER SESSION SET CONTAINER = APP_REFRESH;

ADMINISTER KEY MANAGEMENT IMPORT ENCRYPTION KEYS
  WITH SECRET "TransportSecret"
  FROM '/secure_stage/apppdb_keys.exp'
  FORCE KEYSTORE
  IDENTIFIED BY "TargetWalletPassword"
  WITH BACKUP USING 'before_refresh_pdb_import';

The PDB may initially open restricted. Complete the PDB import, rekey, and reopen before releasing it to the application. The root import and PDB import serve different purposes; do not treat the root import as completion of both steps.

Refresh an encrypted PDB from RMAN backups

I separate this into two handoffs: backup to auxiliary CDB, followed by auxiliary PDB to the existing destination CDB.

Oracle 19c encrypted PDB refresh from RMAN backupsRestore the source backups into AUXCDB with the historical wallet keys. Then plug the restored PDB into TESTCDB and add its keys to the existing destination wallet.Refreshing from backup into an existing CDBRestore with the historical keys first. Then transfer the restored PDB and its keys.Source backup setRoot + seed + APPPDBControlfile + required redoChosen recovery SCNAUXCDB / APPPDBBackup-based duplicateRecover and openProduction keeps runningTESTCDBNew PDB: APP_REFRESHPlug in, rekey, and verifyCut over after validationSource wallet copyRequired root and PDB keysIncludes historical keysAuxiliary walletAvailable during recoveryUsed to export restored keysTESTCDB walletAdd APPPDB keysRetain other PDB keysRecoveryPlug-inTwo separate key handoffs1 Provision the recovery wallet to AUXCDB.2 Use ENCRYPT / DECRYPT, or a PDB key export / import, for TESTCDB.Figure 2. A backup refresh has two key handoffs: recovery into AUXCDB, then plug-in and key transfer into TESTCDB.

1) Make the historical keys available to the auxiliary

The auxiliary needs the keys required by the backup and recovery interval. Rotating the production master key does not eliminate the need for older keys. A wallet backup that predates a required rotation may be missing a key; a later wallet can contain the historical keys if they have been retained. TDE key history

For a newly prepared auxiliary, securely provision a copy of the source password wallet at its configured WALLET_ROOT/tde location. Use a wallet with the required CDB and PDB key history, not just a PDB key export. The duplicate also restores root and seed files. Keep the auxiliary's wallet separate from the live source and from TESTCDB's wallet. Preparing the auxiliary keystore

For example, the auxiliary initialization settings include:

db_name='AUXCDB'
db_unique_name='AUXCDB'
enable_pluggable_database=TRUE
wallet_root='/u01/app/oracle/admin/AUXCDB/wallet'
tde_configuration='KEYSTORE_CONFIGURATION=FILE'

This is only the relevant parameter excerpt. Prepare the auxiliary in NOMOUNT, with its own controlfile, datafile, redo, and recovery-area destinations, plus the password file and Oracle Net configuration required by your duplication method.

2) Restore the PDB into the temporary CDB

Here is a backup-based duplication example, with RMAN connected to PRODCDB as TARGET and AUXCDB as AUXILIARY. Both connections are to the root. The source backup pieces must be accessible to the auxiliary, including root, seed, PDB, controlfile, and the archived redo needed to reach the chosen SCN.

-- RMAN: connected to the two CDB roots
SET DECRYPTION WALLET OPEN
  IDENTIFIED BY 'SourceWalletPassword';

RUN {
  SET UNTIL SCN 123456789;
  SET NEWNAME FOR DATABASE TO '/u02/oradata/AUXCDB/%U';

  DUPLICATE TARGET DATABASE TO AUXCDB
    PLUGGABLE DATABASE APPPDB
    LOGFILE
      GROUP 1 ('/u02/oradata/AUXCDB/redo01.log') SIZE 200M,
      GROUP 2 ('/u02/oradata/AUXCDB/redo02.log') SIZE 200M,
      GROUP 3 ('/u02/oradata/AUXCDB/redo03.log') SIZE 200M;
}

Replace the SCN and file layout with values for your restore. This creates a new auxiliary CDB containing the selected PDB, not a PDB directly inside TESTCDB. Oracle 19c's DUPLICATE PLUGGABLE DATABASE ... TO existing_cdb form supports active duplication; it is not the backup-based command used here. RMAN DUPLICATE reference

SET DECRYPTION WALLET OPEN supplies the wallet password across auxiliary restarts during duplication. It does not transfer missing keys. If the backup pieces have a separate password-encryption layer, also supply the applicable backup password using SET DECRYPTION IDENTIFIED BY. That password does not replace the keys needed for the TDE-encrypted datafiles. RMAN SET reference

3) Transfer from AUXCDB into TESTCDB

After successful recovery and opening the restored PDB, use either Method #1 or Method #2, with AUXCDB as the source.

Create a fresh manifest from the restored PDB, and export its keys there if using Method #2. Update FILE_NAME_CONVERT to match the restored files' actual paths. In the RMAN example, %U generates filenames directly under /u02/oradata/AUXCDB/; inspect those filenames rather than assuming an APPPDB subdirectory exists.

I would create APP_REFRESH alongside the current test PDB, validate it, and then handle the application/service cutover. The destructive replacement of an existing PDB is a separate decision. This is a repeated refresh from backups, not a REFRESH MODE clone.

Rekey the cloned PDB, reopen, and verify

Once the imported keys are available, create and activate a destination master key inside APP_REFRESH.

-- TESTCDB, inside the new PDB only
ALTER SESSION SET CONTAINER = APP_REFRESH;

ADMINISTER KEY MANAGEMENT SET KEY
  FORCE KEYSTORE
  IDENTIFIED BY "TargetWalletPassword"
  WITH BACKUP USING 'app_refresh_rekey'
  CONTAINER = CURRENT;

ALTER SESSION SET CONTAINER = CDB$ROOT;
ALTER PLUGGABLE DATABASE APP_REFRESH CLOSE IMMEDIATE;
ALTER PLUGGABLE DATABASE APP_REFRESH OPEN;

Use CONTAINER=CURRENT for this PDB; there is no reason for a refresh to rotate every PDB's key. Changing the wallet password is also a different operation from generating a new master key. Key-management operations

Rekeying gives the destination a new master key. It does not make historical backups independent of their original keys. Retain those keys for as long as the corresponding recovery requirements exist. TDE key architecture FAQ

Now I check the result.

-- TESTCDB root
SELECT name, open_mode, restricted
FROM   v$pdbs
WHERE  name = 'APP_REFRESH';

SELECT time, name, cause, type, message, status, action
FROM   pdb_plug_in_violations
WHERE  name = 'APP_REFRESH'
AND    status <> 'RESOLVED'
ORDER BY time;

ALTER SESSION SET CONTAINER = APP_REFRESH;

SELECT status, keystore_mode, fully_backed_up
FROM   v$encryption_wallet;

SELECT key_id, creation_time, activation_time
FROM   v$encryption_keys
ORDER BY activation_time;

SELECT tablespace_name, encrypted
FROM   dba_tablespaces
ORDER BY tablespace_name;

I want READ WRITE, RESTRICTED=NO, and all blocking plug-in violations resolved. I also read known application data from an encrypted tablespace and check the expected refresh timestamp or business totals. Opening the PDB alone is not my application validation. Completing a PDB plug-in

Finally, back up the updated destination wallet and the refreshed PDB. WITH BACKUP protects the wallet before a change; I also want a retained copy containing the newly created key.

-- TESTCDB root, after successful rekey
ALTER SESSION SET CONTAINER = CDB$ROOT;

ADMINISTER KEY MANAGEMENT BACKUP KEYSTORE
  USING 'after_app_refresh'
  FORCE KEYSTORE
  IDENTIFIED BY "TargetWalletPassword"
  TO '/secure_wallet_backups/TESTCDB';

Keep password-wallet backups and their passwords recoverable, with access controlled separately from the database backups. An auto-login wallet stored beside encrypted backups weakens that separation. RMAN encryption and wallet backup guidance

Migrate TDE wallet keys with MERGE KEYSTORE

For one PDB, I would normally use the PDB export/import above. A wallet merge has broader scope: it adds the source wallet's keys to another wallet.

Here is a merge example using staged wallet copies, outside the live wallet directories:

-- Optional wallet administration example; not a PDB refresh step
ADMINISTER KEY MANAGEMENT
  MERGE KEYSTORE '/secure_stage/source_wallet'
    IDENTIFIED BY "SourceWalletPassword"
  INTO EXISTING KEYSTORE '/secure_stage/target_wallet_copy'
    IDENTIFIED BY "TargetWalletPassword"
  WITH BACKUP USING 'before_wallet_merge';

This updates the staged destination password wallet and leaves the source unchanged. It does not switch the live database to that staged wallet. A planned wallet migration must also configure the final location, reopen the merged wallet, and update or recreate its auto-login companion as appropriate. Coordinate that change across all consumers of the wallet. Merging and relocating wallets

NOTE: Do not copy the source ewallet.p12 over the wallet of an existing destination CDB. That destination may need keys for other PDBs. Also, MOVE KEYS removes selected keys from the source keystore and is not a substitute for a clone's export/import. MIGRATE USING is for changing the keystore provider, such as migrating to Oracle Key Vault. Key-management SQL reference

The operational check I would add to every refresh job is simple: record which backup was restored, which keys were supplied, where the destination wallet was backed up, and whether encrypted application data was successfully read. Those four items make the next refresh, and the next recovery exercise, much easier to review.

Wednesday, August 12, 2026

Oracle Wallet file settings

 I have been doing more research on how to set wallet locations for the Oracle database as wallets are becoming more and more important to secure your database.



Oracle Wallet usage
Oracle wallet usage


I have found that there are different types of wallets, and  different ways to set the Wallet location depending on it's usage.


1) WALLET_ROOT

Wallet_root is a Database setting (spfile) that is used by a running database.  Below are the different sub-directories that can be created within wallet root and what they can be used for.

The most commonly used directories are

TDE - The wallet in this directory contains the encryption key(s) for the database and replaces the setting ENCRYPTION_WALLET_LOCATION in the sqlnet.ora.

TLS - The wallet in this directory contains the TLS certificate(s) used by the database to make secure TCPS connections.

SERVER_SEPS - The wallet in this location contains the login credentials for other databases or even the same database. Some examples of when this can be useful are.

  • Sending real-time redo to a ZDLRA
  • Creating Database Links to other databases
  • RMAN channel configurations that connect to different nodes in a RAC cluster.
* Note : It was pointed out to me, that using the wallet in server_seps for RMAN channel connects will replace the usage of the "connect ..." string when allocating RMAN channels.


Subdirectory Component / Purpose Typical File Types Notes & Details
tde Transparent Data Encryption (TDE) ewallet.p12, ewallet_*.p12 Created automatically when establishing TDE keystores. Contains master encryption keys for CDB$ROOT, non-CDBs, or isolated PDBs.
tde_seps TDE Auto-Login / SEPS cwallet.sso Stores the Secure External Password Store (SEPS) auto-login file used for automatic opening of the TDE keystore.
tls Transport Layer Security (TLS/SSL) ewallet.p12, cwallet.sso Stores public key infrastructure (PKI) certificates and private keys used for encrypted network communications.
eus Enterprise User Security (EUS) ewallet.p12, cwallet.sso Contains credentials for centralized directory service authentication (e.g., Oracle Internet Directory / LDAP).
xdb_wallet XML Database (XDB) Security ewallet.p12 Used for securing Oracle XML DB HTTP, HTTPS, and FTP server connections.
server_seps Server-Side Credential Store cwallet.sso, ewallet.p12 Used in newer releases (e.g., Oracle 23ai) for passwordless server-side connections and integrations (such as Recovery Appliance).
mfa Multi-Factor Authentication (MFA) ewallet.p12, cwallet.sso Holds certificates and PKI credentials used for native Multi-Factor Authentication integrations (e.g., OMA or Duo). Introduced in Oracle 19.28+.
bctable Blockchain Tables ewallet.p12, cwallet.sso Stores the PKI private key and certificates of the blockchain table owner. Required for signing and verifying rows using the DBMS_BLOCKCHAIN_TABLE package.
<PDB_GUID> Isolated Pluggable Database Root Subdirectories (e.g., /tde, /tls) A 128-bit GUID folder automatically generated per isolated PDB to isolate keystores from CDB$ROOT and other PDBs.



WALLET_ROOT Directory Structure
WALLET_ROOT/
├── bctable/                    # Blockchain Tables PKI store
│   └── ewallet.p12
├── eus/                        # Enterprise User Security credentials
│   └── ewallet.p12
├── mfa/                        # Multi-Factor Authentication wallet (19.28+)
│   └── ewallet.p12
├── server_seps/                # Server-side credential store
│   └── cwallet.sso
├── tde/                        # Master CDB/non-CDB TDE wallet
│   └── ewallet.p12
├── tde_seps/                   # CDB Auto-login keystore
│   └── cwallet.sso
├── tls/                        # TLS/SSL certificate wallet
│   └── ewallet.p12
├── xdb_wallet/                 # XML DB security wallet
│   └── ewallet.p12
└── <PDB_GUID>/                 # Isolated PDB directory
    ├── bctable/
    │   └── ewallet.p12
    ├── mfa/
    │   └── ewallet.p12
    ├── tde/                    # Isolated PDB TDE wallet
    │   └── ewallet.p12
    ├── tde_seps/
    │   └── cwallet.sso
    └── tls/
        └── cwallet.sso


2) SQLNET.ORA

The sqlnet.ora file is used by the database (during startup) and by client sessions.  Client sessions can override which sqlnet.ora is being used by setting TNS_ADMIN.

There are two locations that can be set in the sqlnet.ora file.

ENCRYPTION_WALLET_LOCATION 

This parameter is being deprecated in Oracle 26ai and it is being replaced with WALLET_ROOT mentioned previously.  WALLET_ROOT allows for easily setting individual wallet locations for each database sharing the same $ORACLE_HOME, along with the ability to set individual wallet locations for each PDB. it is recommended to no longer use this setting and migrate to WALLET_ROOT.

WALLET_LOCATION

Wallet_location is used for a number of wallets entries including SEPS, TLS, and EUS.  Because of this you need to be careful when setting the general wallet_location in the default location ($ORACLE_HOME/network/admin).  With consolidation, many environments today have multiple databases sharing the same $ORACLE_HOME and those databases often require separate, unique wallet files files for each database. 
It is best to create a separate sqlnet.ora directory for each database outside of the $ORACLE_HOME and utilize TNS_ADMIN to set your environment.
This parameter is being deprecated for use by the Oracle Database server (during startup), but not by client connections.

3) TNSNAMES.ORA

The tnsnames.ora file is used to make the connection to the database and it is possible set some wallet locations as part of the connection string.
This setting is done with the the (SECURITY= ...) block of the connect string.

SEPS_WALLET_LOCATION


This setting can be used to set the location of the SEPS wallet for each client connection. This can be useful if you make multiple connects in the same client session and wish to use different SEPS wallets for each connection. 

WALLET_LOCATION

Wallet_location is used for a number of wallets entries including SEPS, TLS, and EUS.  The usage of wallet location in the database connect string is the same as it would be within the sqlnet.ora. Using this setting will allow you to isolate which wallets is used for each connection when using the same client session.


Tuesday, June 23, 2026

Automating Creating Long Term backups from the Autonomous Recovery Service

 When I wrote my last blog on listing the Long Term Backups created by Autonomous Recovery Service, I didn't go through the process of how to dynamically create a new backup.

Below is how you create a long term backup in the console, but most customers want to automate this process.


The oci cli command you would use to create a new backup is "oci db backup create".

In order to create an end-of-month backup with a restore point as of midnight what I recommend customers do is

  1. Ensure you have a nightly backup that runs late at night (about 22:00) and will finish by midnight on a regular basis. 
  2. Schedule the automatic long term backup creation to occur about 00:45 on the next day. This ensures that a log sweep has occurred. For BaseDB you would wait until after the next hour.  If you have enabled the zero data loss feature (real-time redo), you want to make sure that the ARCHIVE_LAG_TARGET is set to 30 minutes or less and forcing a periodic log switch.
This ensures you are creating a long term backup with minimal archive logs to defuzzy the backup.

Command inputs

The easiest way to determine the input for this command is to use the --generate-full-command-json-input option.

oci db backup create --generate-full-command-json-input

What is returned is the JSON example below showing you what parameters need to be filled in to create the backup.

{
  "databaseId": "string",
  "displayName": "string",
  "maxWaitSeconds": 0,
  "retentionDays": 0,
  "retentionYears": 0,
  "waitForState": [
    "CREATING|ACTIVE|DELETING|DELETED|FAILED|RESTORING|UPDATING|CANCELING|CANCELED"
  ],
  "waitIntervalSeconds": 0
}

Source database and backup identifying name

  • databaseID : This is the OCID for the database that you want to create the long term backup for.
  • displayName: This is the name to identify the backup from a listing and would match the name I would put in the GUI.

Wait for state of command (Optional)

  • waitForState: Since the backup can take awhile to run, you can have the command wait to return until a specified state (or any one of a list of states) occurs. When creating a new backup the valid states would be
    • ACTIVE
    • CANCELED
    • CANCELING
    • CREATING
    • FAILED
  • maxWaitSeconds: How long to wait between state checks when waiting for a state to occur.
Example of  wait

oci db backup create --waitForState CREATING --waitForState CANCELED 

Would wait for the backup to start to be created or canceled before returning.

Retention (mandatory for long term backups and must be greater than 90 days and less than 10 years)

You would enter the number of days you want to keep backups for (retentionDays), or you would enter the number of years (retentionYears) , but not both.

Below is an example JSON file that would create a new long term backup named "bsgtest" and keep the backup for 100 days.


{
  "databaseId": "ocid1.database.oc1.phx.anyhqljtbv6267ia2wse63oz7xadpv5lfi2gf233333eomezhadfbdkt2eq",
  "displayName": "bsgtest",
  "maxWaitSeconds": 0,
  "retentionDays": 100
}


That's all you need to know about creating a long term backup dynamically.


Thursday, May 21, 2026

Autonomous Recovery Service - Listing backups

Autonomous Recovery Service - Listing backups

One of the unique features of the Autonomous Recovery Service (RCV) is the ability to create Long Term Backups by using existing backups that are currently stored in RCV.

NOTE: Long term backups, also known as "Keep" backups are self contained backups that provide the ability to restore to a small predetermined point-in-time window. These long term backups are often stored for months, or even years and are typically used for auditing purposes.

These backups are created dynamically outside of the database itself and the DB host is not used.
Because the DB host is bypassed, the normal backup listings on the DB host using the DBAASCLI tool do not see long term backups.


Viewing backups with OCI

All of the backups can be viewed in both the OCI Console and by using the OCI CLI tool.
In this blog, I will describe how you can use the OCI Cli tool to view all of the backups.
The command I am utilizing to display backups is

oci db backup list


Unfortunately, the output from this command is JSON objects which can be difficult to read if you want to produce a report.  

In this blog, I show examples leveraging JMESPath queries via the --query flag.



The Foundation: Listing Database Backups

The baseline command to list backups for a specific database requires the --database-id (OCID). By default, we want to output this as a table, grab all records across pages using the --all flag, and project key fields like Shape and Type:

oci db backup list \
  --database-id {DB OCID} \
  --output table \
  --query "data[?\"lifecycle-state\" == 'ACTIVE'] | sort_by(@, &\"time-started\")[].{Backup_Name: \"display-name\", Time_Started: \"time-started\", Status: \"lifecycle-state\", Version: \"version\", OCID: \"id\", Database_size_GBs: \"database-size-in-gbs\",Shape: \"shape\",Type: \"type\"}"

Running this execution in your environment outputs a perfectly structured text report directly in your shell stream:

+------------------+-------------------+-------------------------------------------------------------------------------------+-------------+--------+----------------------------------+-------------+-------------+
| Backup_Name      | Database_size_GBs | OCID                                                                                | Shape       | Status | Time_Started                     | Type        | Version     |
+------------------+-------------------+-------------------------------------------------------------------------------------+-------------+--------+----------------------------------+-------------+-------------+
| Automatic Backup | 24.01171875       | ocid1.dbbackup.oc1.phx.anyhqljtbv6267iafzxovfcfwpr3pcact3e3vu2exz..........         | Exadata.X8M | ACTIVE | 2026-03-30T12:04:06.553000+00:00 | INCREMENTAL | 19.26.0.0.0 |
| Automatic Backup | 24.01171875       | ocid1.dbbackup.oc1.phx.anyhqljtbv6267iat6czxrwvfnhrgl4voypnunsf2qmfc......          | Exadata.X8M | ACTIVE | 2026-03-31T12:05:41.932000+00:00 | INCREMENTAL | 19.26.0.0.0 |
| Automatic Backup | 24.01171875       | ocid1.dbbackup.oc1.phx.anyhqljtbv6267iapdhn7ekp7ax2y74pbbk32yi4c5zr.......          | Exadata.X8M | ACTIVE | 2026-04-01T12:05:13.350000+00:00 | INCREMENTAL | 19.26.0.0.0 |
| Automatic Backup | 24.01171875       | ocid1.dbbackup.oc1.phx.anyhqljtbv6267iakasw46kzqpk74g335esjwehihmkfzuj............. | Exadata.X8M | ACTIVE | 2026-04-02T12:04:54.891000+00:00 | INCREMENTAL | 19.26.0.0.0 |
| Automatic Backup | 24.01171875       | ocid1.dbbackup.oc1.phx.anyhqljtbv6267iagzx75s2s6zravvpjoem2rkvtm7ue54d............. | Exadata.X8M | ACTIVE | 2026-04-03T12:05:40.559000+00:00 | INCREMENTAL | 19.26.0.0.0 |
| Automatic Backup | 24.01171875       | ocid1.dbbackup.oc1.phx.anyhqljtbv6267ia2j7s7r464swgvrfc34dqh7b635hupho............. | Exadata.X8M | ACTIVE | 2026-04-04T12:03:59.219000+00:00 | INCREMENTAL | 19.26.0.0.0 |
| Automatic Backup | 24.01171875       | ocid1.dbbackup.oc1.phx.anyhqljtbv6267iawkwrmemheourosyriczq3v7lecgmrcj............. | Exadata.X8M | ACTIVE | 2026-04-05T07:24:00.238000+00:00 | INCREMENTAL | 19.26.0.0.0 |
| Automatic Backup | 24.01171875       | ocid1.dbbackup.oc1.phx.anyhqljtbv6267ia2j25fk53uf5fmfkivkoa6ijcbn3fdg7c............ | Exadata.X8M | ACTIVE | 2026-04-06T12:04:22.389000+00:00 | INCREMENTAL | 19.26.0.0.0 |
| Automatic Backup | 24.01171875       | ocid1.dbbackup.oc1.phx.anyhqljtbv6267iass2o5w5bryallijsjjpk2nso2654jr2............. | Exadata.X8M | ACTIVE | 2026-04-07T12:03:56.898000+00:00 | INCREMENTAL | 19.26.0.0.0 |
| Automatic Backup | 24.01171875       | ocid1.dbbackup.oc1.phx.anyhqljtbv62

Finding the most recent backup

In this example, I am limiting the command to extract only daily backups (no long term backups), sort by the execution time, and return only the first record.

This command can be also be used in any scripting to ensure you are cloning from the most recent backup.

oci db backup list \
  --database-id {DB OCID} \
--output table \ --query "data[?\"retention-period-in-days\" == \`null\` && \"retention-period-in-years\" == \`null\` && \"lifecycle-state\" == 'ACTIVE'] | sort_by(@, &\"time-started\")[-1].{Backup_Name: \"display-name\", Time_Started: \"time-started\", Status: \"lifecycle-state\", Version: \"version\", OCID: \"id\", Database_size_GBs: \"database-size-in-gbs\",Shape: \"shape\",Type: \"type\"}"

Executing this slice pattern evaluates down to a single, isolated record representing your absolute most recent backup:

+------------------+-------------------+-------------------------------------------------------------------------------------+-------------+--------+----------------------------------+-------------+-------------+
| Backup_Name      | Database_size_GBs | OCID                                                                                | Shape       | Status | Time_Started                     | Type        | Version     |
+------------------+-------------------+-------------------------------------------------------------------------------------+-------------+--------+----------------------------------+-------------+-------------+
| Automatic Backup | 24.01171875       | ocid1.dbbackup.oc1.phx.anyhqljtbv6267iaufuy74ggl2bvf66czd75x3v4366qrwc............. | Exadata.X8M | ACTIVE | 2026-05-19T12:04:12.917000+00:00 | INCREMENTAL | 19.26.0.0.0 |
+------------------+-------------------+-------------------------------------------------------------------------------------+-------------+--------+----------------------------------+-------------+-------------+

Finding all of the long term backups

This command will display only backups that were creating with a future expiration date. This can be used to view all of the long term backups that were created for this database.

 oci db backup list \
>   --database-id {Database OCID} \
>   --output table \
>   --query "data[?\"time-expiry-scheduled\" != \`null\` && \"lifecycle-state\" == 'ACTIVE'] | sort_by(@, &\"time-started\")[].{Backup_Name: \"display-name\", Time_Bacdkup_Started: \"time-started\", Status: \"lifecycle-state\", Version: \"version\", Time_Backup_Expires: \"time-expiry-scheduled\", OCID: \"id\", Database_size_GBs: \"database-size-in-gbs\",Shape: \"shape\"}"


+------------------+-------------------+-------------------------------------------------------------------------------------+-------------+--------+----------------------------------+----------------------------------+-------------+
| Backup_Name      | Database_size_GBs | OCID                                                                                | Shape       | Status | Time_Bacdkup_Started             | Time_Backup_Expires              | Version     |
+------------------+-------------------+-------------------------------------------------------------------------------------+-------------+--------+----------------------------------+----------------------------------+-------------+
| Long_term_backup | 21.0              | ocid1.dbbackup.oc1.phx.anyhqljtbv6267iaskl3otjhgovkhvefzcjzckdg3v5sfwfybglhrl2mqz7a | Exadata.X8M | ACTIVE | 2026-05-14T14:39:17.539000+00:00 | 2027-05-14T14:39:18.871000+00:00 | 19.26.0.0.0 |
+------------------+-------------------+-------------------------------------------------------------------------------------+-------------+--------+----------------------------------+----------------------------------+-------------+

Friday, April 10, 2026

Automating cloning of your Exadata Database Service on Dedicated Infrastructure database

One of the questions I often get from customers is 

"How do I automate the cloning of a production database backup to a non-prod copy?  This is something we do often."

There are three different OCI commands  to do what seems like the exact same thing. Specifically, when it comes to restoring a database from a backup, the OCI CLI gives us three primary paths.

The "secret sauce" to choosing the right command is understanding where the database is going and ensuring you have the right OCIDs for your target infrastructure. Let's break down the full parameter sets you need to keep your automation from failing.

These are all "oci db database " commands


The Restore Matrix: Choosing Your Command

Command Infrastructure Target Required Target ID Primary Use Case
create-database-from-backup Exadata / C@C --db-home-id Restoring into an existing Exadata Home.
create-from-backup Base DB (VM/BM) --db-system-id Adding a DB to an existing DB System.
create --source DB_BACKUP Base DB (VM/BM) --compartment-id Building a NEW DB System from a backup.

1. The Exadata Full Set: create-database-from-backup

This command uses a JSON object for the --database flag. This is where you define the identity of the clone within the Exadata rack along with the backup you want to use to create the new database.

{
  "adminPassword": "YourPassword123#",
  "backupId": "ocid1.dbbackup.oc1...",
  "backupTDEPassword": "SourceWalletPassword",
  "dbName": "EXACLON",
  "dbUniqueName": "EXACLON_PRD",
  "sidPrefix": "EXACL",
  "pluggableDatabases": ["PDB1", "PDB2"],
  "dbHomeId": "ocid1.dbhome.oc1...",
  "storageSizeDetails": {
    "dataStorageSizeInGBs": 256,
    "recoStorageSizeInGBs": 512
  },
  "sourceEncryptionKeyLocationDetails": {
    "providerType": "AWS|AZURE|GCP|EXTERNAL",
    "awsEncryptionKeyId": "string",
    "hsmPassword": "string"
  }
}

2. The VM/BM In-Place Clone: create-from-backup

`

This is for standard Virtual Machine shapes. You must provide the dbSystemId (the OCID of the VM) to tell OCI exactly where to deploy the restored data.

{
  "adminPassword": "NewAdminPassword123#",
  "backupId": "ocid1.dbbackup.oc1...",
  "backupTdePassword": "SourceWalletPassword",
  "dbSystemId": "ocid1.dbsystem.oc1.iad.example_vm_ocid",
  "dbName": "VMCLON",
  "dbUniqueName": "VMCLON_DEV",
  "sidPrefix": "VMCL",
  "kmsKeyId": "ocid1.key.oc1...",
  "dataStorageSizeInGbs": 256,
  "recoStorageSizeInGbs": 512,
  "databaseSoftwareImageId": "ocid1.dbsoftwareimage.oc1...",
  "isUnifiedAuditingEnabled": true,
  "waitForState": ["AVAILABLE"],
  "maxWaitSeconds": 3600
}

3. Provisioning New Infra: create with --source

This is the "All-In-One" command. It creates the VM Cluster or DB System infrastructure from scratch. Because of this, it requires networking IDs (VCN/Subnet) and hardware shapes.

{
  "source": "DB_BACKUP",
  "backupId": "ocid1.dbbackup.oc1...",
  "tdeWalletPassword": "SourceWalletPassword",
  "compartmentId": "ocid1.compartment.oc1...",
  "subnetId": "ocid1.subnet.oc1...",
  "vmClusterId": "ocid1.vmcluster.oc1...",
  "dbSystemId": "ocid1.dbsystem.oc1...",
  "dbHomeId": "ocid1.dbhome.oc1...",
  "dbName": "NEWDB",
  "dbUniqueName": "NEWDB_U",
  "shape": "VM.Standard.E4.Flex",
  "vaultId": "ocid1.vault.oc1...",
  "kmsKeyId": "ocid1.key.oc1...",
  "dbWorkload": "OLTP",
  "autoBackupEnabled": true,
  "waitForState": ["AVAILABLE"]
}

Key Identification Checklist:
  • Exadata: You must have the --db-home-id of an existing home on the rack.
  • VM In-Place: You need the --db-system-id of the running VM instance.
  • Identity: Every command requires a dbName (8 chars max) and dbUniqueName. For automation, use the sidPrefix to prevent instance ID collisions.



Example from my tenancy

This example shows the command I am using in my tenancy to clone a database

{
  oci db database create \
  --config-file          /home/opc/clone/config \		#--> My OCI authentication config file
  --profile              DEFAULT \				#--> Entry in the config file to use credentials for		
  --region               us-phoenix-1 \				#--> Region I am connecting to execute (source region)
  --source               DB_BACKUP \				#--> Source for the new database is a DB_BACKUP
  --db-home-id           ocid1.dbhome.{...} \			#--> Target home OCID to create new DB
  --vm-cluster-id        ocid1.cloudvmcluster.{...} \		#--> Target VM OCID to create the new DB in
  --admin-password       "$_ADMIN_PW" \				#--> Target DB admin password when creating
  --from-json            file:///{file location}/xx.json	#--> JSON input file 


}

Example from my tenancy (cont)

This is the contents of the .json input file

{
  "source": "DB_BACKUP",				#--> Source is a DB_BACKUP
  "dbHomeId": "ocid1.dbhome.{...}",			#--> Target DB Home OCID
  "database": {
    "backupId": "ocid1.dbbackup.{...}",		        #--> Backup OCID to create database from
    "dbName": "BGRENNC",				#--> New DB name
    "dbUniqueName": "bgrennc_clone",		        #--> New DB Unique name
    "adminPassword": "dd",				#--> New DB Admin password (new TDE password will be the same)
    "backupTDEPassword": "dd",				#--> Original TDE wallet password
    "dbBackupConfig": {
      "autoBackupEnabled": true,			#--> Configure automatic backups
      "recoveryWindowInDays": 30			#--> Set recovery window for new backups
    },
    "definedTags": {
      "Oracle-Tags": {
        "CostType": "Shared"
      }
    }
  }
}


Mastering TDE & Key Management

One of the biggest hurdles in database cloning is handling the Transparent Data Encryption (TDE) layer. If your source backup was encrypted using a key from a different cloud provider or a local HSM, you must tell OCI how to decrypt it during the restore process.

1. Cross-Cloud & External Key Providers

When using the create-database-from-backup command (Exadata), you use the sourceEncryptionKeyLocationDetails parameter. This is a JSON object where you must specify the providerType and the corresponding Key OCID or ID from the source provider.

Provider Type Parameter Required Description
AWS awsEncryptionKeyId The ARN of the AWS KMS key used on the source.
AZURE azureEncryptionKeyId The Azure Key Vault key URI.
GCP googleCloudProviderEncryptionKeyId The fully qualified resource name of the GCP KMS key.
EXTERNAL hsmPassword Used for backups protected by an on-premises Hardware Security Module.

2. Native OCI Vault Integration

For native OCI restores, you have two choices: use the standard Oracle-managed keys (default) or use your own keys via OCI Vault (KMS). If you want to use your own keys, you must provide the kmsKeyId and, in some cases, the vaultId.

  • kmsKeyId: The OCID of the Master Encryption Key in the OCI Vault.
  • kmsKeyVersionId: (Optional) Use this if you need to pin the restore to a specific version of your key.
  • vaultId: Required by the create command to identify which Vault the key resides in.
Important Security Note: If you are restoring a database into a different compartment or tenancy than the source, your Dynamic Group for the target DB System must have READ and USE permissions for the Vault and Key. Without these IAM policies, the restore will fail immediately with a "Not Authorized" error.

By correctly mapping these key parameters, you ensure that your data remains encrypted and compliant throughout its entire lifecycle, even as it moves across cloud boundaries.


Automating with Infrastructure as Code (Terraform)

While the CLI is great for one-off tasks, most of my customers eventually want to bake these clones into their CI/CD pipelines. In Terraform, we use the oci_database_database resource. The "magic" happens in the source attribute and the database_details block.

resource "oci_database_database" "cloned_db" {
    # This maps to the --source flag in the CLI
    source = "DB_BACKUP"

    database {
        admin_password      = var.database_admin_password
        db_name             = "CLONEDB"
        db_unique_name      = "CLONEDB_IAD"
        character_set       = "AL32UTF8"
        ncharacter_set      = "AL16UTF16"
        db_workload         = "OLTP"
        
        # TDE Management
        tde_wallet_password = var.source_tde_password
        kms_key_id          = var.target_vault_key_ocid
    }

    # Target Infrastructure IDs
    db_home_id   = var.target_db_home_ocid
    database_id  = var.source_database_backup_ocid

    # Best Practice: Ignore password changes after initial provision
    lifecycle {
        ignore_changes = [database[0].admin_password]
    }
}

Terraform Pro-Tip: Always use the ignore_changes lifecycle hook for the admin_password. Once the database is restored, security policies often require a password rotation. Without this hook, Terraform will try to revert the password to the plain-text value in your .tfvars every time you run an update!

Thursday, April 2, 2026

Autonomous Recovery Service Live Lab available

One of the latest additions to Oracle's Live Labs is the Autonomous Recovery Service. This lab  allow you to understand how to utilize the Autonomous Recovery Service Service for backing up your Oracle database in the cloud, even in a multicloud environment.

If you haven't used it,  Live Labs is Oracle's free, hands-on platform which allows you to go though a workshop or lab to learn more about Oracle's products.

Start Here <-------- Link to this lab


The nice thing about this lab is that you can utilize Oracle's sandbox to learn about the Autonomous Recovery Service (RCV) without the requirement of accessing your OCI/Multicloud tenancy.

Also, the features that are demonstrated in this lab are the same regardless of using the Autonomous Recovery Service in OCI, or in a multcloud environment.

NOTE:

Keep in mind that it does take time to configure your lab for you to use since the provisioning process performs an initial backup.  In my case it took about 60 minutes. You can view the status of building your lab environment on the "My Reservations" page to follow the progress.  Once completed, this makes the environment immediately available once the lab environment is configured.

Setup:

Once your tenancy is configured for the lab you need to log in using the supplied credentials, and change the log in. Be sure to follow the directions and screenshots in the setup portion of the lab before beginning.

Also be sure to note the region and compartment that you will be using, and change the region after logging into the tenancy to the correct region.

Lab 1: Onboarding a database

One you log into the tenancy and region, you can now go through the steps to configure a database to use the recovery service.

NOTE: The lab uses the "Base DB service" for the demo but the steps would be the same regardless of the Oracle Database server utilized or the location (OCI, AWS, GCP, Azure, etc.).

In this section you will 

Create a protection policy - There are default protection policies policies you can use, but most customers chose to create their own for the following reason.

  • You can chose the exact retention period between 14 and 95 days. Since the service is incremental forever, backup usage is not dependent on a weekly full backup.
  • You can chose the backup location if using multicloud. The default is OCI, and you need to create a protection policy if you want to change the location from the default.
  • You can configure a retention lock. Setting a retention lock is only available when creating your own protection policy.

Configure backups for the existing database - In this section you will view the backup configuration for the database.  When the lab environment was provisioned backups were configured, and in this step you will change the protection policy and enable real-time data protection.
Once the configuration changes are saved, you will monitor the update progress.
Lastly you will view the backup information for this database.

Lab 2: Perform point-in-time restores

The next section of the lab will walk through a point-in-time restore.
You will be guided through connecting to the Database directly through "Cloud Shell" and in cloud shell you will
  • Create new table and insert data into it.
  • Determine the current SCN at this point (with the new table).
  • Delete the table
  • Abort the database (demonstrating real-time data protection)
  • Delete the database files
  • Restore the database to the SCN in the second step
This does take a bit and you are encouraged to continue to lab 3 while this occurs.

Lab 3: Create an on-demand backup

This lab walks you through the process to dynamically create an on-demand.
On-demand backups can be either
  1. Kept for the current retention period. This is useful when upgrading, or rolling out a new release and you want to create a known restore point. This type of backup is stored in the recovery service and will age out with the retention period.
  2. Long-Term backup retention period. This type of backup goes to Oracle managed infrequent object storage, and you specify how long the backups are kept for. 

Lab 4: Monitor & Create Alarms

This section of lab walks you through two additional features that are available with the Autonomous Recovery Service.

Observability - In this section of the lab you explore the metrics that available to view.  The lab demonstrates viewing the data loss exposure in either a chart or table

Alarms - This section shows you how to create an alarm that will sent out an alert on data loss exposure (for example).


Summary:

This lab is a great way to learn more about the Autonomous Recovery Service by going through the features in Oracle's tenancy.








Tuesday, March 24, 2026

MCP Server for Autonomous Recovery Service

Wouldn't it be nice if I could just use AI and ask my tenancy to tell me about my Autonomous Recovery Service database backups ? I want to simply ask questions like ....

Well you can and the screenshot below is just part of the answer I got. !!



Thanks to Harini Gavisiddappa who created an MCP server within the Oracle MCP server github project you can do just that.
You can find the project at https://github.com/oracle/mcp

Installing


Here is the streamlined path to getting this running on your local Windows environment.
I chose putting it on my windows environment because I love using the Claude Chat tool so I can ask questions in a UI and get nicely formatted reports/output back.


Phase 1: Repository & Environment Setup

A) Download repository onto my PC


We start by pulling the source from the [Oracle MCP repository](https://github.com/oracle/mcp/tree/main/src/oci-recovery-mcp-server).




B) Initialize a scoped Python 3.13 environment

 To keep the footprint small and the execution fast, we’ll use `uv` for our Python toolchain.


C) Install the Recovery Server in development mode


Phase 2: Identity & Access Management

Before the server can bridge to your tenancy, you need an active OCI session. This ensures the MCP server inherits your existing IAM permissions.


Phase 3: Client Integration

Phase 4: Validation

Restart your client. You should now see the oracle-recovery toolset available. You can verify the connection immediately with a simple natural language query:

Now that it is installed, let's see what it can do

**************************************************

Question 1


List the databases that are actively using the recovery service in my tenancy and include their status, how much storage they are using, where there are any issues and break it down by both compartment and tag if tags are assigned.

Total databases
8
2 compartments
Storage used
354.6 GB
all databases
Protected
4
healthy, within SLA
Alert
4
recovery window = 0
Prod_databases — 2 databases · 139.9 GB
db19vdb19v_837_iad
Alert
20.3 GB used  ·  est. 14.6 GB  ·  14-day policy
Org: HR · Prod
Recovery window 0 of 14 days — redo logs not streaming or recently enrolled
tt2srctt2src_iad1mz
Protected
119.6 GB used  ·  est. 112.9 GB  ·  14-day policy
Org: Finance · Prod
14.3d retention · redo enabled
Nonprod_databases — 6 databases · 214.7 GB
db23cdb23c_npb_iad
Alert
3.0 GB used  ·  est. 9.1 GB  ·  14-day policy
Org: Finance · Pre-prod
⚠ Unprotected window: 70.5 days — no recent backup activity. Requires immediate attention.
ORCL_TESTORCL
Alert
4.1 GB used  ·  est. 11.3 GB  ·  14-day policy
Environment: Trial
⚠ Unprotected window: 24.9 days — recovery point significantly stale.
clprotclprot
Alert
10.5 GB used  ·  est. 22.1 GB  ·  14-day policy
Fleet agent managed
Recovery window 0 of 14 days · retention = 1 sec · min recovery needed 21 days
db23aidb23ai_6rm_iad
Protected
75.6 GB used  ·  est. 68.1 GB  ·  14-day policy
Org: Finance · Dev
15.0d retention · policy locked 2023-11-16
test2test2_rjh_iad
Protected
77.2 GB used  ·  est. 74.6 GB  ·  35-day policy
Org: HR · Dev
35.8d retention · redo enabled
demodbdemodb_tfj_iad
Protected
44.4 GB used  ·  est. 42.7 GB  ·  35-day policy
No Oracle-Standard tags
35.9d retention
By tag — Oracle-Standard · Org
OrgDatabasesStorageStatus
Finance4198.2 GB
2 ok2 alert
HR297.5 GB
1 ok1 alert
Untagged359.0 GB
1 ok2 alert
By tag — Oracle-Standard · Environment
EnvironmentDatabasesStorageStatus
Prod2139.9 GB
1 ok1 alert
Dev2152.8 GB
2 ok
Pre-prod13.0 GB
1 alert
Trial14.1 GB
1 alert
Untagged254.9 GB
1 ok1 alert

**************************************************

 Question 2


List out the databases that I have and look at the usage for the last 30 days.. Based on that usage give a line chart showing my space usage by database and based on the current growth show what the project usage will be for the next year.


Question 3

Using the tags create a pie chart showing the space usage for all of my backups and show the usage for each tag.




Summary:

Above are some examples of what you can do with the new OCI Recovery Service MCP server with Claude.

Below is a list of the tools available to you to use.

Here are the 19 tools available through the Recovery Service MCP server, grouped by what they do:

Protected Databases

  • list_protected_databases — list databases enrolled in Recovery Service, with metrics and subnet details, filtered by compartment, policy, lifecycle state, etc.
  • get_protected_database — get full details for a single protected database by OCID
  • summarize_protected_database_health — count of healthy / warning / alert / unknown databases in a compartment
  • summarize_protected_database_backup_destination — how databases in a compartment are backed up (Recovery Service vs other destinations)
  • summarize_protected_database_redo_status — how many databases have redo transport on or off

Protection Policies

  • list_protection_policies — list policies in a compartment
  • get_protection_policy — get a single policy by OCID

Recovery Service Subnets

  • list_recovery_service_subnets — list subnets in a compartment
  • get_recovery_service_subnet — get a single subnet by OCID

Backups

  • list_backups — list backups with flexible filters and optional auto-paging
  • get_backup — get a single backup by OCID

Metrics

  • get_recovery_service_metrics — time-series metrics for a compartment or single database; supported metrics are SpaceUsedForRecoveryWindow, ProtectedDatabaseSize, ProtectedDatabaseHealth, and DataLossExposure; resolutions of 1m, 5m, 1h, 1d; aggregations of mean, sum, max, min, count

Storage Summaries

  • summarize_backup_space_used — total backup space in GB across databases in a compartment
  • summarize_protected_database_backup_destination — breakdown by backup destination type

DB Systems & Homes (for enrollment context)

  • list_databases — list databases across DB Homes in a compartment, with backup settings and linked protection policy
  • list_db_homes — list DB Homes in a compartment
  • get_db_home — get a single DB Home by OCID
  • list_db_systems — list DB systems in a compartment
  • get_db_system — get a single DB system by OCID