Chat with your documents on your local device using GPT models. No data leaves your device and 100% private.
docker pull ollama/ollama
docker run -d --name localgpt -p 11434:11434 --restart always ollama/ollama:latest
podman run -d --name localgpt -p 11434:11434 --restart always ollama/ollama:latest
skopeo inspect docker://docker.io/ollama/ollama:latest
oras pull ollama/ollama:latest
docker compose up -d
services:
# Ollama service for LLM inference (optional - can use host Ollama instead)
ollama:
image: ollama/ollama:latest
container_name: rag-ollama
ports:
- "11434:11434"
volumes:
- ollama_data:/root/.ollama
environment:
- OLLAMA_HOST=0.0.0.0
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:11434/api/tags"]
interval: 30s
timeout: 10s
retries: 3
restart: unless-stopped
networks:
- rag-network
profiles:
- with-ollama # Optional service - enable with --profile with-ollama
# RAG API server
rag-api:
build:
context: .
dockerfile: Dockerfile.rag-api
container_name: rag-api
ports:
- "8001:8001"
environment:
# Use host Ollama by default, or containerized Ollama if enabled
- OLLAMA_HOST=${OLLAMA_HOST:-http://host.docker.internal:11434}
- NODE_ENV=production
- DB_PATH=${DB_PATH:-/app/backend/chat_data.db}
- LANCEDB_PATH=${LANCEDB_PATH:-/app/lancedb}
- GENERATION_MODEL=${GENERATION_MODEL:-qwen3.5:9b}
- ENRICHMENT_MODEL=${ENRICHMENT_MODEL:-qwen3.5:4b}
- EMBEDDING_MODEL=${EMBEDDING_MODEL:-microsoft/harrier-oss-v1-0.6b}
- RERANKER_MODEL=${RERANKER_MODEL:-Qwen/Qwen3-Reranker-4B}
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
- ./lancedb:/app/lancedb
- ./index_store:/app/index_store
- ./shared_uploads:/app/shared_uploads
# Shared SQLite database - same mount as the backend service
- ./backend:/app/backend
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8001/health"]
interval: 30s
timeout: 10s
retries: 3
restart: unless-stopped
networks:
- rag-network
# Backend API server
backend:
build:
context: .
dockerfile: Dockerfile.backend
container_name: rag-backend
ports:
- "8000:8000"
environment:
- NODE_ENV=production
- RAG_API_URL=${RAG_API_URL:-http://rag-api:8001}
- OLLAMA_HOST=${OLLAMA_HOST:-http://host.docker.internal:11434}
- DB_PATH=${DB_PATH:-/app/backend/chat_data.db}
- LANCEDB_PATH=${LANCEDB_PATH:-/app/lancedb}
- GENERATION_MODEL=${GENERATION_MODEL:-qwen3.5:9b}
- ENRICHMENT_MODEL=${ENRICHMENT_MODEL:-qwen3.5:4b}
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
# Shared SQLite database - same mount as the rag-api service
- ./backend:/app/backend
- ./shared_uploads:/app/shared_uploads
- ./index_store:/app/index_store
- ./lancedb:/app/lancedb
depends_on:
rag-api:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
restart: unless-stopped
networks:
- rag-network
# Frontend Next.js application
frontend:
build:
context: .
dockerfile: Dockerfile.frontend
args:
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-http://localhost:8000}
NEXT_PUBLIC_RAG_API_URL: ${NEXT_PUBLIC_RAG_API_URL:-http://localhost:8001}
container_name: rag-frontend
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL:-http://localhost:8000}
- NEXT_PUBLIC_RAG_API_URL=${NEXT_PUBLIC_RAG_API_URL:-http://localhost:8001}
extra_hosts:
- "host.docker.internal:host-gateway"
depends_on:
backend:
condition: service_healthy
healthcheck:
# node:20-alpine ships busybox wget, not curl
test: ["CMD-SHELL", "wget -qO- http://localhost:3000 >/dev/null 2>&1 || exit 1"]
interval: 30s
timeout: 10s
retries: 3
restart: unless-stopped
networks:
- rag-network
volumes:
ollama_data:
driver: local
networks:
rag-network:
driver: bridge