Skip to main content

Command Palette

Search for a command to run...

Containerize Oracle Database, ORDS and APEX from Oracle Container Registry with Docker Compose

Updated
7 min read
Containerize Oracle Database, ORDS and APEX from Oracle Container Registry with Docker Compose
W
I started working with Oracle during my studies, and in 2017, I joined Pretius as Oracle APEX Developer. Here, I work on international projects across various industries like healthcare, banking and public sector. I enjoy sharing my knowledge with others, so I write blog posts in my free time. You can read my articles on Pretius blog and on my personal blog page on Hashnode. My mission is to tell you about complex things in simple words. I also volunteered in the 'Rewild Earth' APEX project led by Steven Feuerstein as a Google Drive integration expert. I am a conference speaker on events like APEX Meetup Poland and APEX World 2025. I have been awarded the Member of the Month December 2020 title by apex.world portal. In my free time, I enjoy driving sports cars and trying to make the best espresso ever.

Introduction

Sooner or later (definitely sooner in my case) you will need an extra Oracle environment. There is lot of reasons why you will - trying unknown features, building a new prove of concept, preparing a hotfix, or just doing some experimental work in a safe environment, where you can destroy as much as you want. While I have been learning how to use Docker, my goal is to build a working Oracle instance using containers from the official Oracle Container Registry. In this article, I will describe how to use the following containers working together:

Alternatives

There is a couple of available options that can be treated as alternatives, like:

  • Local environment setup (can be sometimes painful)

  • Hashicorp Vagrant builds

  • Oracle VirtualBox Pre-Built Developer Virtual Machine

  • Another database started on your Oracle Cloud instance

All of them are able to meet the need for a fresh copy of the environment and each has their own pros and cons.

Motivation

It would be nice to do the setup quickly and the entire process should take up to standard coffee break :-)

However, I am conscious that some of you want to be pure Oracle Developers and learning new not-related technology sounds like a punishment. Don't worry - my setup concept is easy to understand and requires a minimal effort to start up a new Oracle instance on Docker.

That is why I decided to go for Docker Compose rather than calling particual Docker commands in CLI. All in all, just copy & paste, give your project-specific input and nothing else.

Prerequisites

  • I am using Windows 10 with virtualization enabled

  • I have Docker Desktop installed on my machine

  • I have written my scripts in Visual Studio Code with official Docker extension included, which is really helpful!

💡
There are some cool Oracle-oriented Docker tutorials, but I could not find an article how to work with Docker Compose in a holistic approach. If doubled someone unintentionally, just give me a shout.

Step by Step Configuration

Setup your project

I have assumed that my folder structure for this demo looks like this:

Folder structure proposal

Please notice there is also a docker-compose file in YAML format.

Create docker-compose file

In this step we will provide a yaml representation describing Docker containers that are involved in this configurations and how they depend on each other. Copy the following script to your file. It is time to see what is inside:

services:
  db:
    image: container-registry.oracle.com/database/free:latest
    hostname: database
    ports:
      - 1522:1521
      - 5501:5500
    environment:
      - ORACLE_PWD=pwd
      - ORACLE_CHARACTERSET=AL32UTF8
    volumes:
      - ./oracle_oradata/:/opt/oracle/oradata
      - ./oracle_startup/:/opt/oracle/scripts/startup
      - ./oracle_setup/:/opt/oracle/scripts/setup
  apex:
    image: container-registry.oracle.com/database/ords-developer:latest
    ports:
      - 8181:8181
    volumes:
      - ./ords_secrets/:/opt/oracle/variables
      - ./ords_config/:/etc/ords/config/
    depends_on:
      db:
        condition: service_healthy
💡
Starting from autumn 2024, Oracle Database 23ai Free is available for Oracle Linux ARM. That means a huge help for Mac users and you do not need to try magic tricks to have your dockerized Oracle Database up and running. I have updated this blogpost to provide you docker-compose file working on Mac - check below!. Also, I want to thank Rafał Grzegorczyk, who really supported me on testing the new version of 23ai Free image.
services:
  db:
    image: container-registry.oracle.com/database/free:23.5.0.0-arm64
    hostname: database
    ports:
      - 1522:1521
      - 5501:5500
    environment:
      - ORACLE_PWD=pwd
      - ORACLE_CHARACTERSET=AL32UTF8
    volumes:
      - ./oracle_oradata/:/opt/oracle/oradata
      - ./oracle_startup/:/opt/oracle/scripts/startup
      - ./oracle_setup/:/opt/oracle/scripts/setup
  apex:
    image: container-registry.oracle.com/database/ords-developer:latest
    ports:
      - 8181:8181
    volumes:
      - ./ords_secrets/:/opt/oracle/variables
      - ./ords_config/:/etc/ords/config/
    depends_on:
      db:
        condition: service_healthy

So what is happening there? Let me explain in a few short points:

There are two services defined. Both are pointing to the latests container versions of Oracle DB Express and ORDS with APEX. In addition, I have specified the hostname property for the db container to be just database. As you will see later, that name will be used to run ORDS and APEX container on top of the db service.

db:
  image: container-registry.oracle.com/database/free:latest
  hostname: database

apex:
  image: container-registry.oracle.com/database/ords-developer:latest

I have also specified the port mappings like HOST PORT:CONTAINER PORT. Both containers exposes their own ports and we will use them to connect to the services from my laptop.

# db
ports:
  - 1522:1521
  - 5501:5500

#apex
ports:
  - 8181:8181

For the database container, I have passed values for some environment variables like the SYS user password and the character set.

# db
environment:
  - ORACLE_PWD=pwd
  - ORACLE_CHARACTERSET=AL32UTF8

Both services contain the specification of volumes. In short, we specify the mapping between folder on my local file system (we have created them before) and the container's one. According to the documentation, they can be used for running some scripts for setup actions or for a startup. In my demo, they are all empty, but I am referencing them to be sure that they will be mounted properly.

# db
volumes:
  - ./oracle_oradata/:/opt/oracle/oradata
  - ./oracle_startup/:/opt/oracle/scripts/startup
  - ./oracle_setup/:/opt/oracle/scripts/setup

# apex
volumes:
  - ./ords_secrets/:/opt/oracle/variables
  - ./ords_config/:/etc/ords/config/

This point is really important. By default, all the containers run by Docker Compose are started at the same moment. But, that is not our case. First, we need the database ready to work, and then the ORDS/APEX container should join to ensure the successful setup. This is the place where another part of docker-compose comes into play. We will not move forward without this part.

# apex
depends_on:
  db:
    condition: service_healthy

Create connection string for APEX container

We are almost done! Note that we just created few folders and docker-compose file. This kind of configurations is ready to start on Docker in most of use cases. But, we want to achieve something extra - we want to install APEX and ORDS on top of our container database to make them working together.

To do so, create an empty text file named conn_string.txt and put it inside ords_secrets folder. Paste the following text inside:

CONN_STRING="sys/pwd@database:1521/FREEPDB1"

We are using the SYS user and the password set in our docker-compose file. What is more, the DB hostname is just database what is set in docker-compose too. By default, the Oracle Database container creates a pluggable database called XEPDB1. We are using the database container port (1521), since Docker Compose creates a new network for all of the containers taking part in the configuration. As the documentation states, this file will be removed automatically after successful setup of ORDS/APEX container.

Run & Test

Go to oracle-on-docker folder using your favorite command line tool. Enter the following and press enter. Make sure that Docker Desktop is running.

docker-compose -f ./oracle-on-docker/docker-compose.yml up

This part can take a while during the first execution. You can track the progress in the console output.

Oracle Database container is starting up

If you have not noticed any errors during the instance creation, we can now test if all works fine. Please remember that first run can take a while, so be patient and check the console logs from time to tome.

SQL Developer - I am using port 1522 as I stated it in my docker-compose file.

ORDS & APEX - open your browser and go to this address

http://localhost:8181/ords/

# Workspace: internal
# User:      ADMIN
# Password:  Welcome_1

ORDS and APEX are ready to go

Now you can browse your folder structure - you will see that some of them have new content, depending on their purpose defined at the volume mapping stage.

Now our folders look a bit different

To switch off our Oracle instance, just run the following command or do some clicks from Docker Desktop.

docker-compose -f ./oracle-on-docker/docker-compose.yml down

And to run them again...

docker-compose -f ./oracle-on-docker/docker-compose.yml up

Remember - you can check the status of the containers by running this command.

docker ps

Summary

We are done - now you know another way of setting up the local Oracle instance with APEX. I hope it wasn't painful for you and this tutorial will encourage you to do more experiments in the future! Repositories like Dockerhub offer a lot of amazing container images to discover.

Post Scriptum

I am aware of that there are many nuances and specific case-related issues in that area. If this kind of beginner guide is not detailed as you need, please write a comment below or contact me in DM.

O

Thanks for this post, waiting for a long time, I followed exactly, but in the console I noticed this error apex-1 | INFO : APEX has been installed. apex-1 | INFO : Configuring APEX. apex-1 | INFO : APEX_PUBLIC_USER has been configured as oracle. apex-1 | INFO : APEX ADMIN password has configured as 'Welcome_1'. apex-1 | INFO : Use below login credentials to first time login to APEX service: apex-1 | Workspace: internal apex-1 | User: ADMIN apex-1 | Password: Welcome_1 apex-1 | INFO : Preparing ORDS. apex-1 | apex-1 | Error: ORDS requires Java 17 and above to run. apex-1 | Found Java version 11.0.16.1. apex-1 | Please set JAVA_HOME to appropriate version and update PATH if necessary. apex-1 | apex-1 | grep: /etc/ords/config/global/settings.xml: No such file or directory apex-1 | grep: /etc/ords/config/global/settings.xml: No such file or directory apex-1 | grep: /etc/ords/config/databases/default/pool.xml: No such file or directory apex-1 | grep: /etc/ords/config/databases/default/pool.xml: No such file or directory apex-1 | grep: /etc/ords/config/databases/default/pool.xml: No such file or directory apex-1 | grep: /etc/ords/config/databases/default/pool.xml: No such file or directory apex-1 | INFO : Starting the ORDS services. apex-1 | apex-1 | Error: ORDS requires Java 17 and above to run. apex-1 | Found Java version 11.0.16.1. apex-1 | Please set JAVA_HOME to appropriate version and update PATH if necessary. apex-1 | apex-1 exited with code 1 db-1 | 2024-12-17T06:40:17.673803+00:00

A

Hello, I tried to reproduce these instructions on a MacBook with Apple Silicone (I chose the ARM version of the DB). Unfortunately, the installation of the ORDS-DEVELOPER container aborts every time. After some research on the Internet, I could not find any clues as to what the problem might be. Even the demonstration within the documentation of the container ultimately led to the same error.

Do you have any other idea what the problem could be?

conn_string.txt: CONN_STRING="sys/pwd@database:1521/FREEPDB1"

I hope the formatting of the following will work:

db-1 | Completed: ALTER DATABASE OPEN db-1 | 2024-11-22T08:31:30.289726+00:00 db-1 | =========================================================== db-1 | Dumping current patch information db-1 | =========================================================== db-1 | No patches have been applied db-1 | =========================================================== db-1 | 2024-11-22T08:31:30.473700+00:00 db-1 | FREEPDB1(3):TABLE SYS.ACTIVITY_TABLE$: ADDED INTERVAL PARTITION SYS_P333 (2) VALUES LESS THAN (202) apex-1 | INFO : This container will start a service running ORDS 24.3.1 and APEX 24.1.0. apex-1 | INFO : CONN_STRING has been found in the container variables file. apex-1 | INFO : Database connection established. apex-1 | INFO : Apex is not installed on your database. apex-1 | INFO : Installing APEX on your DB please be patient. apex-1 | INFO : You can check the logs by running the command below in a new terminal window: apex-1 | docker exec -it 8a33b49c57b0 tail -f /tmp/install_container.log apex-1 | WARN : APEX can be installed remotely on PDBs, If you want to install it on a CDB, apex-1 | install it directly on the Database and not remotely. apex-1 | ERROR: APEX installation failed apex-1 exited with code 1