中文 English

我最后留下的自用 Web 剪贴板,居然只有一个文本框

发布时间: 2026-07-10
Docker Web剪贴板 自托管 轻量工具 minimalist-web-notepad 运维

先说结论

我试过不少“跨设备剪贴板”“在线便签”“临时文本同步”工具,最后反而留下了一个看起来最寒酸的东西:minimalist-web-notepad。它打开之后几乎什么都没有,只有一个文本编辑框,只支持纯文本,不支持登录用户、不支持富文本、不支持图片、不支持标签分类,甚至连“保存按钮”都不明显。

但正是这种“功能少到没什么可炫耀”的设计,让它特别适合做自用轻量级 Web 剪贴板:临时从手机传一段命令到电脑、从电脑丢一段说明到平板、给自己留一段短文本、用 curl 在脚本里读写一小段状态。它不像知识库,也不像团队协作文档,更像桌面旁边那张随手撕下来的便签纸。

本文会用 ahfeil/minimalist-web-notepad:latest 镜像演示 Docker Compose 部署;同时给出 Windows 11、Ubuntu 26.04、macOS 26 三套一键脚本,以及人工自动执行和 Agent 自动配置两种方法。全文不展示任何完整内网地址、内网域名、完整计算机名称或真实隐私数据。

原创封面:一个无品牌容器块、一个单文本框浏览器和一个 _tmp 文件夹组成轻量剪贴板。

图 1:AI 生成封面。这个工具的重点不是“功能丰富”,而是“入口简单、行为可预测”。

一、问题背景:我为什么还需要一个 Web 剪贴板

现在能同步文本的工具太多了。IM 可以发给自己,笔记软件可以跨端同步,云文档可以多人协作,密码管理器也能临时保存安全备注。按理说,一个只有文本框的小网页没有多少存在感。

但真实使用场景不是这样。

我经常遇到的是这种碎片化需求:手机上看到一段命令,想丢到电脑;电脑上生成一小段说明,想在另一台设备上临时打开;远程机器里有一段不敏感的检查输出,想先放到网页里看一眼;写文章时临时放一段不值得进知识库的草稿。它们都有一个共同点:内容很短、生命周期很短、没有长期管理价值,但传来传去很烦。

用 IM 发给自己当然可以,但聊天记录会把临时内容混进长期消息里。用知识库也可以,但为一段临时文本新建页面,像为了放一张便利贴去租一个仓库。用云文档也行,但它们通常有账号、权限、同步状态、富文本格式和客户端差异。很多时候,我只是想要一张“云端白纸”。

minimalist-web-notepad 的上游项目正好就是这个定位。项目 README 里把它描述为已经停用的 notepad.cc 的开源克隆,核心想法是一张放在云上的纸。它的界面和数据模型都非常克制:URL 后面的 note 名称就是纸条名,页面中央就是文本框,保存结果最终落到 _tmp 目录里的纯文本文件。

真实截图:上游 README 说明它是 notepad.cc 的开源克隆,并强调 _tmp 可写。

图 2:真实截图。上游 README 的关键点很直接:这是一个云端纸片工具,部署时要保证 Web 服务能写 _tmp

二、问题表现:复杂工具反而不适合临时文本

我之前对“剪贴板工具”的期待其实有点贪心:最好有账号、有历史、有 Markdown、有图片、有搜索、有端到端加密、有客户端、有浏览器扩展。听起来都很合理,但越想越像在造另一个笔记系统。

临时文本工具如果太复杂,会出现几个实际问题。

第一,入口变慢。你只是想贴一行命令,却要登录、选工作区、选分类、确认格式。就像你只是想把钥匙临时放门口,却先被要求登记、拍照、建档。

第二,格式会捣乱。很多富文本编辑器会自动转换引号、缩进、链接、列表和换行。对普通文章没问题,对命令、配置、日志片段就很烦。临时剪贴板最重要的不是排版漂亮,而是“我贴进去是什么,拿出来还是什么”。

第三,数据边界模糊。一个越像知识库的工具,越容易让人把长期材料、敏感材料也放进去。结果本来只想当便签纸,最后变成半个保险柜。临时工具最怕的不是功能少,而是定位不清。

第四,维护成本不值得。数据库、用户系统、对象存储、搜索索引、权限模型都很有价值,但如果目标只是“传一段纯文本”,这些价值很快会变成运维负担。

所以我对这个工具的定位很明确:它不是知识库,不是密码本,不是团队文档,不是生产配置中心。它就是一个自用轻量 Web 剪贴板。

三、为什么 minimalist-web-notepad 正好合适

这个项目最吸引我的地方不是“强”,而是“弱得刚刚好”。

它的页面几乎只有一个 textarea。没有菜单,没有仪表盘,没有复杂权限,也没有富文本编辑器。你打开一个 note 路径,就得到一个空白文本框;你写入内容,它保存;你再次打开同一路径,它读取。对小学生解释也很简单:这就像老师给你一张纸,纸上只能写字,不能贴照片,也不能画花哨格式。

它的数据也很容易理解。_tmp 目录像一个抽屉,每个 note 名称对应抽屉里的一张纸。比如 demo 这个 note,最后就可以理解成 _tmp/demo 这个文本文件。没有数据库迁移,没有复杂索引,没有隐藏状态。出了问题,第一反应也很清楚:看容器是否在跑,看端口是否通,看 _tmp 是否可写,看文件是否存在。

我实际启动临时容器后,浏览器里看到的就是这么一个白板。

真实截图:运行中的 minimalist-web-notepad 只有一个大文本框。

图 3:真实截图。没有标题栏里的项目菜单,也没有富文本工具条,页面主体就是一个纯文本框。

更实用的是,它还天然适合命令行。上游 README 给了 curl 读写示例。也就是说,同一个 note 既可以从浏览器编辑,也可以从脚本里读写。对自托管用户来说,这一点很有用:它不是只能给人点的网页,也可以成为脚本之间传递短文本的临时入口。

真实截图:同一份 note 可以用 raw 形式读取为纯文本。

图 4:真实截图。raw 输出没有富文本包装,适合 curl、脚本和简单自动化场景。

四、部署方法:Compose 文件就是这么短

用户给出的 Docker Compose 部署方式如下,我会保留这个思路,只把它放进更完整的上下文里:

services:
  minimalist-web-notepad:
    image: ahfeil/minimalist-web-notepad:latest
    container_name: webnote
    restart: always
    ports:
      - "8081:80"
    volumes:
      - /docker/minimalist-web-notepad/_tmp:/var/www/html/_tmp

这段配置里真正重要的是三件事。

第一,镜像。这里用的是 ahfeil/minimalist-web-notepad:latest,它把上游 PHP 项目封装成容器,省掉了手动配置 Apache、PHP、rewrite 的工作。

第二,端口。8081:80 表示宿主机的 8081 入口转到容器内的 Web 服务。文章里不写任何真实内网访问地址,你自己部署时可以按本机或反向代理实际情况访问。

第三,持久化目录。/docker/minimalist-web-notepad/_tmp:/var/www/html/_tmp 是最关键的部分。没有这个挂载,容器重建后临时文本很可能丢失;挂载之后,note 内容会落到宿主机目录里。

解释图:Compose 部署时 image、ports、volumes 各自负责什么。

图 5:部署时不要只盯着端口,真正决定数据是否能留下来的,是 _tmp 挂载和写权限。

如果你把它想成开小卖部:镜像是店面,端口是门,_tmp 是柜台后面的抽屉。门开了只能说明顾客能进来;抽屉能写,才说明纸条真的能留下来。

五、一次读写到底发生了什么

minimalist-web-notepad 的读写流程很直白。

当你访问根路径时,如果没有指定 note 名称,应用会生成一个随机 note 名称并跳转。这个设计很像随手拿一张新纸,纸上先有一个编号。

当你访问一个明确的 note 名称时,应用会检查这个名称是否合法。名称太长或包含非法字符,就不会当作有效纸条。这个检查虽然简单,但很重要,因为 note 名最终会变成文件名。

当浏览器保存文本时,应用把提交的 text 写入 _tmp 目录下对应文件。文本为空时,它会删除这个文件。用 curl?raw 读取时,它直接返回纯文本。

解释图:浏览器、PHP 和 _tmp 目录之间的读写循环。

图 6:它不像数据库应用,更像“按纸条名字在抽屉里放一张文本纸”。

这个模型的优点是透明。你不需要学习新的数据结构,也不需要在容器里翻复杂表。排障时可以按这个顺序看:

docker ps --filter name=webnote
docker logs --tail=80 webnote
ls -la /docker/minimalist-web-notepad/_tmp

如果容器在跑、端口能打开、_tmp 目录里有对应文件,问题通常就不复杂。反过来,如果页面能打开但刷新后内容消失,优先看挂载路径和写权限。

六、安全边界:它轻量,但不是保险柜

这类工具最容易被误用的地方是:它太方便了。方便到你会下意识想把所有短文本都扔进去。

我的建议非常明确:不要在里面放密码、长期 Token、私钥、Cookie、数据库连接串、生产密钥、身份证件信息、客户资料、长期业务文档。它的定位是临时剪贴板,不是安全存储系统。

可以放什么?可以放短草稿、不敏感命令片段、一次性说明文字、可公开的检查输出、几分钟后就会删除的临时文本。就像门口便利贴可以写“快递放门口”,但不应该写“保险箱密码是几号”。

解释图:适合放、不该放、建议加的安全边界。

图 7:轻量工具的安全策略不是“相信它足够安全”,而是“只放它应该承载的数据”。

如果你要长期使用,我建议至少加上这些保护:

七、一键脚本:Windows 11 / Ubuntu 26.04 / macOS 26

下面三套脚本做同一件事:创建本地目录、写入 compose.yaml、启动容器、用本机地址做一次最小访问验证。脚本不依赖外部数据库、对象存储或 SaaS 服务;它们只要求你本机已经安装 Docker,并且能拉取公开容器镜像。

7.1 Windows 11 PowerShell

保存为 Install-MinimalistWebNotepad.ps1 后执行:

powershell -ExecutionPolicy Bypass -File .\Install-MinimalistWebNotepad.ps1

脚本如下:

param(
    [string]$InstallDir = "$HOME\webnote",
    [int]$Port = 8081,
    [string]$ContainerName = "webnote"
)

$ErrorActionPreference = "Stop"

if (-not (Get-Command docker -ErrorAction SilentlyContinue)) {
    throw "Docker is required. Install Docker Desktop first."
}

New-Item -ItemType Directory -Force -Path $InstallDir | Out-Null
New-Item -ItemType Directory -Force -Path (Join-Path $InstallDir "_tmp") | Out-Null

$compose = @"
services:
  minimalist-web-notepad:
    image: ahfeil/minimalist-web-notepad:latest
    container_name: $ContainerName
    restart: always
    ports:
      - "$Port`:80"
    volumes:
      - ./_tmp:/var/www/html/_tmp
"@

$compose | Set-Content -Path (Join-Path $InstallDir "compose.yaml") -Encoding UTF8

Push-Location $InstallDir
docker compose up -d
Start-Sleep -Seconds 3
$url = "http://localhost:$Port"
$result = Invoke-WebRequest -UseBasicParsing -Uri $url -MaximumRedirection 0 -SkipHttpErrorCheck
Write-Host "Started $ContainerName in $InstallDir"
Write-Host "Open: $url"
Pop-Location

7.2 Ubuntu 26.04 Bash

保存为 install-minimalist-web-notepad-ubuntu2604.sh 后执行:

chmod +x install-minimalist-web-notepad-ubuntu2604.sh
./install-minimalist-web-notepad-ubuntu2604.sh

脚本如下:

#!/usr/bin/env bash
set -euo pipefail

INSTALL_DIR="${INSTALL_DIR:-$HOME/webnote}"
PORT="${PORT:-8081}"
CONTAINER_NAME="${CONTAINER_NAME:-webnote}"

if ! command -v docker >/dev/null 2>&1; then
  echo "Docker is required. Install Docker Engine first." >&2
  exit 1
fi

mkdir -p "$INSTALL_DIR/_tmp"
cd "$INSTALL_DIR"

cat > compose.yaml <<YAML
services:
  minimalist-web-notepad:
    image: ahfeil/minimalist-web-notepad:latest
    container_name: ${CONTAINER_NAME}
    restart: always
    ports:
      - "${PORT}:80"
    volumes:
      - ./_tmp:/var/www/html/_tmp
YAML

docker compose up -d

for i in $(seq 1 20); do
  if curl -fsS "http://localhost:${PORT}" >/dev/null 2>&1; then
    break
  fi
  sleep 1
done

echo "Started ${CONTAINER_NAME} in ${INSTALL_DIR}"
echo "Open: http://localhost:${PORT}"

7.3 macOS 26 Zsh

保存为 install-minimalist-web-notepad-macos26.zsh 后执行:

chmod +x install-minimalist-web-notepad-macos26.zsh
./install-minimalist-web-notepad-macos26.zsh

脚本如下:

#!/usr/bin/env zsh
set -euo pipefail

INSTALL_DIR="${INSTALL_DIR:-$HOME/webnote}"
PORT="${PORT:-8081}"
CONTAINER_NAME="${CONTAINER_NAME:-webnote}"

if ! command -v docker >/dev/null 2>&1; then
  print -u2 "Docker Desktop or a compatible Docker runtime is required."
  exit 1
fi

mkdir -p "$INSTALL_DIR/_tmp"
cd "$INSTALL_DIR"

cat > compose.yaml <<YAML
services:
  minimalist-web-notepad:
    image: ahfeil/minimalist-web-notepad:latest
    container_name: ${CONTAINER_NAME}
    restart: always
    ports:
      - "${PORT}:80"
    volumes:
      - ./_tmp:/var/www/html/_tmp
YAML

docker compose up -d

for i in {1..20}; do
  if curl -fsS "http://localhost:${PORT}" >/dev/null 2>&1; then
    break
  fi
  sleep 1
done

print "Started ${CONTAINER_NAME} in ${INSTALL_DIR}"
print "Open: http://localhost:${PORT}"

八、人工自动执行方法

如果你自己执行,推荐按下面顺序来,不要上来就暴露入口:

  1. 先确认 Docker 和 Docker Compose 可用。
  2. 选择一个持久化目录,例如 ~/webnote 或你自己的容器数据目录。
  3. 写入 compose.yaml
  4. 执行 docker compose up -d
  5. 打开本机页面确认能进入文本框。
  6. 写入一段无敏感信息的测试文本。
  7. 用 raw 或 curl 读取同一个 note。
  8. _tmp 目录确认文件已经落盘。
  9. 决定是否放到反向代理或私有入口后面。
  10. 设置定期清理策略。

最小验证命令可以是:

curl -fsS --data-urlencode "text=hello from webnote" "http://localhost:8081/demo" >/dev/null
curl -fsS "http://localhost:8081/demo"

如果第二行能输出 hello from webnote,说明浏览器之外的纯文本读写也通了。

九、Agent 自动配置方法

如果你让 Agent 帮你配置,不建议只说“帮我装一下 Web 剪贴板”。更好的提示词是把边界说清楚:

请帮我部署 minimalist-web-notepad,作为自用轻量 Web 剪贴板。
要求:
1. 使用 Docker Compose,不要改动已有无关容器。
2. 镜像使用 ahfeil/minimalist-web-notepad:latest。
3. 只监听我指定的本机端口,默认 8081。
4. 持久化目录使用当前工作目录下的 _tmp,或我明确指定的目录。
5. 部署前先检查 Docker/Compose 是否可用。
6. 部署后用浏览器或 curl 写入一段无敏感信息的测试文本,并读取验证。
7. 输出最终 compose.yaml、容器状态和验证结果。
8. 不要在日志或文章里输出完整内网地址、内网域名、完整机器名或隐私内容。

解释图:人工执行和 Agent 自动配置的分工。

图 8:Agent 不是替你跳过验证,而是替你把检查、写配置、启动、验证串起来。

如果 Agent 需要发布到反向代理后面,再补一句:

入口必须放在已有认证层后面;如果没有认证层,只完成本机部署并停止,不要直接开放公网。

这句话很重要。很多自托管事故不是工具本身复杂,而是“为了方便先裸奔,后面再加安全”。后面通常就没有后面了。

十、问题分析:它有什么坑

第一个坑是 _tmp 权限。上游 README 已经强调 Web 服务必须能写 _tmp。如果挂载目录属于宿主机某个用户,而容器内的 Web 进程没有写权限,页面可能能打开,但保存不成功。排查时先看容器日志,再看宿主机目录权限。

第二个坑是 note 名称。note 名称最终会参与文件路径,所以不要使用奇怪字符,也不要把 note 名当作安全密码。它更像纸条编号,不像访问令牌。

第三个坑是把它当成“共享密钥箱”。这很危险。它没有复杂权限模型,也不是端到端加密系统。你可以在前面加认证和访问控制,但工具本身的定位仍然是临时文本。

第四个坑是清理策略。临时工具最怕长期堆积。建议定期清理 _tmp 里的旧文件,例如只保留最近几天仍在用的 note。清理前要想清楚是否有仍需保留的文本,别把它当数据库备份策略来设计。

第五个坑是公开入口。即使页面看起来只有一个文本框,也不代表可以直接放公网。任何能写文件的 Web 入口,都应该有访问控制。

十一、我最后的使用规则

我给自己定了几条规则。

第一,只放短文本。超过几屏的内容进文档,不进 Web 剪贴板。

第二,只放低敏内容。需要保密的东西进密码管理器、密钥管理系统或加密文档,不进这里。

第三,只保留临时内容。它不是档案馆,不负责历史追溯。

第四,能用随机 note 就不用固定 note。固定 note 方便,但也更容易被猜到和误用。

第五,所有自动化脚本只写演示内容,不写真实密钥。

这几条规则听起来保守,但符合这个工具的性格。轻量工具最好的状态不是“什么都能做”,而是“只做一件事,并且不会诱导你把边界搞混”。

Q&A

Q1:它能替代在线文档或知识库吗?

不能。它适合临时纯文本,不适合长期资料、结构化知识、团队协作和权限管理。

Q2:它支持 Markdown 吗?

不作为富文本渲染。你可以保存 Markdown 源文本,但页面不会把它当文章渲染。这反而适合临时复制,因为格式不会被自动改写。

Q3:为什么不用数据库?

对这个场景来说,文件已经足够。数据库能带来查询、事务、用户和权限,但也带来备份、迁移和维护成本。临时纸条不一定需要档案系统。

Q4:可以公网访问吗?

不建议裸露公网。至少放在已有认证、VPN、零信任或反向代理保护后面。一个能写文件的网页入口,即使再小,也不该没有门禁。

Q5:如果容器重建,内容会不会丢?

如果你正确挂载了 _tmp,内容会留在宿主机目录里。如果没有挂载,容器重建后就可能丢。

Q6:适合多人共用吗?

可以小范围共用,但要先定规则:只放临时、低敏、纯文本内容。多人长期协作请用真正的文档系统。

参考资料