How to Configure ORDS for Oracle APEX

Understand how ORDS serves APEX, where its settings live, and how to change the web server and connection pool settings safely.

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.

ModeHow ORDS connects
proxiedThe 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.
directThe pool connects as the gateway user itself, whose password must be stored in the ORDS configuration. It exists mainly for older setups.
disabledNo 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.pem

There 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.

CommandPurpose
ords config listShow 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 verifyTest 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: No

ords 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 optionSettingDefaultPurpose
--portstandalone.http.port8080HTTP port
--secure with --portstandalone.https.port8443HTTPS port
--context-pathstandalone.context.path/ordsURL path for APEX and REST
--apex-imagesstandalone.static.pathnoneFolder of APEX static files
--apex-images-context-pathstandalone.static.context.path/iURL path of the static files
--document-rootstandalone.doc.rootglobal/doc_rootFolder served at the server root /
--certificate, --keystandalone.https.cert, standalone.https.cert.keynoneYour own TLS certificate and key
nonestandalone.access.lognoneFolder 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.

SettingDefaultMeaning
jdbc.InitialLimit0Connections opened when ORDS starts
jdbc.MinLimit2Connections kept open even when idle
jdbc.MaxLimit10The 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

SymptomLikely cause and fix
ords refuses to start, citing the Java versionJava older than 17 on the PATH. Install Java 21 and set JAVA_HOME.
Address already in useAnother program uses the port. Change standalone.http.port or stop the other program.
HTTP 404 at /ords/apexThe 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 UnavailableORDS 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 filesWrong or missing images folder, or images from another APEX version. Check /i/apex_version.js.
Slow pages under load; requests queuingThe 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

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.

Vinish Kapoor
Vinish Kapoor

An Oracle ACE, author of four books on Oracle APEX, SQL and PL/SQL, and Oracle Forms, and a software developer building Oracle database applications since 2001.

guest

0 Comments
Oldest
Newest Most Voted
00