Run Oracle AI Database Locally

This article shows how to run Oracle AI Database locally with either Docker or Podman and connect oracleagentmemory to that database.

In this article, you will learn how to:

Prerequisites

Install either Docker or Podman and make sure the corresponding CLI is available in your shell. Then pull the Oracle AI Database Free Lite image from the Oracle Container registry.

Choose a strong password for the Oracle SYSTEM user and export it before starting the container:

export ORACLE_PWD='<your-secure-password>'

Docker:

docker pull container-registry.oracle.com/database/free:latest-lite

Podman:

podman pull container-registry.oracle.com/database/free:latest-lite

Start the Oracle AI Database Container

Create a named volume so the database files persist across restarts:

Docker:

docker volume create OracleDBData

Podman:

podman volume create OracleDBData

Then start the container:

Docker:

docker run -d \
  --name oracle-free-lite \
  -p 1521:1521 \
  -e ORACLE_PWD="$ORACLE_PWD" \
  -v OracleDBData:/opt/oracle/oradata \
  container-registry.oracle.com/database/free:latest-lite

Podman:

podman run -d \
  --name oracle-free-lite \
  -p 1521:1521 \
  -e ORACLE_PWD="$ORACLE_PWD" \
  -v OracleDBData:/opt/oracle/oradata \
  container-registry.oracle.com/database/free:latest-lite

Note: If Podman encounters SELinux labeling issues on RHEL, you may want to look into security-opt configuration parameters.

Then follow the container log until the database reports that it is ready.

Docker:

docker logs -f oracle-free-lite

Podman:

podman logs -f oracle-free-lite

When the log contains DATABASE IS READY TO USE!, the listener and the default pluggable database are up.

Once the database is ready, connect to the database.

Docker:

docker exec -it oracle-free-lite sqlplus system/"$ORACLE_PWD"@FREEPDB1

Podman:

podman exec -it oracle-free-lite sqlplus system/"$ORACLE_PWD"@FREEPDB1

Run a simple query from inside the container to confirm that the PDB is open.

SELECT sys_context('USERENV', 'CON_NAME') AS container_name FROM dual;

You should see FREEPDB1.

Then enter exit and press Enter to leave SQL*Plus.

[Optional] Create a Local Oracle User

If you already have an Oracle AI Database and an application user, skip this section and continue with the next section, Try oracleagentmemory against the local database. For a shorter quickstart-style walkthrough of the API itself, see Store and Search Memory for One User.

The example script in this article uses a dedicated local database user:

DB_USER = os.environ.get("ORACLE_MEMORY_DB_USER", "dmuser")
DB_PASSWORD = os.environ["ORACLE_MEMORY_DB_PASSWORD"]
DB_CONNECT_STRING = os.environ.get(
    "ORACLE_MEMORY_DB_CONNECT_STRING",
    "localhost:1521/FREEPDB1",
)

Choose a strong password for that application user and export it before you run the script:

export ORACLE_MEMORY_DB_PASSWORD='<your-app-user-password>'

Then create the user inside the PDB:

Docker:

docker exec -it oracle-free-lite sqlplus system/"$ORACLE_PWD"@FREEPDB1

Podman:

podman exec -it oracle-free-lite sqlplus system/"$ORACLE_PWD"@FREEPDB1
CREATE TABLESPACE dmuser_ts
DATAFILE '/opt/oracle/oradata/FREE/FREEPDB1/dmuser_ts01.dbf'
SIZE 200M
AUTOEXTEND ON NEXT 100M
SEGMENT SPACE MANAGEMENT AUTO;

CREATE USER dmuser IDENTIFIED BY "CHOOSE_A_STRONG_PASSWORD";
GRANT CREATE SESSION, CREATE TABLE, CREATE SEQUENCE, CREATE VIEW, CREATE PROCEDURE TO dmuser;
ALTER USER dmuser DEFAULT TABLESPACE dmuser_ts;
ALTER USER dmuser QUOTA UNLIMITED ON dmuser_ts;

Replace CHOOSE_A_STRONG_PASSWORD with the same password value you store in ORACLE_MEMORY_DB_PASSWORD.

Then verify that the user is ready:

SELECT tablespace_name, contents
FROM dba_tablespaces
WHERE tablespace_name = 'DMUSER_TS';

SELECT username, account_status, default_tablespace, temporary_tablespace
FROM dba_users
WHERE username = 'DMUSER';

SELECT privilege
FROM dba_sys_privs
WHERE grantee = 'DMUSER'
ORDER BY privilege;

The first query should show DMUSER_TS as a permanent tablespace. The second query should show DMUSER with OPEN status and DMUSER_TS as the default tablespace. The privilege query should include at least CREATE SESSION, CREATE TABLE, CREATE SEQUENCE, CREATE VIEW, and CREATE PROCEDURE.

Try oracleagentmemory Against the Local Database

Now that your Oracle connection settings are ready, you can point oracleagentmemory at FREEPDB1 and run a small end-to-end persistence check.

The example below does two things:

Set the application connection variables before running the example. The user and connect string shown below match the earlier optional setup, but you can replace them with your own existing Oracle user and DSN:

export ORACLE_MEMORY_DB_USER='dmuser'
export ORACLE_MEMORY_DB_PASSWORD='<your-app-user-password>'
export ORACLE_MEMORY_DB_CONNECT_STRING='localhost:1521/FREEPDB1'

Configure the Oracle-Backed API

import os

os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = "True"

import oracledb

from oracleagentmemory.core import OracleAgentMemory, SchemaPolicy
from oracleagentmemory.core.embedders.embedder import Embedder
from oracleagentmemory.core.llms.llm import Llm


embedder = Embedder(
    model="YOUR_EMBEDDING_MODEL",
    api_base="YOUR_EMBEDDING_API_BASE",
    api_key="YOUR_EMBEDDING_API_KEY",
)
llm = Llm(
    model="YOUR_LLM_MODEL",
    api_base="YOUR_LLM_API_BASE",
    api_key="YOUR_LLM_API_KEY",
)


DB_USER = os.environ.get("ORACLE_MEMORY_DB_USER", "dmuser")
DB_PASSWORD = os.environ["ORACLE_MEMORY_DB_PASSWORD"]
DB_CONNECT_STRING = os.environ.get("ORACLE_MEMORY_DB_CONNECT_STRING", "localhost:1521/FREEPDB1")
TABLE_NAME_PREFIX = "T_ORACLEMEM_DEMO_"

db_pool = oracledb.SessionPool(
    user=DB_USER,
    password=DB_PASSWORD,
    dsn=DB_CONNECT_STRING,
    min=1,
    max=4,
    increment=1,
    homogeneous=True,
)

agent_memory = OracleAgentMemory(
    connection=db_pool,
    embedder=embedder,
    llm=llm,
    schema_policy=SchemaPolicy.RECREATE,
    table_name_prefix=TABLE_NAME_PREFIX,
)

This configuration uses the local Oracle AI Database together with placeholder LiteLLM embedding and completion settings that you can replace with your own provider values.

Write Records and Verify Persistence

#Keep the same user identifier for the same end user across sessions
#so durable memory can be retrieved consistently.
user_id = "user_123"

thread = agent_memory.create_thread(user_id=user_id)
thread.add_messages(
    [
        {
            "role": "user",
            "content": "Orange juice is my usual breakfast drink.",
        },
        {
            "role": "assistant",
            "content": "Pair it with eggs, toast, or Greek yogurt.",
        },
    ]
)
thread.add_memory("The user currently prefers orange juice with breakfast.")

db_pool2 = oracledb.SessionPool(
    user=DB_USER,
    password=DB_PASSWORD,
    dsn=DB_CONNECT_STRING,
    min=1,
    max=4,
    increment=1,
    homogeneous=True,
)

agent_memory2 = OracleAgentMemory(
    connection=db_pool2,
    embedder=embedder,
    llm=llm,
    schema_policy=SchemaPolicy.REQUIRE_EXISTING,
    table_name_prefix=TABLE_NAME_PREFIX,
)
persisted_thread = agent_memory2.get_thread(thread.thread_id)

print("Messages stored in Oracle:")
print_messages(persisted_thread.get_messages())

print("\nSearch results for 'orange juice':")
print_search_results(
    agent_memory2.search(
        query="orange juice",
        user_id=user_id,
        max_results=5,
        record_types=["memory", "message"],
    )
)

When this example runs successfully, the second Agent Memory API instance prints the stored thread messages and returns search results from the database. That confirms that the records were persisted in Oracle rather than held only in process memory.

Cleanup

When you are done with the local database:

Docker:

docker stop oracle-free-lite
docker rm oracle-free-lite

Podman:

podman stop oracle-free-lite
podman rm oracle-free-lite

If you also want to delete the persisted database files:

Docker:

docker volume rm OracleDBData

Podman:

podman volume rm OracleDBData

Conclusion

In this article we learned how to start Oracle AI Database Free Lite locally with Docker or Podman, prepare a dedicated Oracle user and tablespace for oracleagentmemory, connect an oracleagentmemory API to that database, and verify persistence by reopening and searching the same thread through a separate API instance.

Tip: Having learned how to run oracleagentmemory against a local Oracle AI Database, you may now proceed to Store and Search Memory for One User.

Full Code

#Copyright © 2026 Oracle and/or its affiliates.
#isort:skip_file
#fmt: off
#%%[markdown]
#Agent Memory Code Example - Run Oracle DB locally
#--------------------------------------------------------


#How to use:
#Create a new Python virtual environment and install the latest oracleagentmemory version.

#You can now run the script
#1. As a Python file:
#```bash
#python howto_run_oracledb.py
#```
#2. As a Notebook (in VSCode):
#When viewing the file,
#- press the keys Ctrl + Enter to run the selected cell
#- or Shift + Enter to run the selected cell and move to the cell below


##Configure the local Oracle connection

#%%
import os

os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = "True"

import oracledb

from oracleagentmemory.core import OracleAgentMemory, SchemaPolicy
from oracleagentmemory.core.embedders.embedder import Embedder
from oracleagentmemory.core.llms.llm import Llm


embedder = Embedder(
    model="YOUR_EMBEDDING_MODEL",
    api_base="YOUR_EMBEDDING_API_BASE",
    api_key="YOUR_EMBEDDING_API_KEY",
)
llm = Llm(
    model="YOUR_LLM_MODEL",
    api_base="YOUR_LLM_API_BASE",
    api_key="YOUR_LLM_API_KEY",
)


DB_USER = os.environ.get("ORACLE_MEMORY_DB_USER", "dmuser")
DB_PASSWORD = os.environ["ORACLE_MEMORY_DB_PASSWORD"]
DB_CONNECT_STRING = os.environ.get("ORACLE_MEMORY_DB_CONNECT_STRING", "localhost:1521/FREEPDB1")
TABLE_NAME_PREFIX = "T_ORACLEMEM_DEMO_"

db_pool = oracledb.SessionPool(
    user=DB_USER,
    password=DB_PASSWORD,
    dsn=DB_CONNECT_STRING,
    min=1,
    max=4,
    increment=1,
    homogeneous=True,
)

agent_memory = OracleAgentMemory(
    connection=db_pool,
    embedder=embedder,
    llm=llm,
    schema_policy=SchemaPolicy.RECREATE,
    table_name_prefix=TABLE_NAME_PREFIX,
)


def print_messages(messages: list) -> None:
    for message in messages:
        print(f"[{message.role}] {message.content}")


def print_search_results(results: list) -> None:
    for result in results:
        print(
            f"- [{result.record.record_type}] "
            f"id={result.id} "
            f"user_id={result.record.user_id} "
            f"thread_id={result.record.thread_id} "
            f"content={result.content}"
        )


##Create data and query it

#%%
#Keep the same user identifier for the same end user across sessions so
#durable memory can be retrieved consistently.
user_id = "user_123"

thread = agent_memory.create_thread(user_id=user_id)
#add_messages will add messages to the DB and extract memories automatically
thread.add_messages(
    [
        {
            "role": "user",
            "content": "Orange juice is my usual breakfast drink.",
        },
        {
            "role": "assistant",
            "content": "Pair it with eggs, toast, or Greek yogurt.",
        },
    ]
)
#add_memory adds memory to the DB
thread.add_memory("The user currently prefers orange juice with breakfast.")

db_pool2 = oracledb.SessionPool(
    user=DB_USER,
    password=DB_PASSWORD,
    dsn=DB_CONNECT_STRING,
    min=1,
    max=4,
    increment=1,
    homogeneous=True,
)

agent_memory2 = OracleAgentMemory(
    connection=db_pool2,
    embedder=embedder,
    llm=llm,
    schema_policy=SchemaPolicy.REQUIRE_EXISTING,
    table_name_prefix=TABLE_NAME_PREFIX,
)
persisted_thread = agent_memory2.get_thread(thread.thread_id)

print("Messages stored in Oracle:")
print_messages(persisted_thread.get_messages())

print("\nSearch results for 'orange juice':")
print_search_results(
    agent_memory2.search(
        query="orange juice",
        user_id=user_id,
        max_results=5,
        record_types=["memory", "message"],
    )
)