Oracle Database Client 23ai on Oracle Linux 9
Videos
Overview: Oracle Database Client 23ai on Oracle Linux 9 on Azure: overview (watch on YouTube)
Launch and log in: Oracle Database Client 23ai on Oracle Linux 9 on Azure: launch and log in (watch on YouTube)
Deep Dive: Oracle Database Client 23ai on Oracle Linux 9 on Azure: deep dive (watch on YouTube)
Overview
Oracle Database Client 23ai (23.26) on Oracle Linux 9 is SecureAnyCloud's virtual machine image on the Azure Marketplace. The image carries the full Oracle Database Client 23ai, Administrator install. A virtual machine can reach Oracle databases elsewhere on your network with SQL*Plus, SQL*Loader, Data Pump, Recovery Manager and the Oracle Net utilities, and your own applications can link the Oracle Call Interface (OCI) libraries or the JDBC drivers. No database server runs on the virtual machine.
Every command in this guide was checked on a virtual machine deployed from the public Marketplace image (sac1647283583153:oracle-db-23ai-client-oel9:default:latest, image version 1.0.20260509), and run as azureuser or as the oracle user.
What you get
- Oracle Database Client 23ai, release 23.26.1, Administrator install, in
/u01/app/oracle/product/23.0.0/client_1 - A dedicated
oracleOS user that owns the client, with an environment script,/home/oracle/setEnv.sh - A system-wide profile hook,
/etc/profile.d/oracle-client.sh, so every login user (includingazureuser) has the client tools on itsPATH - SQL*Plus, SQL*Loader (
sqlldr), Data Pump (expdp,impdp), the originalexpandimp, Recovery Manager (rman),tnsping,adrci,tkprof, and the Oracle Wallet toolsmkstoreandorapki - The Oracle Call Interface libraries (
libclntsh.so,libocci.so) and the JDBC driversojdbc8.jar,ojdbc11.jarandojdbc17.jar - A preseeded
sqlnet.oraand atnsnames.oraholding a commented example entry - The bundled Oracle JDK replaced by the system OpenJDK 8 package, so Java updates arrive with
dnf - Oracle Linux 9 with the Azure Linux Agent (
waagent), Gen2 (UEFI) boot and LVM
Platform: Microsoft Azure (Gen2, Hyper-V)
Default user: azureuser
Oracle Home: /u01/app/oracle/product/23.0.0/client_1
TNS Admin: /u01/app/oracle/product/23.0.0/client_1/network/admin
Prerequisites
Before you deploy this image, make sure you have:
- An active Microsoft Azure subscription
- Access to the Azure Portal or the Azure CLI
- An SSH key pair for Linux virtual machine access
- The host name, port and service name of the Oracle Database you want to reach
- A network path from the new virtual machine to that database's listener (usually TCP 1521 outbound)
Choose a virtual machine size that suits your workload.
Deploy the Virtual Machine
Option A: Azure Portal
- Open the Oracle Database Client 23ai (23.26) on Oracle Linux 9 listing on the Azure Marketplace and choose Get it now, then Create.
- Configure the basics:
- Subscription: Select your Azure subscription.
- Resource Group: Create a new group or select an existing one.
- Virtual Machine Name: Enter a name for your VM.
- Region: Select your preferred Azure region.
- Size: Select a size for your workload.
- Under Administrator Account, select SSH public key and enter your key.
- Under Inbound Port Rules, allow SSH (port 22), ideally from your own network only.
- Click Review + Create, then click Create.
Option B: Azure CLI
The image carries a Marketplace purchase plan. Accept its terms once per subscription, then create the virtual machine. Replace <your-vm-size> with the size you chose:
az vm image terms accept --urn sac1647283583153:oracle-db-23ai-client-oel9:default:latest
az vm create \
--resource-group myResourceGroup \
--name my-oracle-client-vm \
--image sac1647283583153:oracle-db-23ai-client-oel9:default:latest \
--size <your-vm-size> \
--admin-username azureuser \
--generate-ssh-keys \
--public-ip-sku StandardThe Oracle Client makes only outbound connections, so the virtual machine needs no inbound port other than SSH. Do not open port 1521 inbound on it. Instead, check that its network security group, route table and any peering or VPN let it reach your database's listener.
Connect via SSH
On your own workstation, find the public IP, then connect:
az vm show --resource-group myResourceGroup --name my-oracle-client-vm --show-details --query publicIps -o tsv ssh azureuser@<vm-ip>There is no password to enter. If SSH asks for one, the key your client is offering is not the key you supplied at create time.
The first-boot update
On its first boot, the image brings its operating system packages up to date. Root's crontab runs /stage/scripts/initial_boot_update.sh (dnf update -y) once, in the background, and logs to /stage/scripts/initial_boot_update.log.
On the virtual machine we checked, the update applied 841 package changes and took about twenty minutes. Check on its progress with:
tail -n 3 /stage/scripts/initial_boot_update.logThe update has finished when the log ends with:
=== dnf update exit code: 0 === === Initial boot OS update finished ... ===After a clean run the script removes itself and its crontab entry, so it never runs again.
New SSH connections can be refused while the update runs. The update upgrades OpenSSH. Until the SSH service restarts at the end of the update, new connections fail with kex_exchange_identification: read: Connection reset by peer. On the virtual machine we checked, this lasted about eleven minutes, starting roughly five minutes after boot.
A session that is already open stays connected. If you see that message on a new virtual machine, wait a few minutes and connect again. Do not run dnf yourself until the update has finished, because the update holds the package lock.
Check the Client
The profile hook puts the client on every user's PATH, so azureuser can check the release straight away:
sqlplus -VExpected output:
SQL*Plus: Release 23.26.1.0.0 - Production Version 23.26.1.0.0The oracle user owns the Oracle home and its Oracle Net configuration. Switch to that user and load its environment:
sudo su - oracle . ./setEnv.sh env | grep -E "^(ORACLE_|TNS_)" | sortExpected output:
ORACLE_BASE=/u01/app/oracle ORACLE_HOME=/u01/app/oracle/product/23.0.0/client_1 TNS_ADMIN=/u01/app/oracle/product/23.0.0/client_1/network/adminsetEnv.sh also sets PATH, LD_LIBRARY_PATH and NLS_LANG=AMERICAN_AMERICA.AL32UTF8.
Explore the Client Tooling
List the main command-line tools in the Oracle home:
ls $ORACLE_HOME/bin | grep -xE "sqlplus|sqlldr|tnsping|expdp|impdp|exp|imp|rman|adrci|tkprof|mkstore|orapki"Expected output:
adrci exp expdp imp impdp mkstore orapki rman sqlldr sqlplus tkprof tnspingEach tool reports the same release. For example, sqlldr with no arguments and expdp help=y both print their banner first:
SQL*Loader: Release 23.26.1.0.0 - Production on ... Export: Release 23.26.1.0.0 - Production on ...The Oracle home also carries SQLcl ($ORACLE_HOME/bin/sql). SQLcl needs Java 11 or later, and the image's Java is OpenJDK 8, so sql stops with Error: SQLcl requires Java 11 and above to run. Use SQL*Plus instead.
Configure tnsnames.ora
Oracle Net reads its configuration from $TNS_ADMIN. The image's sqlnet.ora looks up names in tnsnames.ora first, then accepts Easy Connect strings:
cat $TNS_ADMIN/sqlnet.oraExpected output:
NAMES.DIRECTORY_PATH = (TNSNAMES, EZCONNECT) SQLNET.EXPIRE_TIME = 10 SQLNET.INBOUND_CONNECT_TIMEOUT = 60 SQLNET.OUTBOUND_CONNECT_TIMEOUT = 60tnsnames.ora holds only a commented example. As the oracle user, add an entry for your database. Replace YOUR_DB_HOSTNAME, the port if it is not 1521, and YOUR_SERVICE_NAME:
cat >> "$TNS_ADMIN/tnsnames.ora" << 'TNS' MY_DATABASE = (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = YOUR_DB_HOSTNAME)(PORT = 1521)) ) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = YOUR_SERVICE_NAME) ) ) TNSThen test the alias with tnsping:
tnsping MY_DATABASEYou can expect one of these results:
OK (n msec): the host resolves and the listener answered. This is what you want to see.TNS-12545: Connect failed because target host or object does not exist: the host name does not resolve. The unedited example valueYOUR_DB_HOSTNAMEgives this result. Oracle Net itself is working, so fix the host.TNS-12541orTNS-00511: No listener: the host resolves but nothing listens on that port. Check the listener and the port.TNS-03505: Failed to resolve name: the alias is not intnsnames.ora. Check its spelling.
Connect with SQL*Plus
Connect with the alias you added. SQL*Plus prompts for the password, so the password never appears in your shell history:
sqlplus your_user@MY_DATABASEOr skip tnsnames.ora and use an Easy Connect string in the form //host:port/service_name:
sqlplus your_user@//YOUR_DB_HOSTNAME:1521/YOUR_SERVICE_NAMEWhen the connection succeeds, SQL*Plus prints Connected to: and the database's banner, then the SQL> prompt. Type EXIT to leave.
If the host name in an Easy Connect string does not resolve, SQL*Plus says so plainly:
ERROR: ORA-12262: Cannot connect to database. Could not resolve hostname YOUR_DB_HOSTNAME in Easy Connect connection string //YOUR_DB_HOSTNAME:1521/YOUR_SERVICE_NAMEData Pump, SQL*Loader and Recovery Manager take the same connect identifiers, for example expdp your_user@MY_DATABASE schemas=your_schema directory=DATA_PUMP_DIR dumpfile=your_schema.dmp. Data Pump writes its dump files on the database server, in the directory object that you name.
Drivers for Your Applications
Applications on the virtual machine can use the client's libraries directly. List the drivers and libraries:
ls $ORACLE_HOME/jdbc/lib/ojdbc*.jar
ls $ORACLE_HOME/lib/libclntsh.so* $ORACLE_HOME/lib/libocci.so- JDBC:
ojdbc8.jar(Java 8),ojdbc11.jar(Java 11 and later) andojdbc17.jar(Java 17 and later) - OCI and OCCI:
libclntsh.soandlibocci.so, withlibclntsh.so.23.1and compatibility links for earlier client releases.LD_LIBRARY_PATHfromsetEnv.shpoints at them.
The Oracle home's jdk directory is a link to the system OpenJDK 8, which dnf keeps up to date:
readlink -f $ORACLE_HOME/jdk $ORACLE_HOME/jdk/bin/java -versionNetworking and Security
Nothing on the virtual machine listens for database traffic. Check the listeners and the host firewall:
sudo ss -tlnp sudo firewall-cmd --list-allsshd listens on port 22 and rpcbind listens on port 111. firewalld is active, and its public zone allows only the ssh, cockpit and dhcpv6-client services, so port 111 is not reachable from the network. The Cockpit packages are installed, but the cockpit.socket unit is not active, so nothing answers on its port.
SSH accepts keys only. Confirm it:
sudo sshd -T | grep -E "^(pubkeyauthentication|passwordauthentication) "pubkeyauthentication yes passwordauthentication noSELinux runs in permissive mode (getenforce prints Permissive).
To keep credentials out of scripts, store them in an Oracle Wallet with mkstore and orapki. Both tools are in $ORACLE_HOME/bin.
Server Components
Component Version Notes
Oracle Database Client 23ai (Administrator install) 23.26.1.0.0 /u01/app/oracle/product/23.0.0/client_1
Oracle Linux 9 Built on 9.7. The first-boot update moved our test VM to 9.8
OpenJDK 1.8.0 System package, linked as $ORACLE_HOME/jdk
Azure Linux Agent waagent, active
chrony chronyd, active
firewalld Active, public zone
Filesystem Layout
The OS disk uses LVM. The Oracle home sits on the root filesystem under /u01. There is no separate /u01 mount.
Mount Point Size Description
/ 32 GB Root filesystem (XFS on LVM, rootvg-rootlv), holds /u01
/var/crash 15 GB Kernel crash dumps (XFS on LVM, rootvg-crashlv)
/boot 2 GB Kernel files (XFS)
/boot/efi 100 MB UEFI system partition
The Azure temporary disk is not mounted on this image (see Troubleshooting).
Key Oracle directories:
Directory Purpose
/u01/app/oracle Oracle base
/u01/app/oracle/product/23.0.0/client_1 Oracle home
/u01/app/oracle/product/23.0.0/client_1/bin Client tools
/u01/app/oracle/product/23.0.0/client_1/lib OCI and OCCI libraries
/u01/app/oracle/product/23.0.0/client_1/jdbc/lib JDBC drivers
/u01/app/oracle/product/23.0.0/client_1/network/admin tnsnames.ora and sqlnet.ora
Scripts and Log Files
Path Purpose
/home/oracle/setEnv.sh Sets the Oracle environment for the oracle user
/etc/profile.d/oracle-client.sh Puts the client on every login user's PATH
/stage/scripts/initial_boot_update.sh The one-time first-boot update. It deletes itself after a clean run
/stage/scripts/initial_boot_update.log Output of the first-boot update
/var/log/waagent.log Azure Linux Agent log
On Startup
The Oracle Client is a set of tools and libraries, not a service, so there is nothing to start or stop. On the first boot only, root's crontab runs /stage/scripts/initial_boot_update.sh (dnf update -y), as described in Step 2. After a clean run, the script removes itself and its crontab entry. To stop it before it runs, remove the @reboot /stage/scripts/initial_boot_update.sh line with sudo crontab -e.
Troubleshooting
SSH says "Connection reset by peer" on a new virtual machine
The first-boot update is upgrading OpenSSH (Step 2). Wait a few minutes and connect again. The update logs its progress to /stage/scripts/initial_boot_update.log.
sqlplus: command not found
- Log in again, or run
. /etc/profile.d/oracle-client.sh, so the profile hook sets yourPATH. - As the
oracleuser, load the environment:. ~/setEnv.sh - Check that the binary exists:
ls /u01/app/oracle/product/23.0.0/client_1/bin/sqlplus
ORA-12154 or TNS-03505: the name cannot be resolved
- Check that the alias is in
tnsnames.ora:cat $TNS_ADMIN/tnsnames.ora - Check that
TNS_ADMINpoints at thenetwork/admindirectory:echo $TNS_ADMIN - Test the alias:
tnsping MY_DATABASE
ORA-12170: Connect timeout
- Check that the database host and port are reachable from the virtual machine.
- Check that the database side's network security group allows inbound traffic from this virtual machine.
- Check route tables, peering or VPN between this virtual machine and the database network.
ORA-12541 or TNS-00511: No listener
The host resolves but nothing listens on that port. Check the database listener and the port in your entry.
systemctl reports "degraded"
systemctl --failed lists mnt.mount and temp-disk-dataloss-warning.service. On this image cloud-init is masked (the Azure Linux Agent provisions the virtual machine), so the Azure temporary disk is never formatted for Linux and its /mnt entry fails to mount. Nothing on the image uses the temporary disk.
For the same reason, sudo cloud-init status prints status: not started. Do not run cloud-init status --wait, which waits forever.
The clock shows US Eastern time
The image's time zone is America/New_York. To use UTC, run sudo timedatectl set-timezone UTC.
Security Recommendations
- Restrict outbound access. Allow outbound TCP 1521 (or your listener port) only to the databases this virtual machine needs to reach.
- Keep SSH narrow. Allow port 22 only from your own network.
- Protect tnsnames.ora. It names the database hosts and services on your private network.
- Use a wallet for credentials. Keep passwords out of scripts and shell history with Oracle Wallet (
mkstore,orapki). - Encrypt connections. Configure Oracle Net encryption or TLS to your databases.
- Keep the system updated. Apply operating system updates with
sudo dnf updateafter the first-boot update has finished.
Support
If you have any issues with this product, contact SecureAnyCloud support:
- Email: sales@secureanycloud.com
- Website: secureanycloud.com
- Product catalog: secureanycloud.com/products
- User guides: secureanycloud.com/guides
- Support hours: 24/7, with a guaranteed 24-hour response
Oracle is a registered trademark of Oracle Corporation. This image is a repackaged upstream distribution provided by SecureAnyCloud.
Deploy on Azure
Launch Oracle Database Client 23ai on Oracle Linux 9 on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com