I began with a simple question: what could I actually do with Oracle Database and SQL Developer now that they were installed on my Debian machine?
I wanted realistic practice data: customers, products, orders, and sales histories. Something substantial enough to learn SQL, extract useful information, and eventually practice database security. The goal was to understand how a real system works, at my own pace.
Before I could load any sample data, however, I had to get the database environment working. That led through service management, network ports, hostname resolution, Linux permissions, installation scripts, and process tracing.
At 6:02 a.m., SQL*Plus finally displayed:
Connected to an idle instance.
SQL>
That was my “yay” moment—and a good place to pause.
The milestone was a successful local administrator connection. Database creation was still unfinished, and a working SQL Developer connection had not yet been demonstrated.
The learning goal behind the troubleshooting
During the session, I explained what mattered to me:
“I’m in no hurry to do but rather to understand what I’m learning.”
That changed the rhythm of the work. Each command needed a purpose. I wanted to know which program was interpreting it, what it would inspect or change, and what its output actually proved.
The pattern became: explain, inspect, interpret, change one thing when justified, and retest. I could then log the experience and reflect on it before moving on.
First, understanding the tools
Oracle Database and SQL Developer serve different roles. Oracle stores and processes the data. SQL Developer is the graphical client used to connect, write SQL, and view results. SQL*Plus is another client, used in a terminal.
| Tool or language | Role in this session |
|---|---|
| Bash | Interpreted commands entered at my Debian terminal prompt |
| SQL | The language used to ask the database questions |
| SQL Developer | The graphical database client with my saved connections |
| SQL*Plus | The terminal database client used for local connection tests |
| Oracle Database | The server software I was trying to configure |
systemctl, ss, grep, strace | Linux tools invoked from the shell during diagnosis |
My three SQL Developer entries were saved connection profiles. Their existence did not prove that the database existed, was running, or contained matching user accounts.
An early mistake made this distinction concrete. I entered this SQL statement into Bash:
SELECT banner_full FROM v$version;
Bash responded that SELECT was not a command. The statement belonged in a SQL client connected to Oracle. Where I typed mattered as much as what I typed.
Lab Journal
1. Finding the actual Oracle installation
SQL Developer could not connect to 127.0.0.1 on port 1521:
ORA-12541: Cannot connect. No listener at host 127.0.0.1 port 1521.
I first checked Docker:
docker ps -a --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}"
The -a included stopped containers. This mattered because a stopped Oracle container would not appear in a running-only list.
There was no Oracle container in that Docker environment. There was a running Microsoft SQL Server container named mssql, which is a different database system.
Next, I searched Debian’s registered services:
systemctl list-unit-files --type=service | grep -Ei 'oracle|listener'
That found oracle-xe-21c.service. A status check showed inactive (dead).
“Dead” was a service state, not a statement that my data had been destroyed.
2. Why a successful service command was not enough
I started the service:
sudo systemctl start oracle-xe-21c.service
It returned silently, and the next status check reported:
Active: active (exited)
code=exited, status=0/SUCCESS
But SQL Developer still could not connect. An exact port check returned only headings:
ss -ltn 'sport = :1521'
Nothing was listening there. Reading the service journal explained why:
The Oracle Database is not configured.
The startup script had returned success to the service manager without starting a usable database. A successful script exit and a working service are separate observations.
I checked the expected data location. /opt/oracle/oradata/XE existed but was empty. /etc/oratab and the expected configuration-log directory were initially absent. These findings supported incomplete setup; they were not a claim that we had exhaustively searched every disk for another installation.
3. A false port conflict: 5500 versus 55000
Running Oracle’s configuration script stopped with a warning that port 5500 was occupied.
Port 1521 was intended for database connections. Port 5500 was the initial setting for the EM Express browser administration interface.
An exact check showed nothing listening on 5500. Inspecting the setup script revealed a text search:
ss -n --tcp --listen | grep :5500
That also matched a Python process listening on 55000. The text :5500 occurs at the beginning of :55000.
This was a confirmed false positive: a text-prefix match was being used as though it were an exact port check.
I checked the alternative 5501, backed up the configuration file, and changed:
EM_EXPRESS_PORT=5500
to:
EM_EXPRESS_PORT=5501
The file was /etc/sysconfig/oracle-xe-21c.conf; the backup was saved beside it with .before-port-change appended. We verified the saved value before trying configuration again. The Python service did not need to be stopped.
4. The hostname existed, but Oracle still rejected it
The next failure was:
No valid IP Address returned for the host Debian.
Then examined name resolution rather than immediately assuming the internet connection was broken.
getent hosts Debian
getent ahostsv4 Debian
The first lookup returned IPv6 addresses. The IPv4-specific lookup returned 127.0.1.1, associated with my machine’s hostname. This showed why the first result did not prove IPv4 was missing.
The local hosts file contained:
127.0.0.1 localhost
127.0.1.1 Debian.ny2honolulu Debian
The loopback interface explicitly listed 127.0.0.1/8. Linux can use 127.0.1.1 as a loopback address too; the original mapping was not automatically invalid.
Oracle’s trace showed it finding Debian/127.0.1.1 and then rejecting the address during a later check. We tested a compatibility hypothesis: map the hostname to the explicitly assigned loopback address.
After backing up /etc/hosts as /etc/hosts.before-oracle, I changed the hostname line to:
127.0.0.1 Debian.ny2honolulu Debian
The IPv4 lookup then returned 127.0.0.1. On the next configuration attempt:
Listener configuration succeeded.
That was evidence that the change helped this installation. It was not proof of the internal implementation of Oracle’s check, nor a universal recommendation to rewrite Debian hosts files. Remote access was not tested.
5. A concrete permissions error in oradism
Database configuration next reported incorrect ownership and a missing set-user-ID bit on:
/opt/oracle/product/21c/dbhomeXE/bin/oradism
The file initially showed:
-rwxr-xr-x 1 oracle dba
Oracle explicitly expected root ownership and the set-user-ID bit. I changed the owner to root and added that bit. Verification then showed:
-rwsr-xr-x 1 root dba
The s in the owner’s execute position showed the special permission was enabled. This allows that executable to run with its owner’s effective privileges; it does not grant the entire Oracle account unrestricted root access.
The specific oradism complaint disappeared. A different connection error remained.
This was a targeted repair to the reported prerequisite, not a complete security review of the installation. Later ran Oracle’s root script as well, so this intermediate listing should not be treated as a final permissions baseline.
6. The generic connection error did not prove a bad password
Configuration reported:
DBT-05509: Failed to connect to the specified database (XE).
Its suggested cause was that operating-system authentication might be disabled. I treated that as a hypothesis rather than a diagnosis.
SQL*Plus itself launched successfully:
SQL*Plus: Release 21.0.0.0.0 - Production
Version 21.3.0.0.0
That confirmed the client version. I had not yet obtained a running database’s version banner.
A local connection test as the Linux user oracle failed with:
ORA-12547: TNS:lost contact
Checked the database executable’s shared libraries using ldd. Every listed dependency resolved to a file. That ruled out a missing library in that listing, but not every possible runtime dependency or compatibility issue.
Its permissions showed that the owner, oracle, could execute it. I did not assume that missing special permission bits explained a connection already being attempted as that owner.
7. An installation script expected accounts that did not exist
The earlier error had recommended Oracle’s root installation script. I inspected root.sh, then ran it and read its log.
The log reported:
chown: invalid user: ‘oracle_xe:oinstall’
chgrp: invalid group: ‘oinstall’
The script also made some changes, including creating /etc/oratab, despite those errors. That mattered: this was a partial run, not an all-or-nothing operation.
Traced the settings to install/utl/rootmacro.sh:
ORACLE_OWNER=oracle_xe
OSDBA_GROUP=oinstall
But id oracle showed the actual account:
uid=1001(oracle) gid=1001(dba) groups=1001(dba)
After backing up the script, I changed those two settings to oracle and dba, verified them, and reran root.sh. The new log no longer contained those account/group errors.
The local connection still failed. This was important to record honestly: I repaired a demonstrated inconsistency, but that repair was not the final solution. I did not establish how the mismatched account names originally got into the installation.
8. Using strace to move beyond the generic error
A kernel-log search did not show an Oracle crash matching our attempt. An older gdbus crash concerned another program.
I then used strace to record the failing connection:
sudo -u oracle env ORACLE_HOME=/opt/oracle/product/21c/dbhomeXE ORACLE_SID=XE strace -f -o /tmp/oracle-xe-connect.trace /opt/oracle/product/21c/dbhomeXE/bin/sqlplus -L / as sysdba
-f followed child processes. -o saved the trace to a file. The remaining command was the same local SQL*Plus connection test.
The end of the file initially showed SQL*Plus writing its error and exiting. I needed the activity of the Oracle process it had launched, not just the client’s final complaint.
Filtering for program launches and exits identified separate process IDs. SQLPlus launched the Oracle executable successfully. Oracle then exited with code zero, and SQLPlus exited with code one. Even the zero exit did not prove a successful connection.
Looking at Oracle’s own operations finally exposed a concrete failure:
chdir("/opt/oracle/homes/OraDBHome21cXE/dbs") = -1 ENOENT
chdir means change directory. ENOENT means a path component was missing.
9. Creating the missing dbs directory changed the error
Inspected the path one component at a time:
sudo namei -l /opt/oracle/homes/OraDBHome21cXE/dbs
All parents existed and belonged to oracle:dba; only dbs was missing. I created it as the Oracle account:
sudo -u oracle mkdir -m 750 /opt/oracle/homes/OraDBHome21cXE/dbs
This created a directory, not a database.
The next connection attempt produced a new error:
ORA-09925: Unable to create audit trail file
Linux-x86_64 Error: 2: No such file or directory
The changed error was evidence of progress. Oracle was getting far enough to attempt an audit record.
10. Finding the exact audit destination
Instead of guessing the directory, we captured another trace, this time in /tmp/oracle-xe-audit.trace.
It showed repeated attempts to create a .aud file under:
/opt/oracle/homes/OraDBHome21cXE/rdbms/audit
Those operations included O_CREAT and failed with ENOENT. Oracle wanted to create the audit file, but its directory path was missing.
Then created the destination:
sudo -u oracle mkdir -p -m 750 /opt/oracle/homes/OraDBHome21cXE/rdbms/audit
-p creates missing parents and tolerates directories that already exist. -m 750 sets permissions on the final directory: full access for the owner, read/traverse access for the group, and no access for others. Newly created parents use the normal parent-creation permissions, not necessarily that explicit final-directory mode.
I accidentally repeated the directory command. Because it used -p, that did not duplicate or empty the directory.
I provided the missing audit destination. I did not disable auditing to get past the error.
11. The successful connection
Repeated the local connection test without tracing:
sudo -u oracle env ORACLE_HOME=/opt/oracle/product/21c/dbhomeXE ORACLE_SID=XE /opt/oracle/product/21c/dbhomeXE/bin/sqlplus -L / as sysdba
This time:
Connected to an idle instance.
SQL>
The local administrator connection worked. The earlier generic suggestion that operating-system authentication might be disabled was not the final explanation of the failure we resolved.
What each correction actually accomplished
| Finding | Action taken | Observed result |
|---|---|---|
| Setup matched port 55000 when checking 5500 | Changed EM Express setting to 5501 after checking availability | Setup passed the port check |
| Oracle rejected the hostname associated with 127.0.1.1 | Backed up hosts file and mapped hostname to 127.0.0.1 | Listener configuration succeeded |
oradism owner and set-user-ID bit did not satisfy setup | Changed owner to root and added the bit | Specific permissions complaint disappeared |
| Root scripts referenced nonexistent account/group | Backed up and corrected owner/group settings | Account/group errors disappeared from the next root-script log |
Oracle could not enter its expected dbs directory | Created that directory as oracle | Lost-contact error gave way to a specific audit-file error |
| Audit-file destination was missing | Created the traced audit directory | Local SYSDBA connection reached an idle instance |
The first four corrections did not, by themselves, establish the final connection. Keeping those results separate makes this a more useful troubleshooting record.
Lessons Learned
Read the result, not just the success label
The service manager reported success while the script said the database was unconfigured. The Oracle child process exited zero while SQL*Plus lost contact. A result needs context and a functional test.
Error messages can describe symptoms
“Lost contact” did not name the missing directory. The authentication suggestion did not identify the audit-file problem. Logs and traces let me move from a broad complaint to a specific failed operation.
Exact matching matters
The difference between 5500 and 55000 was enough to stop setup. A text search is not automatically an exact network-port check.
Separate identity, permissions, and paths
The account running a program, the owner of its executable, its permission bits, and the directories it expects are related but different. Changing one does not automatically repair the others.
Preserve evidence and a way back
I backed up configuration files before editing them, verified the saved settings, and retested specific failures. Not every hypothesis was confirmed, and not every correction solved the main problem. Those distinctions belong in the journal too.
Auditing is already a cybersecurity lesson
I originally asked whether a database lab could teach security. Before I even loaded sample data, we encountered administrator authentication, privileged executables, ownership, permissions, and audit records. The immediate lesson was that a missing audit destination could prevent the administrator connection from completing.
Slow learning can still make concrete progress
Understanding grep, pipes, service states, file permissions, and process IDs made the troubleshooting more meaningful than a sequence of commands to paste. Repetition helped those ideas become familiar.
Where I paused—and what remains
| Item | Status at the pause |
|---|---|
| Oracle software layout | XE 21c installation paths and service identified |
| SQL*Plus client | Version 21.3.0.0.0 confirmed |
| Listener configuration | Reported success during setup |
Local / as sysdba connection | Successful; connected to an idle instance |
| Database creation/configuration | Not yet completed successfully |
| Database version query | Not yet run against a running database |
| SQL Developer connection | Not yet retested successfully after these repairs |
| Saved personal database account | Existence and login not yet confirmed |
| Sample data and security exercises | Not yet installed or started |
“Idle instance” is the right description of this stopping point. It does not mean the sales database is ready, the saved SQL Developer accounts work, or a reboot has been tested.
The proposed next step is to leave SQL*Plus with exit, then resume configuration and inspect its results. That next configuration attempt has not happened in this journal. At the pause, my last confirmed prompt was SQL>.
Once configuration succeeds, the larger goal remains, load realistic sample data, learn how to query it, and build controlled security exercises around permissions, access, and auditing.
For now, I have a successful administrator connection and a much clearer understanding of the layers underneath it. That is enough for one session. I want time to log, learn, and reflect before moving on.