Friday, August 7, 2026

Installing and Configuring Oracle Database 19c on Amazon Linux 2023

 A practical, end-to-end guide to installing Oracle Database 19c (Enterprise Edition and Standard Edition 2) on an Amazon Linux 2023 (AL2023) EC2 instance using silent installs, then configuring it for real use: archivelog + Flash Recovery Area, a container database with a pluggable database, systemd auto-start, scheduled archive-log housekeeping, sample schemas, and a lightweight workload — plus a troubleshooting section covering the gotchas you will hit on AL2023.

Note on support: Oracle Database 19c is not certified on Amazon Linux 2023. The steps below use documented workarounds and work well for dev/test/demo environments. For production, use a certified OS (Oracle Linux / RHEL).

All commands run on the database host. Replace placeholders like YourStrongPassword, <MEDIA_LOCATION>, and hostnames to suit your environment.


1. Environment & layout

ItemValue used here
OSAmazon Linux 2023
Instancememory-optimized (e.g. 8 vCPU / 64 GB) for several small DBs
Data diskdedicated EBS volume mounted at /u01 (XFS)
ORACLE_BASE/u01/app/oracle
DB home (EE)/u01/app/oracle/product/19.0.0/dbhome_1
DB home (SE2)/u01/app/oracle/product/19.0.0/dbhome_se2
Inventory/u01/app/oraInventory

Editions live in separate ORACLE_HOMEs (edition is fixed at install time); a single host can run several homes/editions behind one listener.


2. Choose the correct media

Oracle ships several ~2.8 GB zips. Pick the Database image, not Grid Infrastructure:

Part numberProductMarker after unzip
V982063-01.zipOracle Database 19.3 (use this)top-level runInstaller, db_install.rsp
V982068-01.zipGrid Infrastructure 19.3gridSetup.sh, gridsetup.rsp, crs/

Patches used (19.x Release Update set): the latest OPatch (p6880880), the DB RU (-applyRU) and the OJVM RU (-applyOneOffs).


3. OS prerequisites (run as root)

# Packages (AL2023 uses dnf). libnsl + libxcrypt-compat are REQUIRED on AL2023.
dnf install -y \
  bc binutils elfutils-libelf fontconfig glibc glibc-devel \
  libaio libaio-devel libX11 libXau libXi libXrender libXtst \
  libgcc libstdc++ libxcb make policycoreutils smartmontools sysstat \
  unzip ksh libnsl libxcrypt-compat

# Swap (installer checks for it)
if [ "$(swapon --show | wc -l)" -eq 0 ]; then
  fallocate -l 16G /swapfile; chmod 600 /swapfile; mkswap /swapfile; swapon /swapfile
  echo '/swapfile none swap sw 0 0' >> /etc/fstab
fi

# Kernel parameters
cat > /etc/sysctl.d/98-oracle.conf <<'EOF'
fs.file-max = 6815744
kernel.sem = 250 32000 100 128
kernel.shmmni = 4096
kernel.shmall = 1073741824
kernel.shmmax = 4398046511104
fs.aio-max-nr = 1048576
net.ipv4.ip_local_port_range = 9000 65500
net.core.rmem_default = 262144
net.core.rmem_max = 4194304
net.core.wmem_default = 262144
net.core.wmem_max = 1048576
EOF
sysctl --system

# Resource limits
cat > /etc/security/limits.d/98-oracle.conf <<'EOF'
oracle soft nofile 1024
oracle hard nofile 65536
oracle soft nproc 2047
oracle hard nproc 16384
oracle soft stack 10240
oracle hard stack 32768
oracle soft memlock 134217728
oracle hard memlock 134217728
EOF

# *** CRITICAL: RemoveIPC=no (see Troubleshooting) ***
grep -qi RemoveIPC /etc/systemd/logind.conf \
  && sed -i -E 's/^[[:space:]]*#?[[:space:]]*RemoveIPC.*/RemoveIPC=no/I' /etc/systemd/logind.conf \
  || echo 'RemoveIPC=no' >> /etc/systemd/logind.conf
systemctl restart systemd-logind

# Groups + oracle user
for g in "oinstall:54321" "dba:54322" "oper:54323" "backupdba:54324" \
         "dgdba:54325" "kmdba:54326" "racdba:54330"; do
  groupadd -g ${g#*:} ${g%:*} 2>/dev/null || true
done
id oracle 2>/dev/null || useradd -u 54321 -g oinstall \
  -G dba,oper,backupdba,dgdba,kmdba,racdba oracle

# Directories on the data volume
mkdir -p /u01/app/oracle/product/19.0.0/dbhome_1 /u01/app/oraInventory \
         /u01/app/oracle/oradata /u01/app/oracle/fast_recovery_area /u01/stage
chown -R oracle:oinstall /u01/app /u01/stage
chmod -R 775 /u01/app

4. Silent software-only install + patch (run as oracle)

Unzip the DB image into the target home, refresh OPatch, then install with the RU applied. AL2023 needs CV_ASSUME_DISTID=OL8 and -ignorePrereqFailure.

export ORACLE_BASE=/u01/app/oracle
export ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1
export CV_ASSUME_DISTID=OL8

cd $ORACLE_HOME && unzip -oq <MEDIA_LOCATION>/V982063-01.zip
rm -rf $ORACLE_HOME/OPatch && unzip -oq <MEDIA_LOCATION>/p6880880_190000_Linux-x86-64.zip -d /tmp/op && cp -r /tmp/op/OPatch $ORACLE_HOME/OPatch
# unzip the RU + OJVM patches into a staging dir first, note their patch-number folders

cat > /tmp/db_install.rsp <<EOF
oracle.install.responseFileVersion=/oracle/install/rspfmt_dbinstall_response_schema_v19.0.0
oracle.install.option=INSTALL_DB_SWONLY
UNIX_GROUP_NAME=oinstall
INVENTORY_LOCATION=/u01/app/oraInventory
ORACLE_HOME=$ORACLE_HOME
ORACLE_BASE=$ORACLE_BASE
oracle.install.db.InstallEdition=EE           # EE or SE2
oracle.install.db.OSDBA_GROUP=dba
oracle.install.db.OSOPER_GROUP=oper
oracle.install.db.OSBACKUPDBA_GROUP=backupdba
oracle.install.db.OSDGDBA_GROUP=dgdba
oracle.install.db.OSKMDBA_GROUP=kmdba
oracle.install.db.OSRACDBA_GROUP=racdba
oracle.install.db.rootconfig.executeRootScript=false
DECLINE_SECURITY_UPDATES=true
EOF

cd $ORACLE_HOME
./runInstaller -silent -ignorePrereqFailure -waitforcompletion \
  -responseFile /tmp/db_install.rsp \
  -applyRU <STAGE>/patch/<DB_RU_DIR> \
  -applyOneOffs <STAGE>/patch/<OJVM_DIR>

Then run the root scripts as root:

/u01/app/oraInventory/orainstRoot.sh    # first home only
$ORACLE_HOME/root.sh

Verify the patch level: su - oracle -c "$ORACLE_HOME/OPatch/opatch lspatches".

For Standard Edition 2, repeat §4 with a second ORACLE_HOME and oracle.install.db.InstallEdition=SE2.


5. Listener (silent, netca)

One listener on 1521 serves every database/home on the host:

su - oracle -c 'export ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1; \
  $ORACLE_HOME/bin/netca /orahome $ORACLE_HOME /instype typical \
  /inscomp client,oraclenet,javavm,server,ano /insprtcl tcp /cfg local \
  /authadp NO_VALUE /responseFile $ORACLE_HOME/network/install/netca_typ.rsp \
  /lisport 1521 /silent /orahnam OraDB19Home1'

6. Create databases (silent, dbca)

Non-CDB (set SGA/PGA explicitly, AMM off):

dbca -silent -createDatabase -templateName General_Purpose.dbc \
  -gdbName ORCLEE -sid ORCLEE -createAsContainerDatabase false \
  -characterSet AL32UTF8 -sysPassword 'YourStrongPassword' -systemPassword 'YourStrongPassword' \
  -automaticMemoryManagement false -initParams sga_target=8192M,pga_aggregate_target=4096M \
  -emConfiguration NONE -datafileDestination /u01/app/oracle/oradata \
  -storageType FS -sampleSchema false

Container database with one PDB (archivelog + 20 GB FRA at creation):

dbca -silent -createDatabase -templateName General_Purpose.dbc \
  -gdbName ORCLCDB -sid ORCLCDB \
  -createAsContainerDatabase true -numberOfPDBs 1 -pdbName pdb1 -pdbAdminPassword 'YourStrongPassword' \
  -characterSet AL32UTF8 -sysPassword 'YourStrongPassword' -systemPassword 'YourStrongPassword' \
  -automaticMemoryManagement false -initParams sga_target=8192M,pga_aggregate_target=4096M \
  -recoveryAreaDestination /u01/app/oracle/fast_recovery_area -recoveryAreaSize 20480 \
  -enableArchive true -emConfiguration NONE \
  -datafileDestination /u01/app/oracle/oradata -storageType FS -sampleSchema false
# make the PDB auto-open with the CDB:
sqlplus / as sysdba <<< "alter pluggable database pdb1 save state;"

7. Archivelog + Flash Recovery Area (existing DB)

ALTER SYSTEM SET db_recovery_file_dest_size=20G SCOPE=BOTH;
ALTER SYSTEM SET db_recovery_file_dest='/u01/app/oracle/fast_recovery_area' SCOPE=BOTH;
SHUTDOWN IMMEDIATE;  STARTUP MOUNT;  ALTER DATABASE ARCHIVELOG;  ALTER DATABASE OPEN;

8. Auto-start on boot (systemd)

Set /etc/oratab to <SID>:<HOME>:Y, add a per-DB service and a listener service. Example DB unit (/etc/systemd/system/oracle-db-<sid>.service):

[Unit]
Description=Oracle Database <SID>
After=network.target oracle-listener.service
Wants=oracle-listener.service
[Service]
Type=forking
User=oracle
Group=oinstall
Environment=ORACLE_HOME=<HOME>
Environment=ORACLE_SID=<SID>
ExecStart=/usr/local/bin/oradb_ctl.sh start <SID> <HOME>
ExecStop=/usr/local/bin/oradb_ctl.sh stop <SID> <HOME>
RemainAfterExit=yes
TimeoutStartSec=600
[Install]
WantedBy=multi-user.target

Where oradb_ctl.sh start runs sqlplus / as sysdbastartup; alter pluggable database all open;, and stop runs shutdown immediate. systemctl enable --now each unit.


9. Scheduled archive-log cleanup (cron)

Delete archived redo older than N days so the FRA doesn't fill. Put it in cron (works across reboots — ensure crond is enabled: dnf install -y cronie && systemctl enable --now crond). Example oracle crontab entry (delete > 2 days, daily 02:15):

15 2 * * * /usr/local/bin/arch_cleanup.sh

where arch_cleanup.sh runs, per DB:

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

10. Sample schemas (HR, CO, SH)

From github.com/oracle-samples/db-sample-schemas (dirs human_resources, customer_orders, sales_history). The interactive *_install.sql prompts don't take piped input reliably; drive the sub-scripts instead:

cd db-sample-schemas/human_resources
sqlplus -s system/YourStrongPassword@//localhost:1521/<service> <<'EOF'
SET DEFINE OFF
CREATE USER hr IDENTIFIED BY "YourStrongPassword" DEFAULT TABLESPACE USERS QUOTA UNLIMITED ON USERS;
GRANT CREATE SESSION,CREATE TABLE,CREATE VIEW,CREATE SEQUENCE,CREATE PROCEDURE,
      CREATE TRIGGER,CREATE TYPE,CREATE SYNONYM,CREATE MATERIALIZED VIEW TO hr;
ALTER SESSION SET CURRENT_SCHEMA=HR;
@hr_create.sql
@hr_populate.sql
@hr_code.sql
EOF

Repeat for customer_orders (CO) and sales_history (SH). Note SH.SALES fact data is loaded via external tables/data files — a separate step. On SE2, the SH schema may fail where it uses partitioning (an EE-only feature); HR and CO install fine.


11. Lightweight workload (python-oracledb)

pip install oracledb gives you thin mode — no Oracle client needed. A small bounded script that loops read queries + tiny DML on a scratch table against HR/CO makes a good, cron-friendly demo workload:

import os, time, random, oracledb
pw, dsn = os.environ["APP_PW"], os.environ.get("WL_DSN","localhost:1521/pdb1")
with oracledb.connect(user="hr", password=pw, dsn=dsn) as c:
    cur = c.cursor(); end = time.time()+20
    while time.time() < end:
        cur.execute("select department_id,count(*) from employees group by department_id"); cur.fetchall()
        i = random.randint(1,10**7)
        cur.execute("insert into wl_log values(:1,systimestamp,'load')",[i]); c.commit()
        cur.execute("delete from wl_log where id=:1",[i]); c.commit()
        time.sleep(0.2)

Schedule it in cron (e.g. every 15 minutes) so it keeps running after reboots.


12. Monitoring free storage (CloudWatch)

Disk space isn't a default EC2 metric — install the CloudWatch agent and publish disk_free. Minimal agent config:

{ "agent": { "metrics_collection_interval": 60 },
  "metrics": { "namespace": "CWAgent",
    "append_dimensions": { "InstanceId": "${aws:InstanceId}" },
    "aggregation_dimensions": [ ["InstanceId","path"] ],
    "metrics_collected": { "disk": { "measurement": ["free","used_percent"],
      "resources": ["/u01","/"] } } } }

Then create an alarm that fires when free space < 10 GB (disk_free in Bytes, threshold 10737418240, LessThanThreshold). Give the instance role CloudWatchAgentServerPolicy.


13. Troubleshooting (the AL2023 gotchas)

Instances crash after a login session ends — ORA-27157 (RemoveIPC)

Symptom: databases start fine (at boot or by hand) but crash minutes later — very often right after someone does su - oracle / SSHes in as oracle and then logs out. The alert log shows:

ORA-27157: OS post/wait facility removed
ORA-27300: OS system dependent operation:semop failed with status: 43
ORA-27301: OS failure message: Identifier removed
Instance terminated by DBW0

Cause: systemd-logind defaults to RemoveIPC=yes, which deletes a user's SysV IPC (semaphores, shared memory) when that user's last login session exits. The oracle instance's SGA/semaphores go with it, so the instance dies. It looks mysterious because the DB can run for a long time until the first oracle login/logout happens. Fix (mandatory):

sed -i -E 's/^[[:space:]]*#?[[:space:]]*RemoveIPC.*/RemoveIPC=no/I' /etc/systemd/logind.conf \
  || echo 'RemoveIPC=no' >> /etc/systemd/logind.conf
systemctl restart systemd-logind

Verify: open and close an oracle login session, then confirm the instance survives:

su - oracle -c 'true'; sleep 10; ps -ef | grep ora_pmon | grep -v grep

Set this before creating databases so you never get bitten.

libcrypt.so.1: cannot open shared object file

AL2023 ships glibc 2.34, which dropped libcrypt.so.1. The home's bundled perl needs it during -applyRU/relink. Install libnsl and libxcrypt-compat.

Installer prerequisite checks fail (unsupported OS/kernel)

19c isn't certified on AL2023. Export CV_ASSUME_DISTID=OL8 and pass -ignorePrereqFailure to runInstaller. Review the logged checks to make sure only the OS/kernel-version checks are being skipped.

"This is Grid Infrastructure, not the Database"

If your home unzips with gridSetup.sh and there's no top-level runInstaller, you grabbed the GI image. Use the Database home zip instead.

Sample-schema *_install.sql hangs or aborts when scripted

Its ACCEPT ... HIDE prompts don't consume piped stdin reliably (you'll see it stop right after the tablespace prompt). Drive the *_create/_populate/_code sub-scripts directly (create the user + grants yourself, SET DEFINE OFF) as shown in §10.

A crashed instance doesn't come back

Type=forking systemd units start the DB at boot but won't restart an instance that dies later. Add a small watchdog (cron or a systemd timer) that runs startup on any instance whose pmon is missing, if you need auto-recovery.


14. Quick verification checklist

# patches
$ORACLE_HOME/OPatch/opatch lspatches
# archivelog + FRA
sqlplus -s / as sysdba <<< "select log_mode from v\$database; show parameter db_recovery_file_dest"
# listener services
lsnrctl status | grep -i service
# autostart + IPC fix
grep RemoveIPC /etc/systemd/logind.conf
systemctl is-enabled oracle-listener oracle-db-<sid>
# free space alarm exists in CloudWatch, workload + cleanup in crontab
crontab -u oracle -l

Happy (test) databasing on AL2023!

No comments:

Post a Comment