# Oracle23cFREE and ORDS/APEX on Windows using Podman

My company laptop is large enough (32Gb RAM) to host the containerized versions of the Oracle 23c FREE edition and ORDS. But it has also some limitations on installing and configuring things. I mainly ran into networking issues, which are probably related to the fact that I can't change firewall settings.

This is one of the reasons that led me to use Podman instead of Docker Desktop: multiple containers in a *pod* can talk to each other over `localhost:port`. The second reason is that [https://container-registry.oracle.com/ords/ocr/ba/database](https://container-registry.oracle.com/ords/ocr/ba/database) also uses Podman in its examples.

# Prerequisites

## WSL2

You must have WSL2 (Windows Subsystem for Linux) installed. I'm not going to describe that here.

## Podman

On my company laptop, the easiest way to install Podman was to run

```plaintext
winget install RedHat.Podman
```

from a DOS\_box.

For those that want a GUI to go along with that, use Podman Desktop:

```plaintext
# Optional:
winget install RedHat.Podman-Desktop
```

While using that GUI, I found it *not* showing the things I created with it. Pull an image and it does not appear in the list of images. Create a container and it does not appear as a container. Only by going through a rather obscure menu could I refresh the GUI to show the created items. So I switched to the command line.

Being used to Portainer, I opted to use that instead to check on things. Portainer works with Podman, since Podman exposes the same API as Docker does. The only thing Portainer does not show is *Pod*s, but the *Containers* in a Pod are all individually available.

# Setting up

After having installed Podman (just another Windows CLI program) you have to initialize the default *Podman Machine*.

This is another reason why I prefer Podman over Docker Desktop: The Podman Machine is a small WSL distribution. Docker Desktop, on the other hand, soiled my default WSL distribution (which happened to be an Oracle Linux) by adding Docker stuff into that.

So only once, you have to run

```plaintext
podman machine init
```

Running `wsl --list` shows that a new distribution called `podman-machine-default` was created.

If you'd like to add that little machine to Windows Terminal, use `wsl.exe -d podman-machine-default` as command line.

# Starting the Podman Machine

While the command line `podman` program is always available, Podman (the system) needs the Podman Machine to be running (under WSL). So, after the *initialization*, whenever WSL was *shutdown* or whenever the Windows host was *rebooted*, you must start the Podman Machine using

```plaintext
podman machine start
```

# Preparations

## Pulling images

Images are like installation packages. While it is possible to create a container without *pulling* (downloading) the image it will be created from first, I like to pull images separately. Seeing that the one for Oracle 23c FREE is &gt; 9Gb is just one of the reasons.

```plaintext
# 1) The database
podman pull container-registry.oracle.com/database/free:latest
# 2) ORDS (including Apex and Database Actions etc)
podman pull container-registry.oracle.com/database/ords:latest
# 3) Optional: Portianer
podman pull docker.io/portainer/portainer-ce:latest
```

These downloads end up in the filesystem of the `podman-machine-default` WSL distribution.

You can list the available images using `podman images`.

## Volumes

A *Volume* is persisted storage for a *Container*. It is possible to destroy a Container, but keep its Volume and use that Volume for another Container (even one based on a different Image). It is possible to use ordinary Windows folders as persisted storage, but real Volumes have advantages: performance and security.

The database needs a Volume to store its data files (etc), and ORDS also needs a Volume to store its configuration. (Portainer also uses a Volume, but I didn't bother creating that beforehand.)

```plaintext
podman volume create orcl23c_data
podman volume create ords_config
```

At this moment those Volumes, `orcl23c_data` and `ords_config`, are nothing but names. (And maybe empty directories in the `podman-machine-default` WSL distribution.)

Use `podman volume ls` to list the volumes.

## The Pod

A Pod is somewhere to put containers together. The main benefit, for me, is that containers in a Pod are like distinct programs on one computer:

* they can communicate with each other over `localhost:port`.
    
* they share a single process space
    
* ...
    

One drawback of a Podman Pod is that it is the Pod that describes which ports (used by the Container inside the Pod and determined by the images they are based on) are mapped/exposed to which ports on Windows.

It is not possible to add a port later on. (While it is possible to add another container to a pod, later in the pod's life).

From the documentation on [https://container-registry.oracle.com/ords/ocr/ba/database/free](https://container-registry.oracle.com/ords/ocr/ba/database/free) and [https://container-registry.oracle.com/ords/ocr/ba/database/ords](https://container-registry.oracle.com/ords/ocr/ba/database/ords) I know that only 1521 (DB Listener) and 8181 (http cleartext ORDS) are used. But sometime in the future, I'd like to talk https to ORDS, so I foresee already using 8443 as well.

I will be mapping ports in the pod (1521, 8181 and 8443) to the *same* ports at the Windows level since I have nothing else using those ports already.

The commend to create the Pod:

```plaintext
podman pod create --name orcl --infra-name pod_orcl_infra -p 1521:1521 -p 8181:8181 -p 8443:8443
```

### Explanation

`podman pod create` The command to create a pod using Podman. Wow!

`--name orcl` The pod will be named `orcl`.

`--infra-name pod_orcl_infra` Inside every Pod, there is always an extra Container, called the Infrastructure Container, that takes care of pod-relative functionality. This option gives that Container a name, `pod_orcl_infra`, indicating that it is the Infra Container for Pod `orcl`. This is mainly handy when looking at Portainer since it will show all Containers individually, not as part of a Pod.

`-p 1521:1521`, `-p 8181:8181`, `-p 8443:8443` Only those three ports will be exposed. I expose them on the same port numbers. Using for example `-p 8521:1521` would expose port **1**521 (from inside the container) as port **8**521 (in Windows). Using a Windows client, I would then connect using `sys/password@localhost:8521/FREE`, while inside the Container the listener is listening to port 1521 as usual.

# The DB container

The Containers are created using an Image (already pulled) and in our case, they will go in Pod (already created). They will use Volumes that also have already been created.

The ORDS container (which includes Apex) will be created later. First, let's create the DB Container:

```plaintext
podman run -d --pod orcl --name=orcl23c -v orcl23c_data:/opt/oracle/oradata --tz=Europe/Brussels container-registry.oracle.com/database/free:latest
```

## Explanation

`podman run` The container will not only be created but also immediately run.

`-d` run in detached mode. Otherwise, you wouldn't get the Windows command prompt back...

`--pod orcl` Put this Container in a Pod, the one called `orcl`.

`--name=orcl23c` Call this Container `orcl23c`. This name is needed to reference this container later on.

`-v orcl23c_data:/opt/oracle/oradata` Mount the Volume called `orcl23c_data` (already created) as directory `/opt/oracle/oradata` in the Container. That directory will be created based on the Image.

`--tz=Europe/Brussels` Use that timezone. Otherwise, file timestamps (and DB-time!) in the container would be different from the Windows time.

`container-registry.oracle.com/database/free:latest` Use this Image to create the Container. So in the container registry of Oracle (located at [container-registry.oracle.com](http://container-registry.oracle.com)) find Image `database/free` and get its `latest` version. In our case we have already pulled that image, no further download is needed.

### Some things that are *not* in there:

* I did not set a password (`ORACLE_PWD`).  
    The container provides a way to that later (`setPassword.sh`). This way, there will be no environment variable exposing the password.  
    Using `podman secret` did not work for me. Presumably, it is a Windows thing regarding quotes etc.
    
* I did not set `ORACLE_CHARACTERSET`. The default of `AL32UTF8` suits me fine.
    
* I did not add volumes for additional *startup* or *setup* scripts.
    

## Verification

The `podman run` command takes a surprisingly short amount of time to start the database: just a few seconds.

You can use another DOS prompt to execute `podman logs orcl23c`. This will show the logs written by the Container called `orcl23c`. Quite soon this will show

```plaintext
#########################
DATABASE IS READY TO USE!
#########################
```

This log also reveals that the instance is called `FREE` and that there is a PDB called `FREEPDB1`.

### Optional:

From inside the Container, you can already connect to the database (remember that no password has been set yet) using the Bequath connection.

To do this, either create a new Windows Terminal profile with as command line `podman exec -it orcl23c bash`.

Or use that same command straight from the DOS command line.

This tells `podman` to start an interactive (`-i`) terminal (`-t`) (a TTY) to the `orcl23c` Container and launch the `bash` command in there. In other words: start the shell in that Container.

In that bash shell, you can now run `sqlplus / as sysdba`.

## Set the passwords

Oracle provides in the container a utility to set the passwords for `SYS`, `SYSTEM` and `PDBADMIN`. In the end, it does nothing more than

```plaintext
sqlplus / as sysdba
      ALTER USER SYS IDENTIFIED BY ...;
      ALTER USER SYSTEM IDENTIFIED BY ...;
      ALTER SESSION SET CONTAINER=FREEPDB1;
      ALTER USER PDBADMIN IDENTIFIED BY ...;
```

Which you could do in the bash shell shown above. But instead of running bash, you can tell Podman to run that script:

```plaintext
podman exec orcl23c ./setPassword.sh S€cr€tP4ssword
```

In other words: Dear `podman`, in Container `orcl23c`, please `exec`ute script `./setPassword.sh` which takes as its first parameter my new password: `S€cr€tP4ssword`. There is no `-it` in there since we're not interested in interactivity. The output is returned though.

## Connect

At this moment it should be possible to use your favorite DB client (on your Windows machine) and connect to the database.

Using the following connection settings in SQL Developer

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1697638996635/09c4f98d-30b5-43b2-8324-452895a35c44.png align="center")

I can do

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1697638937835/a5db6c83-a35f-4894-88ab-137e90891963.png align="center")

Notice the lack of `from dual`, a new 23c feature.

I guess that *supposing I were able to open port 1521 on my laptop to the network*, remote machines would also be able to use my DB.

# The ORDS Container

Which also includes setting up Apex in the DB!

## Preparation

This container requires setting up a `conn_string.txt` file that contains the connection string to the DB. The directory containing that file will be mounted as a volume. Next, the starting container will read that file, and if all goes well, it will delete the file.

Contrary to the prior volumes created for performance and persistence, this one is just for one-time usage. It is therefore okay to mount an ordinary *host folder*.

So make a new folder. Its location and name do not really matter but mine was `C:\Users\jvdboss1\Documents\Podman\ords_secrets`. You'll find that path later on in the Container creation command.

In that folder, I created the expected `conn_string.txt` file. And that file contained

```plaintext
CONN_STRING=sys/S€cr€tP4ssword@localhost:1521/FREEPDB1
```

The connection must be for `sys` since an `as sysdba` connection is used when installing Apex.

## Creating the container

As explained on [https://container-registry.oracle.com/ords/ocr/ba/database/ords](https://container-registry.oracle.com/ords/ocr/ba/database/ords) these are all the preparations needed.

```plaintext
podman run -d --pod orcl --name ords -v C:\Users\jvdboss1\Documents\Podman\ords_secrets:/opt/oracle/variables -v ords_config:/etc/ords/config/ container-registry.oracle.com/database/ords:latest
```

### Explanation

`podman run` The container will not only be created but also immediately run.

`-d` run in detached mode. Otherwise, you wouldn't get the Windows command prompt back...

`--pod orcl` Put this Container in a Pod, the one called `orcl`. (Which now already has the `orcl23c` container running inside of it)

`--name=ords` Call this Container `ords`. This name is needed to reference this container later on.

`-v C:\Users\jvdboss1\Documents\Podman\ords_secrets:/opt/oracle/variables` Mount the Windows folder `C:\Users\jvdboss1\Documents\Podman\ords_secrets` as directory `/opt/oracle/variables` in the Container. That directory will be created based on the Image.

`-v ords_config:/etc/ords/config/` Mount the Volume called `ords_config` (already created) as directory `/etc/ords/config/` in the Container. That directory will be created based on the Image.

`container-registry.oracle.com/database/ords:latest` Use this Image to create the Container. So in the container registry of Oracle (located at [container-registry.oracle.com](http://container-registry.oracle.com)) find Image `database/ords` and get its `latest` version. In our case we have already pulled that image, no further download is needed.

## Keeping an eye on things

The installation of Apex takes quite some time, so it is nice to be able to see the output of it.

From a separate dos-prompt, run `podman logs -f ords`. The `-f` option makes sure you keep *following* the logs, not just output the current state of the log, once.

This will tell you

```plaintext
INFO : This container will start a service running ORDS 23.2.3 and APEX 23.1.0.
INFO : CONN_STRING has been found in the container variables file.
INFO : Database connection established.
INFO : Apex is not installed on your database.
INFO : Installing APEX on your DB please be patient.
INFO : You can check the logs by running the command below in a new terminal window:
        docker exec -it orcl tail -f /tmp/install_container.log
WARN : APEX can be installed remotely on PDBs, If you want to install it on a CDB,
       install it directly on the Database and not remotely.
```

So, it explains there is a command to follow what is being done for the Apex install. But the listed command needs some adapting: We're using Podman instead of Docker and our container is called `ords` instead of `orcl`. So,

```plaintext
podman exec -it orcl tail -f /tmp/install_container.log
```

can be used to tail the Apex installation. Like before, this command will use container `ords` to `exec`ute the `tail -f /tmp/install_container.log` command.

Eventually (after a couple of minutes) the tail will end, stating

```plaintext
Disconnected from Oracle Database 23c Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free
Version 23.3.0.23.09
```

In the other DOS-terminal, where the container creation was running, more important information can be seen:

```plaintext
INFO : Use below login credentials to first time login to APEX service:
        Workspace: internal
        User:      ADMIN
        Password:  Welcome_1
```

And regarding ORDS (which is installed *after* Apex):

```plaintext
ORDS: Release 23.2 Production on Wed Oct 18 16:04:15 2023

Copyright (c) 2010, 2023, Oracle.

Configuration:
  /etc/ords/config/

The setting named: jdbc.InitialLimit was set to: 10 in configuration: default
INFO : Starting the ORDS services with the following database details:
INFO :   localhost:1521/FREEPDB1.

ORDS: Release 23.2 Production on Wed Oct 18 16:04:17 2023

Copyright (c) 2010, 2023, Oracle.

Configuration:
  /etc/ords/config/

2023-10-18T16:04:17.529Z INFO        HTTP and HTTP/2 cleartext listening on host: 0.0.0.0 port: 8181
2023-10-18T16:04:17.579Z INFO        The document root is serving static resources located in: /etc/ords/config/global/doc_root
```

And finally, you can surf to [http://localhost:8181/ords](http://localhost:8181/ords/) to get

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1697645613365/9ff0ff28-0e86-457e-aa1c-3ee8626b6c88.png align="center")

or to [http://localhost:8181/ords/apex](http://localhost:8181/ords/apex) to get

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1697645492149/1923b8dd-e9aa-4a5f-83c0-9216915f6bae.png align="center")
