A fully offline chat interface for your local Ollama models. No cloud. No telemetry. Runs a lightweight Python backend with optional RAG support.
- Real-time streaming responses from any Ollama model
- Sidebar with grouped chat history (Today / Yesterday / Last 7 days / Older)
- Rename and delete chats
- Switch models mid-chat via a dropdown in the header
- Markdown rendering with syntax-highlighted code blocks
- Copy button on code blocks and messages
- Dark / light theme toggle
- Chat history persisted in
localStorage(survives refreshes) - Clear chat without losing the session
- Voice input. Offline speech-to-text via
faster-whisper, no cloud STT service involved - RAG (Retrieval-Augmented Generation) support via ChromaDB
- Python FastAPI backend proxies all Ollama requests (avoids CORS issues)
There are two ways to run LocalChat: the prebuilt AppImage (fastest, no build tools needed) or from source (needed if you want to modify the code).
Download LocalChat-x86_64.AppImage from the Releases page (or build it yourself. See Building the AppImage below), then:
chmod +x LocalChat-x86_64.AppImage
./LocalChat-x86_64.AppImage
That's it, no installer, no apt/dpkg, nothing written outside your own home directory. It opens http://127.0.0.1:8765 in your default browser automatically.
-
Settings and chat data persist in
~/.config/localchat/and~/.local/share/localchat/regardless of where you keep the AppImage file. -
To remove it, delete the
.AppImagefile — there's nothing else to uninstall. (Exception: if you used AppImageLauncher to integrate it into your app menu, that also creates a.desktopentry; remove that separately via your applications menu if you don't want it registered anymore.) -
To wipe your settings and RAG knowledge base along with it:
rm -rf ~/.config/localchat ~/.local/share/localchat
See Setup below.
-
Ollama must be installed and running.
-
Install: https://ollama.com or
curl -fsSL https://ollama.com/install.sh | sh -
Start:
ollama serve(or it may already be running as a systemd service) -
Pull at least one model:
ollama pull llama3.2
-
-
Node.js 18+ and npm (for building the frontend).
-
Python 3.10+ and pip (for the backend).
# Clone or copy the project
git clone https://github.com/macbuildssys/ollama-orig.git
cd LocalChat
# Install frontend dependencies (one-time)
npm install
# Create and activate a Python virtual environment (one-time)
python3 -m venv venv
source venv/bin/activate # fish shell: source venv/bin/activate.fish
# Install backend dependencies (one-time)
pip install -r requirements.txt
# Build the frontend
npm run build
source venv/bin/activate # fish shell: source venv/bin/activate.fish
python3 run.py
Open http://localhost:8765 in your browser.
The backend proxies all Ollama requests, so you do not need to configure CORS settings in Ollama.
Click the mic icon in the message box to record a voice message. It's transcribed locally via faster-whisper (no audio ever leaves your machine) and the text is inserted into the input box for you to review before sending.
The Whisper model size (tiny / base / small) is configurable in Settings; larger sizes are more accurate but slower on CPU. The very first transcription downloads the selected model from Hugging Face (one-time, needs internet); after that it's cached and runs fully offline.
Voice input needs a "secure context." Browsers only allow microphone (and clipboard) access on https:// or on 127.0.0.1/localhost; this is a browser-level restriction, not something LocalChat can bypass on its own.
-
Running LocalChat locally (the normal case, same machine as your browser): this just works.
run.pyopens your browser athttp://127.0.0.1:8765, which browsers already treat as secure by default. No setup needed. -
Running LocalChat inside a VM and browsing from the host machine: in this setup you're hitting the VM's network IP (e.g.
http://192.168.x.x:8765) from the host browser, which is not a secure context, so voice input (and clipboard) will silently fail. Two fixes:- SSH tunnel (works in any browser): from the host, run
ssh -L 8765:127.0.0.1:8765 <user>@<vm-ip>, then browse tohttp://127.0.0.1:8765on the host. This makes the connection genuinely loopback, so everything works normally. - Chrome-only flag (quicker, single browser): go to
chrome://flags/#unsafely-treat-insecure-origin-as-secure, enable it, and add your VM's address (e.g.http://192.168.x.x:8765) to the text field, then relaunch Chrome. This tells Chrome to trust that one origin despite plain HTTP.
Note this doesn't carry over if the VM's IP changes, and it has no equivalent in Firefox. The SSH tunnel is the more durable fix if you use multiple browsers or a DHCP-assigned VM IP.
- SSH tunnel (works in any browser): from the host, run
By default, LocalChat binds to 127.0.0.1:8765 and is only reachable from the same machine.
To allow access from another device on your network (e.g. a VM host or a second machine), start with:
OLLAMA_HOST=0.0.0.0 python3 run.py
Alternatively, edit the script run.py and change the uvicorn host from 0.0.0.0 to `127.0.0.1 (this is the current default). Then open the machine's local network IP in your browser:
http://192.168.x.x:8765
Note: crypto.randomUUID() requires a secure context in modern browsers. When accessing over plain HTTP from a non-localhost address, the app patches this automatically. If you see UUID-related errors, rebuild after pulling the latest changes.
Voice input does not have an equivalent automatic patch, see Voice Input above if you're in this networking setup and the mic button doesn't work.
If the host Ollama is on a different machine than where LocalChat is running, set the OLLAMA_HOST environment variable before starting:
OLLAMA_HOST=http://192.168.xxx.xxx:11434 python3 run.py
Make sure Ollama on the remote machine is also bound to 0.0.0.0:
# On the machine running Ollama, edit its systemd service:
sudo systemctl edit ollama
# Add (Add the service and environment as shown below):
# [Service]
Environment="OLLAMA_HOST=0.0.0.0:11434"
# Restart ollama:
sudo systemctl restart ollama
qwen3:4b 2.5 GB
phi4-mini:latest 2.5 GB
llama3.2:3b 2.0 GB
ministral-3:3b 3.0 GB
deepseek-r1:1.5b 1.1 GB
gemma4:e4b 9.6 GB
nomic-embed-text:latest 274 MB
All of these appear automatically in the model dropdown once Ollama is running.
ollama pull mistral
ollama pull codellama
ollama pull deepseek-coder-v2
Refresh the browser tab after pulling. The new model appears in the dropdown immediately.
npm run build
python3 run.py
The backend serves the built frontend directly. There is no separate static file server needed.
Requires Node/npm, Python/pip, and pyinstaller (pip install pyinstaller) on the build machine. None of these are needed by whoever just runs the resulting .AppImage.
chmod +x package/build-appimage.sh
./package/build-appimage.sh
This builds the frontend, runs PyInstaller against localchat.spec, assembles the AppDir, downloads appimagetool if it isn't already cached in build/, and produces LocalChat-x86_64.AppImage in the project root. Expect a few minutes for the PyInstaller step (bundling ChromaDB and faster-whisper's dependencies is the slow part) and a final size in the 95–250MB range.
Chat sessions are stored in your browser's localStorage under the key localchat-v1. No chat data is written to disk by the app itself.
RAG document embeddings and app settings are stored outside the project/app directory in standard per-user locations, so they persist across rebuilds and work correctly from a read-only AppImage mount:
-
Settings (
config.json, including Ollama host and Whisper model size):~/.config/localchat/ -
RAG vector store (ChromaDB):
~/.local/share/localchat/chroma_db/
If you're upgrading from an older version that stored these next to the project files, they're migrated automatically the first time the new version runs.
To export or back up chats, open DevTools (F12 or Ctrl + Shift + I) and navigate to Application > Local Storage (Chrome) or Storage > Local Storage (Firefox), then copy the JSON value under the localchat-v1 key.
"Could not connect to Ollama"
-
Run
ollama servein a terminal. -
Check
ss -tlnp | grep 11434to confirm Ollama is listening. -
If Ollama is on a different machine, set
OLLAMA_HOSTas described above and ensure port 11434 is not blocked by a firewall.
Backend fails to start with ModuleNotFoundError
-
Make sure the virtual environment is activated:
source venv/bin/activate.fish -
Run
pip install -r requirements.txtinside the venv.
Model not appearing in the dropdown
-
Run
ollama listto verify it is downloaded. -
Refresh the browser tab.
Slow responses
-
Normal for large models (gemma4, phi4, etc.) on CPU.
-
Use
qwen3:4borllama3.2:3bfor faster responses on lighter hardware.
Port 8765 not reachable from another machine
-
Check the firewall on the machine running LocalChat:
sudo ufw allow 8765 -
Confirm the backend is bound to
0.0.0.0and not127.0.0.1.
Mic button doesn't record / "Cannot read properties of undefined (reading 'getUserMedia')"
-
This means the page isn't running in a browser "secure context" — almost always because you're browsing to a non-
127.0.0.1address (a VM's LAN IP, a remote host, etc.). See Voice Input above for the SSH tunnel / Chrome flag fixes. -
Confirm in DevTools console:
window.isSecureContextshould betrue. If it'sfalseon127.0.0.1itself (rare), check for a system-wide proxy or VPN intercepting loopback traffic.
Transcription fails with an ONNX/model file error
-
Make sure you're on a build with the fixed
localchat.spec(bundlesfaster_whisper's VAD asset explicitly); older AppImage builds could produce a bundle missingsilero_vad_v6.onnx. -
The very first transcription needs internet access once to download the Whisper model from Hugging Face; subsequent runs are fully offline.
| Layer | Technology |
|---|---|
| UI framework | React 18 + TypeScript + Vite |
| Styling | Tailwind CSS |
| State / store | Zustand (persisted to localStorage) |
| Markdown | react-markdown + remark-gfm |
| Highlighting | rehype-highlight + highlight.js |
| Icons | lucide-react |
| Fonts | DM Sans + JetBrains Mono (Google Fonts) |
| Backend | Python + FastAPI + uvicorn |
| Voice input | faster-whisper (offline STT) |
| RAG / embeddings | ChromaDB |
| HTTP client | httpx |
| LLM backend | Ollama (local) |
| Packaging | PyInstaller + AppImage |
Distributed under the MIT License. See LICENSE.
