Support for In-Database JavaScript is one of the most exciting new features in Oracle Database 23ai. JavaScript is the first language supported by Oracle Multilingual Engine (MLE), powered by GraalVM. Oracle APEX was one of the first platforms to support server-side JavaScript. If you want to get started developing using MLE and APEX without having to concern yourself with the installation, using container images is the fastest way to create a lab environment.

If you want even less hands-on work, consider using an Always Free Autonomous Database. As an added bonus all the upgrades will be performed for you so you can focus on developing.

This post describes a fast path to creating a development lab to experiment with MLE and APEX using a (Docker) compose file. Why compose? Because it can be used to consistently tie multiple container images together, including network and and name resolution, with a single command to start and stop the environment. Please note that the compose format is not exclusively tied to Docker. This post was created using podman-compose on Oracle Linux 9.5.

This article uses podman secrets. It might be possible to use Docker and secrets as well, however that combination has not been tested.

Lab environment

This post was originally written using Oracle Linux 9.5 (x86-64) and updated April 1st 2025. The following podman-related packages were present:

  • podman-5.2.2-15.0.1.el9_5.x86_64
  • podman-plugins-5.2.2-15.0.1.el9_5.x86_64
  • kernel-uek-5.15.0-306.177.4.el9uek.x86_64 (UEK 7)

These were installed using the standard package sources and were current at the time of this writing (2025-04-01)

You can install podman-compose from the Extra Packages for Enterprise Linux (EPEL) repository. Alternatively grab the most current release from PyPI, which is 1.3.0 vs 1.0.6 from the official repository. Version 1.3.0 was used when creating this article.

Container Images

Images used in this post have been taken from these registries:

Software Usage Instructions
Oracle Database 23ai Free https://github.com/gvenzl/oci-oracle-free
Oracle REST Data Services https://container-registry.oracle.com/ords/ocr/ba/database/ords-developer

Note that the Oracle REST Data Services (ORDS) image has changed: the one including APEX has been renamed to ords-developer. Another one, nicknamed ords-cli, is available from container-registry.oracle.com/database/ords.

Overview

The compose file you’ll see in this article creates two services based on the database and ORDS images, respectively:

  1. Oracle Database 23ai Free (23.7.0)
  2. ORDS Developer (24.4.0)

If you read this article after its publication, versions might need updating, but the overall principles still apply.

The database provided by the container image will automatically create a single Pluggable Database (PDB) named freepdb1 when the container is started for the first time. The ORDS container image will automagically install APEX and ORDS into that PDB.

Compose File

Before you can start you need to create a podman secret for the SYS and SYSTEM accounts. Pick a secure password and name the secret oracle_dba_pwd. If you haven’t done that before, kindly refer to the documentation. Make sure not to include any newline with the secret! Next up, it’s the compose file:

services:
        oraclefree:
            image: ghcr.io/gvenzl/oracle-free:23.7
            ports:
                - 1521:1521
            volumes:
                - ora_db_vol:/opt/oracle/oradata
            networks:
                - ora_app_network
            healthcheck:
                test: [ "CMD", "/opt/oracle/healthcheck.sh" ]
                interval: 10s
                timeout: 5s
                retries: 10
                start_period: 5s
            environment:
                - ORACLE_PASSWORD_FILE=/run/secrets/oracle_dba_pwd
            secrets:
                - oracle_dba_pwd
    
        ords:
            depends_on:
                oraclefree:
                    condition: service_healthy
    
            image: container-registry.oracle.com/database/ords-developer:24.4.0
            networks:
                - ora_app_network
            volumes:
                - ords_config_vol:/etc/ords/config
                - ./setup:/opt/oracle/variables:Z
            ports:
                - 8181:8181
    
    volumes:
        ora_db_vol:
        ords_config_vol:
    
    secrets:
        oracle_dba_pwd:
          external: true
    
    networks:
        ora_app_network:
    

Store the file as compose.yml in a directory of your choice. This post assumes you created a directory ${HOME}/apex to store the compose.yml.

If you are unfamiliar with compose files, please have a look at the reference documentation.

Preparing for the APEX and ORDS Installation

Create a new directory named setup in the same directory where you stored the compose.yml file, then create ./setup/conn_string.txt with the following contents:

CONN_STRING=sys/yourSYSPassword@apex_oraclefree_1:1521/freepdb1
    

Replace yourSYSPassword with the one you used for the container (remember, you created a Podman secret to store it). If you didn’t create the ${HOME}/apex directory previously, make sure to update the apex prefix in the connection string.

First Start

After the preparation is complete, you can bring the database and ORDS containers up with 1 line. If you’re using {Podman,Docker} Desktop, or any solution relying on a “backend VM”, you should bump the VM’s memory to approximately 8 GB. Or even more, if you can afford to. Bring the stack up:

podman-compose up
    

Due to the dependencies defined in the compose file, the database start first. The ORDS container waits for the database to report that it is healthy. When starting for the first time the ORDS container will detect the absence of a configuration and use the connection details provided in ./setup/conn_string.txt to connect to the database and perform the APEX as well as the ORDS installation. You can see this in the logs or on your screen:

[oraclefree] | FREEPDB1(3):Opening pdb with Resource Manager plan: DEFAULT_PLAN                                                                                                         
    [oraclefree] | Completed: Pluggable database FREEPDB1 opened read write 
    [oraclefree] | Completed: ALTER DATABASE OPEN                                                                                                                                           
    [ords]       | INFO : This container will start a service running ORDS 24.4.0 and APEX 24.1.0.                                                                                          
    [ords]       | INFO : CONN_STRING has been found in the container variables file.  
    [ords]       | INFO : Database connection established.                                      
    [ords]       | rm: cannot remove '/opt/oracle/variables/conn_string.txt': Permission denied
    [ords]       | INFO : Apex is not installed on your database.                      
    [ords]       | INFO : Installing APEX on your DB please be patient.                     
    [ords]       | INFO : You can check the logs by running the command below in a new terminal window:                                                                                     
    [ords]       |          docker exec -it b5fcc28fad42 tail -f /tmp/install_container.log
    [ords]       | WARN : APEX can be installed remotely on PDBs, If you want to install it on a CDB,
    [ords]       |        install it directly on the Database and not remotely.
    [oraclefree] | 2025-04-01T07:43:02.655216+00:00 

After the ORDS and APEX installation have finished, you can test if everything was successful by connecting to ORDS using your favourite browser. How do you know if the installer completed its work? Your terminal should show you something along these lines:

[ords]       | java.vm.version=21.0.3+7-LTS-152
    [ords]       | sun.io.unicode.encoding=UnicodeLittle
    [ords]       | jdbc.InitialLimit=10
    [ords]       | db.connectionType=basic
    [ords]       | java.class.version=65.0
    [ords]       | 
    [ords]       | 2025-04-01T07:48:25.521Z INFO        
    [ords]       | 
    [ords]       | Mapped local pools from /etc/ords/config/databases:
    [ords]       |   /ords/                              => default                        => VALID     
    [ords]       | 
    [ords]       | 
    [ords]       | 2025-04-01T07:48:25.603Z INFO        Oracle REST Data Services initialized
    [ords]       | Oracle REST Data Services version : 24.4.0.r3451601
    [ords]       | Oracle REST Data Services server info: jetty/12.0.13
    [ords]       | Oracle REST Data Services java info: Java HotSpot(TM) 64-Bit Server VM  (build: 21.0.3+7-LTS-152 mixed mode, sharing)
    

That’s your clue to check the ORDS landing page. It should display in your browser if you point it to http://localhost:8181/ords/.

Post-Installation Tasks

Once you’re happy that your installation completed successfully, remove ./setup/conn_string.txt if it still exists.

Change the default APEX admin password by logging into the INTERNAL workspace as described in the documentation. After the validation of your environment, you can take this setup to the next level, for example by

  • Changing all passwords in the database
  • Adding TLS to the ORDS container
  • Experimenting with server-side JavaScript in APEX 🙂

You stop the environment using podman-compose down. When it’s down you start everything using podman-compose up -d. Thanks to the database healthcheck and the depends_on directive ORDS won’t start before the database is healthy.

Summary

Podman compose is a great way to bring up multiple containers on a developer laptop. The dependency mechanism ensures that ORDS starts before the database accepts connections, and the entire process is fully automatic. Happy coding!