How to Configure Docker Deployment and Reverse Proxy for Open Notebook

Open Notebook requires exposing only port 8502 through your reverse proxy, as the Next.js frontend automatically proxies /api/* requests to the FastAPI backend, but you must set the API_URL environment variable to ensure the UI generates correct HTTPS links.

Open Notebook is a self-hosted AI note-taking application that runs as a multi-container Docker stack. Starting with version 1.1, the architecture simplifies reverse proxy configuration by handling API routing internally between the frontend and backend containers. This guide explains how to deploy the complete stack using the official docker-compose.yml from the lfnovo/open-notebook repository and configure a production-ready reverse proxy for secure HTTPS access.

Architecture Overview

The application consists of three primary services defined in docker-compose.yml. The FastAPI back-end runs on port 5055, the Next.js front-end runs on port 8502, and SurrealDB operates on port 8000. From version 1.1 onward, the frontend automatically proxies all /api/* requests to the backend internally. This means your reverse proxy only needs to expose a single port (8502), significantly reducing the attack surface and simplifying SSL termination.

The containers communicate over an internal Docker network. The frontend handles internal routing to http://localhost:5055 for API calls, while external traffic flows through your reverse proxy to the frontend container.

Environment Configuration

Before deploying, create a .env file in your project directory by copying the template from .env.example. The following variables are required for secure operation:

  • OPEN_NOTEBOOK_ENCRYPTION_KEY – A secret string used for application-level encryption. Generate a strong random value (e.g., 32+ characters).
  • SURREAL_USER and SURREAL_PASSWORD – Database credentials for SurrealDB authentication.

# .env

OPEN_NOTEBOOK_ENCRYPTION_KEY=your-secure-random-string-minimum-32-chars
SURREAL_USER=root
SURREAL_PASSWORD=secure-database-password

Never commit the .env file to version control. The docker-compose.yml references these variables to configure the Open Notebook and SurrealDB containers at runtime.

Docker Compose Deployment

Create a docker-compose.yml file that pulls the official images and wires the services together. The composition defines volume mounts for persistent data and establishes network dependencies between the database and application layers.


# docker-compose.yml

services:
  surrealdb:
    image: surrealdb/surrealdb:v2
    command: start --log info --user ${SURREAL_USER:-root} --pass ${SURREAL_PASSWORD:-root} rocksdb:/mydata/mydatabase.db
    ports: ["8000:8000"]
    volumes: ["./surreal_data:/mydata"]
    environment:
      - SURREAL_EXPERIMENTAL_GRAPHQL=true
    restart: always

  open-notebook:
    image: lfnovo/open_notebook:v1-latest
    ports: ["127.0.0.1:8502:8502"]  # Bind to localhost for reverse proxy only

    environment:
      - OPEN_NOTEBOOK_ENCRYPTION_KEY=${OPEN_NOTEBOOK_ENCRYPTION_KEY}
      - API_URL=https://notebook.example.com  # Critical for reverse proxy

      - SURREAL_URL=ws://surrealdb:8000/rpc
      - SURREAL_USER=${SURREAL_USER:-root}
      - SURREAL_PASSWORD=${SURREAL_PASSWORD:-root}
      - SURREAL_NAMESPACE=open_notebook
      - SURREAL_DATABASE=open_notebook
    volumes: ["./notebook_data:/app/data"]
    depends_on: [surrealdb]
    restart: always

Deploy the stack by running:

docker-compose up -d

The services will start on localhost:8502 (UI) and localhost:5055 (API), with SurrealDB available on localhost:8000. For integration with external Ollama instances, refer to the variant configuration in examples/docker-compose-ollama.yml.

Reverse Proxy Configuration

Your reverse proxy must forward all traffic to the Next.js container on port 8502. Since the frontend handles internal API routing, you do not need separate upstream definitions for the backend.

Nginx Configuration

Create an nginx.conf that includes WebSocket support for real-time features, generous timeouts for long-running AI operations (such as podcast generation), and sufficient client body size for file uploads.


# nginx.conf

events { worker_connections 1024; }

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

    # HTTP to HTTPS redirect

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

    # HTTPS server

    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;
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers HIGH:!aNULL:!MD5;

        # Support large file uploads (default 1MB is insufficient)

        client_max_body_size 100M;

        # Security headers

        add_header X-Frame-Options DENY;
        add_header X-Content-Type-Options nosniff;
        add_header X-XSS-Protection "1; mode=block";
        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";

        location / {
            proxy_pass http://notebook;
            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;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_cache_bypass $http_upgrade;

            # Extended timeouts for AI generation tasks

            proxy_read_timeout 600s;
            proxy_connect_timeout 60s;
            proxy_send_timeout 600s;
        }
    }
}

Add the nginx service to your docker-compose.yml:

  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

Caddy Configuration

For automatic HTTPS with Let's Encrypt, use this minimal Caddyfile:

notebook.example.com {
    reverse_proxy open-notebook:8502 {
        transport http {
            read_timeout 600s
            write_timeout 600s
        }
    }
}

The full documentation in docs/5-CONFIGURATION/reverse-proxy.md also includes configuration examples for Traefik and other proxy solutions.

Critical API_URL Configuration

When running behind a reverse proxy, you must explicitly set the API_URL environment variable in the open-notebook service definition. This variable overrides automatic detection and ensures the frontend generates correct absolute URLs for API callbacks and OAuth redirects.

Set this to your public HTTPS domain:

environment:
  - API_URL=https://notebook.example.com

Without this variable, the application may construct internal http:// links or localhost references, breaking authentication flows and external integrations. The container logs will confirm the runtime configuration with the message: ✅ [Config] Runtime API URL from server: https://notebook.example.com.

Verification and Health Checks

After deployment, verify the stack is functioning correctly:

  1. Browser validation – Navigate to https://notebook.example.com. The browser console should show the runtime API URL matching your public domain.
  2. API health check – Run curl https://notebook.example.com/api/config to receive a JSON response containing "status":"ok".
  3. Container logs – Execute docker logs open-notebook to confirm both services initialized: "Next.js ready on http://0.0.0.0:8502" and "Uvicorn running on http://0.0.0.0:5055".

If the UI fails to load or shows connection errors, verify that the API_URL environment variable is set and that your reverse proxy headers include X-Forwarded-Proto.

Summary

  • Expose only port 8502 – The Next.js frontend in versions 1.1+ handles internal proxying to the FastAPI backend, eliminating the need to expose port 5055 publicly.
  • Set API_URL – This environment variable is mandatory for reverse proxy deployments to ensure correct HTTPS URL generation.
  • Configure timeouts – Set proxy_read_timeout and proxy_send_timeout to at least 600 seconds in your reverse proxy to accommodate long-running AI tasks.
  • Allow large uploads – Set client_max_body_size to 100MB or higher in Nginx (or equivalent) to support file attachments.
  • Reference official docs – The docs/5-CONFIGURATION/reverse-proxy.md file contains platform-specific examples for Nginx, Caddy, and Traefik.

Frequently Asked Questions

Do I need to expose the FastAPI backend (port 5055) through my reverse proxy?

No. Starting with version 1.1, the Next.js frontend automatically proxies all /api/* requests to the FastAPI backend internally. Configure your reverse proxy to forward traffic only to port 8502. The backend should remain accessible only within the Docker network for security.

This occurs when the API_URL environment variable is not set or when your reverse proxy fails to forward the X-Forwarded-Proto header. Set API_URL=https://your-domain.com in the open-notebook service environment variables, and ensure your proxy passes X-Forwarded-Proto $scheme to the container.

How do I handle large file uploads through the reverse proxy?

File uploads in Open Notebook can exceed the default 1MB limit of most reverse proxies. In Nginx, set client_max_body_size 100M; in your server block. For Caddy, this is handled automatically, but you may need to adjust the read_timeout settings for slow connections during upload.

Can I deploy Open Notebook without SurrealDB?

No. SurrealDB is a required dependency for the application stack. The docker-compose.yml defines SurrealDB as a service dependency that must be running before the Open Notebook container starts. The database stores all user data, AI conversation history, and application configuration.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →