# How to Set Up Open Notebook Behind a Reverse Proxy (NGINX or Traefik)

> Securely expose Open Notebook via NGINX or Traefik reverse proxy. Learn to set up a single HTTPS endpoint for your Next.js frontend and FastAPI backend.

- Repository: [Luis Novo/open-notebook](https://github.com/lfnovo/open-notebook)
- Tags: how-to-guide
- Published: 2026-07-05

---

**Open Notebook requires a reverse proxy to securely expose its Next.js frontend (port 3000) and FastAPI backend (port 5055) through a single HTTPS endpoint while keeping internal services isolated on the Docker network.**

Open Notebook is a three-tier open-source application that bundles a Next.js UI, FastAPI service, and SurrealDB database. To deploy it securely in production, you must set up Open Notebook behind a reverse proxy such as NGINX or Traefik that handles TLS termination and path-based routing. This guide references the official configuration documented in [`docs/5-CONFIGURATION/reverse-proxy.md`](https://github.com/lfnovo/open-notebook/blob/main/docs/5-CONFIGURATION/reverse-proxy.md) and provides runnable configurations for both proxy options.

## Architecture Overview

Open Notebook runs as three distinct services:

- **Frontend**: A Next.js application served internally on port `3000`.
- **API**: A FastAPI service on port `5055` that manages notebook logic, source documents, and chat interactions.
- **Database**: SurrealDB on port `8000` for persistent graph storage and vector embeddings.

The FastAPI backend in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) exposes HTTP endpoints and health checks at `/healthz`, while the frontend in [`frontend/src/pages/_app.tsx`](https://github.com/lfnovo/open-notebook/blob/main/frontend/src/pages/_app.tsx) expects an API URL defined at build time. In production, neither service should be directly exposed to the internet; instead, a reverse proxy handles all external traffic and forwards requests based on path prefixes.

## Step 1: Isolate Services on the Docker Network

Before adding a reverse proxy, secure the internal services by removing public port bindings in your [`docker-compose.yml`](https://github.com/lfnovo/open-notebook/blob/main/docker-compose.yml). Edit the file referenced in [`docs/1-INSTALLATION/docker-compose.md`](https://github.com/lfnovo/open-notebook/blob/main/docs/1-INSTALLATION/docker-compose.md) to ensure the **frontend** and **api** services are only accessible within the Docker network:

```yaml
services:
  api:
    # Remove or comment out: ports: ["5055:5055"]

    expose:
      - "5055"
    
  frontend:
    # Remove or comment out: ports: ["3000:3000"]

    expose:
      - "3000"
    
  surrealdb:
    # Keep isolated on the internal network

    expose:
      - "8000"

```

Binding ports to `127.0.0.1:5055:5055` is also acceptable if the proxy runs on the host machine, but exposing them to `0.0.0.0` creates a security risk.

## Step 2: Configure NGINX as a Reverse Proxy

**NGINX** terminates TLS and routes traffic based on URL paths. Create a configuration file that proxies all `/api/*` requests to the FastAPI service and all other traffic to the Next.js frontend.

```nginx
server {
    listen 80;
    listen 443 ssl;
    server_name notebook.example.com;

    # TLS certificate paths (replace with your own or use Let's Encrypt)

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

    # Frontend (Next.js) – all non-API requests

    location / {
        proxy_pass http://frontend:3000;
        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;
    }

    # API – path-based routing to FastAPI

    location /api/ {
        proxy_pass http://api:5055;
        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;
    }

    # Health check endpoint for container orchestration

    location /healthz {
        return 200 'OK';
        add_header Content-Type text/plain;
    }
}

```

Mount this configuration into an NGINX container or place it in `/etc/nginx/conf.d/` if running NGINX on the host. The `proxy_set_header` directives ensure the FastAPI service in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) receives the correct client IP and protocol information for CORS validation.

## Step 3: Configure Traefik as a Reverse Proxy

**Traefik** discovers services automatically via Docker labels and handles TLS with Let's Encrypt automatically. Add a `traefik` service to your [`docker-compose.yml`](https://github.com/lfnovo/open-notebook/blob/main/docker-compose.yml) and label the existing services:

```yaml
services:
  api:
    image: open-notebook-api
    expose:
      - "5055"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.api.rule=PathPrefix(`/api`)"
      - "traefik.http.routers.api.entrypoints=websecure"
      - "traefik.http.routers.api.tls.certresolver=myresolver"
      - "traefik.http.services.api.loadbalancer.server.port=5055"

  frontend:
    image: open-notebook-frontend
    expose:
      - "3000"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.frontend.rule=PathPrefix(`/`)"
      - "traefik.http.routers.frontend.entrypoints=websecure"
      - "traefik.http.routers.frontend.tls.certresolver=myresolver"
      - "traefik.http.services.frontend.loadbalancer.server.port=3000"

  traefik:
    image: traefik:v2.11
    command:
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.myresolver.acme.tlschallenge=true"
      - "--certificatesresolvers.myresolver.acme.email=you@example.com"
      - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"  # Traefik dashboard

    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "./letsencrypt:/letsencrypt"

```

Traefik routes requests matching `/api/*` to the FastAPI container on port `5055` and all other traffic to the Next.js frontend on port `3000`. The `myresolver` certificate resolver automatically provisions and renews TLS certificates from Let's Encrypt.

## Step 4: Update the Frontend Base URL

The Next.js frontend reads the API endpoint from `frontend/.env.local`. When running behind a reverse proxy, you must update this variable to point to the public HTTPS endpoint rather than the internal Docker service name.

Create or edit `frontend/.env.local`:

```dotenv

# .env.local

NEXT_PUBLIC_API_URL=https://notebook.example.com/api

```

As shown in `frontend/.env.local.example`, this variable is consumed by the application entry point in [`frontend/src/pages/_app.tsx`](https://github.com/lfnovo/open-notebook/blob/main/frontend/src/pages/_app.tsx) to construct API requests. Rebuild the frontend container after changing this value so the Next.js build process embeds the correct URL.

## Summary

Setting up Open Notebook behind a reverse proxy requires four key steps:

- **Isolate the Docker network** by removing port bindings from the `frontend` and `api` services in [`docker-compose.yml`](https://github.com/lfnovo/open-notebook/blob/main/docker-compose.yml) to prevent direct external access.
- **Configure path-based routing** so that `/api/*` requests reach the FastAPI service on port `5055` and all other traffic reaches the Next.js frontend on port `3000`.
- **Terminate TLS** at the proxy level using either NGINX with mounted certificates or Traefik with automatic Let's Encrypt provisioning.
- **Update the frontend environment** variable `NEXT_PUBLIC_API_URL` to the public HTTPS origin so the UI correctly routes API calls through the proxy.

## Frequently Asked Questions

### How do I expose Open Notebook on a subdomain instead of a root domain?

Configure your reverse proxy to route based on the `Host` header rather than path prefixes. In NGINX, use `server_name notebook.example.com;` for the frontend and `api.notebook.example.com` for the backend with separate server blocks. In Traefik, use `traefik.http.routers.frontend.rule=Host(\`notebook.example.com\`)` and `traefik.http.routers.api.rule=Host(\`api.notebook.example.com\`)` instead of `PathPrefix` rules.

### Why does the frontend show connection errors after setting up the proxy?

The Next.js frontend likely still points to `http://localhost:5055` from the default `.env.local.example` file. You must set `NEXT_PUBLIC_API_URL=https://your-domain.com/api` in the actual `frontend/.env.local` file and rebuild the container, as Next.js bakes environment variables into the client bundle at build time rather than runtime.

### Does SurrealDB need to be exposed through the reverse proxy?

No, SurrealDB should remain isolated on the internal Docker network and accessible only to the API service. The database on port `8000` does not require external exposure; only the FastAPI backend communicates with it directly. Ensure your [`docker-compose.yml`](https://github.com/lfnovo/open-notebook/blob/main/docker-compose.yml) does not map port `8000` to the host when using a reverse proxy.

### Which proxy is better for Open Notebook: NGINX or Traefik?

**Traefik** simplifies TLS management with automatic Let's Encrypt integration and dynamic service discovery via Docker labels, making it ideal for containerized deployments. **NGINX** offers more complex configuration options and is preferable if you require specific request manipulation, custom authentication modules, or already operate an NGINX infrastructure. Both handle the WebSocket upgrades required for streaming chat responses in the FastAPI backend.