# Security Best Practices for Deploying Open Notebook Publicly: A Production Hardening Guide

> Securely deploy Open Notebook in production. Learn best practices for public deployment including reverse proxy, HTTPS, secrets management, CORS, and security headers.

- Repository: [Luis Novo/open-notebook](https://github.com/lfnovo/open-notebook)
- Tags: best-practices
- Published: 2026-06-27

---

**Deploy Open Notebook behind a reverse proxy with HTTPS, bind the frontend to localhost only, store secrets using Docker secrets or encrypted env files, and configure strict CORS and security headers to protect the FastAPI backend and encrypted credentials.**

Open Notebook is a privacy-first research assistant that encrypts sensitive provider API keys using Fernet encryption. When exposing the application to the Internet, administrators must harden both the **Next.js frontend** and **FastAPI backend** according to the security documentation in [`docs/5-CONFIGURATION/security.md`](https://github.com/lfnovo/open-notebook/blob/main/docs/5-CONFIGURATION/security.md) and [`docs/5-CONFIGURATION/reverse-proxy.md`](https://github.com/lfnovo/open-notebook/blob/main/docs/5-CONFIGURATION/reverse-proxy.md).

## Encrypt and Protect Credentials

Open Notebook derives a Fernet encryption key from the `OPEN_NOTEBOOK_ENCRYPTION_KEY` environment variable using SHA-256 and uses it to secure all stored LLM API keys in `open_notebook/ai/…`. Never commit this key to version control, and use Docker secrets or a secure vault in production.

Set a strong `OPEN_NOTEBOOK_PASSWORD` with 20+ characters of high entropy. The frontend automatically sends this via `Authorization: Bearer <password>` headers to authenticate requests. For production, mount secrets as files rather than environment variables:

```yaml
environment:
  - OPEN_NOTEBOOK_PASSWORD_FILE=/run/secrets/app_password
  - OPEN_NOTEBOOK_ENCRYPTION_KEY_FILE=/run/secrets/encryption_key

```

**Critical:** Back up the encryption key separately from the database. Rotating it without re-saving all API keys will render stored credentials unrecoverable, as noted in [`docs/5-CONFIGURATION/security.md`](https://github.com/lfnovo/open-notebook/blob/main/docs/5-CONFIGURATION/security.md).

## Run Behind a Reverse Proxy and Enforce HTTPS

The reverse proxy terminates TLS, hides internal ports, and injects security headers. The frontend runs on port **8502**, while the FastAPI backend listens on port **5055** and should never be exposed publicly—the Next.js frontend automatically routes `/api/*` traffic to the backend internally.

### Nginx Configuration

```nginx
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;
    client_max_body_size 100M;

    location / {
        proxy_pass http://open-notebook:8502;
        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;
        proxy_read_timeout 600s;
        proxy_send_timeout 600s;
    }
}

```

### Caddy (Auto-TLS)

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

```

### Traefik (Docker Labels)

```yaml
labels:
  - "traefik.enable=true"
  - "traefik.http.routers.notebook.rule=Host(`notebook.example.com`)"
  - "traefik.http.routers.notebook.entrypoints=websecure"
  - "traefik.http.routers.notebook.tls.certresolver=myresolver"
  - "traefik.http.services.notebook.loadbalancer.server.port=8502"

```

## Restrict Network Access

Bind the frontend container to localhost only to prevent direct external access, forcing all traffic through the reverse proxy.

In [`docker-compose.yml`](https://github.com/lfnovo/open-notebook/blob/main/docker-compose.yml):

```yaml
ports:
  - "127.0.0.1:8502:8502"

```

Block the FastAPI backend port from the public Internet using firewall rules:

```bash

# Block direct access to backend

iptables -A INPUT -p tcp --dport 5055 -j DROP

# Allow only necessary ports

ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable

```

Place the **SurrealDB** container (port 8000) on a private Docker network unreachable from the proxy to implement network segmentation.

## Lock Down CORS Configuration

By default, the FastAPI backend allows all origins (`*`). For public deployments, explicitly whitelist your domain to prevent cross-origin attacks:

```bash
CORS_ORIGINS=https://notebook.example.com,https://admin.example.com

```

This prevents malicious sites from issuing authenticated requests using stolen session tokens, as implemented in the API configuration.

## Add Hardening HTTP Headers

Inject security headers at the reverse proxy to mitigate clickjacking, MIME sniffing, and XSS attacks:

```nginx
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";

```

These headers ensure browsers handle the content securely and enforce HTTPS connections for all subdomains.

## Advanced Enterprise Controls

For deployments requiring more than basic password protection, consider these additional layers:

- **SSO / OAuth2:** Deploy an OAuth2 proxy (e.g., `oauth2-proxy`) in front of Open Notebook and forward tokens via the `Authorization` header.
- **Role-Based Access:** Implement custom FastAPI middleware that reads JWT roles from `open_notebook/…` and enforces per-endpoint permissions.
- **Rate Limiting:** Add `limit_req` in Nginx or use Traefik's rate-limit middleware to prevent brute-force attacks.
- **Audit Logging:** Pipe container logs to a centralized shipper (Loki, Elastic) to track authentication attempts and data access.
- **At-Rest Encryption:** Store `notebook_data` on an encrypted volume (LUKS or Docker volume driver) to protect against host compromise.

These recommendations are compiled in the *Enterprise Considerations* section of [`docs/5-CONFIGURATION/security.md`](https://github.com/lfnovo/open-notebook/blob/main/docs/5-CONFIGURATION/security.md).

## Operational Practices

Maintain security through continuous monitoring and hygiene:

- **Regular Updates:** Keep the Open Notebook image, SurrealDB, and reverse-proxy software patched to latest stable versions.
- **Backup Strategy:** Encrypt backups of both the database and the encryption key separately; loss of the key makes API keys unrecoverable.
- **Log Monitoring:** Review `docker logs open-notebook` and proxy access logs for unauthorized authentication attempts.
- **Pre-Deployment Testing:** Verify that `curl https://your-domain.com/health` returns JSON without authentication, while `curl https://your-domain.com/api/config` rejects requests missing the `Authorization` header.
- **HTTPS Enforcement:** Ensure TLS termination happens at the proxy so passwords and API keys never traverse the network in plaintext.

## Sample Production Docker Compose

```yaml
services:
  surrealdb:
    image: surrealdb/surrealdb:v2
    command: start --log info --user root --pass root rocksdb:/mydata/mydatabase.db
    ports: ["8000:8000"]
    volumes: ["./surreal_data:/mydata"]
    restart: always

  open-notebook:
    image: lfnovo/open_notebook:v1-latest
    environment:
      - OPEN_NOTEBOOK_ENCRYPTION_KEY=${OPEN_NOTEBOOK_ENCRYPTION_KEY}
      - OPEN_NOTEBOOK_PASSWORD=${OPEN_NOTEBOOK_PASSWORD}
      - CORS_ORIGINS=https://notebook.example.com
      - API_URL=https://notebook.example.com
    ports:
      - "127.0.0.1:8502:8502"
    volumes: ["./notebook_data:/app/data"]
    depends_on: [surrealdb]
    restart: always

  nginx:
    image: nginx:alpine
    ports: ["80:80","443:443"]
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./ssl:/etc/nginx/ssl:ro
    depends_on: [open-notebook]
    restart: always

```

This configuration binds the application to localhost, separates the database, and prepares for TLS termination at the reverse proxy.

## Summary

- **Store secrets safely** using Docker secrets or encrypted env files for `OPEN_NOTEBOOK_ENCRYPTION_KEY` and `OPEN_NOTEBOOK_PASSWORD`.
- **Expose only port 8502** via localhost binding and route all traffic through a reverse proxy with valid TLS certificates.
- **Block port 5055** publicly and restrict database access to the application container only.
- **Set explicit CORS origins** instead of allowing wildcard access.
- **Inject security headers** (HSTS, X-Frame-Options) at the proxy layer.
- **Monitor logs** and maintain regular encrypted backups of both data and encryption keys.

## Frequently Asked Questions

### Is Open Notebook secure for public deployment by default?

Open Notebook provides a solid privacy-first foundation with Fernet encryption for API keys and password-based authentication, but it requires explicit hardening for public exposure. According to [`docs/SECURITY_REVIEW.md`](https://github.com/lfnovo/open-notebook/blob/main/docs/SECURITY_REVIEW.md), you must configure reverse proxies, CORS restrictions, and network isolation to achieve production-grade security.

### How do I rotate the encryption key without losing data?

You cannot rotate the `OPEN_NOTEBOOK_ENCRYPTION_KEY` without re-encrypting all stored credentials. The key is SHA-256-derived and used to encrypt provider API keys in `open_notebook/ai/…`. To rotate safely, decrypt all credentials using the old key, update the environment variable, and re-save each API key through the application interface to re-encrypt with the new key.

### Should I expose the FastAPI backend directly?

No. The FastAPI service on port **5055** should remain inaccessible from the public Internet. The Next.js frontend routes `/api/*` requests internally to the backend. Exposing port 5055 directly bypasses the reverse proxy's security headers and TLS termination, violating the architecture described in [`docs/5-CONFIGURATION/reverse-proxy.md`](https://github.com/lfnovo/open-notebook/blob/main/docs/5-CONFIGURATION/reverse-proxy.md).

### What is the recommended way to handle database credentials?

Store SurrealDB credentials as Docker secrets or environment variables excluded from version control. The database container should reside on an internal Docker network with no published ports, accessible only to the Open Notebook application container. This network segmentation prevents direct database access even if the application layer is compromised.