Two ORDS commands, install and serve, are enough to get APEX into a browser on a development laptop. The moment you share APEX with other people, though, you need to know what ORDS is doing: which settings it uses, where it keeps them, how many database connections it opens, and how to change all of that safely.
This guide explains how ORDS serves APEX, the PL/SQL gateway modes and allow list, the configuration folder, the ords config commands, the standalone web server settings, and connection pool sizing. The examples use the configuration folder ~/apex-lab/ords-config, and the output comes from ORDS 26.2.3.
These steps come from Oracle APEX 26.1 Book: The Complete Guide.
How ORDS Serves APEX
ORDS has several jobs, including REST services, SQL Developer Web, the Database API, and the MongoDB API, but for APEX it does two things.
It serves static files. Every APEX page loads JavaScript libraries, style sheets, fonts, and images. They are not in the database; they are the files in the images folder of the APEX download, and ORDS serves them at the URL path /i/, the image prefix given to apexins.sql. That folder must belong to exactly the same APEX version as the one installed in the database. To check which version ORDS is serving, open /i/apex_version.js.
Example:
curl -s http://localhost:8080/i/apex_version.js
Output:
var gApexVersion = "26.1.0";
It runs the PL/SQL gateway. Every other APEX URL, such as /ords/r/..., /ords/f?p=..., and /ords/apex, is a call to a PL/SQL procedure in the APEX engine. ORDS takes a connection from its pool, calls the procedure with the request's parameters, cookies, and headers, and streams the output back to the browser as the HTTP response. Only this second job needs a database connection.
Gateway Modes
The plsql.gateway.mode setting controls how ORDS connects for gateway calls.
| Mode | How ORDS connects |
|---|---|
| proxied | The pool connects as ORDS_PUBLIC_USER and uses proxy authentication to act as APEX_PUBLIC_USER for each call. APEX_PUBLIC_USER's own password is never needed. This is the recommended mode. |
| direct | The pool connects as the gateway user itself, whose password must be stored in the ORDS configuration. It exists mainly for older setups. |
| disabled | No PL/SQL gateway: ORDS serves REST services and other features, but not APEX. This is the state after installing ORDS before APEX. |
The Gateway Allow List
A gateway that calls procedures named in a URL could be dangerous: a crafted URL could try to call any procedure APEX_PUBLIC_USER can execute. So ORDS calls a request validation function before every gateway request, set by security.requestValidationFunction. The installer sets it to ords_util.authorize_plsql_gateway, which consults an allow list.
The APEX installer registers APEX's entry points in that list (the "Syncing ORDS Gateway Allow List" step in its output), so nothing needs changing for APEX. If you write your own PL/SQL procedures to be called directly through a URL outside APEX, add them to the allow list first, as the ORDS documentation's section on the PL/SQL gateway allow list shows, or ORDS refuses to run them.
The Configuration Folder
Since release 22.1, ORDS keeps its configuration in a folder you choose with --config. After an installation it looks like this:
Folder layout:
ords-config/
|-- databases/
| `-- default/
| |-- pool.xml <- settings of the "default" connection pool
| `-- wallet/
| `-- cwallet.sso <- encrypted passwords for this pool
`-- global/
|-- settings.xml <- settings that apply to the whole server
`-- standalone/ <- created when you first use HTTPS
|-- self-signed.key
`-- self-signed.pemThere are two kinds of settings:
- Global settings apply to the whole ORDS server: ports, the context path, the APEX image path, HTTPS, and logging. They are stored in global/settings.xml.
- Pool settings apply to one database connection pool: the database host, port, and service name, the user names, the gateway mode, and the pool size. They are stored in databases/<pool>/pool.xml. The pool created by ords install is called default.
Both files are Java property files in XML. Here is pool.xml after an installation:
Contents of pool.xml:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> <properties> <comment>Saved on Wed Sep 23 12:31:22 UTC 2026</comment> <entry key="db.connectionType">basic</entry> <entry key="db.hostname">localhost</entry> <entry key="db.port">1521</entry> <entry key="db.servicename">FREEPDB1</entry> <entry key="db.username">ORDS_PUBLIC_USER</entry> <entry key="feature.sdw">true</entry> <entry key="plsql.gateway.mode">proxied</entry> <entry key="restEnabledSql.active">true</entry> <entry key="security.requestValidationFunction">ords_util.authorize_plsql_gateway</entry> </properties>
The password of ORDS_PUBLIC_USER is missing on purpose. The installer generated a random password, set it in the database, and stored it in the wallet, cwallet.sso, next to pool.xml.
The wallet is encrypted, but ORDS opens it without a password, so anyone who can read the folder can recover the stored passwords; ords config get --secret db.password prints the connection password in plain text. Restrict the folder to the operating-system account that runs ORDS, back it up securely, and keep it out of source control.
Read and Change Settings with ords config
You can edit the XML files by hand while ORDS is stopped, but ords config is safer: it validates setting names, knows whether a setting is global or belongs to a pool, and stores passwords in the wallet.
| Command | Purpose |
|---|---|
| ords config list | Show every setting that has been set explicitly, and where it comes from |
| ords config get <name> | Show one setting's value |
| ords config set <name> <value> | Set a setting (not for passwords) |
| ords config secret <name> | Store a password or other sensitive value in the wallet; prompts for the value |
| ords config delete <name> | Remove a setting so its default applies again |
| ords config info <name> | Describe a setting: its meaning, default, and whether it is global |
| ords config verify | Test the pool's database connection |
Add --db-pool <name> to work with a pool other than default, and --quiet to suppress the banner.
Example:
ords --config ~/apex-lab/ords-config config list
Output:
Database pool: default Setting Value Source ---------------------------------- --------------------------------- ----------- database.api.enabled true Global db.connectionType basic Pool db.hostname localhost Pool db.password ****** Pool Wallet db.port 1521 Pool db.servicename FREEPDB1 Pool db.username ORDS_PUBLIC_USER Pool feature.sdw true Pool plsql.gateway.mode proxied Pool restEnabledSql.active true Pool security.requestValidationFunction ords_util.authorize_plsql_gateway Pool
ords config info is the built-in documentation. Ask it about any unfamiliar setting.
Example:
ords --config ~/apex-lab/ords-config config --quiet info jdbc.MaxLimit
Output:
Setting: jdbc.MaxLimit
Description: The maximum number of connections. Defaults to 10.
Value: None
Default: 10
Sensitive: No
Global: Noords config verify is the first thing to run when APEX does not respond, because it tells you whether ORDS can reach the database at all.
Example:
ords --config ~/apex-lab/ords-config config verify
Output:
Database pool default: Connection state: valid ORDS installed version in database: 26.2.3.r2371104
ORDS reads its configuration when it starts, so restart ORDS after changing a setting.
Standalone Web Server Settings
In standalone mode, ords serve runs an embedded Jetty web server. It is production-grade, and for most APEX installations it is all you need. Each ords serve option has a matching global setting, so you can store your choices instead of typing them every time.
| serve option | Setting | Default | Purpose |
|---|---|---|---|
| --port | standalone.http.port | 8080 | HTTP port |
| --secure with --port | standalone.https.port | 8443 | HTTPS port |
| --context-path | standalone.context.path | /ords | URL path for APEX and REST |
| --apex-images | standalone.static.path | none | Folder of APEX static files |
| --apex-images-context-path | standalone.static.context.path | /i | URL path of the static files |
| --document-root | standalone.doc.root | global/doc_root | Folder served at the server root / |
| --certificate, --key | standalone.https.cert, standalone.https.cert.key | none | Your own TLS certificate and key |
| none | standalone.access.log | none | Folder for HTTP access logs |
Store the image path and port, so a bare ords serve does the right thing.
Example:
ords --config ~/apex-lab/ords-config config set \ standalone.static.path ~/apex-lab/apex/images ords --config ~/apex-lab/ords-config config set \ standalone.http.port 8080
Output:
The global setting named: standalone.static.path was set to: /Users/yourname/apex-lab/apex/images The global setting named: standalone.http.port was set to: 8080
Turn On Access Logs
On any shared server, switch on standalone.access.log. The access log records every request with its time, status, and duration, which is invaluable when someone reports that APEX was slow at ten o'clock. Rotate the files with your operating system's log rotation.
Example:
ords --config /etc/ords/config config set standalone.access.log /var/log/ords
Size the Connection Pool
Every APEX page request uses one database connection for as long as it takes to produce the page, usually a few milliseconds to a few hundred. ORDS keeps these connections open in a pool so each request does not have to log in. With the defaults, ORDS prints a warning at startup.
Output:
WARNING *** jdbc.MaxLimit in configuration |default|lo| is using a value of 10,
this setting may not be sized adequately for a production environment ***Three settings control the pool size.
| Setting | Default | Meaning |
|---|---|---|
| jdbc.InitialLimit | 0 | Connections opened when ORDS starts |
| jdbc.MinLimit | 2 | Connections kept open even when idle |
| jdbc.MaxLimit | 10 | The most connections the pool may open |
Ten connections are plenty for one developer and serve surprisingly many users, because each is busy for only a fraction of a second per page. When all are busy, new requests wait, and if they wait too long, users see errors. For a team of developers or a departmental application, start with a maximum of 25 to 50 and watch the database.
Example:
ords --config ~/apex-lab/ords-config config set jdbc.MaxLimit 25 ords --config ~/apex-lab/ords-config config set jdbc.InitialLimit 5
Output:
The setting named: jdbc.MaxLimit was set to: 25 in configuration: default The setting named: jdbc.InitialLimit was set to: 5 in configuration: default
Every connection is a real database session that uses memory, so do not set the maximum far higher than you need; Oracle AI Database Free, with its 2 GB memory limit, prefers a smaller pool. The database's own processes and sessions limits also cap what the pool can usefully open.
Troubleshooting
| Symptom | Likely cause and fix |
|---|---|
| ords refuses to start, citing the Java version | Java older than 17 on the PATH. Install Java 21 and set JAVA_HOME. |
| Address already in use | Another program uses the port. Change standalone.http.port or stop the other program. |
| HTTP 404 at /ords/apex | The gateway is disabled or APEX is not in the pool's database. Check plsql.gateway.mode with ords config get, and rerun ords install. |
| HTTP 503 Service Unavailable | ORDS cannot get a connection: the database is down or the pool password is wrong. Run ords config verify. |
| APEX pages without styles; JavaScript errors about missing files | Wrong or missing images folder, or images from another APEX version. Check /i/apex_version.js. |
| Slow pages under load; requests queuing | The pool is too small, or long-running SQL holds connections. Raise jdbc.MaxLimit and look for slow queries. |
Error details are in the ORDS log: the terminal where it runs, the ords.log of your start script, or the service log on Linux. On a development computer you can set debug.printDebugToScreen to true to show error details in the browser; never do this on a shared server, because error details help attackers.
Related Guides
- How to Install Oracle APEX 26.1 with Docker
- How to Install Oracle APEX 26.1 on Oracle Linux
- Publishing REST Services with ORDS and Oracle APEX
Conclusion
ORDS serves APEX in two ways: it delivers the static files at /i/, which must match the installed APEX version, and it runs the PL/SQL gateway, normally in proxied mode, behind an allow list. Its configuration folder holds global settings, one folder per pool, and an encrypted wallet you must protect. Use ords config list, info, set, and verify to manage it, store the standalone port and image path as settings, switch on access logs, and raise jdbc.MaxLimit to 25 or more on a shared server.
