Private LLM On-Premises with Ollama: Enterprise Deployment Guide

TL;DROllama is an open-source tool for running LLMs locally — Llama 3, Mistral, Qwen, Phi. Use it on-premises for data sovereignty (GDPR, HIPAA, air-gapped), zero per-token costs, and full control over model behavior. GPU requirements: 8GB VRAM for 7-8B models, 24GB for 33-35B, 48GB for 70B. Apple Silicon M2/M3 with 32GB unified memory runs 70B models. Ollama exposes an OpenAI-compatible API — your application code works with both Ollama and cloud APIs. Ollama has no built-in auth or TLS — secure with Nginx reverse proxy (Bearer token auth, rate limiting, TLS), bind to localhost, Docker isolation, and firewall rules. Cost: ~$2,000-4,000/month infrastructure for unlimited queries vs $2,500/month for 500K GPT-4o queries — break-even at ~500K queries/month. For 5M queries: $4,000/month self-hosted vs $25,000/month cloud (6x cheaper). Hybrid approach: LiteLLM routes 80% to Ollama, 20% to cloud APIs. Deploy with Docker Compose as part of self-hosted AI stack.

Ollama makes running LLMs locally as simple as ollama pull llama3. But production deployment requires security hardening, GPU planning, monitoring, and integration with your RAG pipeline. This guide covers everything from model selection to production deployment.

Ollama Architecture

flowchart TD subgraph Client["Client Layer"] App["Application
(OpenAI-compatible)"] end subgraph Proxy["Security Layer"] Nginx["Nginx
TLS + Auth + Rate Limit"] end subgraph Ollama["Ollama Server"] API["Ollama API
:11434 (localhost only)"] Models["Model Manager
(Llama 3, Mistral, Qwen)"] GPU["GPU Runtime
(CUDA / Metal)"] end subgraph Infra["Infrastructure"] Storage["Model Storage
(/root/.ollama)"] Monitor["Prometheus
+ Grafana"] end App -->|"HTTPS + Bearer token"| Nginx Nginx -->|"HTTP (localhost)"| API API --> Models Models --> GPU API -.->|"metrics"| Monitor Models -.->|"weights"| Storage

Model Selection and GPU Requirements

Model Parameters VRAM (Q4_K_M) Min GPU Quality Use Case
Llama 3.2 3B 3B 2GB Any GPU Basic Edge, mobile
Llama 3.1 8B 8B 6GB RTX 4060 (8GB) Good Prototyping, simple RAG
Mistral 7B 7B 5GB RTX 4060 (8GB) Good Fast inference
Qwen 2.5 14B 14B 10GB RTX 4070 (12GB) Very good Multilingual, code
Llama 3.3 70B 70B 40GB A100 (40GB) Excellent Enterprise RAG, matching GPT-4o
Llama 3.1 405B 405B 230GB 4x A100 (80GB) Frontier Best open-weight model
Phi-3 Mini 3.8B 2.5GB CPU only Good CPU-only deployment

Apple Silicon: M2/M3 with 32GB unified memory runs Llama 3 70B (no NVIDIA GPU needed). M2/M3 with 16GB runs 8B models. Metal acceleration is automatic.

Production Security Hardening

Ollama has no built-in authentication, TLS, or rate limiting. Never expose port 11434 to the internet. Always use a reverse proxy.

Nginx Reverse Proxy with Auth and Rate Limiting

# nginx.conf — Ollama production security
upstream ollama_backend {
    server 127.0.0.1:11434;
    keepalive 32;
}

# Rate limiting: 10 requests/second per IP
limit_req_zone $binary_remote_addr zone=ollama_limit:10m rate=10r/s;

server {
    listen 443 ssl http2;
    server_name llm.internal.company.com;

    # TLS
    ssl_certificate /etc/nginx/certs/fullchain.pem;
    ssl_certificate_key /etc/nginx/certs/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;

    # Auth: Bearer token
    auth_request /auth;

    # Proxy to Ollama (localhost only)
    location / {
        limit_req zone=ollama_limit burst=20 nodelay;

        proxy_pass http://ollama_backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_buffering off;  # Streaming responses
        proxy_read_timeout 300s;  # Long generation time
    }

    # Auth endpoint (validate Bearer token)
    location = /auth {
        internal;
        proxy_pass http://127.0.0.1:8001/validate;
        proxy_pass_request_body off;
        proxy_set_header Content-Length "";
        proxy_set_header X-Original-URI $request_uri;
    }
}

Docker Deployment with Isolation

# docker-compose.yml — Ollama production
services:
  ollama:
    image: ollama/ollama:latest
    container_name: ollama
    ports:
      - "127.0.0.1:11434:11434"  # Localhost only!
    volumes:
      - ollama_data:/root/.ollama
    restart: always
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
        limits:
          memory: 48G
    read_only: true  # Read-only filesystem
    tmpfs:
      - /tmp
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    healthcheck:
      test: ["CMD-SHELL", "curl -sf http://localhost:11434/api/tags || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3

  nginx:
    image: nginx:alpine
    ports:
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./certs:/etc/nginx/certs:ro
    depends_on:
      - ollama
    restart: always

volumes:
  ollama_data:

Cost Comparison: Ollama vs Cloud APIs

Scale Ollama (self-hosted) GPT-4o (cloud) Claude Sonnet (cloud) Savings
100K queries/mo $2,000/mo $500/mo $750/mo Cloud cheaper
500K queries/mo $2,500/mo $2,500/mo $3,750/mo Break-even
1M queries/mo $3,000/mo $5,000/mo $7,500/mo 1.7-2.5x
5M queries/mo $4,000/mo $25,000/mo $37,500/mo 6-9x
10M queries/mo $6,000/mo $50,000/mo $75,000/mo 8-12x

Ollama costs include server + GPU + electricity. Cloud costs = tokens only. At 500K queries/month, self-hosting breaks even. Above that, savings compound rapidly.

Monitoring

# prometheus.yml — Ollama metrics
scrape_configs:
  - job_name: 'ollama'
    static_configs:
      - targets: ['localhost:11434']
    metrics_path: /metrics
    scrape_interval: 15s

  - job_name: 'nginx'
    static_configs:
      - targets: ['nginx:9113']
Metric Alert Threshold Why
ollama_request_duration_seconds (p99) > 30s Model overloaded
ollama_gpu_memory_used_bytes > 90% Need more VRAM
ollama_requests_per_second > 10 Rate limit hit
nginx_4xx_errors > 5/min Auth failures
nginx_5xx_errors > 1/min Ollama crashed
ollama_model_load_time_seconds > 60s Slow model loading

Hybrid Deployment with LiteLLM

# Route between Ollama (self-hosted) and cloud APIs
from litellm import Router

router = Router(model_list=[
    # Primary: self-hosted Llama 3 70B (free, data sovereign)
    {
        "model_name": "primary",
        "litellm_params": {
            "model": "ollama/llama3:70b",
            "api_base": "https://llm.internal.company.com",
            "api_key": os.environ["OLLAMA_API_KEY"],
        },
    },
    # Fallback: Claude Sonnet (for complex reasoning)
    {
        "model_name": "fallback",
        "litellm_params": {
            "model": "anthropic/claude-3-5-sonnet",
            "api_key": os.environ["ANTHROPIC_API_KEY"],
        },
    },
],
fallbacks=[{"primary": ["fallback"]}],
)

# 80% of queries go to Ollama (free, on-premises)
# 20% failover to Claude (complex reasoning, cloud)
response = router.completion(
    model="primary",
    messages=[{"role": "user", "content": "Analyze this contract"}],
)

Private LLM Ollama Deployment Checklist

  • [ ] Choose model: Llama 3 8B (prototyping) or 70B (production)
  • [ ] Verify GPU VRAM: 8GB for 8B, 40GB for 70B, or Apple Silicon 32GB
  • [ ] Install Ollama: curl -fsSL https://ollama.com/install.sh | sh
  • [ ] Pull model: ollama pull llama3:70b
  • [ ] Bind Ollama to localhost: OLLAMA_HOST=127.0.0.1:11434
  • [ ] Set up Nginx reverse proxy with TLS (Let's Encrypt or internal CA)
  • [ ] Configure Bearer token authentication in Nginx
  • [ ] Set rate limiting: 10 req/s per IP, burst 20
  • [ ] Configure Docker with read-only filesystem and resource limits
  • [ ] Drop all Linux capabilities (cap_drop: ALL)
  • [ ] Set no-new-privileges: true in Docker security options
  • [ ] Configure firewall: only allow proxy IP to access port 11434
  • [ ] Set up Docker Compose deployment with GPU passthrough
  • [ ] Configure Prometheus metrics scraping
  • [ ] Set up Grafana dashboards: latency, GPU memory, request rate
  • [ ] Configure alerts: p99 latency, GPU memory, error rate
  • [ ] Pin Ollama version (don't auto-update in production)
  • [ ] Set up model storage on persistent volume
  • [ ] Use LiteLLM as gateway for hybrid routing
  • [ ] Route 80% to Ollama, 20% to cloud APIs for complex queries
  • [ ] Integrate with FastAPI + PostgreSQL stack
  • [ ] Use pgvector for RAG retrieval
  • [ ] Add semantic answer caching to reduce GPU load
  • [ ] Implement query rewriting before LLM call
  • [ ] Set up cross-encoder reranking for precision
  • [ ] Configure hallucination prevention: confidence threshold
  • [ ] Evaluate TCO vs cloud APIs
  • [ ] Consider self-hosted AI architecture for full stack
  • [ ] Test failover: kill Ollama, verify LiteLLM routes to cloud API
  • [ ] Set up audit logging for EU AI Act compliance
  • [ ] Consider zero-trust security between services
  • [ ] Document model versions and quantization settings
  • [ ] Plan GPU upgrades: monitor VRAM usage and query latency trends

FAQ

What is Ollama and why use it for on-premises LLM?

Ollama is an open-source tool for running large language models locally. It supports Llama 3, Mistral, Qwen, Phi, and other open-weight models. You use it on-premises when: data cannot leave your network (GDPR, HIPAA, air-gapped), you want to eliminate per-token API costs, you need full control over model behavior, or you want to avoid vendor lock-in. Ollama runs on any machine with 8GB+ RAM (CPU) or a GPU (NVIDIA, Apple Silicon). It exposes an OpenAI-compatible API, so your application code works with both Ollama and cloud APIs without changes.

What GPU do I need for Ollama?

GPU requirements depend on model size. 7-8B models (Llama 3 8B): 8GB VRAM (RTX 4060, T4). 13-14B models: 12GB VRAM (RTX 4070, A10G). 33-35B models: 24GB VRAM (RTX 4090, A10). 70B models: 48GB VRAM (2x A6000, A100 40GB with quantization). Apple Silicon: M2/M3 with 32GB unified memory runs 70B models. For production, use NVIDIA GPUs with CUDA. For prototyping, Apple Silicon or CPU-only works for 7-8B models. Quantization (Q4_K_M default) reduces memory by 4x with minimal quality loss.

How do you secure Ollama for production?

Ollama has no built-in authentication or TLS. Secure it with: (1) Reverse proxy (Nginx/Caddy) for TLS termination and Bearer token authentication, (2) Rate limiting in Nginx to prevent GPU DoS (limit_req_zone with 10 requests/second), (3) Network isolation — bind Ollama to localhost (127.0.0.1) and only expose through proxy, (4) Docker isolation with read-only filesystem and resource limits, (5) API key management with rotating tokens, (6) Audit logging of all requests, (7) Firewall rules restricting access to known IPs. Never expose Ollama's raw API (port 11434) to the internet.

How much does a private LLM with Ollama cost?

Ollama itself is free (MIT license). Costs are infrastructure: a single server with 1x NVIDIA A100 (80GB) costs ~$10,000-12,000 one-time or ~$2,000-4,000/month cloud. This runs Llama 3 70B for unlimited queries. Compare to OpenAI GPT-4o at $2.50/M tokens: at 500K queries/month (avg 2K tokens each), that's $2,500/month. Break-even at ~500K queries/month. For 5M queries/month, self-hosted costs ~$4,000/month vs $25,000/month for GPT-4o — 6x cheaper. Add electricity (~$100-200/month for a GPU server) and a DBA/devops engineer for maintenance.

Can Ollama replace cloud LLM APIs entirely?

For most enterprise use cases, yes — with caveats. Llama 3 70B matches GPT-4o on most benchmarks for RAG, summarization, and Q&A. It falls behind on: complex multi-step reasoning (GPT-5, Claude 3.5 Sonnet), code generation (Claude), and multimodal tasks (GPT-4o vision). Use a hybrid approach: Ollama for 80% of queries (high-volume, simple), cloud APIs for 20% (complex, frontier). Use LiteLLM as a gateway to route between Ollama and cloud APIs. This gives you data sovereignty for most queries while maintaining access to frontier models when needed.


Want a self-hosted AI company brain that does all of this out of the box?
Book a demo →