ML Stack — Local AI Orchestration Toolkit
This repository packages a complete self-hosted assistant stack around Open WebUI plus several companion services: a scheduler that can trigger chats and workflows, a docker-backed code runner, a Roku remote tool server, Nextcloud file access, SearxNG metasearch, and a headless browser UI for deep-research sessions. Everything is wired together through docker-compose.yml so the stack can be brought up on a single host.
Last updated: 2025-10-03
A (Few) Notes
-
ports are currently exposed on most services for development purposes (e.g. 12253 for the scheduler), remove these in production or consider adding a proxy
-
ALL DATA IS STORED IN VOLUMES!!! This means if you do
docker compose down -vyour data WILL dissapear. Consider mounting a persistant directory to avoid this -
Before starting the cluster, check if you need the different components (e.g. Nextcloud Tool Server). They are set to restart on failiure and will throw if missing env vars/credentials, which will loop endlessly
-
If you do not use cloudflared for tunneling, please adjust the CORS policies accordingly, and consider adding a reverse proxy to either your local machine or the compose
-
The code runner and scheduler both mount the host Docker socket. Ensure the host user/group IDs match the compose configuration (
DOCKER_GIDbuild arg defaults to 977) so containers can operate without root. This will be replaced when I enentually migrate this to a kubernetes cluster -
When adjusting
NEXTCLOUD_ACCESS_DIRS, remember to restartollama-nextcloudso the regex list is reloaded
Stack At A Glance
| Compose service | Directory / build context | External ports | Primary role |
|---|---|---|---|
open-webui |
(image: ghcr.io/open-webui/open-webui:main) |
4000 -> 8080 |
Chat UI, agent orchestration, embedded knowledge base & RAG powered by Postgres |
postgres |
– | – | Persistence for Open WebUI (users, KB, events) |
searxng |
searxng.yml |
4001 -> 8080 (debug only) |
Private SearxNG instance used for live web search tools |
coderunner |
coderunner/ |
– (internal 8787) |
Bun service that executes pure source code inside sandboxed Docker containers |
openwebui_tools |
tools/ |
– (internal 1331) |
Python Roku remote API exposed as an OpenAPI tool server |
browser |
browser/ |
7788 -> 7788 |
Playwright Chromium UI for autonomous browsing / research |
schedules-api |
scheduler/ |
12253 -> 12253 |
Cron-style job scheduler that can open chats, call templates, and upload files |
ollama-nextcloud |
nextcloud/ |
13284 -> 1111 |
Nextcloud WebDAV proxy with caching and access controls |
Volumes declared in compose: open-webui, pgdata, searxng_data, webui_data, schedule_data, and nextcloud_data
Caution
PLEASE I BEG OF YOU REMEMBER TO BACK THESE UP/USE A LOCAL DIRECTORY. IF YOU DO NOT AND REMOVE OR PRUNE THE VOLUMES YOU WILL LOSE ALL DATA
Service Details
Open WebUI (open-webui)
-
Runs the latest
ghcr.io/open-webui/open-webui:mainimage with Postgres backing for durable data (open-webuiandpgdatavolumes) -
.envenables the login form, optional API keys (not currently used), and forwards identifying headers so downstream tools know which user initiated a request -
Depends on the tool containers (
openwebui_tools,coderunner,schedules-api,ollama-nextcloud) via internal networking; discover their OpenAPI docs from inside the UI to register tools
Postgres (postgres)
Important
If you plan on exposing ports on this service, please move the inline credentials to the
.envfile
-
Standard
postgres:latestimage. Credentials are set inline in compose for local development -
Health-checked with
pg_isready; the data volumepgdatastores Open WebUI metadata
SearxNG (searxng)
-
Private SearxNG deployment for agent web search tasks with HTML/JSON outputs enabled
-
Mounts
searxng.ymland persists internal data tosearxng_data. External port 4001 is exposed only for local debugging and should be removed in production
Code Runner (coderunner)
-
Bun-based HTTP server that accepts pure source code plus optional extra files, then runs the workload in a throwaway Docker container pinned to an allow-listed base image per language
-
Enforces strict limits (
--network=none, read-only root FS, tmpfs workdir, CPU/memory caps, dropped capabilities). Supported Languages:pythonnodebunbashrubygorustjavaccpp
-
Exposes
GET /openapi.jsonandPOST /executeinside the internal network (http://coderunner:8787). Requires the host Docker socket to spawn child sandboxes; the compose file mounts it read-only with matching group ID.
Roku Tool Server (openwebui_tools)
-
Lightweight Python HTTP server that proxies Roku remote commands
-
Reads
ROKU_IPfrom.env; returns helpful errors when the IP is missing or the device is offline -
Serves
GET /roku/openapi.jsonfor automatic tool registration and handlesGET /roku/{command}requests. Supported command list matches the enum inspec/roku.openapi.json(navigation, inputs, power, volume, remote finder)
Browser Research UI (browser)
-
Builds the upstream
browser-use/web-uiproject, installs Chromium plus dependencies, and launches the UI on port 7788 -
Runs as an unprivileged user (uid 1000) with dedicated tmpfs directories and a
webui_datavolume for persisted history/state -
Configure resolution, telemetry, and default LLM via
browser/.envor container environment variables -
The browser-use docs can be found at https://docs.browser-use.com/
Scheduler API (schedules-api)
-
Bun/Node cron worker that lets you schedule Open WebUI chats or template-driven jobs using authenticated user tokens
-
Persists schedule definitions to
schedule_data(JSON payload) and can store uploaded supporting files under the same volume -
Reads workflow templates from the bundled
scheduler/templates.json. To inject custom templates, mount a host file or populate the root-leveltemplates.json/directory and update the compose volume mapping -
Key endpoints (documented in
scheduler/openapi.json):GET /openapi.json: tool contract.POST /api/schedules: create or replace a schedule (cron or one-shot ISO timestamp). Validates feature flags, attachments, and template referencesGET /api/schedules: list schedules scoped to the calling user (identified via Open WebUI bearer token)DELETE /api/schedules/{name}: remove a schedule the user owns
-
Includes a static UI in
scheduler/public/for manual interaction. Usesnode-cronto avoid overlapping executions; failed jobs clean themselves up
Nextcloud Files Tool (ollama-nextcloud)
-
Express + WebDAV proxy that exposes a simple JSON API for browsing, downloading, and uploading files stored in Nextcloud
-
Environment variables (configured in
.env):NEXTCLOUD_APP_ID/NEXTCLOUD_APP_PASS/NEXTCLOUD_WEBDAV_ADDR: service credentialsNEXTCLOUD_ACCESS_DIRS: JSON array of regex strings that whitelist readable paths (e.g.["^/Notes", "^/School"]). When unset, the tool has full access
-
Cached downloads are stored under
/tmpusing an embedded SQLite index (cache.ts). The server keeps ETags in sync and reuses cached bytes when possible unlessbypasscacheis requested -
Major endpoints (see
nextcloud/openapi.json):GET /openapi.json: discovery document for tool registration.POST /file: fetch a file. Automatically caches and returns metadata + content-type.POST /dir: list directory contents (shallow or recursive).PUT /file: upload via multipart form-data (optional recursive dir creation, never overwrites existing files).
Cloudflared Tunnel Config
cloudflared-tunnel-config.ymlmaps friendly hostnames to the local services (Ollama, Open WebUI, tool servers). Use it as a blueprint when exposing the stack through Cloudflare Tunnels.
Configuration (.env)
ROKU_IP=
WEBUI_URL=
# use built-in login form (username/password)
ENABLE_LOGIN_FORM="true"
# forward identity on outbound model requests (if you're going to use openAI/external LLM)
ENABLE_FORWARD_USER_INFO_HEADERS="true"
# allow user api keys for the scheduler calling OWUI’s
ENABLE_API_KEY_AUTH="true"
NEXTCLOUD_APP_ID=
NEXTCLOUD_APP_PASS=
NEXTCLOUD_WEBDAV_ADDR=
NEXTCLOUD_ACCESS_DIRS=
Running the Stack
-
Install Docker and Docker
-
Populate
.envwith the correct Roku and Nextcloud settings plus any Open WebUI options -
Build images (pull base layers and bake GID overrides where needed):
docker compose build --pull -
Launch everything:
docker compose up -d -
Open WebUI is available on http://localhost:4000 (use credentials from the UI setup). The supporting services are reachable on the ports listed above or through the internal Docker network
To inspect logs for a specific service:
docker compose logs -f coderunner
Bring the stack down (volumes persist):
docker compose down
Registering Tool Servers in Open WebUI
Inside Open WebUI (Settings --> Tools --> Add tool server), point to the internal URLs:
- Code runner:
http://coderunner:8787/openapi.json - Scheduler:
http://schedules-api:12253/openapi.json - Nextcloud files:
http://ollama-nextcloud:1111/openapi.json - Roku remote:
http://openwebui_tools:1331/roku/openapi.json
These should be fully internal in the docker network. If you expose them consider using a reverse proxy/authentication
Data, Volumes, and Shared Paths
open-webuivolume: Open WebUI application state (uploads, knowledge base, configs)pgdatavolume: Postgres cluster data directorysearxng_datavolume: SearxNG runtime fileswebui_datavolume: browser-use web UI session dataschedule_datavolume: scheduler persisted schedules and stored file attachmentsnextcloud_datavolume: temp storage for cached Nextcloud content
Important
Back up the volumes you care about before upgrading images
License
The repository and reference code are released under Apache-2.0 (see LICENSE).