Bring up Oracle 26ai Free in Docker, create a project-scoped app user with USERS tablespace + ONNX-friendly grants, and write a `.env` ready for the rest of the build-paths skills. Idempotent — safe to re-invoke. Use when any project needs a fresh local Oracle 26ai instance.
72
88%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Low
Low-risk findings worth noting
You scaffold the Oracle 26ai Free container AND create the app user the rest of the build-paths skills will use. Nothing else. No Python app code beyond the user-creation script.
Why an app user (not SYSTEM)? OracleVS declares its metadata column as JSON. Oracle's JSON validation requires Automatic Segment Space Management (ASSM). The SYSTEM tablespace lacks ASSM, so connecting as SYSTEM and creating any OracleVS table fails with ORA-43853: JSON type cannot be used in non-automatic segment space management tablespace "SYSTEM". The app user we create has DEFAULT TABLESPACE USERS (which has ASSM).
shared/references/oracle-26ai-free-docker.md — image, healthcheck, password rules, app-user creation rationale.shared/templates/docker-compose.oracle-free.yml — the canonical compose file (parameterised — port + volume + project name come from .env).docker group membership to take effect — or use sudo docker ... for the rest of this session.conda activate <env> does not work in non-interactive bash. Call binaries by absolute path (~/miniconda3/envs/<env>/bin/python).python=3.12 does not include pip — when creating an env, list pip explicitly: conda create -n <env> -c conda-forge --override-channels python=3.12 pip -y.:3000 and the FastAPI adapter on :8000 — if you're running multiple build-paths projects at once, override these in the project's .env (OPEN_WEBUI_PORT, ADAPTER_PORT) and read them from docker-compose.yml + the adapter's uvicorn.run(port=...) call.In target_dir:
docker-compose.yml exists AND it contains image: container-registry.oracle.com/database/free:, don't overwrite. Read its ORACLE_PWD from the existing .env and skip to Step 4.docker-compose.yml exists but is for something else, stop and ask the user. Don't clobber.If the user passed oracle_pwd, use it (validate first: 12+ chars, mixed case, digit, no $/@/").
Otherwise generate one:
import secrets, string
alphabet = string.ascii_letters + string.digits
pwd = "Or" + "".join(secrets.choice(alphabet) for _ in range(18))Why no symbols: Oracle's ORACLE_PWD env var splits on $, breaks on @ (interpreted as connect string), and chokes on quotes. Letters + digits avoid the whole class.
target_dir/docker-compose.yml — copied verbatim from shared/templates/docker-compose.oracle-free.yml. The template uses ${ORACLE_PORT}, ${ORACLE_PWD}, ${ORACLE_VOLUME}, and ${COMPOSE_PROJECT_NAME} — compose reads them from .env.
target_dir/.env:
COMPOSE_PROJECT_NAME=<compose_project>
ORACLE_PWD=<generated>
ORACLE_PORT=<port>
ORACLE_EM_PORT=<em_port>
ORACLE_VOLUME=<volume_name>
# SYS / SYSTEM creds — only used for the app-user creation in Step 6 and for
# any SYSDBA-only operations (e.g. GRANT EXECUTE ON SYS.DBMS_VECTOR).
SYS_USER=SYSTEM
SYS_PASSWORD=<same as ORACLE_PWD>
# Application credentials — what every other skill uses.
DB_USER=<app_user>
DB_PASSWORD=<generated app pwd>
DB_DSN=localhost:<port>/FREEPDB1Don't commit .env. The skill confirms target_dir/.gitignore contains .env — adds the line if missing.
Before docker compose up, check the host arch and force a native-arch pull. The Oracle 26ai Free image is multi-arch, but Docker's manifest auto-resolution is unreliable when a stale image is on disk or the host VM is degraded — most commonly amd64 layers end up on an arm64 Mac. Under emulation the entrypoint's su oracle step silently fails with "Authentication failure", no Oracle processes ever start, and the old sqlplus | grep '1' healthcheck false-positives on the ORA-01034 error text (the new /opt/oracle/checkDBStatus.sh check catches it, but pulling the right image up front avoids the whole detour).
HOST_ARCH=$(uname -m)
case "$HOST_ARCH" in
x86_64) PLATFORM=linux/amd64 ;;
arm64|aarch64) PLATFORM=linux/arm64 ;;
*) echo "unsupported host arch: $HOST_ARCH"; exit 1 ;;
esac
EXISTING=$(docker image inspect container-registry.oracle.com/database/free:latest \
--format '{{.Architecture}}' 2>/dev/null || true)
if [ -n "$EXISTING" ] && [ "$EXISTING" != "${PLATFORM##*/}" ]; then
echo "stale $EXISTING image on disk; re-pulling for $PLATFORM"
docker rmi container-registry.oracle.com/database/free:latest
fi
docker pull --platform "$PLATFORM" container-registry.oracle.com/database/free:latestOn amd64 Linux hosts the inspect matches and the docker pull is a no-op cache hit. The cost is one extra command; the saving is the user not spending an hour on "the container is healthy but I can't connect" on a Mac.
cd target_dir
docker compose up -d --wait--wait blocks until the healthcheck passes. First boot: ~90s. Subsequent boots: ~15s.
The compose template uses the image's own /opt/oracle/checkDBStatus.sh for the healthcheck — it returns 0 only when the PDB is actually OPEN. Do not regress this to the older echo 'SELECT 1 FROM DUAL;' | sqlplus -L ... | grep -q '1' pattern: when the entrypoint aborts (e.g. arch mismatch — see Step 3b), sqlplus returns an ORA-01034: ORACLE not available banner containing the digit 1, the grep matches the error text, and compose reports HEALTHY for a container that has zero Oracle processes inside. This is the friction Step 3b was added to catch.
If --wait exits non-zero:
docker compose logs oracle | tail -30.docker compose down -v AFTER confirming with the user), arch mismatch (look for su: Authentication failure or Permission denied in the logs — re-run Step 3b).import oracledb, os
from dotenv import load_dotenv
load_dotenv(f"{target_dir}/.env")
conn = oracledb.connect(
user=os.environ["SYS_USER"],
password=os.environ["SYS_PASSWORD"],
dsn=os.environ["DB_DSN"],
)
with conn.cursor() as cur:
cur.execute("SELECT 'ok' FROM dual")
assert cur.fetchone()[0] == "ok"
print("oracle-aidb-docker-setup: SYSTEM connect OK")If this fails with ORA-12541 (no listener), wait another 30s and retry once — the container claims healthy a few seconds before the listener is ready in some kernels. If it still fails, stop and surface the error.
If oracledb isn't installed in the current env, install it: pip install oracledb.
Connect as SYSTEM and create an app user named after app_user with DEFAULT TABLESPACE USERS plus the grants needed by the rest of the build-paths skill set:
APP_USER = os.environ["DB_USER"] # e.g. PDF_CHAT
APP_PWD = os.environ["DB_PASSWORD"]
SYS_PWD = os.environ["SYS_PASSWORD"]
with conn.cursor() as cur:
# Drop if exists, then create. Idempotent.
try:
cur.execute(f"DROP USER {APP_USER} CASCADE")
except oracledb.DatabaseError as e:
if "ORA-01918" not in str(e): # user does not exist
raise
cur.execute(
f'CREATE USER {APP_USER} IDENTIFIED BY "{APP_PWD}" '
f'DEFAULT TABLESPACE USERS QUOTA UNLIMITED ON USERS'
)
cur.execute(
f"GRANT CONNECT, RESOURCE, CREATE SESSION, CREATE TABLE, "
f"CREATE VIEW, CREATE PROCEDURE TO {APP_USER}"
)
cur.execute(f"GRANT CREATE MINING MODEL TO {APP_USER}")
conn.commit()
# Required for VECTOR_EMBEDDING(MODEL ...) — the in-DB ONNX path used by
# intermediate / advanced. GRANT EXECUTE ON SYS.DBMS_VECTOR ALWAYS requires
# SYSDBA in 26ai Free (verified during the friction pass — SYSTEM hits
# ORA-01031). Open a separate SYSDBA connection for this one GRANT.
sysdba_conn = oracledb.connect(
user="SYS",
password=os.environ["SYS_PASSWORD"],
dsn=os.environ["DB_DSN"],
mode=oracledb.AUTH_MODE_SYSDBA,
)
with sysdba_conn.cursor() as cur:
cur.execute(f"GRANT EXECUTE ON SYS.DBMS_VECTOR TO {APP_USER}")
sysdba_conn.commit()
print(f"oracle-aidb-docker-setup: created app user {APP_USER}")Now smoke the app-user connection (this is the one every other skill uses):
app_conn = oracledb.connect(
user=APP_USER, password=APP_PWD, dsn=os.environ["DB_DSN"]
)
with app_conn.cursor() as cur:
cur.execute("SELECT 'ok' FROM dual")
assert cur.fetchone()[0] == "ok"
print("oracle-aidb-docker-setup: app user connect OK")docker not on PATH. Tell the user to install Docker Desktop / engine and stop.target_dir is a different project's repo and already has a docker-compose.yml. Don't clobber.Welcome123).docker system prune or anything that touches other containers.0.0.0.0 — bind to 127.0.0.1:<port>:1521.oracle-aidb-docker-setup: OK
compose: target_dir/docker-compose.yml
env: target_dir/.env (ORACLE_PWD generated)
dsn: localhost:<port>/FREEPDB1
status: healthy (smoke connect OK)
next: hand off to a higher-level skill, or run your own oracledb.connect(...)22d9a38
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.