---
title: "How to Run Oracle Database on a Mac (Apple Silicon) in 2026"
description: "Oracle Database on a Mac: run 26ai Free on Apple Silicon in Docker without emulation, use FREEPDB1, create a user, keep data, fix connection errors."
url: "https://jamsql.com/blog/2026-10-14-run-oracle-database-on-mac/"
html_url: "https://jamsql.com/blog/2026-10-14-run-oracle-database-on-mac/"
generated: "2026-10-14T21:24:01.374Z"
---

Published: 2026-10-14

# How to Run Oracle Database on a Mac (Apple Silicon) in 2026

Oracle ships no macOS build of Oracle Database, so on a Mac you run it as a Linux container. Since Oracle Database 23.5 Free there is an Arm64 Linux build, and the `gvenzl/oracle-free` image — Oracle AI Database 26ai Free, release 23.26.3 at the time of writing — runs on Apple Silicon without Rosetta. On an M4 Max it accepted connections a median of 20 seconds after `docker run` and held about 2.5 GiB of memory. Connect to `localhost:1521/FREEPDB1`: the service name is where most first connections go wrong. Among new Jam SQL Studio installations over six weeks, 42% of those that tried to reach an Oracle database never connected, against 17% for SQL Server, and “service not registered with the listener” was the most common error. Below: the commands, what we measured, and what each error means.

## The short version

```bash
docker run -d --name oracle \
  -p 1521:1521 \
  -e ORACLE_PASSWORD=ChangeMe_2026 \
  -e APP_USER=app -e APP_USER_PASSWORD=App_2026 \
  -v oracle-data:/opt/oracle/oradata \
  gvenzl/oracle-free:23.26.3

docker logs -f oracle      # wait for: DATABASE IS READY TO USE!
```

Then connect any client with these values:

Field

Value

Host / port

`localhost` / `1521`

Service name

`FREEPDB1` (not `FREE`, `XE`, `XEPDB1` or `ORCL`)

Application user

`app` / `App_2026` — created in `FREEPDB1` by `APP_USER`

Admin user

`system` / the `ORACLE_PASSWORD` value

EZConnect string

`localhost:1521/FREEPDB1`

JDBC URL

`jdbc:oracle:thin:@//localhost:1521/FREEPDB1`

The tag is pinned on purpose. `latest` pointed at the same image (digest `7ed34d0ade89`) when we checked on October 14, 2026, but it moves with every release, and a later `docker pull` would change the database version under you. The rest of this guide explains each part of that command and what happens when one of them is missing.

## Pick a container runtime

The image is an ordinary OCI image, so Docker Desktop, Colima and Podman all run it with the same arguments. Every number in this post comes from Docker Desktop 4.54.0 on an M4 Max; we did not repeat the runs on Colima or Podman. Two things differ between them, and both are about the Linux VM the runtime starts:

-   **Memory.** The container used about 2.5 GiB once the database was open (more on that below), and its startup script prints a warning when it sees less than 2 GB. Colima’s default VM has 2 GiB of memory, per its [README](https://github.com/abiosoft/colima), which leaves nothing for anything else — start it with `colima start --memory 4`. On Podman, stop the machine and run `podman machine set --memory 4096`. In Docker Desktop the limit is under **Settings → Resources**.
-   **CPU architecture.** Leave it alone. All three pick the arm64 variant of a multi-arch image on an M-series Mac. Don’t add `--platform linux/amd64` (or create a Colima VM with `--arch x86_64`) because an old tutorial did — the next section shows what that costs.

## What runs on Apple Silicon without emulation

Oracle’s [Database Free page](https://www.oracle.com/database/free/) says Mac developers “using Arm-based or Intel-based computers” can run Oracle AI Database 26ai Free “using a Docker image”. The [gvenzl/oci-oracle-free README](https://github.com/gvenzl/oci-oracle-free) dates the change: Oracle has provided Arm ports since Oracle Database 23.5 Free, and the images have been multi-arch since then. Older editions never got an Arm64 build. We read each image’s manifest on October 14, 2026:

Image

Database

Architectures

On an M-series Mac

`gvenzl/oracle-free` (all tags: regular, `-slim`, `-full`, `-faststart`)

26ai Free, 23.26.3

amd64, arm64

Runs as arm64

`container-registry.oracle.com/database/free`

26ai Free (Oracle’s own image)

amd64, arm64

Runs as arm64

`gvenzl/oracle-xe` (11g, 18c, 21c)

Express Edition; last image update October 2024

amd64 only

x86-64 emulation

`container-registry.oracle.com/database/express`

21c Express Edition

amd64 only

x86-64 emulation

Oracle’s Enterprise Edition images sit behind a registry sign-in and licence acceptance, so we did not check them. On the Docker Desktop setup above, an amd64 container ran under Rosetta: `/proc/cpuinfo` inside it reported the vendor `VirtualApple`.

To see what emulation costs, we ran the amd64-only tag `23.26.3-slim-amd64` with `--platform linux/amd64` and compared it with the arm64 `23.26.3-slim` on the same Mac. We did not run the XE images themselves; the amd64 build of the same Free release is the closest like-for-like comparison. Under Rosetta the database started without errors, ready in 22.1 and 31.1 seconds over two runs, against a 17.0-second median for the arm64 slim tag; with the spread we saw between runs, two runs cannot show whether emulation slows startup. The difference showed once it was running:

Measurement

arm64 (`-slim`)

amd64 under Rosetta (`-slim-amd64`)

PL/SQL loop, 20 million iterations

2.59 s

5.27 s

Insert 1 million generated rows

0.76 s

1.25 s

Container memory while idle

2.06 GiB

3.37–3.48 GiB

CPU while idle

about 1%

about 2%

Times are medians of three runs, measured with SQL\*Plus `SET TIMING ON`. Twice the CPU time for PL/SQL and over 1 GiB more memory is tolerable for a quick test, but there is no reason to accept it when an arm64 image exists. There is also a deadline: Apple’s [September 1, 2026 developer notice](https://developer.apple.com/news/?id=w5ngl9k2) says macOS 27 is the final release to support Rosetta for Intel apps. It does not mention the Rosetta for Linux that Docker Desktop uses here, so treat an emulated Oracle as a stopgap rather than a setup to build on.

## Which image flavor to use

The gvenzl image comes in four flavors with the same database inside. We pulled the 23.26.3 tags and timed fresh containers from `docker run` to the `DATABASE IS READY TO USE!` line in the log, five runs each:

Tag

Download

On disk

Ready after: median (range)

Memory at ready

`23.26.3` (regular; also `latest`)

1.00 GB

3.33 GB

20.1 s (7.3–26.0, 5 runs)

2.0 GiB

`23.26.3-slim`

714 MB

2.29 GB

17.0 s (6.1–23.9, 5 runs)

2.0 GiB

`23.26.3-faststart`

1.49 GB

7.11 GB, of which 3.33 GB is shared with the regular tag

22.3 s (9.5–36.5, 6 runs)

2.0 GiB

`23.26.3-full`

2.01 GB (Docker Hub)

not pulled

not measured

—

Existing container, `docker stop` then `docker start`

—

—

10.2 s (1 run; the stop took 2.5 s)

—

The differences between flavors are smaller than the spread within each one. Almost all of that spread came from one step: the time between the log lines `starting up Oracle Database...` and `ORACLE instance started.`, which took anywhere from 1.7 to 15 seconds from run to run with no pattern we could find. Unpacking the compressed datafiles, the step the faststart images exist to skip, took 3–5 seconds. Faststart saved neither time nor disk here: in the five faststart runs we logged phase by phase, the step from `ORACLE instance started.` to `Database opened.` took 8–22 seconds against about 2 for the other tags, and after one start the container’s writable layer measured 3.29 GB, the same as a regular container’s. That is consistent with the overlay filesystem copying each datafile out of the image layer the first time the database writes to it. On Docker Desktop, use the regular tag, or `-slim` for a smaller download if you only need relational features.

Per the image’s [ImageDetails.md](https://github.com/gvenzl/oci-oracle-free/blob/main/ImageDetails.md), the regular image already drops Java in the database, SQLcl, OLAP, Workspace Manager and the JDBC drivers; `-slim` also uninstalls Oracle Text, Spatial and Locator and the Multilingual Engine. We compared `DBA_REGISTRY` in the two: the regular container lists Oracle Text and Spatial as `VALID`, while the slim one has neither and shows Oracle XML Database as `INVALID`. Skip `-slim` if you use `CONTAINS` text searches, `SDO_GEOMETRY` or XML DB. Use `-full` only when you need a component the others removed. All flavors keep SQL\*Plus, Data Pump (`expdp`/`impdp`) and SQL\*Loader inside the container, which you can run with `docker exec` instead of installing them on the Mac.

## FREE or FREEPDB1: the service name

Oracle Database Free is a container database (CDB) called `FREE` with one pluggable database (PDB) called `FREEPDB1`. Your tables belong in the PDB. The CDB root is for administration, and a user created in the PDB does not exist there. Here is what the listener inside a fresh container offers (`docker exec oracle lsnrctl status`):

```
Service "5a4ccb1829931092e0634d00010ab936" has 1 instance(s).
Service "FREE" has 1 instance(s).
Service "FREEXDB" has 1 instance(s).
Service "freepdb1" has 1 instance(s).
```

The long hexadecimal name is the PDB’s internal service and `FREEXDB` serves XML DB; neither is something you connect to. We then connected with node-oracledb 6.10 in Thin mode — the driver Jam SQL Studio uses — and with SQL\*Plus inside the container, trying the names people copy from older tutorials:

Connect string (port 1521)

Result

`localhost:1521/FREEPDB1` or `/freepdb1`

Connects to `FREEPDB1`; the service name is not case-sensitive

`localhost:1521/FREE`

Connects `system` to `CDB$ROOT`; your application user gets `ORA-01017`

`localhost:1521` (no service)

Also lands in `CDB$ROOT`, because the listener’s `DEFAULT_SERVICE_LISTENER` is `FREE`

`localhost:1521/XEPDB1`, `/XE`, `/ORCLPDB1`

`NJS-518: cannot connect to Oracle Database. Service "XEPDB1" is not registered with the listener at host 127.0.0.1 port 1521.` SQL\*Plus reports the same as `ORA-12514`

`SID=FREEPDB1` in a descriptor

`NJS-519: … SID "FREEPDB1" is not registered with the listener` — `FREEPDB1` is a service, not a SID

`SID=FREE` in a descriptor

Connects, to `CDB$ROOT`

`XEPDB1` and `XE` come from Oracle XE 18c/21c guides; `ORCLPDB1` and `ORCL` from Enterprise Edition defaults. If you create your own PDB with the image’s `ORACLE_DATABASE` variable, its name becomes another service. When you are not sure, `lsnrctl status` inside the container is the authority. In Jam SQL Studio, the **Discover** button next to the service-name field asks the listener which of the common names are registered and lists them as pills you can click.

## Create a user you can log in with

The `APP_USER` and `APP_USER_PASSWORD` variables create the user on first start. The image’s `createAppUser` script switches to `FREEPDB1` and runs, in effect:

```sql
ALTER SESSION SET CONTAINER = FREEPDB1;
CREATE USER app IDENTIFIED BY "App_2026" DEFAULT TABLESPACE users QUOTA UNLIMITED ON users;
GRANT DB_DEVELOPER_ROLE TO app;
```

To add a user later, connect as `system` to `FREEPDB1` and run the last two statements yourself. The grant is the part people get wrong. We created four users with different grants and logged in as each over Thin mode:

Grants

Login to FREEPDB1

None

`ORA-01045: Login denied. User NO_GRANTS does not have CREATE SESSION privilege.`

`RESOURCE` only

`ORA-01045` — `RESOURCE` holds 15 `CREATE` privileges for objects, but not `CREATE SESSION`

`CONNECT` only

Logs in. In 26ai `CONNECT` is exactly `CREATE SESSION` and `SET CONTAINER`, so the user can log in but cannot create a table

`DB_DEVELOPER_ROLE`

Logs in. The role carries 14 system privileges, including `CREATE SESSION` and `DEBUG CONNECT SESSION`, plus the `RESOURCE` and `CTXAPP` roles

So `ORA-01045` appears when the user was given object privileges, typically `RESOURCE` alone, and never `CREATE SESSION`. `GRANT CONNECT, RESOURCE`, the pair in many older guides, works; `DB_DEVELOPER_ROLE`, added in 23ai, is the single grant meant for application developers. The other common surprise is `ORA-01017: invalid credential or not authorized; logon denied` with the right password: the user lives in `FREEPDB1` and the connection went to `FREE`, or to no service at all.

If you plan to read execution plans, watch sessions or step through PL/SQL, those need further grants such as `SELECT ANY DICTIONARY` and `DEBUG ANY PROCEDURE`; the [reproducible Oracle workflow](/alternatives/toad-for-oracle/#workflow) lists them against this same container.

## Keep your data: volumes and restarts

Without a volume the database lives in the container’s writable layer. It survives `docker stop` and `docker start` — the users we had created were still there after a restart — but `docker rm` deletes it. Mount a named volume at `/opt/oracle/oradata`, as in the command at the top, and the data outlives the container. We checked the whole cycle:

1.  A first container on an empty volume took 7.9 seconds to become ready and left 3.1 GB in the volume.
2.  We added a row, removed the container with `docker rm -f`, and started a new one on the same volume with a *different* `ORACLE_PASSWORD`. It was ready in 5.1 seconds, and the row was there.
3.  The new password did not apply. The log said so:
    
    ```
    CONTAINER: database already initialized.
    CONTAINER: WARNING: $ORACLE_PASSWORD has been specified but the database is already initialized. The password will be ignored.
    ```
    
    Logging in as `system` with the new password returned `ORA-01017`; the old one still worked.
4.  The image’s own script changes it: `docker exec oracle resetPassword NewPassword_2026` sets the `SYS` and `SYSTEM` passwords, and the new password worked straight away.

The same rule covers every variable the image reads (`ORACLE_PASSWORD`, `APP_USER`, `ORACLE_DATABASE`): they apply when the database is created, not on later starts. To begin again from scratch, remove the container and the volume (`docker volume rm oracle-data`).

## Init scripts run once, as SYS, in the root container

Files in `/container-entrypoint-initdb.d` (`.sql`, `.sql.gz`, `.sql.zip` and `.sh`, in alphabetical order) run after the database is created. Two details trip people up, and we checked both with a volume and a script mounted from the Mac:

-   **They run only when the database is created.** On the first start our script ran; on the second container, reusing the volume, the log went straight from `database already initialized` to the startup scripts. Without a volume every new container creates a fresh database, so the scripts run each time. For something that must happen on every start, use `/container-entrypoint-startdb.d` instead — our script there ran on both starts.
-   **`.sql` files run as `SYS` in the root container, not in `FREEPDB1`.** The image runs them with `sqlplus / as sysdba`. Our script printed its container before and after switching:
    
    ```
    init script connected to: CDB$ROOT
    after ALTER SESSION: FREEPDB1
    ```
    
    A `CREATE TABLE` without the switch lands in the root container, where your application never looks.

So an init script for an application schema starts like this:

```sql
-- db/init/01_app_schema.sql
ALTER SESSION SET CONTAINER = FREEPDB1;

CREATE USER app IDENTIFIED BY "App_2026" DEFAULT TABLESPACE users QUOTA UNLIMITED ON users;
GRANT DB_DEVELOPER_ROLE TO app;

CREATE TABLE app.notes (
  id   NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  body VARCHAR2(100)
);
```

Per the [README](https://github.com/gvenzl/oci-oracle-free#initialization-scripts), a failing sourced `.sh` script, or an executable one that exits non-zero, stops the container and can leave the database half-initialized. With Docker Compose, the image’s `healthcheck.sh` lets an application container wait until the database, including your init scripts, is ready:

```yaml
services:
  oracle:
    image: gvenzl/oracle-free:23.26.3
    ports:
      - "1521:1521"
    environment:
      ORACLE_PASSWORD: ChangeMe_2026
    volumes:
      - oracle-data:/opt/oracle/oradata
      - ./db/init:/container-entrypoint-initdb.d:ro
    healthcheck:
      test: ["CMD", "healthcheck.sh"]
      interval: 5s
      timeout: 5s
      retries: 30
  app:
    build: .
    depends_on:
      oracle:
        condition: service_healthy
volumes:
  oracle-data:
```

In the 23.26.3 image, `healthcheck.sh` returns a non-zero status while the container is still initializing or running scripts from either directory, and zero once `FREEPDB1` is open read-write. We ran this file on the same Mac with a placeholder app container and the init script above: `docker compose up -d --wait` returned after 17.3 seconds, and the app container started about 15 seconds after the database container, once the database reported healthy.

## Memory, CPU and disk

Oracle’s terms cap Oracle AI Database 26ai Free at 2 CPUs, 2 GB of RAM and 12 GB of user data, per the [Database Free page](https://www.oracle.com/database/free/). Inside the container we read the settings that implement it:

-   `cpu_count` = 2, which the gvenzl image sets explicitly.
-   A System Global Area of 1,603,392,144 bytes (about 1.5 GiB, from the startup log) and `pga_aggregate_target` = 512 MiB, with a 2 GiB `pga_aggregate_limit`.
-   `docker stats` showed about 2.0 GiB as the database opened and 2.50 GiB a few minutes later, flat for the next three minutes with the database idle and 1–5% CPU.
-   The datafiles take 3.1 GB under `/opt/oracle/oradata` after the first start, on top of the image itself (3.33 GB on disk for the regular tag).

We would give the runtime’s VM at least 4 GB, so the database’s 2.5 GiB leaves room for the VM itself and an application container next to it. The 12 GB data cap is the limit you are most likely to meet if you load a copy of a production database.

## Connect from a client on the Mac

The container is a normal Oracle listener on `localhost:1521`, so the connection is the same for every tool:

-   **SQL\*Plus in the container**: `docker exec -it oracle sqlplus app/App_2026@FREEPDB1`. The image’s `tnsnames.ora` defines the `FREE` and `FREEPDB1` aliases, so the short form works there.
-   **Drivers on the Mac**: EZConnect `localhost:1521/FREEPDB1` for node-oracledb and python-oracledb, `jdbc:oracle:thin:@//localhost:1521/FREEPDB1` for JDBC. The Thin modes of the Node.js and Python drivers need no Oracle Instant Client; [Oracle on Apple Silicon without Instant Client](/blog/2026-07-07-oracle-apple-silicon-no-instant-client/) covers why that matters on an M-series Mac.
-   **From another container**: on a shared Docker network the host is the container name (`oracle:1521/FREEPDB1`), not `localhost`.

In [Jam SQL Studio](/databases/oracle/), **Detect local databases** (in the New Connection dialog, the command palette and the Saved Connections dialog) lists running `gvenzl/oracle-*` and Oracle-registry containers. For a `gvenzl/oracle-free` container it fills in `localhost`, the published port and `FREEPDB1`, and takes the user and password from `APP_USER` / `APP_USER_PASSWORD`, or `system` and `ORACLE_PASSWORD` when no app user is set. Detection reads `/var/run/docker.sock`, or `DOCKER_HOST` when it points at a `unix://` socket; with a Colima or Podman socket elsewhere, type the values in by hand. The [connections guide](/docs/connections/#detect-local-docker-databases) has the details.

![Jam SQL Studio New Connection dialog on the Oracle form: Connect by EZConnect, a host, port 1521, the service name FREEPDB1 with a Discover button, and password authentication](/images/docs/connection-oracle.png)
*The Oracle connection form. For the container, the host is `localhost`; **Discover** lists the service names the listener knows.*

## Connection errors and what they mean

Some of these errors are only a matter of timing. We started a container and, at the same moment, began logging in from the Mac with a Thin-mode client every quarter of a second. It got four different errors before the first successful login 8.6 seconds later:

After

What the client got

What the container was doing

0.1 s

`NJS-500: connection to Oracle Database was closed or broken`

Docker’s published port accepted the connection, but the listener inside had not started

3.9 s

`NJS-518: … Service "FREEPDB1" is not registered with the listener`

Listener running, database not yet started

6.9 s

`ORA-01109: database not open`

Instance started, `FREEPDB1` not yet open

8.3 s

`ORA-01017: invalid credential or not authorized; logon denied`

Database open, but the startup script had not yet applied `ORACLE_PASSWORD`

8.6 s

Connected

The log had just printed `DATABASE IS READY TO USE!`

So an `NJS-518` or `ORA-01017` in the first seconds after `docker run` says nothing about your settings. Wait for the ready line, then look at the table below.

Error

Cause with a local container

Fix

`NJS-503`: connection could not be established

Nothing is published on that port: the container is not running, the `-p` mapping is different, or the port is wrong

`docker ps` and check the `PORTS` column

`NJS-500`: connection was closed or broken

The port is published but the listener inside has not started, or the container is restarting

`docker logs oracle`; wait for the ready line

`ORA-01109`: database not open

The database is still starting

Wait for the ready line

`NJS-518` / `ORA-12514`: service is not registered with the listener

Wrong service name (`XEPDB1`, `ORCL`…), or the database is still starting

Use `FREEPDB1`; wait for `DATABASE IS READY TO USE!`

`NJS-519` / `ORA-12505`: SID is not registered

A service name entered as a SID

Untick **Use SID**, or use `SERVICE_NAME=` in the descriptor

`ORA-01017`: invalid credential or not authorized

Wrong password; a PDB user connecting to `FREE`; a password changed with `ORACLE_PASSWORD` on an existing volume; or a login in the second before the startup script sets the password

Connect to `FREEPDB1`; use `resetPassword` for `system`

`ORA-01045`: user does not have CREATE SESSION privilege

The user has `RESOURCE` or object grants only

`GRANT DB_DEVELOPER_ROLE` (or `CREATE SESSION`)

Three more Thin-mode errors showed up in our telemetry, but never against a local Free container, because they come from older or locked-down servers:

-   **`NJS-138`**: *“connections to this database server version are not supported by node-oracledb in Thin mode”*. The server is older than 12.1, typically 11g. No setting in a Thin-mode client changes that; the server needs an upgrade, or the connection needs a Thick-mode client with Oracle Client libraries.
-   **`NJS-116`**: *“password verifier type 0x939 is not supported”*. Per the [node-oracledb troubleshooting guide](https://node-oracledb.readthedocs.io/en/latest/user_guide/troubleshooting.html), the account has only a 10G password verifier and Thin mode needs 11G or later; an administrator has to upgrade the verifier, which means resetting the password. Accounts in the container have `11G 12C` verifiers, which we checked in `DBA_USERS.PASSWORD_VERSIONS`.
-   **`NJS-533`**: *“Advanced Networking Option service negotiation failed. Native Network Encryption and DataIntegrity only supported in node-oracledb thick mode”*. The server requires Oracle Native Network Encryption; Thin-mode clients need TLS instead.

For the full list of Oracle connection fixes Jam SQL Studio offers in the error notice — including **Browse services on this listener…** for `NJS-518` — see [Oracle connection issues](/docs/connections/#oracle-connection-fixes) in the docs.

## What the telemetry shows

Jam SQL Studio sends anonymous events when a connection succeeds, when a connection test returns, and when a connection fails — with the engine and the driver’s error code, never the host, user or message. We followed every installation first seen between August 27 and October 7, 2026 until October 14, and counted those that tried an engine but never had a successful connection or connection test with it:

Engine

New installations that tried it

Never connected

Share

SQL Server

301

51

17%

MySQL / MariaDB

175

16

9%

PostgreSQL

109

12

11%

**Oracle**

**98**

**41**

**42%**

Oracle is the outlier, and the sample is small: 98 installations, of which 25 were on macOS (15 of those never connected — too few to read much into). These are the Oracle error codes those 98 installations hit; one installation can appear under several codes:

Code

Meaning

Installations

Of which never connected

`NJS-518`

Service not registered with the listener

35

14

`NJS-138`

Server older than 12.1

17

16

`NJS-530`

Host name could not be resolved

12

9

`NJS-116`

10G-only password verifier

11

10

`NJS-503`

Connection could not be established

11

9

`ORA-01017`

Invalid credential

11

0

`NJS-519`

SID not registered with the listener

8

3

`NJS-510` / `NJS-511`

Connection timed out / refused by the listener

7 / 6

4 / 4

`NJS-515` / `NJS-514`

Error in an Easy Connect string / syntax error in a connection string

6 / 3

3 / 2

`NJS-516` / `NJS-520`

No `tnsnames.ora` folder set / file not found

4 / 3

4 / 2

`NJS-533`

Server requires Native Network Encryption

3

3

Read plainly:

-   **The service name is the most common stumble, and often a recoverable one.** 35 installations hit `NJS-518`; 14 of them never connected, and the rest found the right name. A local container removes the guesswork because the name is fixed: `FREEPDB1`.
-   **Wrong passwords get fixed.** All 11 installations that saw `ORA-01017` connected later.
-   **Old servers are dead ends for a Thin-mode client.** `NJS-138`, `NJS-116` and `NJS-533` together reached 31 installations, and 29 of those never connected. None of them can be fixed from the client side.

What the data cannot say is what each failing database was: a container on a laptop, a company server or a cloud instance. The error codes only tell us that the Oracle failures that end a trial are mostly about finding the database, not about the SQL client.

## Methodology

-   **Machine:** Apple M4 Max with 128 GB of memory, macOS 26.5.2, Docker Desktop 4.54.0 (Engine 29.1.2, containerd image store) with a 16-CPU, 32 GB Linux VM. About 30 unrelated containers were running throughout, so treat the times as indicative of this machine, not as a benchmark.
-   **Images:** `gvenzl/oracle-free` tags `23.26.3`, `23.26.3-slim`, `23.26.3-faststart` and `23.26.3-slim-amd64`, pulled on October 14, 2026. Sizes are Docker’s `CONTENT SIZE` (download) and `DISK USAGE` columns.
-   **Start time:** wall-clock seconds from issuing `docker run -d` to the first log line `DATABASE IS READY TO USE!`, polled every 0.5 s; each run used a new container with no volume unless stated, removed afterwards, with a 20-second pause between runs. Memory is the `docker stats` figure.
-   **Connection tests:** node-oracledb 6.10.0 in Thin mode from the Mac, and SQL\*Plus 23.26.3 inside the container. All test containers, users and the volume were deleted afterwards.
-   **Telemetry:** anonymous Jam SQL Studio events `feature:connect` (a successful connection from the app, not from the connection dialog), `feature:connection_test_result` (success or failure of the dialog’s test), `connection:error` and `feature:connection_error_hint` (engine and driver error code only). An installation is one anonymous device identifier; “new” means its first `app:installed` event fell between August 27 and October 7, 2026, and events were counted until October 14. Installations with telemetry turned off are not included. A narrower check — installations from September 30 that pressed **Test connection** — gave the same picture: 11 of 27 never connected to Oracle, against 6 of 50 for SQL Server.

## Quick Answers

Short answers to common questions about running Oracle Database on a Mac.

### Q: Can you run Oracle Database on a Mac?

A: Yes, in a Linux container. Oracle ships no macOS build of Oracle Database, but Oracle AI Database 26ai Free has an Arm64 Linux build, so on an Apple Silicon Mac the gvenzl/oracle-free image or Oracle's container-registry.oracle.com/database/free image runs in Docker Desktop or another container runtime without x86 emulation. On an M4 Max the database accepted connections a median of 20 seconds after docker run and used about 2.5 GiB of memory.

### Q: Does Oracle Database run natively on Apple Silicon?

A: Oracle Database Free does, inside a Linux container. Oracle has provided Arm64 builds since Oracle Database 23.5 Free, and the gvenzl/oracle-free and container-registry.oracle.com/database/free images are multi-arch, so Docker runs the arm64 variant. Oracle XE images (gvenzl/oracle-xe and container-registry.oracle.com/database/express) are amd64 only and run under x86-64 emulation. In our test the amd64 build of the same Free release did run under Rosetta, but a PL/SQL loop took twice as long and the container used over 1 GiB more memory.

### Q: What is the service name for Oracle Database Free in Docker?

A: FREEPDB1, the pluggable database, so the EZConnect string is localhost:1521/FREEPDB1. FREE is the root of the container database and is meant for administration; a user created in FREEPDB1 gets ORA-01017 there. XE, XEPDB1, ORCL and ORCLPDB1 belong to other editions and return NJS-518 or ORA-12514, service not registered with the listener.

### Q: Why does a new Oracle user get ORA-01045 when logging in?

A: The user has no CREATE SESSION privilege. In Oracle Database 26ai the RESOURCE role grants privileges to create tables, views and other objects but not CREATE SESSION, so a user given only RESOURCE cannot log in. Grant DB\_DEVELOPER\_ROLE, which includes CREATE SESSION, or CONNECT, which in 26ai consists of CREATE SESSION and SET CONTAINER.

### Q: Why didn't my script in /container-entrypoint-initdb.d run?

A: The gvenzl image runs those scripts only when it creates a new database. If the container reuses a volume that already holds one, the log says database already initialized and skips them. The scripts also run as SYS connected to the root container, CDB$ROOT, so a script that creates application objects must start with ALTER SESSION SET CONTAINER = FREEPDB1. Scripts in /container-entrypoint-startdb.d run on every start instead.

### Q: How much memory does Oracle Database Free need on a Mac?

A: Give the container VM at least 4 GB. Oracle AI Database 26ai Free is limited to 2 GB of database memory, and in our measurement the container used about 2.5 GiB once it was open. The image warns at startup when it sees less than 2 GB, and Colima's default VM has 2 GiB, so start Colima with --memory 4 or raise the Podman machine's memory.

### Q: Do I need Oracle Instant Client to connect to the container?

A: No. Thin-mode drivers such as node-oracledb and python-oracledb, the JDBC thin driver and Jam SQL Studio connect over TCP without Instant Client. SQL\*Plus, Data Pump and SQL\*Loader are inside the container and run with docker exec, for example docker exec -it oracle sqlplus app/App\_2026@FREEPDB1.

## Summary

On an Apple Silicon Mac, Oracle Database means a Linux container, and since 23.5 Free that container runs as arm64: `gvenzl/oracle-free` or Oracle’s own `database/free` image, about 20 seconds to a usable database on our M4 Max and about 2.5 GiB of memory. Connect to `FREEPDB1`, give your user `DB_DEVELOPER_ROLE`, mount a named volume if the data should outlive the container, and remember that init scripts run once, as `SYS`, in the root container. If the database you actually need is an older company server, the client side is covered in [Oracle Database on Mac and Linux without an Oracle Client](/blog/oracle-database-gui-cross-platform/); for SQL Server the equivalent of this guide is [How to Run SQL Server on a Mac](/blog/sql-server-on-macos/).

### An Oracle Client for Apple Silicon

Jam SQL Studio connects to Oracle 12.2 and later through the Thin driver, with no Instant Client: Object Explorer for packages and sequences, execution plans, schema and data compare, and a PL/SQL debugger. Free for personal use.

[Download for Mac](/download/mac/) [Jam SQL Studio for Oracle](/databases/oracle/)

### Related

-   [Oracle on Apple Silicon, No Instant Client](/blog/2026-07-07-oracle-apple-silicon-no-instant-client/)
-   [Oracle on Mac and Linux](/blog/oracle-database-gui-cross-platform/)
-   [Oracle Client](/databases/oracle/)
-   [Oracle Connection Fixes](/docs/connections/#oracle-connection-fixes)
-   [What's New in Oracle 26ai](/blog/2026-08-27-whats-new-oracle-26ai/)
-   [SQL Server on a Mac](/blog/sql-server-on-macos/)

## Connect to the Oracle Container From Your Mac

Jam SQL Studio finds a running `gvenzl/oracle-free` container with Detect Local Databases, fills in the published port, `FREEPDB1` and the `APP_USER` credentials, and connects through the Thin driver — no Oracle Instant Client on the Mac.

[Download Jam SQL Studio Free](/#download) [Detect local Docker databases](/docs/connections/#detect-local-docker-databases)

Free for personal use • No account required • Mac, Windows, Linux