Open Notebook 基于 EasyPanel 模板的一键部署:模板结构、服务编排与环境变量全解析
【免费下载链接】open-notebookAn Open Source implementation of Notebook LM with more flexibility and features项目地址: https://gitcode.com/GitHub_Trending/op/open-notebook
EasyPanel 是一个图形化的自托管应用面板,通过"模板 + 可填写表单"的方式即可完成多服务应用的部署。Open Notebook 在 examples/easypanel 目录中提供了一套官方 EasyPanel 模板,把「Open Notebook 应用 + SurrealDB 数据库」两个服务封装为一个可复用的部署单元。读完本文,你将掌握该模板meta.yaml与index.ts的文件格式、每个配置字段的含义,了解两个服务各自暴露的端口、环境变量与持久化卷,并能够在 EasyPanel 中完成从模板导入、密码设置到首次登录配置 AI Provider 的完整实操流程。
模板定位:一次点击拉起「应用 + 数据库」
这套模板位于仓库 examples/easypanel/ 目录,由meta.yaml与index.ts两个文件组成,遵循 EasyPanel 官方模板仓库(easypanel-io/templates)的目录与代码约定:
meta.yaml:描述模板元数据与用户输入(input schema),即 EasyPanel 创建模板时展示给用户的表单;index.ts:实现generate(input)函数,把用户的表单输入展开为具体的 EasyPanel services 定义。
模板部署后会创建两个服务:
| 服务 | 角色 | 网络可见性 | 说明 |
|---|---|---|---|
| Open Notebook | 应用(Next.js 前端 + Python 后端) | 对外暴露,入口端口8502 | 通过域名直接访问,反向代理友好 |
| SurrealDB v2 | 数据库 | 仅项目内部网络可见 | 通过ws://...:8000/rpc供应用连接,数据持久化在卷中 |
仓库根目录的 examples/README.md 也明确说明:该 easypanel 模板可直接复制进官方 EasyPanel 模板仓库,并在 EasyPanel templates playground 中测试。
meta.yaml:模板元数据与输入表单定义
examples/easypanel/meta.yaml 完整描述了模板的名称、说明、安装指引、变更日志、标签与输入项。其中schema部分是 EasyPanel 渲染创建表单的依据,共定义了 5 个输入字段:
| 字段 | 类型 | 标题 | 默认值 | 是否必填 | 用途 |
|---|---|---|---|---|---|
appServiceName | string | App Service Name | open-notebook | 是 | 应用服务的名称 |
appServiceImage | string | App Service Image | lfnovo/open_notebook:1.10.0 | 是 | Open Notebook 镜像及版本标签 |
databaseServiceName | string | Database Service Name | surrealdb | 是 | 数据库服务的名称 |
databaseServiceImage | string | Database Service Image | surrealdb/surrealdb:v2 | 是 | SurrealDB v2 官方镜像 |
appPassword | string | Open Notebook Password | ""(空) | 否 | 应用登录密码,留空则自动生成 |
值得注意的细节:appServiceImage默认值被固定为lfnovo/open_notebook:1.10.0(模板首个版本发布于 2026-06-19)。与之对照,仓库根目录 docker-compose.yml 使用的是滚动标签lfnovo/open_notebook:v1-latest。因此在 EasyPanel 上通过该模板部署后,如需升级版本,应修改这个输入项的镜像标签。
meta.yaml中还包含面向模板市场的展示信息:description介绍 Open Notebook 是开源自托管的 notebook 与研究型工作区,支持收集资料、记笔记、搜索、与内容对话及生成播客;instructions给出部署后的引导文案;benefits与features分别说明模板带来的部署价值与产品能力(源码收集、notebook 组织、带引用的 AI 问答、播客生成);changeLog、links、contributors、tags则服务于模板仓库的版本与分类管理。
index.ts:一次 generate 调用编排两个服务
examples/easypanel/index.ts 是整个模板的核心逻辑。它从~templates-utils引入Output、randomPassword、randomString、Services等辅助工具,在generate(input)内完成密钥生成与服务定义:
export function generate(input: Input): Output { const services: Services = []; const appPassword = input.appPassword || randomPassword(); // 用户未填 → 自动生成 const databasePassword = randomPassword(); // 数据库密码随机生成 const encryptionKey = randomString(64); // 加密密钥随机生成(64 字符) // ...push SurrealDB service、push Open Notebook service... return { services }; }三个关键密钥的生成策略正好呼应meta.yaml中benefits声明的 "Secure defaults":
appPassword:优先取用户在表单中填写的密码;留空则调用randomPassword()生成一个随机密码,并写入应用服务环境变量OPEN_NOTEBOOK_PASSWORD(README 中对该行为有专门说明);databasePassword:每次模板创建都随机生成;encryptionKey:每次模板创建都生成 64 字符的随机串,作为OPEN_NOTEBOOK_ENCRYPTION_KEY。
每次在 EasyPanel 中执行模板创建,都会重新走一遍generate(),也就意味着会生成一组全新的随机密钥与数据库密码,它们被持久化在应用服务(app service)的环境变量中。
服务一:私有部署的 SurrealDB v2
模板把 SurrealDB 设计为"项目内部私有"服务,不对外暴露任何域名或宿主端口,应用在项目网络中通过内部 DNS 访问。其服务定义要点如下:
{ type: "app", data: { serviceName: input.databaseServiceName, env: [`SURREAL_EXPERIMENTAL_GRAPHQL=true`].join("\n"), source: { type: "image", image: input.databaseServiceImage }, deploy: { command: [ "start", "--log info", "--user root", `--pass ${databasePassword}`, "--bind 0.0.0.0:8000", "rocksdb:/mydata/mydatabase.db", ].join(" "), }, mounts: [{ type: "volume", name: "surreal-data", mountPath: "/mydata" }], }, }几点值得展开:
- 启动命令使用
rocksdb:/mydata/mydatabase.db,即让 SurrealDB 以RocksDB 存储引擎运行,数据文件落在容器内/mydata/mydatabase.db; - 卷
surreal-data挂载到/mydata,保证数据库文件在容器重建后依然存在; - 端口
8000仅对项目网络内部开放,日志级别为info,凭据为root+ 随机生成的密码; SURREAL_EXPERIMENTAL_GRAPHQL=true开启 SurrealDB 的实验性 GraphQL 支持,这与仓库根目录 docker-compose.yml 中 surrealdb 服务保持一致的配置习惯。
由于该服务在模板中并未配置domains,它不会被 EasyPanel 暴露到公网,这正是"数据库私密"这一安全设计的体现。
服务二:Open Notebook 应用与它的全部环境变量
应用服务承载了 Open Notebook 的 Web UI(Next.js 前端)与内部 REST API(FastAPI),通过domains把 EasyPanel 自动分配的域名映射到容器端口8502。模板为该服务注入了一批环境变量,它们是理解整套模板如何工作的钥匙:
env: [ `API_URL=https://$(PRIMARY_DOMAIN)`, `INTERNAL_API_URL=http://localhost:5055`, `OPEN_NOTEBOOK_ENCRYPTION_KEY=${encryptionKey}`, `OPEN_NOTEBOOK_PASSWORD=${appPassword}`, `SURREAL_URL=ws://$(PROJECT_NAME)_${input.databaseServiceName}:8000/rpc`, `SURREAL_USER=root`, `SURREAL_PASSWORD=${databasePassword}`, `SURREAL_NAMESPACE=open_notebook`, `SURREAL_DATABASE=open_notebook`, ].join("\n"), // domains: [{ host: "$(EASYPANEL_DOMAIN)", port: 8502 }] // mounts: [{ type: "volume", name: "notebook-data", mountPath: "/app/data" }]下面逐一说明每个变量的作用,以及它们在 Open Notebook 运行时中的落点:
数据库连接变量
| 环境变量 | 值 | 作用 |
|---|---|---|
SURREAL_URL | ws://$(PROJECT_NAME)_<databaseServiceName>:8000/rpc | SurrealDB 的 WebSocket RPC 地址,$(PROJECT_NAME)_surrealdb是 EasyPanel 项目内部网络的域名规则 |
SURREAL_USER | root | 数据库用户名,与 SurrealDB 服务启动参数一致 |
SURREAL_PASSWORD | 随机生成 | 数据库密码,与 SurrealDB 服务启动参数中的--pass相同 |
SURREAL_NAMESPACE | open_notebook | SurrealDB 命名空间 |
SURREAL_DATABASE | open_notebook | SurrealDB 数据库名 |
namespace/database两级命名对应 SurrealDB 的数据组织模型,在仓库根目录 docker-compose.yml 的多容器编排中使用的是同一组值(open_notebook/open_notebook),而 examples/docker-compose-single.yml 这类单容器场景则需覆盖为ws://localhost:8000/rpc。EasyPanel 模板选择 WebSocket RPC(ws://而非http://)作为连接协议,与官方 compose 部署保持一致。
认证与加密变量
OPEN_NOTEBOOK_PASSWORD(应用登录密码):这是模板中与用户体验直接相关的一个变量。若在创建表单中填写了密码,EasyPanel 会把它写入应用服务环境变量;若留空,模板会生成随机密码并同样以OPEN_NOTEBOOK_PASSWORD存储(见 README 与meta.yaml的instructions)。运行时层面,该变量驱动内置的密码认证中间件:从源码 api/auth.py 看,其认证器通过get_secret_from_env("OPEN_NOTEBOOK_PASSWORD")读取密码,并支持OPEN_NOTEBOOK_PASSWORD_FILE的 Docker secrets 形式;仓库变更记录 CHANGELOG.md 同时说明——当OPEN_NOTEBOOK_PASSWORD未设置时,认证被完全禁用(本地零配置部署场景),而一旦设置即启用登录保护,前端对应 LoginForm.tsx 登录页。因此 EasyPanel 部署务必设置该密码。
OPEN_NOTEBOOK_ENCRYPTION_KEY(加密密钥):用于在数据库中加密保存用户配置的各类 AI Provider API Key。字段级加密实现位于 open_notebook/utils/encryption.py:它接受任意字符串作为密钥,通过 SHA-256 派生并转换为 Fernet 密钥,再使用 Fernet(AES-128-CBC + HMAC-SHA256 认证加密)完成加解密。该文件明确要求生产部署必须显式设置此变量,否则存储 API Key 时会因ValueError失败。模板每次创建都通过randomString(64)生成强随机密钥,比人工设置的弱口令安全得多。
前端反向代理相关变量
API_URL=https://$(PRIMARY_DOMAIN):浏览器端发起请求时使用的 API 基址。$(PRIMARY_DOMAIN)与后续domains中使用的$(EASYPANEL_DOMAIN)指向同一个公网域名。
INTERNAL_API_URL=http://localhost:5055:Next.js 服务端把/api/*请求内部转发到 REST API(运行在localhost:5055)所用地址。
两个变量分工的源码依据非常清晰。前端运行时配置 frontend/src/lib/config.ts 会先请求服务端/config接口获取apiUrl;而/config路由的实现 frontend/src/app/config/route.ts 明确注释了"为什么需要两个变量":API_URL供浏览器直接访问、可以是https://your-domain.com这类公网地址,INTERNAL_API_URL则供 Next.js rewrites 在服务端做代理转发(默认http://localhost:5055)。再看 Next.js 配置 frontend/next.config.ts:rewrites()把/api/:path*代理到INTERNAL_API_URL对应的后端,注释直接说明——"反向代理只需把流量导到 8502,Next.js 负责内部把请求路由到 5055 的 API"。这正是meta.yaml中 "Reverse-proxy friendly" benefit 所描述的设计:只有前端端口(8502)被暴露。
数据持久化
应用服务的卷notebook-data被挂载到容器内/app/data。为什么是这个路径?因为 Open Notebook 的数据根目录DATA_FOLDER = "./data"定义在 open_notebook/config.py(容器工作目录下即/app/data),其中包含 LangGraph 检查点 SQLite(sqlite-db/checkpoints.sqlite)、上传文件目录uploads/、播客输出目录podcasts/以及 tiktoken 缓存目录。把整个/app/data持久化为卷,可以确保会话检查点、上传的源文件、生成的播客音频与分词缓存都在容器重建后保留。数据库数据则由独立的surreal-data卷承载,与应用数据卷相互隔离。
端到端部署实操流程
结合 README 的 Testing 章节与meta.yaml的instructions,一次完整的部署包含以下步骤:
第 1 步:把模板放进 EasyPanel 模板仓库
将 examples/easypanel 整个目录复制到本地克隆的easypanel-io/templates仓库中,目录名改为templates/open-notebook:
cp -r examples/easypanel <path-to-easypanel-io/templates>/templates/open-notebook第 2 步:启动 EasyPanel 模板开发环境
在easypanel-io/templates仓库根目录运行模板 playground:
npm run devplayground 会读取模板目录下的meta.yaml与index.ts,渲染出与正式创建时一致的表单,方便你在导入实例前验证输入项与生成的 services JSON 是否符合预期。
第 3 步:在 EasyPanel 实例中创建模板并部署
- 打开 EasyPanel 面板,通过模板管理导入上述目录生成的模板 JSON;
- 在创建表单中至少确认
appServiceName、appServiceImage、databaseServiceName、databaseServiceImage四个必填项(均有合理默认值,通常无需改动); - 在 "Open Notebook Password" 字段设置一个应用密码。若留空,EasyPanel 会生成随机密码并把
OPEN_NOTEBOOK_PASSWORD写入应用服务环境变量(可在服务环境变量面板中查看); - 提交创建。模板会依次生成 SurrealDB 服务与 Open Notebook 服务、挂载两个持久化卷、注入全部环境变量。
第 4 步:首次登录并配置 AI Provider
部署完成后,打开 EasyPanel 为应用分配的域名(流量经 EasyPanel 入口转发到容器的 8502 端口),使用你设置的密码登录。随后进入Settings > API Keys页面,配置所需的 AI 提供方密钥(如 OpenAI、Anthropic 或 Ollama 等本地模型)。这也是meta.yaml中instructions明确引导的流程。
安全与运维要点
基于模板实现与仓库源码,可以总结出以下运维注意事项:
- 数据库与加密密钥仅在创建时生成一次:
databasePassword与encryptionKey由generate()在模板创建时生成并固化在服务环境中。如果日后重新从模板创建新服务或重建项目,会得到一组全新密钥,不会自动沿用旧值。 OPEN_NOTEBOOK_ENCRYPTION_KEY必须保持稳定:API Key 在入库前经 Fernet 加密(见 open_notebook/utils/encryption.py)。一旦更换密钥,已存储的加密凭据将无法解密(解密函数会针对"看似加密但密钥错误"的数据抛出配置检查提示),因此在已有数据的环境上切勿随意改动该变量。- 登录密码用于保护 Web 界面与 API:设置
OPEN_NOTEBOOK_PASSWORD即启用了内置密码认证;未设置时认证完全禁用(本地/内网零配置场景可用)。公网部署务必通过表单或环境变量设置强密码。 - 升级方式:把模板输入项
appServiceImage从默认的lfnovo/open_notebook:1.10.0改为更新的发布标签后重新部署即可完成镜像升级;容器内/app/data与数据库卷中的数据会随卷保留。 - 仅暴露前端端口:模板刻意不向数据库服务配置域名,公网入口只有映射到 8502 的应用域名,后端 5055 仅在容器内部被 Next.js 代理访问,外部无法直接触达,减小了攻击面。
与官方 Docker Compose 部署的对照
如果你想在 EasyPanel 之外使用传统编排方式,仓库提供了等价的参考实现:
- docker-compose.yml:标准的双容器部署(surrealdb + open_notebook),环境变量集合与 EasyPanel 模板几乎一一对应(
SURREAL_URL使用服务名ws://surrealdb:8000/rpc),可作为理解 EasyPanel 模板字段含义的权威对照; - examples/docker-compose-single.yml:单容器形态(不推荐用于生产,README 亦标注为 deprecated),需要把
SURREAL_URL覆盖为ws://localhost:8000/rpc; - 完整的环境变量参考见 docs/5-CONFIGURATION/environment-reference.md,安装总览见 docs/1-INSTALLATION/index.md。
综上,这套 EasyPanel 模板把 Open Notebook 的"应用 + SurrealDB"部署浓缩为一个表单、一次点击:meta.yaml定义人机交互的表单,index.ts负责把表单翻译成包含随机密钥、内部网络寻址、持久化卷与反向代理友好配置的两个服务。理解了OPEN_NOTEBOOK_PASSWORD、OPEN_NOTEBOOK_ENCRYPTION_KEY与API_URL/INTERNAL_API_URL/SURREAL_*这组环境变量的作用,你就能在任何支持 EasyPanel 的自托管主机上,稳定、安全地跑起一套属于自己的 Open Notebook 研究空间。
【免费下载链接】open-notebookAn Open Source implementation of Notebook LM with more flexibility and features项目地址: https://gitcode.com/GitHub_Trending/op/open-notebook
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考