Self-hosted deployment · For IT & security review
Deployment & Operations Guide
Jozie AI runs entirely inside your own infrastructure — no customer data, documents, or model outputs are sent to a third party. This document is written for the people who have to approve, install, and operate it: IT, security review, and ops.
Document
Deployment & Ops Guide
Audience
IT / Security / Ops
Deployment model
Self-hosted, single node
Status
Current as of install
Overview
Jozie AI is a platform for building, cleaning, and evaluating training data, then fine-tuning and serving domain-specific AI models — for one team, department, or the whole company, run on hardware you control.
It has three moving parts, all of which run on infrastructure you own:
- The application — a web app (FastAPI backend + React frontend) with organizations, projects, role-based access, and an audit log.
- The data layer — PostgreSQL (with the pgvector extension) and Redis, both standard, well-understood infrastructure your team likely already runs elsewhere.
- The model layer — Ollama for local LLM inference, and an optional on-device fine-tuning pipeline (Apple Silicon only — see System requirements).
Why self-hosted matters here: the documents your teams upload, the examples they approve, and the models they train are training data for AI systems — often the most sensitive category of company IP. Running this on your own network means none of it transits or is stored by us.
Architecture
A single logical instance is one host (or one VM) running every service below. There is no multi-node clustering in this version — see Known limitations.
flowchart TB
Browser["Employee browser"]
subgraph Host["Self-hosted instance — one server or Mac"]
Caddy["Reverse proxy (Caddy)\nTLS termination"]
FE["Frontend\nstatic build"]
API["Backend\nFastAPI"]
Worker["Workers\nCelery"]
PG[("PostgreSQL\n+ pgvector")]
Redis[("Redis\nqueue + live updates")]
Ollama["Ollama\nlocal LLM inference"]
MLX["Fine-Tuning\nMLX — Apple Silicon only"]
end
subgraph External["Outbound only, on demand — no company data sent"]
HF["Hugging Face\nbase model weights"]
Reg["Ollama registry\nmodel weights"]
end
Browser -- HTTPS --> Caddy
Caddy --> FE
Caddy -- "/api/*" --> API
API --> PG
API --> Redis
API --> Ollama
Worker --> PG
Worker --> Redis
Worker --> Ollama
Worker --> MLX
MLX --> Ollama
Ollama -. pulls weights .-> Reg
MLX -. downloads base model .-> HF
Dashed lines are the only outbound network calls — and only model weight downloads, never company data.
Components
| Component | Technology | Role |
|---|---|---|
| Frontend | React + Vite, static build | The web UI, served by the reverse proxy |
| Backend | FastAPI (Python) | REST API, auth, RBAC, business logic |
| Workers | Celery | Document extraction, dataset generation, RAG indexing, evaluation, fine-tuning |
| Database | PostgreSQL 16 + pgvector | All application data, including vector embeddings for RAG |
| Queue / pub-sub | Redis | Task queue, and live job-progress updates over WebSockets |
| LLM inference | Ollama | Runs chat and embedding models locally — no external API calls |
| Fine-tuning | Apple MLX + llama.cpp converter | Optional: on-device LoRA training, Apple Silicon only |
| Reverse proxy | Caddy | TLS termination, single origin for frontend + API |
System requirements
Sized by usage tier. All tiers assume the single-node architecture above.
| Tier | CPU | RAM | Disk | Notes |
|---|---|---|---|---|
| Pilot ≤10 users | 8 cores | 32 GB | 200 GB SSD | Fine-tuning excluded; chat/embedding models only |
| Team 10–50 users | 12+ cores | 64 GB | 500 GB SSD | Headroom for larger Ollama models and concurrent RAG indexing |
| + Fine-tuning | — | 36 GB+ unified memory | +100 GB | Requires an Apple Silicon Mac (M-series). Add HF model cache + per-job artifact storage. |
Fine-tuning is hardware-gated, not a config toggle. It runs on Apple's MLX framework, which only executes on Apple Silicon (M-series Macs). There is no supported path to run it on a standard Linux/Windows server or a cloud GPU VM. Every other module — including using fine-tuned models afterward — runs fine on any modern server.
Network & ports
Only the reverse proxy should be reachable from outside the host. Everything else should be bound to localhost or an internal network segment.
| Service | Port | Exposure |
|---|---|---|
| Caddy (reverse proxy) | 443 | Company network |
| Backend API (direct) | 8000 | Localhost / internal only |
| PostgreSQL | 5432 | Never external |
| Redis | 6379 | Never external |
| Ollama | 11434 | Never external |
Outbound: HTTPS to ollama.com and huggingface.co, only when
an admin explicitly pulls a new model or starts a fine-tuning job that needs a base
model not yet cached. Nothing calls out on a schedule, and no request carries
uploaded documents, dataset content, or conversation text.
Data residency
Everything the product touches is written to local disk, under paths you control and back up.
| Data | Location |
|---|---|
| Application data (users, projects, datasets, prompts, audit log) | PostgreSQL data directory |
| Uploaded documents | backend/data/uploads/ |
| Fine-tuning artifacts (adapters, fused weights, GGUF exports) | backend/data/finetuning/<job-id>/ |
| Downloaded LLM models | ~/.ollama/models |
| Downloaded base models for fine-tuning | ~/.cache/huggingface |
| Application & access logs | backend/logs/ (rotating files) |
Security checklist
Confirm each of these before exposing the instance to real users.
- Set
ENVIRONMENT=productionand a realJWT_SECRET(openssl rand -hex 32)The app refuses to start in production mode with the default secret — this is enforced, not just documented. - Confirm
/docs(the API reference) is disabledDisabled by default outside local/dev mode. - Issue a real TLS certificate for the reverse proxyThe default local setup uses Caddy's self-signed local CA, which is fine for a single machine but should be replaced with your internal CA or a public cert for shared access.
- Confirm Postgres, Redis, Ollama, and the backend port are not reachable from outside the hostOnly the reverse proxy's port should be exposed — see Network & ports.
- Assign roles deliberately:
owner>admin>editor>viewerOnly owner/admin can manage users and API keys; keep that group small. - Rotate or scope API keys per integration, not sharedAPI keys are hashed at rest and revocable individually — treat each integration as its own key.
- Confirm backups are scheduled and a restore has been testedSee Backup & recovery.
- Review the audit log retention against your compliance requirementsEvery mutating action (create/edit/delete/export) is recorded with actor, action, and timestamp.
Backup & recovery
Logical backups of the database; file-level backup for everything else.
| What | Method | Retention |
|---|---|---|
| Database | pg_dump, gzip-compressed, nightly | Last 30 backups |
| Uploaded documents & fine-tuning artifacts | File-level backup of backend/data/ | Per your existing file-backup policy |
RPO is up to 24 hours with the default nightly schedule — reduce the
interval if that doesn't meet your requirements. This is a logical (pg_dump)
backup, not continuous point-in-time recovery.
To restore: stop the backend and workers, run the restore script against the target database, then restart. Test this procedure before you need it for real.
Upgrade runbook
- Back up first. Run a manual backup even if a nightly one ran recently.
- Announce a maintenance window. The app is unavailable while services restart (single-node, no rolling upgrade in this version).
- Stop the backend and worker processes. Leave Postgres, Redis, and Ollama running.
- Deploy the new backend and frontend code. Rebuild the frontend if it changed.
- Apply database migrations before starting the new backend.
- Start the backend and worker processes and confirm the Dashboard shows Ollama and Database as reachable.
- Smoke-test the pipeline that matters most to your users (e.g. upload a document, or run one conversation).
- Roll back by restoring the previous backup and redeploying the previous code version if step 7 fails.
Monitoring
What to watch day to day, and where to look when something's wrong.
| Signal | Where |
|---|---|
| Ollama & database reachability | Dashboard (in-app), refreshed live |
| Host CPU / memory | Dashboard (in-app) |
| Application errors | backend/logs/backend.log (rotating; JSON-formatted if LOG_JSON=true) |
| Background job failures | backend/logs/worker.log |
| Who did what, when | Administration → Audit Log (in-app) |
Known limitations
Stated plainly, so your evaluation is based on what this actually does today — not on smoothed-over marketing language.
Single node, no clustering
No built-in horizontal scaling, load balancing, or multi-node database HA. Sized for one team or department per instance today.
Process supervision is macOS-native
The included service supervisor uses launchd. A Linux deployment (systemd units, or a container orchestrator) is straightforward but not yet packaged.
No SSO / SAML integration
Authentication is local (email + password, JWT) plus API keys for machine access. No Okta/Azure AD/SAML connector today.
Fine-tuning requires Apple Silicon
Not a licensing restriction — MLX genuinely does not run on other hardware. Plan for a dedicated Mac if this module matters to you.
Backups are logical, not continuous
Nightly pg_dump, not point-in-time recovery. Up to 24h of data can be lost on the default schedule.
Support boundaries
What's covered, and what stays your team's responsibility in a self-hosted model.
| Area | Owner |
|---|---|
| Application bugs, upgrades, security patches to the product | Vendor |
| Host OS, hardware, network, firewall configuration | Your IT/ops team |
| Postgres/Redis tuning, backup storage, disaster recovery execution | Your ops team (runbooks provided) |
| TLS certificates and internal DNS | Your IT team |
| Ollama model selection and content moderation policy | Your team |