ORDS Connection Pool Failures: Why APEX Breaks After a Restart (and the Reboot-Safe Fix)
When APEX or ORDS fails straight after a restart or an upgrade, the database is rarely the problem. Usually ORDS either cannot find its configuration folder, or an ORDS connection pool's stored password no longer matches the database. Check those two things, in that order, and most of these outages end in minutes.
I learned that order on a pharmaceutical company's Oracle stack in May 2026. Every APEX link returned 404, The request could not be mapped to any database, although all eight pools were sitting on disk, unbroken.
Tomcat had started ORDS without telling it where its configuration lived. One line in setenv.sh brought seven pools back; the eighth had a stale wallet password, and fixing it quietly broke a neighbour.
Key Takeaways
- An ORDS 404 saying the request could not be mapped to any database after a restart usually means ORDS loaded no pools, even when
ords config listlooks healthy. - Standalone ORDS reads
--config,ORDS_CONFIGor the current directory. Under Tomcat or WebLogic it needs theconfig.urlsystem property, or a WAR built with the location inside. - Since ORDS 24.2, pool failures have their own codes: 570 locked account, 571 network, 572 overloaded, 574 bad or expired credentials. Older releases send a 503.
- Pools that log in to one database as
ORDS_PUBLIC_USERshare one password. Change it and every one of those pools needsords config secret db.password. - Pool defaults change between releases (
jdbc.InitialLimitis 10 in the 23.4 guide and 0 in 26.3), so size pools from measured peak sessions. - A fix is reboot-safe only when it lives in a file the service manager reads and survives a real, hands-off reboot.

1. What Is an ORDS Connection Pool?
An ORDS connection pool is a set of database connections that Oracle REST Data Services opens and reuses for one database, defined by a folder under databases/ in the ORDS configuration directory. Each pool logs in as a runtime user, normally ORDS_PUBLIC_USER, and the folder name becomes the URL prefix that routes requests to it.
So a pool folder named sales serves /ords/sales/..., and the reserved default folder catches any request that no other pool claims.
/etc/ords/config/ # the configuration folder
+- global/
| +- settings.xml # instance-wide settings
+- databases/
+- default/ # catches requests no other pool claims
| +- pool.xml
| +- wallet/cwallet.sso # this pool's credentials
+- sales/ # serves /ords/sales/...
+- pool.xml
+- wallet/cwallet.sso
The password is not in pool.xml. Oracle's configuration file reference says the credentials must be stored in the pool's auto-login wallet, and that the db.password setting must not be used.
So when a pool reports a credential error, the broken thing is usually a small file called cwallet.sso, not the database.
2. Which ORDS Error Points to Which Layer?
Read the status code and the message together, and ORDS tells you which layer failed.
| What you see | What it usually means | Check first |
|---|---|---|
| 404 could not be mapped to any database | No pool matched; after a restart, often none were loaded | The configuration inputs of the live process (section 8, step 1) |
| 503, pool "not correctly configured" (before 24.2) | A generic pool failure | The log line naming the pool and the ORA- code |
| 570 Account Is Locked | The pool's account is locked | DBA_USERS |
| 571 Database Connection Error | Network or listener, such as ORA-12514 or ORA-12506 | Listener, service name, firewall |
| 572 Recoverable Database Connection Error | No connection available, for example an overloaded database | Sessions, processes, jdbc.MaxLimit |
| 574 Database Credential Error | Invalid or expired password | The pool wallet against the database |
| ...not authorized to proxy to the schema... | A missing proxy grant | DBA_PROXIES |
The ORDS 24.2 release notes added the 57x codes to help log monitoring and alarms. On older releases those rows all arrive as 503, and only the log shows the difference.
A 571 is a listener problem wearing an ORDS badge, and my guide to listener and TNS troubleshooting for ORA-12514 covers it. A 413 on an APEX import is your proxy, not a pool, as I explained in fixing 413 Request Entity Too Large behind nginx.
3. How Does ORDS Find Its Configuration Folder?
Most post-restart failures start here. The running service decides where to look, not the command-line tool you configured it with.
Standalone mode (ords serve)
The install guide names three sources: the --config option, the ORDS_CONFIG environment variable, and, if neither is set, the current working directory. That fallback is the trap, because a service manager rarely starts ORDS from the folder you tested in.
Tomcat and WebLogic (ords.war)
A Java container has no --config flag. Oracle's deployment guide says the location must come from the config.url system property, or from a WAR generated with the ords war command, which it calls the recommended approach.
This is why ords config list can show eight healthy pools while the running container has loaded none. The tool reads the folder you name with --config; the container reads only what it was started with.
| Deployment | How the location is given | Reboot-safe place for it |
|---|---|---|
| Standalone, your own unit | ords --config /etc/ords/config serve | The unit's ExecStart line |
| Standalone, from the RPM | The packaged service | The ords.conf file the install guide names |
| Apache Tomcat 9 | -Dconfig.url=/etc/ords/config | bin/setenv.sh; on a Windows service, its Java options |
| WebLogic 14.1.2 | -Dconfig.url in JAVA_OPTIONS, or a WAR from ords war | setUserOverrides.sh, or the server's start arguments |
Both containers also name the context root after the WAR file, so renaming ords.war changes the URL. Oracle's current checklist supports Tomcat 9.0.x and WebLogic 14.1.2, on Java 17 or 21.

4. What Went Wrong on the Pharmaceutical Stack?
The stack was ordinary: Apache terminating TLS, ords.war on Tomcat 9, and a group of Oracle databases behind them. The configuration held a default pool plus seven named pools spread across the group's databases.
Every /ords/ URL returned 404, yet the proxy was correct and every pool folder was present. Tomcat's command line simply carried no -Dconfig.url, so ORDS had started with nothing to map requests to.
Fix one: tell Tomcat where the configuration lives
# $CATALINA_BASE/bin/setenv.sh (owned by the OS user that runs Tomcat)
export CATALINA_OPTS="$CATALINA_OPTS -Dconfig.url=/etc/ords/config"
Tomcat's documentation recommends a setenv script for this and keeps start-only options in CATALINA_OPTS; Oracle's guide uses JAVA_OPTS, and either works for a system property.
Two details cost time. Tomcat had to start as the OS user that owns its files, not as root, and a shutdown that never released port 8005 made the next start fail until the old process was cleared.
Seven pools answered at once.
Fix two: the pool with the stale wallet
The default pool failed with ORA-01017. Its wallet was more than a year old, and the password inside it no longer matched ORDS_PUBLIC_USER on that database.
I reconfigured that pool cleanly, which regenerated its wallet and set a new ORDS_PUBLIC_USER password in the database. Then a named pool on the same database, still holding the old password, started returning 574.
# set the secret at a prompt - never as a command-line argument
ords --config /etc/ords/config config --db-pool <pool> secret db.password
# then restart the container and re-test every pool
All eight answered after that. The lesson I kept: a password belongs to a database user, not to a pool, so rotating it means updating every pool that logs in as that user.
Fix three: one WAR, not four
The product folder also held three renamed copies of ords.war, and the ords command printed a stray Unknown command line on every run. One WAR serves every pool, and moving the copies out silenced the noise.
The box was also rebooted on purpose. Every service came back untouched, and Tomcat came up with -Dconfig.url on its command line.
5. Why Do ORDS Pools Break After an Upgrade or Reinstall?
Upgrades break pools in a few predictable ways, and every one of them looks like a database fault.
- A password changed. A reinstall or reconfiguration can reset
ORDS_PUBLIC_USER, while the other pools on that database keep the old password. - The configuration moved. ORDS 22.1 introduced today's command-line tool, the old
apex.xmlpool corresponds to thedefaultfolder, and the installer's--legacy-configoption migrates an old layout. - APEX went in after ORDS. The install guide's default gateway mode is
disabled. In that case Oracle says to setplsql.gateway.modetoproxied, or the gateway APEX needs stays off. - Java changed. ORDS needs Java 17 or 21 and refuses older versions with ORDS requires Java 17 and above to run. On the pharmaceutical stack,
sudo -u oracledroppedJAVA_HOME, andsudo -i -u oraclefixed it. - A password expired. A
PASSWORD_LIFE_TIMEin the user's profile fails the pool on a schedule nobody wrote down, which shows as 574 on 24.2 and later.
On WebLogic, remember that ORDS builds its own Universal Connection Pools from its configuration folder, so tuning a JDBC data source changes nothing. My WebLogic administration guide covers the data sources WebLogic does own.
6. What Do ORDS_PUBLIC_USER and APEX_PUBLIC_USER Actually Do?
ORDS connects as one low-privilege user, then proxies to whoever should run the request.
| Account | What it does | What fails when it breaks |
|---|---|---|
ORDS_PUBLIC_USER | The runtime user in db.username; proxies to ORDS-enabled schemas or the gateway user | Everything on that pool |
ORDS_METADATA | Holds ORDS metadata; ORDS never connects to it | Nothing directly; it is not a pool user |
APEX_PUBLIC_USER | The minimally privileged APEX account, reached by proxy | APEX pages |
APEX_REST_PUBLIC_USER | Used to invoke RESTful Services defined in APEX | REST services defined in APEX |
-- run as a DBA on every database a pool points at
SELECT username, account_status, expiry_date, profile
FROM dba_users
WHERE username IN ('ORDS_PUBLIC_USER','APEX_PUBLIC_USER','APEX_REST_PUBLIC_USER');
-- which users may ORDS_PUBLIC_USER proxy to?
SELECT proxy, client FROM dba_proxies WHERE proxy = 'ORDS_PUBLIC_USER';
On the pharmaceutical stack the chain was exactly that, and APEX routed workspaces to schemas itself. So do not grant proxy rights to every application schema to "fix" an app; that widens what a stolen pool credential can reach.
7. How Should You Size jdbc.InitialLimit, MinLimit and MaxLimit?
Pool settings are per pool and are changed with ords config --db-pool <pool> set. Oracle's configuration reference documents these defaults:
| Setting | What it controls | Documented default |
|---|---|---|
jdbc.InitialLimit | Connections opened when the pool starts | 10 in the 23.4 and 24.4 guides; 0 in 25.4 and 26.3 |
jdbc.MinLimit | The minimum number of connections | 2 |
jdbc.MaxLimit | The ceiling on connections | 10, which "might be too low for some production environments" |
jdbc.InactivityTimeout | Idle seconds before a connection is closed | 1800 |
The first row changed between releases, which is why I never copy pool numbers from a blog post, this one included. Measure instead, several times through your busiest hour:
SELECT username, status, COUNT(*) AS sessions
FROM v$session
WHERE username IN ('ORDS_PUBLIC_USER','APEX_PUBLIC_USER','APEX_REST_PUBLIC_USER')
GROUP BY username, status
ORDER BY username, status;
Proxied work can appear under the target user, hence all three names. Keep the highest ACTIVE figure, add headroom, and set jdbc.MaxLimit there.
Then multiply that ceiling by the pools on that database and by your ORDS instances, and compare it with the database's processes and sessions parameters: eight pools at 30 each can ask one ORDS node for 240 connections.
# example value only - derive yours from the query above
ords --config /etc/ords/config config --db-pool <pool> set jdbc.MaxLimit 30
ords --config /etc/ords/config config --db-pool <pool> list --include-defaults
Keep jdbc.InactivityTimeout below any firewall idle timeout between the tiers, or the pool may hold connections the firewall has already dropped.
jdbc.MaxConnectionReuseCount, documented with a default of 1000, retires a connection after that many uses and replaces it with a fresh one. Leave it alone unless you have a measured reason to change it.

8. How Do You Diagnose a Failing ORDS Pool, Step by Step?
This is the order I follow now. Each step rules out one layer, and the first three take seconds.
- Check what the running ORDS was actually given, looking only at the inputs your deployment reads (section 3; the commands are in the box after this list). Standalone (
ords serve):--configon its command line, thenORDS_CONFIGin its environment, then the working directory it started in. Tomcat or WebLogic (ords.war):-Dconfig.urlon the JVM command line, or aconfig.urlthatords warset inside the deployed WAR. - List the pools on disk. Run
ls /etc/ords/config/databases/(on Windows,dirin your configuration folder): one folder, one pool, one URL prefix. - Read each pool's settings. Run
ords --config /etc/ords/config config --db-pool <pool> list. In its Source column,db.passwordshould say Pool Wallet. - Read the log before restarting. Use
catalina.out, the WebLogic server log, or theords serveconsole, and look for the pool name and an ORA- code. - Test every pool with the loop below, not only the broken one.
- Check the accounts on each database with the queries in section 6.
- Set the secret at a prompt with
ords config --db-pool <pool> secret db.password. Oracle warns that a value passed as an argument shows inpsoutput. - Restart cleanly, with the old process gone and its ports free, then re-test everything.
Step 1 in detail, on Linux
These commands read /proc and use systemd, so they work on Linux only. Run them as root or as the user that runs ORDS: the kernel checks permission before anyone reads another process's environment or working directory.
# Linux only (systemd + /proc). 1. the PID of the running ORDS JVM
PID=$(systemctl show -p MainPID --value ords.service) # or your Tomcat unit's name
pgrep -af 'org.apache.catalina.startup.Bootstrap|ords\.war|ords serve' # no unit? list the candidates
# 2. both deployments: the command line the JVM started with
tr '\0' ' ' < /proc/$PID/cmdline; echo
# standalone: look for --config Tomcat / WebLogic: look for -Dconfig.url=
# 3. standalone only: ORDS_CONFIG, then the folder it started in
tr '\0' '\n' < /proc/$PID/environ | grep '^ORDS_CONFIG='
( cd /proc/$PID/cwd && pwd -P )
# 4. Tomcat / WebLogic only: a config.url that `ords war` put inside the WAR
# (use the WAR file you actually deployed)
unzip -p /path/to/tomcat/webapps/ords.war 'WEB-INF/*.xml' | grep -i -A2 'config.url'
On Windows there is no /proc and no systemctl. Find the ORDS process with jps -ml, the command Oracle's deploying and monitoring guide gives, and read the Java options the Tomcat service or WebLogic server was started with (section 3).
Call it a missing configuration only when every input your deployment reads is empty, or names a folder with no pools under databases/. That is what users then see: on the pharmaceutical stack, every /ords/ URL answered 404 with The request could not be mapped to any database.
# step 5 (bash): every named pool, through the same path your users take
for p in sales finance hr; do # your pool folder names
printf '%-10s %s\n' "$p" "$(curl -k -s -o /dev/null -w '%{http_code}' https://localhost:8443/ords/$p/)"
done
On the pharmaceutical stack, healthy named pools answered 302, a redirect into the application. The default pool needs a real application URL instead, as the FAQ explains.
On a live system, the Instance API at /ords/_/instance-api/stable/status counts valid and invalid pools once instance.api.enabled is set (it needs the System Administrator role). ords pool --pid <pid> list shows a running instance's pools without a restart; both are in Oracle's deploying and monitoring guide.
9. How Do You Make the Fix Survive a Reboot?
A fix typed into a shell is not a fix yet. It must live where the service manager reads it, and a real reboot must prove it.
- Use the file the service reads. That is
setenv.shfor Tomcat,setUserOverrides.shor the start arguments for WebLogic, andExecStartorords.conffor standalone. Anexporttyped into your session dies at logout. - Run as the owner. One start as root can leave root-owned files behind that break the next normal start.
- Let systemd see the JVM. There, a
Type=forkingTomcat unit whose stop script failed left the old JVM holding its ports, and the automatic restart looped on Address already in use. Runningcatalina.sh runin the foreground underType=simpleis the cleaner design. - Protect and archive the folder. It holds every pool's wallet, so restrict it to the ORDS owner. Take a dated archive of it, and of
setenv.sh, before each change.
# Linux only (systemd, /proc, ss): after a deliberate reboot - no manual starts allowed
systemctl --failed
PID=$(systemctl show -p MainPID --value ords.service) # or your Tomcat unit's name
tr '\0' ' ' < /proc/$PID/cmdline | grep -oE -- '-Dconfig.url=[^ ]+|--config +[^ ]+'
tr '\0' '\n' < /proc/$PID/environ | grep '^ORDS_CONFIG=' # standalone only
ss -tlnp | grep -E ':8443|:8080' # your container's ports
# then the per-pool loop from section 8, plus one real app URL for default
On Windows, the same proof is the service list after the reboot, jps -ml for the ORDS process and the same per-pool URLs. The test passes only if nobody touches the box between power-on and the last pool check. That test is what proved the setenv.sh fix permanent.

10. What Else Looks Like a Pool Failure but Is Not?
Redirects to plain http behind TLS. If the proxy sends no X-Forwarded-Proto: https and Tomcat does not trust it (a RemoteIpValve does that), ORDS redirects users to http, which an outside firewall may simply block.
On the pharmaceutical stack this looked like ORDS working inside the office and hanging from outside. LAN browsers quietly followed the http redirect, while the outside firewall dropped port 80.
Two configuration folders. An old run of ords without --config can leave a second configuration in the product folder, which Oracle advises against. Keep exactly one.
For anything else, my systematic method for diagnosing Oracle errors applies unchanged.
Most ORDS connection pool outages after a restart come down to two questions. Did the running process get its configuration folder, and does every pool's wallet still match the database today?
Frequently Asked Questions
Why does ORDS say the request could not be mapped to any database?
No pool matched the URL, and after a restart that usually means ORDS loaded no pools at all. Under Tomcat or WebLogic, check the running JVM for -Dconfig.url and the deployed WAR for a config.url set by ords war; in standalone mode, check --config, ORDS_CONFIG and the folder ORDS was started in. If the configuration did load, check that the URL prefix matches a folder under databases/.
Where does ORDS look for its configuration folder?
Standalone ORDS uses the --config option, the ORDS_CONFIG variable or, failing both, the current working directory. In Tomcat or WebLogic the location must come from the config.url system property or from a WAR built with ords war. Oracle recommends keeping the folder outside the ORDS product folder.
What does ORDS error 574 mean?
Since ORDS 24.2, status 574 is a database credential error, such as an invalid or expired password for the pool's user. ORDS reads that password from the pool's wallet, so reset it with ords config --db-pool <pool> secret db.password if it no longer matches. Older releases report the same failure as a 503.
How do I change the ORDS_PUBLIC_USER password without breaking pools?
First list every pool that logs in as ORDS_PUBLIC_USER on that database, because they all share one password. Change it in the database, run ords config --db-pool <pool> secret db.password for each of those pools, and restart ORDS. Then test every pool, not only the one you meant to fix.
What is a good value for jdbc.MaxLimit?
There is no universal number. The documented default is 10, which Oracle says might be too low for some production environments. Measure peak active sessions for the pool users, add headroom, and confirm the total across pools and ORDS instances fits the database's processes and sessions limits.
Is ORDS standalone mode supported in production?
Yes. Oracle's deployment guide says standalone mode is suitable for development and supported in production, though it has fewer management features than a Java EE application server. Oracle also recommends running it with HTTPS rather than HTTP.
Why does /ords/default/ return 404 when the default pool works?
The default folder is reserved for requests that no other pool claims, so it is not meant to be addressed by name. On the stack in this article, /ords/default/ returned 404 while real application URLs on that pool worked normally. Test the default pool with a real application URL instead.
🔒 Is your ORDS tier exposing more than APEX?
Every pool wallet sits in one folder, and a container's plain-HTTP port is easy to leave reachable. If you would rather know what your middleware exposes before someone else checks, I will look at it properly.
🔍 Digital Exposure AuditReferences & Further Reading
- 📄 ORDS Installation and Configuration Guide (26.3) - configuration files, pool settings and defaults
- 📄 ORDS Installation and Configuration Guide (26.3) - deploying and monitoring: config.url, Tomcat, WebLogic, Instance API
- 📄 ORDS 24.2 release notes - the 570 to 578 status codes
The case was diagnosed and fixed by the author on a production system in May 2026; names are withheld and paths generalised. Confirm settings against the documentation for your ORDS release before production use.
