Last updated: 2026-08-08
This guide helps diagnose and fix Docker-related issues with LocalGPT's containerized deployment.
# Check Docker daemon
docker version
# Check Ollama status
curl http://localhost:11434/api/tags
# Check containers
./start-docker.sh status
# Test all endpoints
curl -f http://localhost:3000 && echo "✅ Frontend OK"
curl -f http://localhost:8000/health && echo "✅ Backend OK"
curl -f http://localhost:8001/health && echo "✅ RAG API OK"
curl -f http://localhost:11434/api/tags && echo "✅ Ollama OK"✅ Frontend OK
✅ Backend OK
✅ RAG API OK
✅ Ollama OK
rag-api (healthy: GET /health) → backend (healthy: GET /health) → frontend
backend has depends_on: rag-api: condition: service_healthy, and rag-api only
answers /health once the agent and the embedding model have loaded (the reranker
loads lazily, on the first reranked query).
On a cold start that is a multi-minute HuggingFace download. backend sitting in
created is usually patience, not a bug — confirm with
docker compose logs -f rag-api.
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
# Quit Docker Desktop completely
# macOS: Click Docker icon → "Quit Docker Desktop"
# Windows: Right-click Docker icon → "Quit Docker Desktop"
# Wait for it to fully shut down
sleep 10
# Start Docker Desktop
open -a Docker # macOS
# Windows: Click Docker Desktop from Start menu
# Wait for Docker to be ready (2-3 minutes)
docker versionsudo systemctl status docker
sudo systemctl restart docker
sudo systemctl enable docker
docker version# Kill all Docker processes
sudo pkill -f docker
# Remove socket files
sudo rm -f /var/run/docker.sock
sudo rm -f "$HOME/.docker/run/docker.sock" # macOS
# Restart Docker Desktop
open -a Docker # macOSConnectionError: Failed to connect to Ollama at http://host.docker.internal:11434
curl http://localhost:11434/api/tags
# If not running, start it
ollama serve
# Install the required models
ollama pull qwen3.5:9b
ollama pull qwen3.5:4brun_system.py pulls these automatically, but start-docker.sh does not — with
host Ollama you must pull them yourself.
docker compose exec rag-api curl -sv http://host.docker.internal:11434/api/tags
# Inspect the network. The compose project name is the directory name,
# so the network is localgpt_rag-network.
docker network ls
docker network inspect localgpt_rag-networkAll services declare extra_hosts: ["host.docker.internal:host-gateway"], so this
name resolves on Linux as well as macOS and Windows. If it still fails, check that
Ollama is listening on all interfaces rather than only the loopback:
OLLAMA_HOST=0.0.0.0:11434 ollama serve./start-docker.sh container
docker compose --profile with-ollama exec ollama ollama pull qwen3.5:9b
docker compose --profile with-ollama exec ollama ollama pull qwen3.5:4bThis exports OLLAMA_HOST=http://ollama:11434, which wins over docker.env
because shell variables take precedence over --env-file.
# Edit docker.env rather than appending duplicates - the last value wins,
# but duplicate keys make the file confusing.
# macOS example:
OLLAMA_HOST=http://$(ipconfig getifaddr en0):11434 ./start-docker.shThe backend builds /chat and /index from RAG_API_URL. Inside compose that
must be http://rag-api:8001 — localhost there refers to the backend container.
Chat failures come back as JSON error bodies: 502 when the RAG API is unreachable,
504 when the call times out.
docker compose exec backend env | grep RAG_API_URL
docker compose exec backend curl -s http://rag-api:8001/healthThe RAG API is single-threaded, so a chat request queued behind an indexing run can
exceed the timeout. Watch docker compose logs -f rag-api, and raise the limits if
your hardware is genuinely slow:
RAG_API_TIMEOUT=1200 RAG_API_INDEX_TIMEOUT=7200 \
docker compose --env-file docker.env up -d backendNEXT_PUBLIC_API_URL and NEXT_PUBLIC_RAG_API_URL are inlined by next build.
Setting them only in environment: cannot change an already-built image.
NEXT_PUBLIC_API_URL=http://localhost:8000 \
NEXT_PUBLIC_RAG_API_URL=http://localhost:8001 \
docker compose --env-file docker.env up -d --build frontendERROR: Failed to build frontend container
./start-docker.sh stop
docker system prune -f
docker builder prune -f
docker compose build --no-cache frontend
docker compose up -d frontend
docker compose logs frontendDockerfile.frontend copies package.json, package-lock.json, src/,
public/, next.config.ts, tsconfig.json, postcss.config.mjs and
eslint.config.mjs. If you deleted one of those in a fork, the COPY fails.
public/ is kept non-empty by public/.gitkeep.
ERROR: Could not install packages due to an EnvironmentError
# Both Python images install requirements-docker.txt
ls -la requirements-docker.txt
# Test locally
pip install -r requirements-docker.txt --dry-run
# Rebuild with an updated base image
docker compose build --no-cache --pull rag-apiError starting userland proxy: listen tcp4 0.0.0.0:3000: bind: address already in use
lsof -i :3000 -i :8000 -i :8001
# A direct (non-Docker) run of the same stack is the usual culprit
python run_system.py --stop
# Or by port
sudo kill -9 $(lsof -t -i:3000)
sudo kill -9 $(lsof -t -i:8000)
sudo kill -9 $(lsof -t -i:8001)
./start-docker.shContainer killed due to memory limit
The rag-api container holds the embedding model and the reranker in memory for
the life of the process, and enabling late chunking loads a second copy of the
embedder during indexing.
docker stats --no-stream
# Docker Desktop → Settings → Resources → Memory → 8GB+
# Lighter configuration (rebuild indexes after changing the embedding model)
GENERATION_MODEL=qwen3.5:4b \
EMBEDDING_MODEL=Qwen/Qwen3-Embedding-0.6B \
docker compose --env-file docker.env up -d rag-api backend
# Or switch the whole RAG API to the fast profile
RAG_CONFIG_MODE=fast docker compose --env-file docker.env up -d rag-apifree -h # Linux
vm_stat # macOS
docker system prune -f
docker volume prune -fPermission denied: /app/lancedb
mkdir -p lancedb index_store shared_uploads backend
chmod -R 755 lancedb index_store shared_uploads
chmod 664 backend/chat_data.db
ls -la lancedb/ shared_uploads/ backend/
sudo chown -R $USER:$USER lancedb shared_uploads index_store backendNo such file or directory: '/app/backend/chat_data.db'
ChatDatabase creates the parent directory and the file itself, so this normally
means the ./backend bind mount is missing or DB_PATH points somewhere
unmounted.
docker compose exec backend env | grep DB_PATH # expect /app/backend/chat_data.db
docker compose exec backend ls -la /app/backend
# Create it from the host if needed
python -c "
from backend.database import ChatDatabase
db = ChatDatabase()
print('Database initialized at', db.db_path)
"
./start-docker.sh stop
./start-docker.shThe SQLite rows and the LanceDB tables must match. Restoring one without the other,
or changing LANCEDB_PATH, leaves index records pointing at tables that do not
exist.
docker compose exec rag-api python -c "
import lancedb
print(lancedb.connect('/app/lancedb').table_names())
"
docker compose exec backend sqlite3 /app/backend/chat_data.db \
"select id, name, vector_table_name from indexes;"Vector width is read from the loaded embedding model. Delete and rebuild the index
after changing EMBEDDING_MODEL.
# RAG API container (most issues happen here)
docker compose exec rag-api bash
# Check the wiring
docker compose exec rag-api env | grep -E "OLLAMA|MODEL|DB_PATH|LANCEDB|RAG_CONFIG"
# Test Python imports
docker compose exec rag-api python -c "
import sys
print('Python version:', sys.version)
from rag_system.factory import get_agent
print('✅ RAG system imports work')
"
# Backend container
docker compose exec backend bash
docker compose exec backend python -c "
from backend.database import ChatDatabase
print('✅ Database imports work')
"
# Frontend container (alpine -> sh, not bash)
docker compose exec frontend sh -c "node --version && npm --version"docker stats
docker compose ps
docker inspect rag-api --format='{{.State.Health.Status}}'
docker inspect rag-api --format='{{json .State.Health}}' | head -c 2000
docker compose configThe Python images are python:3.11-slim and the frontend is node:20-alpine;
none of them ship ping or nslookup. Use curl (installed in both Python
images) or busybox wget (in the alpine image):
# Backend ↔ RAG API by service name
docker compose exec backend curl -sv http://rag-api:8001/health
docker compose exec rag-api curl -sv http://backend:8000/health
# Container → host Ollama
docker compose exec rag-api curl -sv http://host.docker.internal:11434/api/tags
# From the frontend (alpine)
docker compose exec frontend wget -qO- http://backend:8000/health
# If you really want ping/nslookup, install them in the running container first
docker compose exec rag-api sh -c "apt-get update && apt-get install -y iputils-ping dnsutils"./start-docker.sh logs
docker compose logs -f rag-api
docker compose logs -f backend
docker compose logs -f frontend
docker compose logs rag-api 2>&1 | grep -i error
docker compose logs backend 2>&1 | grep -i "traceback\|error"
docker compose logs > docker-debug.log 2>&1The RAG API logs each stage of a query, so docker compose logs -f rag-api while
you ask a question shows exactly where time is going: retrieval, reranking, context
expansion, pruning, synthesis, verification.
# Docker daemon logs (Linux)
journalctl -u docker.service -f
# macOS: Check Console app for Docker logs
# Windows: Check Event Viewertest_docker_build.sh automates exactly this — building each image and probing its
health endpoint:
./test_docker_build.shBy hand:
# RAG API alone
docker build -f Dockerfile.rag-api -t test-rag-api .
docker run -d --name test-rag-api -p 8001:8001 \
-e OLLAMA_HOST=http://host.docker.internal:11434 \
--add-host host.docker.internal:host-gateway test-rag-api
sleep 60 # models load before /health answers
curl http://localhost:8001/health
docker rm -f test-rag-api
# Backend alone
docker build -f Dockerfile.backend -t test-backend .
docker run -d --name test-backend -p 8000:8000 test-backend
sleep 15
curl http://localhost:8000/health
docker rm -f test-backend./start-docker.sh -y
# Wait for the health-gated chain to come up
until curl -sf http://localhost:8000/health >/dev/null; do sleep 5; done
# Create a session
curl -X POST http://localhost:8000/sessions \
-H "Content-Type: application/json" \
-d '{"title": "Test Session"}'
# Create an index, upload a document, build it
IDX=$(curl -s -X POST http://localhost:8000/indexes \
-H "Content-Type: application/json" \
-d '{"name":"Smoke Test"}' | python3 -c "import sys,json;print(json.load(sys.stdin)['index_id'])")
curl -X POST "http://localhost:8000/indexes/$IDX/upload" -F "files=@test.pdf"
curl -X POST "http://localhost:8000/indexes/$IDX/build" \
-H "Content-Type: application/json" -d '{"chunk_size": 512}'
./start-docker.sh stopThe upload form field must be named files.
./start-docker.sh stop
docker system prune -f
./start-docker.sh./start-docker.sh stop
docker compose down
# Application data lives in host directories, not volumes -
# `docker compose down -v` alone will NOT remove it.
rm -rf lancedb/* index_store/* shared_uploads/* backend/chat_data.db
docker system prune -a --volumes
./start-docker.sh# Just the chat/index database
./start-docker.sh stop
rm backend/chat_data.db
./start-docker.sh
# Just vector storage (indexes must be rebuilt; delete the DB rows too or they
# will point at tables that no longer exist)
./start-docker.sh stop
rm -rf lancedb/*
./start-docker.sh
# Just uploaded documents
rm -rf shared_uploads/*watch -n 5 'docker stats --no-stream'
docker system df
du -sh lancedb shared_uploads index_store backend
htop # Linux
top # macOS# Fast profile: vector-only retrieval, no reranking, decomposition or verification
RAG_CONFIG_MODE=fast docker compose --env-file docker.env up -d rag-api
# Smaller generation model
GENERATION_MODEL=qwen3.5:4b docker compose --env-file docker.env up -d rag-api backend
# Smaller embedding model (rebuild indexes afterwards)
EMBEDDING_MODEL=Qwen/Qwen3-Embedding-0.6B docker compose --env-file docker.env up -d rag-api
# Faster indexing: turn off contextual enrichment in the index build options
# (it makes one LLM call per chunk)Structural limits worth knowing before you tune:
- The RAG API is single-threaded — requests are serialised.
- Indexing is synchronous;
POST /indexes/{id}/buildstays open until it finishes. - Model weights are downloaded into the container's writable layer, so recreating
rag-apire-downloads them unless you mount a cache and setHF_HOME.
./start-docker.sh stop
python run_system.pydocker build -f Dockerfile.rag-api -t rag-api .
docker run -p 8001:8001 \
-e OLLAMA_HOST=http://host.docker.internal:11434 \
--add-host host.docker.internal:host-gateway rag-api
# Run the rest directly, from the repository root
python backend/server.py &
npm run devdocker compose --env-file docker.env up -d rag-api
# Point the host backend at the container
RAG_API_URL=http://localhost:8001 python backend/server.py &
npm run devdocker version
docker compose version
uname -a
docker compose ps
docker compose config
docker stats --no-stream
docker system df
docker compose logs > docker-errors.log 2>&1- Check GitHub Issues: Search existing issues for similar problems
- Documentation: Review
Documentation/andDOCKER_README.md - Create Issue: Include the diagnostic information above
Your Docker deployment is working correctly when:
- ✅
docker versionshows Docker is running - ✅
curl http://localhost:11434/api/tagsshows Ollama is accessible - ✅
docker compose psshows all services healthy - ✅
curl -f http://localhost:8000/healthand.../8001/healthboth return 200 - ✅ You can access the frontend at http://localhost:3000
- ✅ You can create document indexes successfully
- ✅ You can chat with your documents
- ✅ No error messages in container logs
If all boxes are checked, your Docker deployment is successful! 🎉
Still having issues? Check DOCKER_README.md or create an issue with your diagnostic information.