Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Oracle Database 19c — Two Independent Instances on Oracle Linux 8

A production-ready guide to installing Oracle Database 19c SE2 with two independent databases, dedicated listeners, ARCHIVELOG, RMAN backup strategy, and systemd auto-start. Built from real deployment experience.

Oracle 19c Oracle Linux Edition License


Features

  • Two independent databases — PROD1 and PROD2 on separate mount points, fully isolated data and FRA
  • Dedicated listenersLISTENER_PROD1 on 1521, LISTENER_PROD2 on 15210, each registered independently
  • ARCHIVELOG mode + FORCE LOGGING — both instances ready for RMAN and standby
  • Fast Recovery Area — per-database FRA with configurable size
  • Systemd integration — listeners and databases start/stop automatically on boot via two service units
  • RMAN backup strategy — incremental level 0 + archivelog backup with obsolete cleanup
  • Hardened OS — SELinux enforcing, firewalld with only required ports open
  • Silent install — fully scriptable, no GUI required

Architecture

dbhost1.example.com
│
├── Oracle Home (shared)
│   └── /u01/app/oracle/product/19c/dbhome_1
│
├── PROD1
│   ├── Data:     /u02/oradata/PROD1
│   ├── FRA:      /u02/fra/PROD1
│   └── Listener: LISTENER_PROD1  →  TCP 1521
│
└── PROD2
    ├── Data:     /u03/oradata/PROD2
    ├── FRA:      /u03/fra/PROD2
    └── Listener: LISTENER_PROD2  →  TCP 15210

Why two listeners instead of one?

Approach Trade-off
Single LISTENER on 1521, both DBs registered Cannot independently stop/start listener per database; harder to isolate connections
Dedicated listener per database ✅ Independent control, cleaner separation, easier firewall rules per service

Repository Structure

oracle19c-two-instances/
├── config/
│   ├── listener.ora              # Dual-listener network config
│   └── tnsnames.ora              # Local TNS aliases for both instances
├── scripts/
│   ├── 01_os_prep.sh             # Hostname, hosts file, DNF packages
│   ├── 02_user_dirs.sh           # Mount points and ownership
│   ├── 03_env_profile.sh         # oracle user bash_profile
│   ├── 04_install_sw.sh          # Silent Oracle software install
│   ├── 05_patch_ru.sh            # OPatch replacement and RU apply
│   ├── 07_create_db.sh           # DBCA silent DB creation (both instances)
│   ├── 08_archivelog.sql         # Enable ARCHIVELOG + FRA (run per instance)
│   ├── 09_systemd_setup.sh       # Install and enable systemd service units
│   ├── 10_firewall.sh            # Firewalld rules
│   └── 11_validate.sh            # Post-install validation
├── rman/
│   └── backup_level0.rman        # RMAN incremental level 0 + archivelog script
├── systemd/
│   ├── oracle-listeners.service  # Systemd unit — both listeners
│   └── oracle-databases.service  # Systemd unit — dbstart / dbshut
└── README.md

Prerequisites

  • Oracle Linux 8 (minimal install)
  • Oracle Database 19c software ZIP (LINUX.X64_193000_db_home.zip)
  • OPatch 12.2.0.1.x or later ZIP
  • Desired Release Update (RU) patch ZIP
  • Root or sudo access
  • /u01, /u02, /u03 mount points available (separate LUNs or partitions recommended)

Step 1 — OS Preparation

sudo hostnamectl set-hostname dbhost1.example.com
echo "192.0.2.10  dbhost1.example.com dbhost1" | sudo tee -a /etc/hosts

sudo dnf update -y
sudo dnf install -y oracle-database-preinstall-19c unzip tar

oracle-database-preinstall-19c automatically:

  • Creates the oracle user and oinstall/dba groups
  • Sets kernel parameters (shmall, shmmax, semaphores, etc.)
  • Configures /etc/security/limits.conf for Oracle

Step 2 — User and Directory Setup

sudo mkdir -p /u01/app/oracle/product/19c/dbhome_1
sudo mkdir -p /u01/app/oraInventory
sudo mkdir -p /u02/oradata/PROD1 /u02/fra/PROD1
sudo mkdir -p /u03/oradata/PROD2 /u03/fra/PROD2
sudo chown -R oracle:oinstall /u01 /u02 /u03
sudo chmod -R 775 /u01 /u02 /u03

Step 3 — Oracle User Environment

Add to /home/oracle/.bash_profile (or run scripts/03_env_profile.sh):

export ORACLE_BASE=/u01/app/oracle
export ORACLE_HOME=/u01/app/oracle/product/19c/dbhome_1
export ORA_INVENTORY=/u01/app/oraInventory
export PATH=$ORACLE_HOME/bin:$PATH
export ORACLE_HOSTNAME=dbhost1.example.com
source /home/oracle/.bash_profile

Step 4 — Oracle Software Installation

su - oracle

cd $ORACLE_HOME
unzip -oq /path/to/LINUX.X64_193000_db_home.zip

export CV_ASSUME_DISTID=OEL7.6

./runInstaller -ignorePrereq \
               -waitforcompletion \
               -silent \
               -responseFile $ORACLE_HOME/install/response/db_install.rsp \
               oracle.install.option=INSTALL_DB_SWONLY \
               ORACLE_HOSTNAME=${ORACLE_HOSTNAME} \
               UNIX_GROUP_NAME=oinstall \
               INVENTORY_LOCATION=${ORA_INVENTORY} \
               ORACLE_HOME=${ORACLE_HOME} \
               ORACLE_BASE=${ORACLE_BASE} \
               oracle.install.db.InstallEdition=SE2 \
               oracle.install.db.OSDBA_GROUP=dba \
               oracle.install.db.OSBACKUPDBA_GROUP=dba \
               oracle.install.db.OSDGDBA_GROUP=dba \
               oracle.install.db.OSKMDBA_GROUP=dba \
               oracle.install.db.OSRACDBA_GROUP=dba \
               SECURITY_UPDATES_VIA_MYORACLESUPPORT=false \
               DECLINE_SECURITY_UPDATES=true

Run root scripts when prompted:

sudo /u01/app/oraInventory/orainstRoot.sh
sudo $ORACLE_HOME/root.sh

Step 5 — Patch to Supported Release Update

su - oracle

# Replace OPatch
rm -rf $ORACLE_HOME/OPatch
unzip -oq /path/to/p6880880_190000_Linux-x86-64.zip -d $ORACLE_HOME
export PATH=$ORACLE_HOME/OPatch:$PATH

# Apply RU
unzip -oq /path/to/pNNNNNNNN_190000_Linux-x86-64.zip -d /tmp/ru
opatch apply -silent /tmp/ru/NNNNNNNNN

Verify:

opatch lspatches

Step 6 — Listener Configuration

Copy config/listener.ora and config/tnsnames.ora to $ORACLE_HOME/network/admin/ or create them directly:

listener.ora

LISTENER_PROD1 =
  (DESCRIPTION_LIST =
    (DESCRIPTION =
      (ADDRESS = (PROTOCOL = TCP)(HOST = dbhost1.example.com)(PORT = 1521))
    )
  )

LISTENER_PROD2 =
  (DESCRIPTION_LIST =
    (DESCRIPTION =
      (ADDRESS = (PROTOCOL = TCP)(HOST = dbhost1.example.com)(PORT = 15210))
    )
  )

tnsnames.ora

PROD1_LOCAL =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = dbhost1.example.com)(PORT = 1521))
    (CONNECT_DATA = (SERVICE_NAME = PROD1))
  )

PROD2_LOCAL =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = dbhost1.example.com)(PORT = 15210))
    (CONNECT_DATA = (SERVICE_NAME = PROD2))
  )

Start listeners:

lsnrctl start LISTENER_PROD1
lsnrctl start LISTENER_PROD2

Step 7 — Database Creation

PROD1:

su - oracle

dbca -silent -createDatabase \
     -templateName General_Purpose.dbc \
     -gdbname PROD1 -sid PROD1 \
     -datafileDestination /u02/oradata \
     -characterSet AL32UTF8 \
     -sysPassword 'StrongPass_1234' \
     -systemPassword 'StrongPass_1234' \
     -emConfiguration NONE

export ORACLE_SID=PROD1

sqlplus / as sysdba <<'SQL'
ALTER SYSTEM SET local_listener='PROD1_LOCAL' SCOPE=BOTH;
SHUTDOWN IMMEDIATE;
STARTUP;
SQL

lsnrctl status LISTENER_PROD1

PROD2 (repeat with PROD2 parameters):

dbca -silent -createDatabase \
     -templateName General_Purpose.dbc \
     -gdbname PROD2 -sid PROD2 \
     -datafileDestination /u03/oradata \
     -characterSet AL32UTF8 \
     -sysPassword 'StrongPass_1234' \
     -systemPassword 'StrongPass_1234' \
     -emConfiguration NONE

export ORACLE_SID=PROD2

sqlplus / as sysdba <<'SQL'
ALTER SYSTEM SET local_listener='PROD2_LOCAL' SCOPE=BOTH;
SHUTDOWN IMMEDIATE;
STARTUP;
SQL

lsnrctl status LISTENER_PROD2

Step 8 — Enable ARCHIVELOG and FRA

Run for each instance (change ORACLE_SID, path, and size accordingly):

su - oracle
export ORACLE_SID=PROD1

sqlplus / as sysdba <<'SQL'
ALTER SYSTEM SET db_recovery_file_dest_size = 100G SCOPE=BOTH;
ALTER SYSTEM SET db_recovery_file_dest = '/u02/fra/PROD1' SCOPE=BOTH;

SHUTDOWN IMMEDIATE;
STARTUP MOUNT;
ALTER DATABASE ARCHIVELOG;
ALTER DATABASE OPEN;
ALTER DATABASE FORCE LOGGING;

ARCHIVE LOG LIST;
SHOW PARAMETER db_recovery_file_dest;
SQL

Repeat for PROD2 with /u03/fra/PROD2.


Step 9 — Systemd Integration

Add both databases to /etc/oratab:

PROD1:/u01/app/oracle/product/19c/dbhome_1:Y
PROD2:/u01/app/oracle/product/19c/dbhome_1:Y

Copy service units from systemd/ or install them directly:

sudo cp systemd/oracle-listeners.service /etc/systemd/system/
sudo cp systemd/oracle-databases.service /etc/systemd/system/

sudo systemctl daemon-reload
sudo systemctl enable oracle-listeners.service oracle-databases.service
sudo systemctl start oracle-listeners.service oracle-databases.service
sudo systemctl status oracle-listeners.service oracle-databases.service

Step 10 — Firewall Configuration

sudo firewall-cmd --permanent --add-port=1521/tcp
sudo firewall-cmd --permanent --add-port=15210/tcp
sudo firewall-cmd --reload
sudo firewall-cmd --list-ports
# → 1521/tcp 15210/tcp

Step 11 — Validation

su - oracle

lsnrctl status LISTENER_PROD1
lsnrctl status LISTENER_PROD2

tnsping PROD1_LOCAL
tnsping PROD2_LOCAL

export ORACLE_SID=PROD1
sqlplus / as sysdba <<'SQL'
SELECT name, open_mode, log_mode FROM v$database;
SQL

export ORACLE_SID=PROD2
sqlplus / as sysdba <<'SQL'
SELECT name, open_mode, log_mode FROM v$database;
SQL

Expected output for each database:

NAME    OPEN_MODE            LOG_MODE
------- -------------------- ------------
PROD1   READ WRITE           ARCHIVELOG

Step 12 — RMAN Backup Strategy

Use rman/backup_level0.rman or run directly:

su - oracle
export ORACLE_SID=PROD1

rman target / <<'EOF'
RUN {
  CONFIGURE CONTROLFILE AUTOBACKUP ON;
  CONFIGURE RETENTION POLICY TO RECOVERY WINDOW OF 7 DAYS;
  BACKUP INCREMENTAL LEVEL 0 DATABASE PLUS ARCHIVELOG;
  DELETE NOPROMPT OBSOLETE;
}
EOF

Repeat for PROD2. Schedule via cron:

# Crontab entry for oracle user — level 0 every Sunday at 01:00
0 1 * * 0 ORACLE_SID=PROD1 $ORACLE_HOME/bin/rman target / @/home/oracle/rman/backup_level0.rman >> /home/oracle/rman/logs/PROD1_$(date +\%F).log 2>&1
0 2 * * 0 ORACLE_SID=PROD2 $ORACLE_HOME/bin/rman target / @/home/oracle/rman/backup_level0.rman >> /home/oracle/rman/logs/PROD2_$(date +\%F).log 2>&1

Step 13 — Hardening & Operations Checklist

  • SELinux set to enforcing — verify with getenforce
  • Firewalld active with only ports 1521 and 15210 open
  • ARCHIVELOG mode confirmed on both instances
  • RMAN backups scheduled and tested
  • Release Updates applied quarterly
  • SYS and SYSTEM passwords changed from defaults
  • Listener passwords set if required by policy
  • oratab entries set to Y for both instances (enables dbstart/dbshut)
  • Systemd services enabled: oracle-listeners and oracle-databases

Troubleshooting

Listener not starting

# Check listener log
cat $ORACLE_BASE/diag/tnslsnr/$(hostname)/listener_prod1/alert/log.xml | tail -50

# Verify port not in use
ss -tlnp | grep -E '1521|15210'

# Try manual start with debug
lsnrctl start LISTENER_PROD1
lsnrctl status LISTENER_PROD1

Database not registering with listener

# Verify local_listener parameter
sqlplus / as sysdba <<'SQL'
SHOW PARAMETER local_listener;
-- Should show PROD1_LOCAL or PROD2_LOCAL
ALTER SYSTEM REGISTER;
SQL

lsnrctl status LISTENER_PROD1
# Service "PROD1" should appear

Systemd service fails to start

journalctl -u oracle-databases.service -n 50

# Verify oratab entries
grep -E 'PROD1|PROD2' /etc/oratab
# Both lines must end with :Y

# Test dbstart manually as oracle user
$ORACLE_HOME/bin/dbstart $ORACLE_HOME

ARCHIVELOG destination filling up

export ORACLE_SID=PROD1
rman target / <<'EOF'
CROSSCHECK ARCHIVELOG ALL;
DELETE NOPROMPT ARCHIVELOG ALL COMPLETED BEFORE 'SYSDATE-2';
EOF

# Check FRA usage
sqlplus / as sysdba <<'SQL'
SELECT * FROM v$recovery_area_usage;
SQL

Security Notes

  • Change default passwordsSYS and SYSTEM use placeholder passwords in this guide; replace before going live
  • Dedicated OS mount points — separate /u02 and /u03 prevent one database from consuming all disk space
  • SELinux enforcing — do not disable; Oracle 19c runs correctly with enforcing mode on Oracle Linux 8
  • Minimal firewall exposure — only listener ports are open; no EM Express, no HTTP/HTTPS unless required
  • Listener without EXTPROC — the listener config in this guide does not expose external procedure calls

License

MIT — free to use, modify, and distribute.


Author

Przemysław Pradela

Built through real production deployment on Oracle Linux 8.

GitHub LinkedIn Website

Contributions and issue reports welcome.

About

Step-by-step guide to installing Oracle Database 19c SE2 on Oracle Linux 8 with two independent instances, dedicated listeners, ARCHIVELOG, RMAN backups, and systemd auto-start.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages