AI

Run a local LLM (Ollama) inside Oracle APEX

Private, on-prem AI for APEX — call a local Ollama model from PL/SQL or register it as an APEX Generative AI Service, so prompts never leave your network.

Intermediate⏱ 3 min readUpdated: 2026-10-10

For sensitive workloads you may not want to send data to a cloud LLM. Ollama runs open-weight models on your own machine or server and exposes them over a local HTTP API. Oracle APEX can call that API from PL/SQL — or, from APEX 26.1, use it as a built-in AI provider — so inference stays on infrastructure you control.

Run Ollama

Install Ollama and pull a small instruct model (llama3.2 is the 3B model, about 2 GB). The Windows app runs in the background after install; on Linux the server usually runs as a systemd service — if it isn't running, start it with ollama serve. Either way it serves an HTTP API on http://localhost:11434.

# Install Ollama from https://ollama.com/download (it runs in the background), then:
ollama pull llama3.2
ollama run llama3.2 "Say hello in one sentence"
curl.exe http://localhost:11434/api/version

⚠ Can the database actually reach Ollama?

Ollama binds to 127.0.0.1:11434 by default. The examples below use host.docker.internal, which works when the database runs in a container under Docker Desktop (on Linux Docker Engine, start the container with --add-host=host.docker.internal:host-gateway — that name then points at the Docker bridge gateway, not at loopback, so Ollama must also listen beyond 127.0.0.1: run systemctl edit ollama.service, add Environment="OLLAMA_HOST=0.0.0.0:11434" under [Service], then reload systemd and restart Ollama). If the database is on another server, use the Ollama host's name or IP and set the same OLLAMA_HOST=0.0.0.0:11434 on that host. A local Ollama server does not check API keys, so keep port 11434 on a trusted network and never expose it to the internet.

Allow the database to make the call

Outbound network calls are blocked until a network ACL allows them. APEX_WEB_SERVICE and the APEX AI features make the call as the APEX engine schema (for example APEX_260100 on APEX 26.1) — not as APEX_PUBLIC_USER and not as your parsing schema. Many installs already grant it during setup, so check first. Connect as a DBA to the PDB where APEX is installed — Oracle AI Database 26ai is always a container database, and CDB$ROOT has no APEX, so the grant below fails there with PLS-00201 on APEX_APPLICATION. Replace FREEPDB1 with your PDB name:

ALTER SESSION SET CONTAINER = FREEPDB1;

SELECT host, lower_port, upper_port, principal, privilege
FROM   dba_host_aces
ORDER  BY host, principal;

If there is no matching entry, grant connect on just the Ollama host and port. Run this as SYS (or another user allowed to run DBMS_NETWORK_ACL_ADMIN) in the same APEX PDB session; APEX_APPLICATION.g_flow_schema_owner resolves to the current APEX engine schema, so the script survives APEX upgrades:

BEGIN
  DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE(
    host       => 'host.docker.internal',
    lower_port => 11434,
    upper_port => 11434,
    ace        => xs$ace_type(privilege_list => xs$name_list('connect'),
                              principal_name => APEX_APPLICATION.g_flow_schema_owner,
                              principal_type => xs_acl.ptype_db));
END;
/

ℹ Calling UTL_HTTP yourself?

If your own code uses UTL_HTTP directly instead of APEX_WEB_SERVICE, grant the same ACE to your parsing schema as well — otherwise you get ORA-24247: network access denied by access control list (ACL). On Autonomous Database the network rules are different (outbound calls must use HTTPS), so this guide targets a self-managed database.

Call the model from PL/SQL

Post to Ollama's /api/generate endpoint. Set "stream": false — the API streams by default — and read the generated text from the response field. Building the body with JSON_OBJECT_T escapes quotes in user input for you.

DECLARE
  l_req    json_object_t := json_object_t();
  l_resp   CLOB;
  l_answer CLOB;
BEGIN
  l_req.put('model',  'llama3.2');
  l_req.put('prompt', 'Summarize in one sentence: order 1001 shipped two days late because of a customs hold.');
  l_req.put('stream', false);

  apex_web_service.set_request_headers(
    p_name_01  => 'Content-Type',
    p_value_01 => 'application/json');

  l_resp := apex_web_service.make_rest_request(
    p_url              => 'http://host.docker.internal:11434/api/generate',
    p_http_method      => 'POST',
    p_body             => l_req.to_clob,
    p_transfer_timeout => 120);

  IF apex_web_service.g_status_code <> 200 THEN
    raise_application_error(-20001,
      'Ollama returned HTTP ' || apex_web_service.g_status_code || ': ' ||
      dbms_lob.substr(l_resp, 500, 1));
  END IF;

  -- The generated text is in the "response" field
  l_answer := json_object_t.parse(l_resp).get_clob('response');
  dbms_output.put_line(l_answer);
END;
/

For multi-turn conversations use /api/chat with a messages array instead. Ollama also offers an OpenAI-compatible API under http://localhost:11434/v1 (for example /v1/chat/completions), which is handy if your code already speaks the OpenAI format.

Or: register Ollama as an APEX Generative AI Service (APEX 26.1+)

From APEX 26.1, Ollama and OpenAI-compatible endpoints are built-in AI providers. In App Builder go to Workspace Utilities → Generative AI, click Create, choose the Ollama provider, enter the Ollama server URL as the Base URL, set AI Model to llama3.2, give it a Static ID such as LOCAL_OLLAMA, and click Test Connection. The same network ACL applies. Which Ollama API APEX calls depends on the release: APEX 26.1 uses Ollama's native /api/chat and /api/embed; in APEX 26.2 new Ollama services default to the Responses API (Chat Completions is deprecated), selectable under Advanced → Provider API. Ollama added its OpenAI-compatible /v1/responses endpoint in v0.13.3, so upgrade older Ollama installs before using that default. The service can then back the declarative AI features or be called from PL/SQL running in that workspace's apps with APEX_AI:

DECLARE
  l_answer CLOB;
BEGIN
  l_answer := apex_ai.generate(
    p_prompt            => 'Summarize in one sentence: order 1001 shipped two days late because of a customs hold.',
    p_system_prompt     => 'You are a concise assistant for an order-management app.',
    p_service_static_id => 'LOCAL_OLLAMA');
  dbms_output.put_line(l_answer);
END;
/

💡 Keep prompts grounded

Pass only the rows the user is allowed to see into the prompt, and validate the model's output before acting on it. Small local models can be wrong with confidence — treat their answers as drafts.

Check your understanding

Check your understanding

0% · 0/3

What's the main reason to run a local LLM?

How does PL/SQL reach Ollama?

Which principal needs the network ACL for APEX_WEB_SERVICE calls?

Need this delivered?

Request a quote