What Is Oracle ZDM?

Oracle Zero Downtime Migration (ZDM) is Oracle's own tooling for migrating Oracle databases with minimal or zero application downtime. Introduced alongside Oracle Database 19c, it has matured significantly through 21c and 23ai patch cycles.

ZDM handles the complexity of database migration by orchestrating:

The phrase "zero downtime" is slightly optimistic. You will have a brief switchover window. What ZDM eliminates is the multi-hour planned outage of traditional export/import or straight RMAN restore approaches.

Architecture Overview

ZDM uses three distinct tiers:

ZDM Service Host — A dedicated server or VM where the ZDM software is installed. This host orchestrates the migration but processes no data itself. It needs SSH access to both source and target database hosts.

Source Database Host — Your existing production Oracle server. ZDM connects here to take backups, configure Data Guard, or drive a Data Pump export.

Target Database Host — The destination. For cloud migrations this is typically an Oracle Base Database Service or ExaCS instance. For on-premises targets it is any Oracle-supported Linux host.

The ZDM service coordinates everything through a sequential phase model: it SSH's into both endpoints, executes remote commands, and tracks migration progress through a series of named phases with checkpoint/resume capability.

Migration Methods: Physical vs Logical

ZDM offers three migration methods, and the choice matters enormously for large databases:

Physical Migration (RMAN-based) — Uses RMAN backup and restore to transfer the database to the target, then configures a Data Guard standby for ongoing redo apply. Switchover is a Data Guard role change. Fastest for large databases (multi-TB). Requires compatible OS and endian format between source and target.

Logical Migration (Data Pump-based) — Exports schema and data using Oracle Data Pump, with optional GoldenGate for continuous replication. Required for cross-platform, cross-version, or cross-endian migrations. Slower for large databases but more flexible.

Online Logical Migration — Combines Data Pump for the initial load with GoldenGate Microservices for continuous replication. True near-zero downtime. Requires GoldenGate licensing.

For most lift-and-shift migrations to OCI, physical migration is the right choice. You get faster transfer times and a simpler change model.

Prerequisites Checklist

Before you run a single zdmcli command, verify each of these:

Network and SSH Connectivity

# From ZDM host — test passwordless SSH to both source and target
ssh -i /home/zdmuser/.ssh/id_rsa oracle@sourcehost "echo OK"
ssh -i /home/zdmuser/.ssh/id_rsa oracle@targethost "echo OK"

Verify oracle user has passwordless sudo

ssh oracle@sourcehost "sudo /bin/ls /etc/oratab"

Oracle Database Versions

Wallets and sys Credentials

# Create source wallet with MKSTORE
mkstore -wrl /home/zdmuser/wallets/src -create
mkstore -wrl /home/zdmuser/wallets/src -createCredential "sourcehost:1521/PRODDB" sys

Create target wallet

mkstore -wrl /home/zdmuser/wallets/tgt -create mkstore -wrl /home/zdmuser/wallets/tgt -createCredential "targethost:1521/PRODDB_TGT" sys

Create sqlnet.ora on ZDM host pointing to wallet

cat > $ORACLE_HOME/network/admin/sqlnet.ora <<EOF WALLET_LOCATION = (SOURCE = (METHOD = FILE)(METHOD_DATA = (DIRECTORY = /home/zdmuser/wallets/src))) SQLNET.WALLET_OVERRIDE = TRUE EOF

Disk Space

Starting the ZDM Service

export ZDM_HOME=/u01/zdmhome
export ZDM_BASE=/u01/zdmbase

Start

$ZDM_HOME/bin/zdmservice start

Verify

$ZDM_HOME/bin/zdmservice status

Expected:

ZDM Service is up and running.

Listening on port: 8895

Logs go here if something is wrong

tail -100 $ZDM_BASE/zdm/log/zdm_service.log

The Response File

The response file is the single most important artefact in the migration. Every parameter lives here.

# /home/zdmuser/response/prod_to_oci.rsp

Target DB unique name (must match what you provisioned on OCI)

TGT_DB_UNIQUE_NAME=PRODDB_TGT

Physical migration using Data Guard replication

MIGRATION_METHOD=ONLINE_PHYSICAL

Transfer data via OCI Object Storage

DATA_TRANSFER_MEDIUM=OSS HOST=https://objectstorage.us-ashburn-1.oraclecloud.com OPC_CONTAINER=zdm-migration-bucket

RMAN compression — reduces transfer time significantly for large DBs

RMAN_COMPRESSION_ALGORITHM=MEDIUM

Run datapatch on target post-switchover (recommended: TRUE)

TGT_SKIP_DATAPATCH=FALSE

Source DB unique name

SRC_DB_UNIQUE_NAME=PRODDB

Keep source running after switchover until you confirm target is stable

SHUTDOWN_SOURCE=FALSE

Key Parameters Explained

MIGRATION_METHOD — This is the fundamental choice. ONLINE_PHYSICAL gives you live Data Guard sync with a rapid switchover. OFFLINE_PHYSICAL is a backup/restore with no sync. ONLINE_LOGICAL requires GoldenGate.

DATA_TRANSFER_MEDIUM — OSS uses OCI Object Storage (fast, scalable, recommended for anything >100GB). DBLINK transfers directly over SQL*Net (simpler setup, but slower for large DBs). NFS works for collocated target.

TGT_SKIP_DATAPATCH — Only set TRUE if you're doing a test migration and want to save time. For production migrations, always FALSE. Skipping datapatch can leave objects in an invalid state.

Running the Migration

$ZDM_HOME/bin/zdmcli migrate database \
  -sourcedb PRODDB \
  -sourcenode sourcehost.example.com \
  -srcauth zdmauth \
  -srcarg1 user:oracle \
  -srcarg2 identity_file:/home/zdmuser/.ssh/id_rsa \
  -srcarg3 sudo_location:/usr/bin/sudo \
  -targetnode targethost.oci.example.com \
  -tgtauth zdmauth \
  -tgtarg1 user:oracle \
  -tgtarg2 identity_file:/home/zdmuser/.ssh/id_rsa \
  -tgtarg3 sudo_location:/usr/bin/sudo \
  -rsp /home/zdmuser/response/prod_to_oci.rsp \
  -sourcesyswallet /home/zdmuser/wallets/src \
  -targetsyswallet /home/zdmuser/wallets/tgt

Returns immediately with a job ID

Job ID: 1

Important: ZDM submits the job and returns immediately. The migration runs asynchronously. Do not close your terminal session — use screen or tmux for long migrations.

Monitoring with zdmcli query job

# Poll current status
$ZDM_HOME/bin/zdmcli query job -jobid 1

Watch it live (runs every 60s)

watch -n 60 "$ZDM_HOME/bin/zdmcli query job -jobid 1"

Phase-by-Phase Breakdown

| Phase | Duration (typical) | What Happens |
|-------|-------------------|-------------|
| SETUP | 2–5 min | Creates working dirs, validates SSH, checks Oracle Net |
| VALIDATESOURCE | 1–2 min | Connects to source DB, checks version, mode, archive log |
| VALIDATETARGET | 1–2 min | Connects to target DB, checks provisioning |
| INITIALTRANSFER | Hours | RMAN backup from source → OCI Object Storage; restore to target |
| SYNCTARGET | Ongoing | Archive log apply loop — keeps target current |
| SWITCHOVER | 2–15 min | Role change: source → standby, target → primary |
| POSTSWITCHOVER | 30–60 min | TNS config update, datapatch if enabled, cleanup |

The Switchover Decision

ZDM pauses in SYNCTARGET and waits for you to initiate switchover. Check the apply lag before proceeding:

-- On target: check Data Guard apply lag
SELECT NAME, VALUE, UNIT FROM V$DATAGUARD_STATS WHERE NAME IN ('apply lag','transport lag');
-- Acceptable: apply lag < 30 seconds

When you are ready:

$ZDM_HOME/bin/zdmcli resume job -jobid 1

Common Failures and Fixes

SSH Authentication Failures

Error: SSH connection to sourcehost failed — Permission denied (publickey)
Fix: Verify the identity file path is correct and the public key is in the oracle user's authorized_keys on both source and target.

Wallet Cannot Open

ORA-28353: failed to open wallet
Check that the wallet path in your sqlnet.ora on the ZDM host matches where you created the wallet, and that the wallet contains the correct credential. Use mkstore -wrl /path/to/wallet -listCredential to verify.

Archive Log Destination Full

-- On source during SYNCTARGET
SELECT DEST_ID, STATUS, ERROR FROM V$ARCHIVE_DEST WHERE STATUS != 'INACTIVE';
ALTER SYSTEM SET DB_RECOVERY_FILE_DEST_SIZE = 500G SCOPE=BOTH;

Object Storage Auth Errors

# Test OCI OS connectivity from ZDM host
oci os bucket list --compartment-id <ocid> --namespace <namespace>

Post-Migration Validation

-- 1. Confirm database role and mode
SELECT NAME, DB_UNIQUE_NAME, DATABASE_ROLE, OPEN_MODE FROM V$DATABASE;

-- 2. Check for invalid objects
SELECT OWNER, COUNT(*) CNT FROM DBA_OBJECTS
WHERE STATUS = 'INVALID'
GROUP BY OWNER ORDER BY 2 DESC;

-- 3. Verify key schemas accessible
SELECT USERNAME, ACCOUNT_STATUS FROM DBA_USERS
WHERE ACCOUNT_STATUS = 'OPEN' ORDER BY USERNAME;

TuneVault and Post-Migration Health

The 48 hours after a ZDM migration are the highest-risk period. TuneVault's automated health checks surface the common post-migration issues — missing optimizer statistics, tablespace sizing differences, redo log configuration, and invalid objects — within minutes of pointing at the new instance.