SOP-002: Local LLM Inference Stack: Operation & Troubleshooting¶
Sep 29, 2026 ยท @Lloyd
1. Document Control¶
| Field | Value |
|---|---|
| Document ID | SOP-002 |
| Version | 0.2 (Draft) |
| Owner | Lloyd Johnson, Johnson Technical Systems LLC |
| Milestone | Network Operations & Troubleshooting |
| Review cycle | Every 6 months, or after any stack upgrade or new failure mode |
| Classification | Internal / Portfolio sample |
| Related | SOP-001 Secure Remote Access to Windows Workstations |
2. Purpose and Scope¶
This SOP keeps local AI inference running, recoverable, and private. It covers startup checks, parameter tuning, backup before upgrades, and diagnosis of known failures.
In scope: the Windows desktop running Ollama and the Open WebUI Docker container, plus access to it over Tailscale.
Out of scope: remote desktop access (see SOP-001), model training, and any processing of client data. No client data enters this stack unless a contract permits it.
3. System Description¶
Ollama serves models on the host GPU; Open WebUI runs in Docker and reaches Ollama through host.docker.internal:11434. The container publishes its port on the host loopback address only, and Tailscale Serve publishes that port to the tailnet over HTTPS. Devices on the local network that are not on the tailnet cannot reach the UI.
| Component | Detail |
|---|---|
| Host | Windows 11 Pro, Ryzen 7 7800X3D, RTX 5080 (16 GB) |
| Inference engine | Ollama, native on host, port 11434 |
| Interface | Open WebUI container open-webui, image pinned to a release tag (record it here), host 127.0.0.1:3001 to container 8080, volume open-webui |
| Primary model | gpt-oss:20b (custom SOP Drafter and Network+ Tutor models built on it) |
| Remote access | Tailscale Serve (tailscale serve --bg 3001); UI at https://<host>.<tailnet>.ts.net |
| Auto-start | Docker Desktop at login; container --restart always; host power plan never sleeps |
4. Procedure A: Startup and Health Check¶
Run top to bottom; stop at the first failed check and go to Section 8.
- Confirm no consumer VPN (NordVPN or similar) is active on the host, or that Ollama and Docker are excluded from it.
- Confirm the GPU and driver respond:
nvidia-smilists the RTX 5080 with no errors. - Confirm Ollama is running:
ollama listreturns installed models. - Test the engine directly, bypassing the UI:
ollama run gpt-oss:20b "Reply with OK". - Confirm the container is up:
docker psshowsopen-webuiwith127.0.0.1:3001->8080/tcp. If it shows0.0.0.0:3001, the UI is exposed to the whole local network; recreate it with Procedure C, step 7. - Open
http://localhost:3001on the host and send a short prompt. - Confirm Tailscale Serve is publishing the UI:
tailscale serve statuslists port 3001. - From a remote client on Tailscale, open
https://<host>.<tailnet>.ts.netand send a short prompt.
5. Procedure B: Model Parameter Tuning¶
Reasoning models spend output tokens on hidden reasoning before answering, so default limits truncate long responses. Set both values per custom model in Open WebUI under Advanced Params.
| Parameter | Controls | Default | Set to (long-form output) |
|---|---|---|---|
Max Tokens (num_predict) |
Output cap, reasoning included | Low or unset | 8192 |
Context Length (num_ctx) |
Prompt + reasoning + answer | 4096 | 16384 |
Raise num_ctx in steps and watch VRAM in nvidia-smi; if the model spills out of the 16 GB card, speed drops sharply. Record every change in Section 10.
6. Procedure C: Backup and Open WebUI Upgrade¶
Back up before every upgrade; the container is disposable, the open-webui volume is not. Upgrades move from one pinned release tag to the next. Do not run the :main tag, which changes without notice and leaves no known version to roll back to.
- Record the current run configuration and image tag:
docker inspect open-webui --format "{{json .HostConfig}}"anddocker inspect open-webui --format "{{.Config.Image}}". - Stop the container so the database is not written to during the copy:
docker stop open-webui. - Copy the database out of the stopped container:
docker cp open-webui:/app/backend/data/webui.db C:\Users\<user>\webui-backup-<date>.db. Copy the single file, not the whole data folder (see Section 8). - Confirm the backup file exists and is not zero bytes.
- Choose the target release from the Open WebUI releases page, read its release notes for breaking changes, and pull it:
docker pull ghcr.io/open-webui/open-webui:<release tag>. - Remove the old container:
docker rm open-webui. The volume is kept. -
Recreate it with the port bound to loopback only:
docker run -d -p 127.0.0.1:3001:8080 -v open-webui:/app/backend/data --add-host=host.docker.internal:host-gateway --name open-webui --restart always ghcr.io/open-webui/open-webui:<release tag> -
Watch
docker logs -f open-webuiuntil database migrations complete without errors, then run Section 7. - Record the new release tag in Section 3 and in Section 10.
Rollback. If migrations fail or the verification checks do not pass, stop and remove the new container, restore the backup into the volume, and recreate the container on the previous tag recorded in step 1:
docker stop open-webui
docker rm open-webui
docker create -v open-webui:/app/backend/data --name owui-restore alpine
docker cp C:\Users\<user>\webui-backup-<date>.db owui-restore:/app/backend/data/webui.db
docker rm owui-restore
Then rerun step 7 with the previous release tag.
7. Verification¶
The stack passes only when every check holds.
- Existing chats, custom models, and knowledge bases are present after any upgrade
- A 10-question quiz prompt to the Network+ Tutor returns all 10 questions, fully formatted
- The same prompt works from a remote client over cellular data (Tailscale path, not LAN)
- Port 3001 is not reachable from another device on the local network that is not on the tailnet
- Port 3001 is not reachable from the public internet (no router port forward exists)
- The running image tag matches the tag recorded in Section 3
- A current
webui.dbbackup exists and is newer than the last upgrade
8. Known Failure Modes¶
Observed during operation of this stack. The first row matters most: the error message pointed at the wrong component.
| Symptom | Cause | Resolution |
|---|---|---|
| UI shows brief "thinking" then no answer, or long jumbled unformatted text; logs report a CUDA initialization error | NordVPN network filter driver interfering with Ollama runtime startup. GPU and driver were healthy (nvidia-smi clean) |
Disconnect the VPN and retest with ollama run. For permanent coexistence, exclude ollama.exe and Docker via split tunneling and retest |
| Long output stops early (quiz ended at question 9 of 10) | Output cap reached; reasoning tokens consumed the budget | Raise num_predict to 8192 and num_ctx to 16384 (Section 5) |
docker cp of the whole data folder fails with a symlink error |
Model cache inside the container uses symlinks that Windows cannot create without admin rights or Developer Mode | Copy only webui.db; the cache re-downloads if needed |
PowerShell errors on a docker inspect --format template containing \n |
PowerShell escapes differ from bash | Use {{json ...}} output instead of a hand-built template |
| UI unreachable remotely but works on the host | Tailscale Serve not running, or host asleep | Run tailscale serve status; if port 3001 is missing, run tailscale serve --bg 3001. Set power plan to never sleep. Do not rebind the container to all interfaces |
| UI reachable from other devices on the local network | Container published on 0.0.0.0:3001 instead of loopback |
Recreate the container with -p 127.0.0.1:3001:8080 (Procedure C, step 7) |
9. Control Mapping¶
Verify each reference against the published framework text before changing status to Reviewed.
| Procedure step | NIST SP 800-53 Rev 5 | ISO/IEC 27001:2022 Annex A |
|---|---|---|
| Documented system configuration with pinned image tag (Section 3) | CM-2 Baseline Configuration | 8.9 Configuration management |
| Backup before upgrade (Procedure C) | CP-9 System Backup | 8.13 Information backup |
| Controlled upgrade with rollback path (Procedure C) | CM-3 Configuration Change Control | 8.32 Change management |
| Keeping the container image current | SI-2 Flaw Remediation | 8.8 Management of technical vulnerabilities |
| Loopback-only port, published to the tailnet only, no public port | SC-7 Boundary Protection; AC-17 Remote Access | 8.20 Networks security |
10. Revision History¶
| Version | Date | Change |
|---|---|---|
| 0.1 | 2026-09-29 | Initial draft from observed operation: NordVPN conflict, output truncation, Open WebUI upgrade |
| 0.2 | 2026-10-03 | Bound the UI to loopback and published it with Tailscale Serve, so it is no longer exposed to the local network. Pinned the image to a release tag. Stop the container before backing up the database. Added a rollback procedure. Changes not yet tested on the host |