Administration of a Oracle Standby Database

This post covers some basic tasks regarding Oracle Standby Databases that can pop up from time to time. The tasks are based on a already existing primary/standby database configuration with Oracle Data Guard Broker. The steps used to setup such a configuration are described here.

Check the health of the Data Guard configuration

Display the overall status

show configurations displays the status of the Data Guard configuration. It should return a Configuration Status: SUCCESS. Be aware that the command does not run any checks. It only outputs the result of the last check which by default could be 60 seconds ‘old’.

show configuration
[oracle@lin ~]$ dgmgrl sys/oracle
DGMGRL for Linux: Release 19.0.0.0.0 - Production on Thu Dec 26 12:58:57 2024
Version 19.25.0.0.0

Copyright (c) 1982, 2019, Oracle and/or its affiliates.  All rights reserved.

Welcome to DGMGRL, type "help" for information.
Connected to "orcl_lin"
Connected as SYSDBA.
DGMGRL> show configuration

Configuration - dgconf

  Protection Mode: MaxPerformance
  Members:
  orcl_lin  - Primary database
    orcl_lin2 - Physical standby database

Fast-Start Failover:  Disabled

Configuration Status:
SUCCESS   (status updated 45 seconds ago)

DGMGRL>

If you would like to get a ‘fresh’ status of the configuration (by actually running the checks) do this instead:

show configuration verbose
DGMGRL> show configuration verbose

Configuration - dgconf

  Protection Mode: MaxPerformance
  Members:
  orcl_lin  - Primary database
    orcl_lin2 - Physical standby database

  Properties:
    FastStartFailoverThreshold      = '30'
    OperationTimeout                = '30'
    TraceLevel                      = 'USER'
    FastStartFailoverLagLimit       = '30'
    CommunicationTimeout            = '180'
    ObserverReconnect               = '0'
    FastStartFailoverLagGraceTime   = '0'
    FastStartFailoverAutoReinstate  = 'TRUE'
    FastStartFailoverPmyShutdown    = 'TRUE'
    BystandersFollowRoleChange      = 'ALL'
    ObserverOverride                = 'FALSE'
    ExternalDestination1            = ''
    ExternalDestination2            = ''
    PrimaryLostWriteAction          = 'CONTINUE'
    ConfigurationWideServiceName    = 'orcl_CFG'
    FastStartFailoverLagType        = 'APPLY'

Fast-Start Failover:  Disabled

Configuration Status:
SUCCESS

DGMGRL>

Display the database status

show database orcl_lin
DGMGRL> show database orcl_lin

Database - orcl_lin

  Role:               PRIMARY
  Intended State:     TRANSPORT-ON
  Instance(s):
    orcl

Database Status:
SUCCESS

DGMGRL>

Display the transport / apply lag

show configuration lag
DGMGRL> show configuration lag

Configuration - dgconf

  Protection Mode: MaxPerformance
  Members:
  orcl_lin  - Primary database
    orcl_lin2 - Physical standby database
                Transport Lag:      0 seconds (computed 0 seconds ago)
                Apply Lag:          0 seconds (computed 0 seconds ago)

Fast-Start Failover:  Disabled

Configuration Status:
SUCCESS   (status updated 15 seconds ago)

DGMGRL>

Perform a database switchover

A switchover transitions the roles of the primary database and standby database. This means the primary will become a standby and the standby database will become the primary database. To perform a switchover both databases need to be able to communicate with each other.

dgmgrl sys/oracle << EOF
set time on
switchover to orcl_lin2
host sleep 30
show configuration verbose
EOF
[oracle@lin ~]$ dgmgrl sys/oracle
DGMGRL for Linux: Release 19.0.0.0.0 - Production on Thu Dec 26 13:33:47 2024
Version 19.25.0.0.0

Copyright (c) 1982, 2019, Oracle and/or its affiliates.  All rights reserved.

Welcome to DGMGRL, type "help" for information.
Connected to "orcl_lin"
Connected as SYSDBA.
DGMGRL> set time on
13:33:51 DGMGRL> show configuration

Configuration - dgconf

  Protection Mode: MaxPerformance
  Members:
  orcl_lin  - Primary database
    orcl_lin2 - Physical standby database

Fast-Start Failover:  Disabled

Configuration Status:
SUCCESS   (status updated 22 seconds ago)

13:33:52 DGMGRL> switchover to orcl_lin2
Performing switchover NOW, please wait...
Operation requires a connection to database "orcl_lin2"
Connecting ...
Connected to "orcl_lin2"
Connected as SYSDBA.
New primary database "orcl_lin2" is opening...
Oracle Clusterware is restarting database "orcl_lin" ...
Connected to an idle instance.
Connected to an idle instance.
Connected to an idle instance.
Connected to an idle instance.
Connected to an idle instance.
Connected to "orcl_lin"
Switchover succeeded, new primary is "orcl_lin2"
13:35:20 DGMGRL> show configuration verbose

Configuration - dgconf

  Protection Mode: MaxPerformance
  Members:
  orcl_lin2 - Primary database
    orcl_lin  - Physical standby database

  Properties:
    FastStartFailoverThreshold      = '30'
    OperationTimeout                = '30'
    TraceLevel                      = 'USER'
    FastStartFailoverLagLimit       = '30'
    CommunicationTimeout            = '180'
    ObserverReconnect               = '0'
    FastStartFailoverAutoReinstate  = 'TRUE'
    FastStartFailoverPmyShutdown    = 'TRUE'
    BystandersFollowRoleChange      = 'ALL'
    ObserverOverride                = 'FALSE'
    ExternalDestination1            = ''
    ExternalDestination2            = ''
    PrimaryLostWriteAction          = 'CONTINUE'
    ConfigurationWideServiceName    = 'orcl_CFG'

Fast-Start Failover:  Disabled

Configuration Status:
SUCCESS

13:35:52 DGMGRL>

Perform a database failover

A failover can be done when a primary database fails or has become unreachable. Make sure to connect to the current standby database:

dgmgrl sys/oracle << EOF
set time on
failover to orcl_lin2
show configuration verbose
EOF
[oracle@lin2 ~]$ dgmgrl sys/oracle
DGMGRL for Linux: Release 19.0.0.0.0 - Production on Thu Dec 26 13:49:43 2024
Version 19.3.0.0.0

Copyright (c) 1982, 2019, Oracle and/or its affiliates.  All rights reserved.

Welcome to DGMGRL, type "help" for information.
Connected to "orcl_lin2"
Connected as SYSDBA.
DGMGRL> set time on
13:49:47 DGMGRL> failover to orcl_lin2
Performing failover NOW, please wait...
Failover succeeded, new primary is "orcl_lin2"
13:51:03 DGMGRL> show configuration verbose

Configuration - dgconf

  Protection Mode: MaxPerformance
  Members:
  orcl_lin2 - Primary database
    orcl_lin  - Physical standby database (disabled)
      ORA-16661: the standby database needs to be reinstated

  Properties:
    FastStartFailoverThreshold      = '30'
    OperationTimeout                = '30'
    TraceLevel                      = 'USER'
    FastStartFailoverLagLimit       = '30'
    CommunicationTimeout            = '180'
    ObserverReconnect               = '0'
    FastStartFailoverAutoReinstate  = 'TRUE'
    FastStartFailoverPmyShutdown    = 'TRUE'
    BystandersFollowRoleChange      = 'ALL'
    ObserverOverride                = 'FALSE'
    ExternalDestination1            = ''
    ExternalDestination2            = ''
    PrimaryLostWriteAction          = 'CONTINUE'
    ConfigurationWideServiceName    = 'orcl_CFG'

Fast-Start Failover:  Disabled

Configuration Status:
SUCCESS

13:51:14 DGMGRL>

As we can see after the failover the old primary database is disabled and needs to be reinstated to be part of the Data Guard configuration again.

Reinstate a database after a failover

To reinstate a database after a failover event follow these steps.
on the failed database run:

sqlplus / as sysdba << EOF
shutdown immediate
startup mount
EOF

on the current primary database run:

dgmgrl sys/oracle << EOF
set time on
show configuration verbose
reinstate database orcl_lin
host sleep 20
show configuration verbose
EOF

Configure the DB client to connect to the current primary node

This is a simple example for a client tnsnames.ora that clients can use to connect to the current primary node of the Data Guard setup. First we create a new pdb:

sqlplus -S / as sysdba << EOF
create pluggable database pdb1 admin user pdb1admin identified by oracle;
alter pluggable database pdb1 open;
alter pluggable database pdb1 save state;
alter session set container=pdb1;
grant select on v_\$instance to pdb1admin;
EOF

Now we have a service that will be active on the current primary node:

lsnrctl services
...
Service "pdb1.fritz.box" has 1 instance(s).
  Instance "orcl", status READY, has 1 handler(s) for this service...
    Handler(s):
      "DEDICATED" established:4164 refused:0 state:ready
         LOCAL SERVER
The command completed successfully
[oracle@lin ~]$
...

Next we add the alias pdb1 to the tnsnames.ora of a client (can be on the server machine or on a separate client machine):

echo "
pdb1=
  (DESCRIPTION=(ADDRESS_LIST=
    (ADDRESS=(PROTOCOL=TCP)(HOST=lin.fritz.box)(PORT=1521))
    (ADDRESS=(PROTOCOL=TCP)(HOST=lin2.fritz.box)(PORT=1521)
  ))
  (CONNECT_DATA=(SERVICE_NAME=pdb1.fritz.box)))  
" >> /u01/app/oracle/product/19.0.0/dbhome_1/network/admin/tnsnames.ora

Using this alias we connect to the first node where the service pdb1.fritz.box is available:

echo "select host_name from v\$instance;"|sqlplus -S pdb1admin/oracle@pdb1
HOST_NAME
----------------------------------------------------------------
lin.fritz.box

After performing a switchover or failover we can see that we are connecting to the new primary node now. In case of a failover make sure that the previous primary db is really unavailable (e.g. shutdown).

echo "select host_name from v\$instance;"|sqlplus -S pdb1admin/oracle@pdb1
HOST_NAME
----------------------------------------------------------------
lin2.fritz.box

Delete old patches from an ORACLE_HOME

It is considered good practice to delete unneeded patches from an ORACLE_HOME in order to save space and reduce the time to install new patches. I made some tests to see the effects of that. More information about the removal of inactive patches can be found in Oracle Doc ID 2942102.1 .

On a Linux System I did an software only installation of Oracle EE 19c (19.3). After that Oracle Release Updates (RU) 19.21 up to 19.25 have been installed. You can see the size of the ORACLE_HOME and the time it took to install the patches in the following table:

OH
Version
OH size
(GB)
patch duration
(min)
19.37
19.20910
19.21129
19.221411
19.231717
19.241921
19.252227

The time it took to install a Release Update increased from 10 minutes to 27 minutes! Also the size of the ORACLE_HOME increased around 3 times the size of the initial installation (19.3).

Now let’s take a look at which patches are considered Active and Inactive:

[oracle@lin2]$ $ORACLE_HOME/OPatch/opatch util listorderedinactivepatches
Oracle Interim Patch Installer version 12.2.0.1.43
Copyright (c) 2024, Oracle Corporation.  All rights reserved.


Oracle Home       : /u01/app/oracle/product/19.0.0/dbhome_1
Central Inventory : /u01/app/oraInventory
   from           : /u01/app/oracle/product/19.0.0/dbhome_1/oraInst.loc
OPatch version    : 12.2.0.1.43
OUI version       : 12.2.0.7.0
Log file location : /u01/app/oracle/product/19.0.0/dbhome_1/cfgtoollogs/opatch/opatch2024-12-25_09-49-57AM_1.log

Invoking utility "listorderedinactivepatches"
List Inactive patches option provided

The oracle home has the following inactive patch(es) and their respective overlay patches:

The number of RU chains is  1

***** There are 5 inactive RU patches in chain 1
-Inactive RU/BP 29517242:Database Release Update : 19.3.0.0.190416 (29517242), installed on: Thu Apr 18 09:21:17 CEST 2019, with no overlays
-Inactive RU/BP 35320081:Database Release Update : 19.20.0.0.230718 (35320081), installed on: Tue Dec 24 22:47:56 CET 2024, with no overlays
-Inactive RU/BP 35643107:Database Release Update : 19.21.0.0.231017 (35643107), installed on: Tue Dec 24 23:04:57 CET 2024, with no overlays
-Inactive RU/BP 35943157:Database Release Update : 19.22.0.0.240116 (35943157), installed on: Tue Dec 24 23:21:13 CET 2024, with no overlays
-Inactive RU/BP 36233263:Database Release Update : 19.23.0.0.240416 (36233263), installed on: Tue Dec 24 23:46:57 CET 2024, with no overlays
-Active RU/BP 36582781:Database Release Update : 19.24.0.0.240716 (36582781), installed on: Wed Dec 25 00:21:56 CET 2024, with no overlays

OPatch succeeded.
[oracle@lin2]$

Now we delete all inactive patches. If we had a running database we didn’t have to stop it:

[oracle@lin2]$ $ORACLE_HOME/OPatch/opatch util deleteinactivepatches -silent
Oracle Interim Patch Installer version 12.2.0.1.43
Copyright (c) 2024, Oracle Corporation.  All rights reserved.


Oracle Home       : /u01/app/oracle/product/19.0.0/dbhome_1
Central Inventory : /u01/app/oraInventory
   from           : /u01/app/oracle/product/19.0.0/dbhome_1/oraInst.loc
OPatch version    : 12.2.0.1.43
OUI version       : 12.2.0.7.0
Log file location : /u01/app/oracle/product/19.0.0/dbhome_1/cfgtoollogs/opatch/opatch2024-12-25_10-11-35AM_1.log

Invoking utility "deleteinactivepatches"
Inactive Patches Cleanup option provided
Delete Inactive Patches .......

***** There are 5 inactive RU patches in chain 1

***** 4 inactive patches will be deleted
-To be deleted inactive RU/BP 29517242:Database Release Update : 19.3.0.0.190416 (29517242), installed on: Thu Apr 18 09:21:17 CEST 2019, with no overlays
-To be deleted inactive RU/BP 35320081:Database Release Update : 19.20.0.0.230718 (35320081), installed on: Tue Dec 24 22:47:56 CET 2024, with no overlays
-To be deleted inactive RU/BP 35643107:Database Release Update : 19.21.0.0.231017 (35643107), installed on: Tue Dec 24 23:04:57 CET 2024, with no overlays
-To be deleted inactive RU/BP 35943157:Database Release Update : 19.22.0.0.240116 (35943157), installed on: Tue Dec 24 23:21:13 CET 2024, with no overlays
-To be retained inactive RU/BP 36233263:Database Release Update : 19.23.0.0.240416 (36233263), installed on: Tue Dec 24 23:46:57 CET 2024, with no overlays
-Active RU/BP 36582781:Database Release Update : 19.24.0.0.240716 (36582781), installed on: Wed Dec 25 00:21:56 CET 2024, with no overlays

Do you want to proceed? [y|n]
Y (auto-answered by -silent)
User Responded with: Y
Deleted RU/BP patch: 29517242
Deleted RU/BP patch: 35320081
Deleted RU/BP patch: 35643107
Deleted RU/BP patch: 35943157

OPatch succeeded.
[oracle@lin2]$

The removal of the inactive patches took 20 minutes and the size of the ORACLE_HOME shrunk down to 14GB. The final test was to find out how long it takes to install RU 19.25 after the inactive patches have been deleted. So I did a rollback of 19.25 and reinstalled 19.25 on top of 19.24 in 11 minutes. Pretty impressive!

Setup a Oracle Physical Standby Database

The following steps can be used to setup a Oracle Physical Standby Database managed by Data Guard Broker. The software versions used are: Oracle 19.25 + ASM (RAC One Node) and RHEL 8.1. On the first node a database (orcl) is already running, on the second node only the database software is installed and an ASM instance with configured diskgroups (DATA and FRA) is running.

Prepare the primary node

# prepare and source environment files containing ORACLE_HOME and ORACLE_SID
# ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1 ORACLE_SID=orcl
source ~/ora19.env
sqlplus / as sysdba << EOF
-- enable archivelog mode on primary
shutdown immediate;
startup mount;
alter database archivelog;
alter database open;
-- enable force logging
alter database force logging;
-- enable flashback database
alter database flashback on;
EOF

sqlplus -S / as sysdba << EOF
set head off
-- create the standby redo logfiles (same size as the normal redo logfiles, 1 group more than the number of the normal redo log groups)
spool /tmp/crsblf1.sql
select 'alter database add standby logfile thread '||thread#||' group '||(group#+10)||' size '||bytes||';' from v\$log;
spool off
@/tmp/crsblf1.sql
spool /tmp/crsblf2.sql
select 'alter database add standby logfile thread '||thread#||' group '||(group#+1)||' size '||bytes||';' from v\$standby_log where group#=(select max(group#) from v\$standby_log);
spool off
@/tmp/crsblf2.sql
-- enable automatic standby file management
alter system set standby_file_management=auto;
EOF

# set the db_unique_name of the primary db to a unique value (s.a. Oracle Doc 1604421.1)
# show current config
srvctl config database -d orcl
sqlplus -S / as sysdba << EOF
set head off lines 200
spool /tmp/s1.sql
select 'host echo "spfile='''||value||'''" >> $ORACLE_HOME/dbs/initorcl.ora' from v\$parameter where name='spfile';
select 'startup' from dual;
select 'alter system set db_unique_name='''||lower(name)||'_'||regexp_substr(host_name, '[^.]*')||''' scope=spfile;' from v\$instance, v\$database;
select 'alter user sys identified by oracle;' from dual;
select 'shutdown immediate' from dual;
spool off
EOF

srvctl stop database -d orcl
srvctl remove database -noprompt -d orcl
echo @/tmp/s1 | sqlplus / as sysdba
srvctl add database -db orcl_lin -dbname orcl -instance orcl -oraclehome $ORACLE_HOME -spfile `cat $ORACLE_HOME/dbs/initorcl.ora|awk -F= {'print $2'}|sed s/\'//g`
srvctl modify database -d orcl_lin -a "DATA,FRA"
srvctl start database -d orcl_lin

Setup the network configuration

# add the following lines to the primary and standby tnsnames.ora:
echo "
orcl_lin=
  (DESCRIPTION=
    (ADDRESS_LIST=
      (ADDRESS=(PROTOCOL=TCP)(HOST=lin)(PORT=1521))
    )
    (CONNECT_DATA=(SID=orcl)
    )
  )

orcl_lin2=
  (DESCRIPTION=
    (ADDRESS_LIST=
      (ADDRESS=(PROTOCOL=TCP)(HOST=lin2)(PORT=1521))
    )
    (CONNECT_DATA=(SID=orcl)
    )
  )
" >> /u01/app/oracle/product/19.0.0/dbhome_1/network/admin/tnsnames.ora
# add the following entries to the standby side
echo "
LISTENER_ORCL =
  (ADDRESS = (PROTOCOL = TCP)(HOST = lin2.fritz.box)(PORT = 1521))

" >> /u01/app/oracle/product/19.0.0/dbhome_1/network/admin/tnsnames.ora
echo "
NAMES.DIRECTORY_PATH= (TNSNAMES, EZCONNECT)
" >> /u01/app/oracle/product/19.0.0/dbhome_1/network/admin/sqlnet.ora
# adjust the listener.ora on the primary side:
vi /u01/app/19.0.0/grid/network/admin/listener.ora
LISTENER=
  (DESCRIPTION_LIST=
    (DESCRIPTION=
      (ADDRESS=(PROTOCOL=TCP)(HOST=lin)(PORT=1521))
      (ADDRESS=(PROTOCOL=IPC)(KEY=EXTPROC1521))
    )
  )

SID_LIST_LISTENER=
  (SID_LIST=
    (SID_DESC=
      (GLOBAL_DBNAME=orcl_lin_DGMGRL.fritz.box)  # set to db_unique_name_DGMGRL.db_domain
      (ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1)
      (SID_NAME=orcl)
      (ENVS='TNS_ADMIN=/u01/app/19.0.0/grid/network/admin')
    )
  )
# adjust the listener.ora on the standby side:
vi /u01/app/19.0.0/grid/network/admin/listener.ora
LISTENER=
  (DESCRIPTION_LIST=
    (DESCRIPTION=
      (ADDRESS=(PROTOCOL=TCP)(HOST=lin2)(PORT=1521))
      (ADDRESS=(PROTOCOL=IPC)(KEY=EXTPROC1521))
    )
  )

SID_LIST_LISTENER=
  (SID_LIST=
    (SID_DESC=
      (GLOBAL_DBNAME=orcl_lin2_DGMGRL.fritz.box)  # set to db_unique_name_DGMGRL.db_domain
      (ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1)
      (SID_NAME=orcl)
      (ENVS='TNS_ADMIN=/u01/app/19.0.0/grid/network/admin')
    )
  )
# restart the listeners on both nodes:
srvctl stop listener && srvctl start listener

Setup the standby node and duplicate the database

# prepare and source environment files containing ORACLE_HOME and ORACLE_SID
# ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1 ORACLE_SID=orcl
source ~/ora19.env
# create directories
mkdir -p /u01/app/oracle/admin/orcl/adump
# create password file (same sys pw as primary side)
orapwd file=$ORACLE_HOME/dbs/orapworcl password=oracle format=12 entries=10
# create a temporary pfile
echo "*.db_name='orcl'" > /tmp/pfile
# start the standby db in nomount mode
echo 'startup nomount pfile=/tmp/pfile' | sqlplus / as sysdba
# duplicate the standby database from the primary database via rman
echo "duplicate target database for standby from active database dorecover spfile set db_unique_name='orcl_lin2' nofilenamecheck;" | rman target sys/oracle@orcl_lin auxiliary sys/oracle@orcl_lin2
# create a spfile in ASM
echo "create spfile='+DATA/ORCL_LIN2/PARAMETERFILE/spfileorcl.ora' from memory;" | sqlplus / as sysdba
echo "spfile='+DATA/ORCL_LIN2/PARAMETERFILE/spfileorcl.ora'" > $ORACLE_HOME/dbs/initorcl.ora
rm $ORACLE_HOME/dbs/spfileorcl.ora
# enable flashback logging and shutdown the database
sqlplus / as sysdba << EOF
alter database archivelog;
alter database flashback on;
shutdown immediate
EOF
# add the db to the Oracle RAC config
srvctl add database -db orcl_lin2 -dbname orcl -instance orcl -oraclehome $ORACLE_HOME -spfile `cat $ORACLE_HOME/dbs/initorcl.ora|awk -F= {'print $2'}|sed s/\'//g`
srvctl modify database -d orcl_lin2 -a "DATA,FRA"
srvctl modify database -db orcl_lin2 -startoption mount
srvctl start database -db orcl_lin2

Create and enable the Data Guard Broker configuration

# start the Data Guard Broker on the primary and on the standby node
echo "alter system set dg_broker_start=true;" | sqlplus / as sysdba
# create and enable the dg configuration on the primary node
dgmgrl sys/oracle@orcl_lin << EOF
create configuration dgconf as primary database is orcl_lin connect identifier is orcl_lin;
add database orcl_lin2 as connect identifier is orcl_lin2;
rem connect identifier is a tnsnames alias
enable configuration
enable database orcl_lin2
host sleep 20
show configuration verbose
quit
EOF

Setup tcps / ssl encrypted database connections using orapki and wallets

This post shows how to setup and use tcps / ssl encrypted database connections. Self signed certificates created by orapki and stored in a Oracle Wallet will be used on the database server and on the client machine. The database server is running Oracle 19.25 on RHEL 8.1. The client machine is running Windows 10.

1. Create the server wallet and a self-signed (root) certificate. Run as the Oracle user on the database server.

# Create a wallet
mkdir ~/wallet && cd ~/wallet
orapki wallet create -wallet `pwd` -pwd test123# -auto_login
# Create a self signed certificate (this creates a CA root certificate and creates a user certificate that is signed by it)
orapki wallet add -wallet `pwd` -dn "CN=`hostname`" -keysize 4096 -self_signed -validity 20 -pwd test123#
# Display the contents of the wallet
orapki wallet display -wallet `pwd`

2. Adjust sqlnet.ora and listener.ora on the database server and start the ssl listener

sqlnet.ora:
WALLET_LOCATION=(SOURCE=(METHOD=FILE)(METHOD_DATA=(DIRECTORY=/home/oracle/wallet)))
SQLNET.AUTHENTICATION_SERVICES=(BEQ,TCPS)
SSL_CLIENT_AUTHENTICATION=FALSE

listener.ora:
SSL_CLIENT_AUTHENTICATION = FALSE
WALLET_LOCATION=(SOURCE=(METHOD = FILE)(METHOD_DATA=(DIRECTORY=/home/oracle/wallet)))
LISTENER_SSL=
  (DESCRIPTION_LIST=
    (DESCRIPTION=
      (ADDRESS=(PROTOCOL=TCPS)(HOST=lin2.fritz.box)(PORT=1522))))
SID_LIST_LISTENER_SSL=
  (SID_LIST=
    (SID_DESC=
      (GLOBAL_DBNAME=orcl.fritz.box)
      (ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1)
      (SID_NAME=orcl)))

lsnrctl start listener_ssl

# Adjust the database so that it registers its services with the listener
alter system set local_listener='LISTENER_ORCL,(ADDRESS=(PROTOCOL=TCPS)(HOST=lin2.fritz.box)(PORT=1522))';  (LISTENER_ORCL already existed)
alter system register; (registers the services to the new listener. Can be checked with lsnrctl services listener_ssl)

3. Create a wallet which can be copied and used on a client. Run as the Oracle user on the database server.

mkdir ~/clientwallet && cd ~/clientwallet
orapki wallet create -wallet `pwd` -pwd clienttest123# -auto_login
# Export the server certificate and import in the client wallet
orapki wallet export -wallet ~/wallet -dn "CN=`hostname`" -cert server.cer
orapki wallet add -wallet `pwd` -dn "CN=`hostname`" -trusted_cert -cert server.cer -pwd clienttest123#
# Display the contents of the wallet
orapki wallet display -wallet `pwd`

4. Preparation of the client sqlnet.ora and listener.ora on the database server.

cd ~/clientwallet
sqlnet.ora:
WALLET_LOCATION=(SOURCE=(METHOD=FILE)(METHOD_DATA=(DIRECTORY="C:\sw\cwallet")))
SSL_CLIENT_AUTHENTICATION=FALSE
SSL_SERVER_DN_MATCH=ON

tnsnames.ora:
orcl_ssl=
  (DESCRIPTION=
    (ADDRESS_LIST=
      (ADDRESS=(PROTOCOL=TCPS)(HOST=lin2.fritz.box)(PORT=1522)))
    (CONNECT_DATA=
      (SERVICE_NAME=orcl.fritz.box)
      (SERVER=dedicated))
      (SECURITY=
        (SSL_SERVER_CERT_DN="CN=lin2.fritz.box")))

5. Implement the client wallet in the SQL Developer + use the client wallet with SQL*Plus

cd ~/clientwallet && zip ../cwallet.zip *
Copy cwallet.zip to the client machine
Create a SQL Developer connection of type 'Cloud Wallet' and select cwallet.zip. 
Select the tns alias for the connection
test the tcps connection with: select sys_context('USERENV', 'NETWORK_PROTOCOL') from dual;    (must be tcps)

Test the connection with sqlplus from the client machine:
 mkdir c:\sw\cwallet && cd c:\sw\cwallet && tar -xf c:\sw\cwallet.zip
 set TNS_ADMIN=C:\sw\cwallet
 rem test the tcps connection with:
 tnsping orcl_ssl
 sqlplus sys@orcl_ssl as sysdba  or  sqlplus sys@tcps://lin2.fritz.box:1522/orcl.fritz.box as sysdba
 select sys_context('USERENV', 'NETWORK_PROTOCOL') from dual;  (must be tcps)