Oracle RMAN Nightly Backups with FRA + DBMS_SCHEDULER 19c · Linux

A clean, reproducible setup: write nightly RMAN backups to the Fast Recovery Area (FRA), then sync them to /backup/rman, scheduled via DBMS_SCHEDULER. Includes copy‑paste scripts, scheduler jobs, and troubleshooting.

0) Why this design

1) Pre‑flight checks

# Directories & ownerships (adjust as needed)
sudo mkdir -p /u03/fra /backup/rman /u01/app/oracle/admin/log /u01/app/oracle/admin/backup
sudo chown -R oracle:oinstall /u03/fra /backup/rman /u01/app/oracle/admin
sudo chmod 750 /backup/rman
-- As SYSDBA in SQL*Plus
SHOW PARAMETER db_recovery_file_dest;
SHOW PARAMETER db_recovery_file_dest_size;

2) RMAN configuration (once)

Clear hardcoded FORMATs so backups land in the FRA; set sane retention and controls.

sudo -iu oracle bash -lc "cat > /u01/app/oracle/admin/backup/rman_config.rman <<'RMAN'
CONFIGURE CHANNEL DEVICE TYPE DISK CLEAR;
CONFIGURE RETENTION POLICY TO RECOVERY WINDOW OF 7 DAYS;
CONFIGURE CONTROLFILE AUTOBACKUP ON;
CONFIGURE ARCHIVELOG DELETION POLICY TO BACKED UP 1 TIMES TO DEVICE TYPE DISK;
CONFIGURE BACKUP OPTIMIZATION ON;
SHOW ALL;
RMAN
rman target / cmdfile=/u01/app/oracle/admin/backup/rman_config.rman \
  log=/u01/app/oracle/admin/log/rman_config_$(date +%F).log"

3) Shell scripts

3.1 Nightly backup driver (rman_backup.sh)

sudo -iu oracle bash -lc "cat > /u01/app/oracle/admin/backup/rman_backup.sh <<'BASH'
#!/bin/bash
set -euo pipefail

export ORACLE_SID=${ORACLE_SID:-ORCL}
export ORACLE_HOME=/u01/app/oracle/product/19c/dbhome_1
export LD_LIBRARY_PATH=$ORACLE_HOME/lib
export PATH=$ORACLE_HOME/bin:/bin:/usr/bin:$PATH

LOG_DIR=/u01/app/oracle/admin/log
mkdir -p "$LOG_DIR"
STAMP=$(date +%F_%H%M%S)
LOG="$LOG_DIR/rman_backup_$STAMP.log"

FRA=/u03/fra
[[ -d "$FRA" ]] || { echo "FATAL: FRA $FRA missing" | tee -a "$LOG"; exit 1; }

DOW=$(date +%u)   # 1=Mon ... 7=Sun
if [[ "$DOW" == "7" ]]; then
  LEVEL_CMD="INCREMENTAL LEVEL 0"
  TAG="WEEKLY_L0"
else
  LEVEL_CMD="INCREMENTAL LEVEL 1 CUMULATIVE"
  TAG="DAILY_L1C"
fi

echo "ENV: SID=$ORACLE_SID HOME=$ORACLE_HOME PATH=$PATH" >>"$LOG"

"$ORACLE_HOME/bin/rman" target / log="$LOG" <<RMAN
RUN {
  BACKUP AS BACKUPSET $LEVEL_CMD DATABASE TAG '$TAG';
  BACKUP AS BACKUPSET ARCHIVELOG ALL NOT BACKED UP TAG 'ARCH_SYNC';
  DELETE NOPROMPT OBSOLETE;
}
LIST BACKUP SUMMARY;
RMAN

echo "Backup complete ($TAG). Log: $LOG"
BASH
chmod +x /u01/app/oracle/admin/backup/rman_backup.sh"

3.2 FRA → /backup copy (rman_fra_sync.sh)

Uses a cmdfile and TO DESTINATION '/backup/rman'.

sudo -iu oracle bash -lc "cat > /u01/app/oracle/admin/backup/rman_fra_sync.sh <<'BASH'
#!/bin/bash
set -euo pipefail

export ORACLE_SID=${ORACLE_SID:-ORCL}
export ORACLE_HOME=/u01/app/oracle/product/19c/dbhome_1
export LD_LIBRARY_PATH=$ORACLE_HOME/lib
export PATH=$ORACLE_HOME/bin:/bin:/usr/bin:$PATH

LOG_DIR=/u01/app/oracle/admin/log
mkdir -p "$LOG_DIR"
STAMP=$(date +%F_%H%M%S)
LOG="$LOG_DIR/rman_fra_sync_$STAMP.log"

DEST=/backup/rman
mkdir -p "$DEST"

CMDFILE=/u01/app/oracle/admin/backup/.rman_fra_sync.rcv
cat > "$CMDFILE" <<'RCV'
RUN {
  ALLOCATE CHANNEL ch1 DEVICE TYPE DISK FORMAT '/backup/rman/%d_%T_%U.bkp';
  ALLOCATE CHANNEL ch2 DEVICE TYPE DISK FORMAT '/backup/rman/%d_%T_%U.bkp';
  BACKUP RECOVERY AREA TO DESTINATION '/backup/rman' TAG 'FRA_SYNC';
  RELEASE CHANNEL ch1;
  RELEASE CHANNEL ch2;
}
LIST BACKUP SUMMARY;
RCV

echo "ENV: SID=$ORACLE_SID HOME=$ORACLE_HOME PATH=$PATH DEST=$DEST" >>"$LOG"
"$ORACLE_HOME/bin/rman" target / cmdfile="$CMDFILE" log="$LOG"

echo "FRA sync complete. Log: $LOG"
BASH
chmod +x /u01/app/oracle/admin/backup/rman_fra_sync.sh"

4) DBMS_SCHEDULER jobs (replace cron)

sudo -iu oracle bash -lc "cat > /u01/app/oracle/admin/backup/rman_jobs.sql <<'SQL'
BEGIN
  FOR j IN (SELECT job_name FROM dba_scheduler_jobs
            WHERE job_name IN ('RMAN_BACKUP_NIGHTLY','RMAN_FRA_SYNC_NIGHTLY')) LOOP
    DBMS_SCHEDULER.DROP_JOB(j.job_name, force => TRUE);
  END LOOP;
END;
/

BEGIN
  DBMS_SCHEDULER.CREATE_JOB(
    job_name        => 'RMAN_BACKUP_NIGHTLY',
    job_type        => 'EXECUTABLE',
    job_action      => '/u01/app/oracle/admin/backup/rman_backup.sh',
    start_date      => SYSTIMESTAMP,
    repeat_interval => 'FREQ=DAILY;BYHOUR=2;BYMINUTE=0;BYSECOND=0',
    enabled         => TRUE,
    auto_drop       => FALSE,
    comments        => 'Nightly RMAN backup to FRA (/u03/fra). L0 Sun, L1-cum Mon–Sat.'
  );
END;
/

BEGIN
  DBMS_SCHEDULER.CREATE_JOB(
    job_name        => 'RMAN_FRA_SYNC_NIGHTLY',
    job_type        => 'EXECUTABLE',
    job_action      => '/u01/app/oracle/admin/backup/rman_fra_sync.sh',
    start_date      => SYSTIMESTAMP,
    repeat_interval => 'FREQ=DAILY;BYHOUR=3;BYMINUTE=0;BYSECOND=0',
    enabled         => TRUE,
    auto_drop       => FALSE,
    comments        => 'Copy FRA contents to /backup/rman'
  );
END;
/

SET LINES 200
COL job_name FORMAT A24
COL state    FORMAT A12
SELECT job_name, state,
       TO_CHAR(next_run_date,'YYYY-MM-DD HH24:MI:SS TZH:TZM') AS next_run
FROM   dba_scheduler_jobs
WHERE  job_name IN ('RMAN_BACKUP_NIGHTLY','RMAN_FRA_SYNC_NIGHTLY')
ORDER  BY job_name;
SQL
sqlplus / as sysdba @/u01/app/oracle/admin/backup/rman_jobs.sql"

Kick once now

sudo -iu oracle sqlplus -s / as sysdba <<'SQL'
BEGIN
  DBMS_SCHEDULER.RUN_JOB('RMAN_BACKUP_NIGHTLY',   use_current_session => FALSE);
  DBMS_SCHEDULER.RUN_JOB('RMAN_FRA_SYNC_NIGHTLY', use_current_session => FALSE);
END;
/
SQL

5) Quality‑of‑life helpers

# Append to ~oracle/.bashrc to tail newest logs
cat >> ~oracle/.bashrc <<'SH'

tail_rman_sync () {
  local LOG
  LOG=$(find /u01/app/oracle/admin/log -maxdepth 1 -type f -name "rman_fra_sync_*.log" \
        -printf "%T@ %p\n" | sort -nr | awk 'NR==1{print $2}')
  if [ -n "$LOG" ]; then
    echo ">>> $LOG"
    /bin/tail -n 60 "$LOG"
  else
    echo "No sync log yet."
  fi
}

tail_rman_backup () {
  local LOG
  LOG=$(find /u01/app/oracle/admin/log -maxdepth 1 -type f -name "rman_backup_*.log" \
        -printf "%T@ %p\n" | sort -nr | awk 'NR==1{print $2}')
  if [ -n "$LOG" ]; then
    echo ">>> $LOG"
    /bin/tail -n 60 "$LOG"
  else
    echo "No backup log yet."
  fi
}
SH

# Reload and use
sudo -iu oracle bash -lc 'source ~/.bashrc; tail_rman_backup; tail_rman_sync'

6) Verifications & common queries

-- Newest pieces (from the controlfile)
set lines 200 pages 100
col handle format a100
select completion_time, status, handle
from   v$backup_piece
order  by completion_time desc fetch first 20 rows only;
-- Scheduler run details
set lines 200
col job_name format a24
col status   format a10
col additional_info format a120
select job_name, status, actual_start_date, run_duration, additional_info
from   dba_scheduler_job_run_details
where  job_name in ('RMAN_BACKUP_NIGHTLY','RMAN_FRA_SYNC_NIGHTLY')
order  by actual_start_date desc fetch first 10 rows only;

7) Troubleshooting notes

8) Optional: FRA space report

set lines 180 pages 100
col name format a40
select name, space_limit/1024/1024/1024 as fra_gb,
       space_used/1024/1024/1024  as used_gb,
       space_reclaimable/1024/1024/1024 as reclaimable_gb
from   v$recovery_file_dest;

select object_type, count(*) cnt
from   v$flash_recovery_area_usage
group  by object_type
order  by 1;

Wrap‑up: Nightly RMAN backups to FRA (weekly L0, daily L1‑cum) + a secondary copy under /backup/rman, all owned by DBMS_SCHEDULER with simple log helpers. Ship it. 🚀