open-notebook/docs/deployment/reverse-proxy.md

8.7 KiB

Reverse Proxy Configuration

This guide helps you deploy Open Notebook behind a reverse proxy (nginx, Caddy, Traefik, etc.) or with a custom domain.

The API_URL Environment Variable

Starting with v1.0+, Open Notebook supports runtime configuration of the API URL through the API_URL environment variable. This means you can use the same Docker image in different deployment scenarios without rebuilding.

How It Works

The frontend uses a three-tier priority system to determine the API URL:

  1. Runtime Configuration (Highest Priority): API_URL environment variable set at container runtime
  2. Build-time Configuration: NEXT_PUBLIC_API_URL baked into the Docker image
  3. Auto-detection (Fallback): Infers from the incoming HTTP request headers

Auto-detection details:

  • The Next.js frontend analyzes the incoming HTTP request
  • Extracts the hostname from the host header
  • Respects the X-Forwarded-Proto header (for HTTPS behind reverse proxies)
  • Constructs the API URL as {protocol}://{hostname}:5055
  • Example: Request to http://10.20.30.20:8502 → API URL becomes http://10.20.30.20:5055

Common Scenarios

Scenario 1: Docker on Localhost (Default)

No configuration needed! The system auto-detects.

docker run -d \
  --name open-notebook \
  -p 8502:8502 -p 5055:5055 \
  -v ./notebook_data:/app/data \
  -v ./surreal_data:/mydata \
  lfnovo/open_notebook:v1-latest-single

Scenario 2: Docker on Remote Server (LAN/VPS)

Access via IP address - auto-detection works, but you can be explicit:

docker run -d \
  --name open-notebook \
  -p 8502:8502 -p 5055:5055 \
  -e API_URL=http://192.168.1.100:5055 \
  -v ./notebook_data:/app/data \
  -v ./surreal_data:/mydata \
  lfnovo/open_notebook:v1-latest-single

Note: Don't include /api at the end - the system adds this automatically!

Scenario 3: Behind Reverse Proxy with Custom Domain

This is where API_URL is essential. Your reverse proxy handles HTTPS and routing.

Important: If your reverse proxy forwards /api requests to the backend, set API_URL to just the domain (without /api suffix). The frontend will append /api automatically.

Example: nginx + Docker Compose

docker-compose.yml:

version: '3.8'

services:
  open-notebook:
    image: lfnovo/open_notebook:v1-latest-single
    container_name: open-notebook
    environment:
      - API_URL=https://notebook.example.com
      - OPENAI_API_KEY=${OPENAI_API_KEY}
    volumes:
      - ./notebook_data:/app/data
      - ./surreal_data:/mydata
    ports:
      - "8502:8502"  # Frontend
      - "5055:5055"  # API
    restart: unless-stopped

  nginx:
    image: nginx:alpine
    container_name: nginx-proxy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./ssl:/etc/nginx/ssl:ro
    depends_on:
      - open-notebook
    restart: unless-stopped

nginx.conf:

http {
    upstream frontend {
        server open-notebook:8502;
    }

    upstream api {
        server open-notebook:5055;
    }

    server {
        listen 80;
        server_name notebook.example.com;
        return 301 https://$server_name$request_uri;
    }

    server {
        listen 443 ssl http2;
        server_name notebook.example.com;

        ssl_certificate /etc/nginx/ssl/fullchain.pem;
        ssl_certificate_key /etc/nginx/ssl/privkey.pem;

        # Frontend
        location / {
            proxy_pass http://frontend;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_cache_bypass $http_upgrade;
        }

        # API
        location /api/ {
            proxy_pass http://api/api/;
            proxy_http_version 1.1;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
}

Scenario 4: Behind Reverse Proxy with Subdomain

If you want API on a separate subdomain:

docker-compose.yml:

services:
  open-notebook:
    image: lfnovo/open_notebook:v1-latest-single
    environment:
      - API_URL=https://api.notebook.example.com
      # ... other env vars

nginx.conf:

# Frontend server
server {
    listen 443 ssl http2;
    server_name notebook.example.com;

    location / {
        proxy_pass http://open-notebook:8502;
        # ... proxy headers
    }
}

# API server
server {
    listen 443 ssl http2;
    server_name api.notebook.example.com;

    location / {
        proxy_pass http://open-notebook:5055;
        # ... proxy headers
    }
}

Scenario 5: Traefik

docker-compose.yml:

version: '3.8'

services:
  open-notebook:
    image: lfnovo/open_notebook:v1-latest-single
    environment:
      - API_URL=https://notebook.example.com
    labels:
      # Frontend
      - "traefik.enable=true"
      - "traefik.http.routers.notebook-frontend.rule=Host(`notebook.example.com`)"
      - "traefik.http.routers.notebook-frontend.entrypoints=websecure"
      - "traefik.http.routers.notebook-frontend.tls.certresolver=myresolver"
      - "traefik.http.services.notebook-frontend.loadbalancer.server.port=8502"

      # API
      - "traefik.http.routers.notebook-api.rule=Host(`notebook.example.com`) && PathPrefix(`/api`)"
      - "traefik.http.routers.notebook-api.entrypoints=websecure"
      - "traefik.http.routers.notebook-api.tls.certresolver=myresolver"
      - "traefik.http.services.notebook-api.loadbalancer.server.port=5055"
    networks:
      - traefik-network

networks:
  traefik-network:
    external: true

Scenario 6: Caddy

Caddyfile:

notebook.example.com {
    # Frontend
    reverse_proxy / open-notebook:8502

    # API
    reverse_proxy /api/* open-notebook:5055
}

docker-compose.yml:

services:
  open-notebook:
    image: lfnovo/open_notebook:v1-latest-single
    environment:
      - API_URL=https://notebook.example.com
    # No need to expose ports if using Caddy in same network

Troubleshooting

Connection Error: Unable to connect to server

Symptoms: Frontend displays "Unable to connect to server. Please check if the API is running."

Possible Causes:

  1. API_URL not set correctly for your reverse proxy setup

    • Check browser console (F12) for connection errors
    • Look for logs showing what URL the frontend is trying
  2. Reverse proxy not forwarding to correct port

    • API should be accessible at the URL specified in API_URL
    • Test: curl https://your-domain.com/api/config should return JSON
  3. CORS issues

    • Ensure X-Forwarded-Proto and X-Forwarded-For headers are set in proxy config
    • Check API logs for CORS errors
  4. SSL/TLS certificate issues

    • Ensure your reverse proxy has valid SSL certificates
    • Mixed content errors (HTTPS frontend trying to reach HTTP API)

How to Debug

  1. Check browser console (F12 → Console tab):

    • Look for messages starting with 🔧 [Config]
    • These show the configuration detection process
    • You'll see which API URL is being used
  2. Test API directly:

    # Should return JSON config
    curl https://your-domain.com/api/config
    
  3. Check Docker logs:

    docker logs open-notebook
    
    • Look for frontend and API startup messages
    • Check for connection errors
  4. Verify environment variable:

    docker exec open-notebook env | grep API_URL
    

Missing Authorization Header

Symptoms: API returns {"detail": "Missing authorization header"}

This happens when:

  • You have set OPEN_NOTEBOOK_PASSWORD for authentication
  • You're trying to access /api/config directly without logging in first

Solution: This is expected behavior! The frontend handles this automatically. Just access the frontend URL and log in through the UI.

Best Practices

  1. Always use HTTPS in production with reverse proxies
  2. Set API_URL explicitly when using reverse proxies to avoid auto-detection issues
  3. Use environment files (.env or docker.env) to manage configuration
  4. Test your setup by accessing the frontend and checking browser console logs
  5. Keep ports 5055 and 8502 accessible from your reverse proxy container

Additional Resources