So far, with the manuals both on Oracle's website as well as the ones found on blogs etc. I never really succeeded in putting up the APEX stack on docker.
However, starting to try with some more (docker) experience after procrastinating APEX in the recent 3 years, and with new energy, I found some key hints to get it done.
This docker project contains the following:
Here's the recipe to get this done:
- Create folders
- Initialize Express DB
- Setup APEX in the Express DB
- Download APEX files
- Create & Run Docker Compose
- Optional: Access APEX from WAN with HTTPS / Reverse Proxy
I wanted to use the following images:
- Express DB:
- ORDS:
The ords-developer image does not contain and install APEX, but there's a solution.
A few remarks about the names of objects:
I generally try to name docker containers, networks etc. by using prefixes so I can identify them easier in, e.g., lists (docker ls ...
) or when searching for their repositories (directories).
In this context, rad
is short for Rapid Application Development, and, since there are several platforms, oracle-apex
is the second prefix.
express
and ords
are then the specifying parts of the name.
In the case of docker service names, I use only express
and ords
because those service names are referred to only within the compose file.
Start in the docker project's direcctory.
According to Oracle, the following rights have to be applied to the directory ./express/oradata
:
-v /opt/oracle/oradata
The data volume to use for the database.
Has to be writable by the Unix "oracle" (uid: 54321) user inside the container.
If omitted the database will not be persisted over container recreation.
sudo bash -c '
mkdir -p ./express/{oradata,cfgtoollogs,scripts/startup,scripts/setup} &&
chown -R 54321:54321 ./express/{oradata,cfgtoollogs} &&
mkdir -p ./ORDS/{variables,config} &&
chown -R 54321:54321 ./ORDS/{config,variables} &&
chmod -R 777 ./ORDS/config
'
The cfgtoollogs
-diretory is for analysis in case of database creation failure (./cfgtoollogs/dbca/XE/XE.log
).
Create .env
file containing a ORACLE_PWD
variable and a password (do not use special characters, only numbers, small and caps for compatibility reasons; Oracle recommends that the password entered should be at least 8 characters in length, contain at least 1 uppercase character, 1 lower case character and 1 digit [0-9]. Note that the same password will be used for SYS, SYSTEM and PDBADMIN accounts):
ORACLE_PWD=<password without quotes of any kind>
, e.g., 1230321abcABC
.
Script: ✔️✔️
#!/bin/bash
# Prompt user for ORACLE_PWD
read -p "Enter a value for ORACLE_PWD: " ORACLE_PWD
# Write the variable to .env file
echo "ORACLE_PWD=$ORACLE_PWD" > ./.env
echo "Password has been written to ./.env"
Download and extract the latest APEX files to the project directory; the APEX ZIP file contains the apex directory as root, so no extra dir has to be created.
If you have unzip: ✔️✔️
curl -o apex.zip https://download.oracle.com/otn_software/apex/apex-latest.zip && \
unzip -o apex.zip
If you have 7z (e.g., Synology NAS): ✔️✔️
curl -o apex.zip https://download.oracle.com/otn_software/apex/apex-latest.zip && \
7z x apex.zip
The files should now reside in ./apex
.
for img in express ords; do
docker pull "container-registry.oracle.com/database/$img:latest"
done
echo "container-registry.oracle.com/database/"{express,ords}":latest" \
| xargs -n1 docker pull
Run the following command to ✔️✔️
- create the network
rad-oracle-apex-temp
- create and run the container
rad-oracle-apex-express-temp
- set up a persistent database (stored in
./express/oradata
)
docker network create rad-oracle-apex-temp & \
docker run \
-d \
--name rad-oracle-apex-express-temp \
--network rad-oracle-apex-temp \
--hostname express \
--env-file ./.env \
-p 1521:1521 \
-v "$(pwd)/express/oradata/:/opt/oracle/oradata" \
-v "$(pwd)/express/cfgtoollogs/:/opt/oracle/cfgtoollogs" \
-v "$(pwd)/apex/:/opt/oracle/oradata/apex" \
container-registry.oracle.com/database/express:latest && \
docker logs -f rad-oracle-apex-express-temp
Note
- Note that
-e ORACLE_PWD=${ORACLE_PWD}
as in the original documentation has been removed from the script above because the password is defined in the .env file- running the container for the first time (initialization of persistent data) takes a long time - on my Synology DS918+, it took ~2.5hrs, on a laptop, however, it takes, e.g., under 10 minutes
Important
If the first time fails, a second run with the code above might solve it.
Keep the container running for the next steps of the installation (until you start the containers with docker-compose).
Already done in the preparation steps above.
- Create a shell in the express container:
docker exec -it rad-oracle-apex-express-temp bash
- Change to the mounted apex directory:
cd /opt/oracle/oradata/apex
- Connect to the DB XEPDB1:
- In separate steps:
- Start SQL:
(note that unlike described in the documentation, steps 4 and 6, instead of
sqlplus /nolog
sql
,sqlplus
is used) - Connect to DB XEPDB1:
- With extra PW prompt:
-
CONNECT SYS@<express hostname>:1521/XEPDB1 as SYSDBA
- enter PW (defined in
.env
-file)
-
- With PW in command:
CONNECT SYS/<ORACLE_PWD>@<express hostname>:1521/XEPDB1 as SYSDBA
- With extra PW prompt:
- Start SQL:
- In single step:
e.g.,
cd /opt/oracle/oradata/apex && sqlplus sys/${ORACLE_PWD}@<express hostname>:1521/XEPDB1 AS SYSDBA
(note thatcd /opt/oracle/oradata/apex && sqlplus sys/${ORACLE_PWD}@express:1521/XEPDB1 AS SYSDBA
${ORACLE_PWD}
does not have to be replaced here since taken from the environment variable in this case; also, this will be used for the CONN_STRING file below, but there, ORACLE_PWD needs to be explicit)
- In separate steps:
- Run install script:
@apexins.sql SYSAUX SYSAUX TEMP /i/
After successful installation, leave SQL open for the next step
- Create the Instance Administration Account:
In the same SQL prompt as before, enter
to create the workspace admin account.
@apxchpwd.sql
- Unlock APEX_PUBLIC_USER account:
In the same SQL prompt as before, enter
ALTER USER APEX_PUBLIC_USER ACCOUNT UNLOCK;
- Change password (optional?):
ALTER USER APEX_PUBLIC_USER IDENTIFIED BY <new_password>;
- Unlimit account expiration:
From a blog post:
- Create unlimited expiration profile apex_public:
create profile apex_public limit password_life_time unlimited;
- Assign profile to user:
alter user apex_public_user profile apex_public;
- Create unlimited expiration profile apex_public:
quit
the SQL promptexit
the container's bash
Note
Things have changed since release of ORDS v25. A container can be started in 2 ways:
- with an interactive CLI can be started in order to generate the configuration files in
./ORDS/config
and establish the connection between ORDS and Express:docker run \ -it \ --rm \ --network rad-oracle-apex-temp \ --name rad-oracle-apex-ords-temp \ -v ./ORDS/config:/etc/ords/config \ container-registry.oracle.com/database/ords:latest \ install
- automated setup (no interaction) - works for this tutorial:
docker run \ -i \ --rm \ --network rad-oracle-apex-temp \ --name rad-oracle-apex-ords-temp \ -v ./ORDS/config:/etc/ords/config \ -v ./apex/:/opt/oracle/apex \ container-registry.oracle.com/database/ords:latest \ install \ --admin-user SYS \ --db-hostname express \ --db-port 1521 \ --db-servicename XEPDB1 \ --feature-sdw true \ --password-stdin < <(grep '^ORACLE_PWD=' .env | cut -d= -f2-)
Double-check if the network name is correct (must be same as for the Express temporary container).
docker rm -f rad-oracle-apex-{ords-temp,express-temp}
services:
express: # XE database
image: container-registry.oracle.com/database/express:latest # 21.3.0-xe
container_name: rad-oracle-apex-express
# hostname: oracledev
restart: unless-stopped
environment:
- ORACLE_PWD=${ORACLE_PWD} # make sure the declaration is in the .env file as ORACLE_PWD=<your password, non complex, min. 8 chars small, cap, & numbers>
networks:
- apex
#ports:
# - 1521:1521
# - 5500:5500
# depends_on:
# - oracle-ords
volumes:
- ./express/oradata:/opt/oracle/oradata
- ./express/scripts/setup:/opt/oracle/scripts/setup
- ./express/scripts/startup:/opt/oracle/scripts/startup
- ./apex:/opt/oracle/apex
healthcheck:
#test command below is with grep because in my case, the output of checkDBstatus.sh is always "The Oracle base remains unchanged with value /opt/oracle" which seems to indicate the DB is fine.
#test: /opt/oracle/checkDBStatus.sh | grep -q 'remains unchanged'
test: [ "CMD", "/opt/oracle/checkDBStatus.sh"]
interval: 30s
timeout: 30s
retries: 100
start_period: 600s
ords:
#image: container-registry.oracle.com/database/ords-developer:latest
image: container-registry.oracle.com/database/ords:latest
container_name: rad-oracle-apex-ords
restart: unless-stopped
#depends_on:
# express:
# condition: service_healthy
volumes:
- ./ORDS/variables:/opt/oracle/variables
- ./ORDS/config:/etc/ords/config
- ./apex/:/opt/oracle/apex
networks:
- apex
ports:
#- 8181:8181 this is for the developer image
- 8080:8080
networks:
apex:
name: rad-oracle-apex
In case you want to debug the healthcheck, use docker inspect --format "{{json .State.Health }}" rad-oracle-apex-express | jq
:
Once the express-container has started up, the exit code changes from 3 over 2 to 0 (0=healthy).
- Go to your instance's APEX homepage, e.g.,
http://<docker-host>
. - Select Oracle APEX (the middle pane)
- Login:
- Workspace:
internal
- User:
ADMIN
- Password:
Welcome_1
- Workspace:
Warning
If you changed the password during log-in check from running the temporary ORDS-Developer container, use the updated password!
- Go to your instance's APEX homepage, e.g.,
http://<docker-host>
. - Select Oracle APEX (the middle pane)
- Go to the bottom of the page and select Administration in the Tasks column
- Login:
- User:
admin
- Password: The one you changed the default password
Welcome_1
to
- User:
Well, that's a whole different story: The workspace/database schema needs to be enabled for SDW as follows:
- Log into the express container's CLI:
- To get to the SQL prompt directly:
docker exec -it oracle-apex-express sqlplus sys/<ORACLE_PWD>@//localhost:1521/XEPDB1 as sysdba
- Via shell:
docker exec -it oracle-apex-express sh
and then entersqlplus sys/<ORACLE_PWD>@//localhost:1521/XEPDB1 as sysdba
at the prompt
- To get to the SQL prompt directly:
- Now, the following must be entered:
BEGIN ords_admin.enable_schema( p_enabled => TRUE, p_schema => 'schema-name', p_url_mapping_type => 'BASE_PATH', p_url_mapping_pattern => 'schema-alias', p_auto_rest_auth => NULL ); commit; END; /
Important
- The value of
p_schema
(schema-name
) has to be all upper case! - The value of
p_url_mapping_pattern
(schema-alias
) has to be all lower case! - The
/
at the end of the statement is important in order to execute the statement.1
E.g.,
BEGIN
ords_admin.enable_schema(
p_enabled => TRUE,
p_schema => 'WORKSPACE1',
p_url_mapping_type => 'BASE_PATH',
p_url_mapping_pattern => 'workspace1',
p_auto_rest_auth => NULL
);
commit;
END;
/
- Optional: Verify if schema (= value used for
p_schema
) has been enabled withselect username from all_users order by username
- Change user password via SQL prompt:
alter user <user> identified by <password>;
(replace<user
) - every workspace, in the database, is basically a user, hence this step - Go to
http(s)://<domain name>/ords/sql-developer
and log in with the credentials used above
Warning
There seems to be a bug in ORDS which prevents objects from loading in SDW's Data Modeler and SQL Navigator. The DB user (equals workspace name) therefore needs to be granted resource permissions as follows:
grant <privilege> to <user>
This solution might work, with me, it didn't. However, the ORDS update from Nov. 8, 2024, solved the issue.
Put the following 2 lines into ./ORDS/config/global/settings.xml
, replacing <your apex domain, no trailing slash>
with your domain's name:
<entry key="security.externalSessionTrustedOrigins">http://<your apex domain, no trailing slash>, https://<your apex domain, no trailing slash>:443</entry>
<entry key="security.forceHTTPS">true</entry>
The complete settings.xml might now look similar to:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>Saved on Mon Oct 21 16:07:13 UTC 2024</comment>
<entry key="database.api.enabled">true</entry>
<entry key="db.invalidPoolTimeout">5s</entry>
<entry key="debug.printDebugToScreen">true</entry>
<entry key="standalone.static.path">/opt/oracle/apex/images</entry>
<entry key="security.externalSessionTrustedOrigins">http://<your apex domain, no trailing slash>, https://<your apex domain, no trailing slash>:443</entry>
<entry key="security.forceHTTPS">true</entry>
</properties>
- Perform all steps described in Install APEX
- Stay connected to DB in sqlplus
- Delete old schema
- Enter
select username from dba_users;
- Identify old schema (starts with
APEX_
and the old version ID, e.g.APEX_240100
) as opposed to new schma (same user name with a higher version ID) - Enter
drop user <user name> cascade;
, e.g.drop user APEX_240100 cascade;
- If an error message is returned (e.g.,
ORA-28014: cannot drop administrative user or role
), enteralter session set "_oracle_script"=TRUE;
and repeat the previous command
- Enter
ORDS has been released in a v25. In order to configure the ORDS images (starting with v25), run a container and start the bash:
docker run -it --name ords_new -v ./ORDS/config:/etc/ords/config container-registry.oracle.com/database/ords:latest <command>
E.g.,
docker run -it --name ords_new -v ./ORDS/config:/etc/ords/config container-registry.oracle.com/database/ords:latest install
- Oracle:
- https://container-registry.oracle.com/ords/ocr/ba/database/express
- https://container-registry.oracle.com/ords/ocr/ba/database/ords-developer
- https://container-registry.oracle.com/ords/ocr/ba/database/ords
- https://docs.oracle.com/en/database/oracle/sql-developer-web/19.1/sdweb/about-sdw.html#GUID-A79032C3-86DC-4547-8D39-85674334B4FE
- Schnell und einfach: Erstellen einer lokalen APEX-Umgebung mit Docker-Compose [https://blog.ordix.de/erstellen-einer-lokalen-apex-umgebung-docker-compose]
- https://github.com/akridge/oracle-apex-docker-stack?tab=readme-ov-file#how-to-install
- Docker Compose for Oracle APEX (https://tm-apex.hashnode.dev/docker-compose-for-oracle-apex)
- Oracle 23c Free Docker, APEX & ORDS – all in one simple guide - Pretius (https://pretius.com/blog/oracle-apex-docker-ords/)
- ... and, finally, for SSL: How to Secure Oracle APEX Development Environment with Free SSL from Let's Encrypt?
- oracle-apex-docker-stack
- https://github.com/oraclebase/dockerfiles/blob/master/ords/ol8_ords/Dockerfile
- https://oracle-base.com/articles/linux/articles-linux#docker
- https://oracle-base.com/blog/2021/11/05/apex-21-2-vagrant-and-docker-builds/
- https://registry.hub.docker.com/r/esestt/oracle-apex
- OraOpenSource/apex-nitro#334
- https://chronicler.tech/mint-oracle-18c-xe/
- https://kenny-chlim.medium.com/oracle-18c-xe-installation-debian-10-4-buster-9cbf7f957d9f
- https://luca-bindi.medium.com/oracle-xe-and-apex-in-a-docker-container-25f00a2b8306
- https://www.google.com/search?q=sqlplus+begin+end
- https://www.google.com/search?q=install+%22sql+developer+web
- https://oracle-base.com/articles/misc/oracle-rest-data-services-ords-sql-developer-web#create-test-db-user
- https://matthiashoys.wordpress.com/2020/01/03/how-to-enable-sql-developer-web-sdw-on-ords-19-4/
- https://www.oracletutorial.com/oracle-administration/oracle-list-users/
- https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/24.3/books.html#AELIG90217
... and a few more.
- Report Generation
- Responsive Classic Report in ORACLE APEX [https://blogs.ontoorsolutions.com/post/responsive-classic-report-in-oracle-apex/]
- How to control interative report columns [https://forums.oracle.com/ords/apexds/post/how-to-control-interactive-report-columns-width-9688]
- Authentication
- How to log in with user credentials from database table [https://forums.oracle.com/ords/apexds/post/how-to-log-in-with-user-credentials-from-database-table-6626]
- Custom Authentication in Oracle APEX [https://o7planning.org/10443/custom-authentication-in-oracle-apex]
- Keycloak
- https://levelupdata.eu/oracle-apex-keycloak/
- https://stackoverflow.com/questions/45352880/keycloak-invalid-parameter-redirect-uri
- https://stackoverflow.com/questions/53564499/keycloak-invalid-parameter-redirect-uri-behind-a-reverse-proxy
- OpenID Connect with Nextcloud and Keycloak [https://janikvonrotz.ch/2020/10/20/openid-connect-with-nextcloud-and-keycloak/]