2026-02-13 00:00:00

之前一直用 Feedly 订阅各类 RSS,但发现有些网站没有原生 RSS,不能直接订阅。听说 RSSHub 可以给这些网站生成 RSS,于是决定自己装一个。
mkdir -p /root/docker/rsshub
cd /root/docker/rsshub
version: '3.3'
services:
rsshub:
image: ghcr.io/diygod/rsshub:latest
container_name: rsshub
restart: unless-stopped
network_mode: host
environment:
- NODE_ENV=production
- PORT=1200
- CACHE_TYPE=memory
- CACHE_EXPIRE=60
volumes:
- ./data:/tmp/rsshub
这里用了 host 网络模式,端口直接映射到宿主机。数据目录挂载到 ./data,方便持久化。
mkdir -p data
docker-compose up -d
curl http://localhost:1200/
看到 Welcome 页面就说明装好了。
RSSHub 装起来比想象中简单,Docker 化部署基本上就是写个配置文件的事。
2026-02-12 00:00:00

最近在搭自动化工具,选了 n8n。网上教程不少,但要么版本太旧,要么踩坑没写清楚。
我把自己实际部署的过程记录下来,配上完整的配置文件。你跟着做一遍,应该能跑起来。
简单说,n8n 是个开源的工作流自动化工具。可以理解为:
官方提供云服务(收费),也支持自己部署(免费)。本文讲的是自己部署。
核心就这几步:
# 1. 确保 Docker 安装好了
docker --version
# 推荐:Docker 20.10+
# 2. 确保 Docker Compose 有
docker-compose --version
# 推荐:v2.0+
# 创建部署目录
mkdir -p /root/n8n/{data,backups}
cd /root/n8n
这一步会生成两个关键配置:
# 生成 64 位十六进制加密密钥
openssl rand -hex 32
# 复制输出,后面要用。示例:
# a155a297a3cf9f3a9fbb4eadf3b4be08adcb9cff7f3e152e2575b7b23f1b3ade
# 生成管理员密码(20位,包含大小写、数字、特殊字符)
openssl rand -base64 24 | tr -dc 'A-Za-z0-9!@#$%^&*' | head -c 20
# 复制输出。示例:
# dgYN71mMEbkS3uD5djSi
# 获取服务器 IP(用于 Webhook URL)
hostname -I | awk '{print $1}'
# 示例输出:10.0.12.8
创建一个 .env 文件,把上面的值填进去:
# /root/n8n/.env
# ========== 认证配置 ==========
N8N_BASIC_AUTH_ACTIVE=true
N8N_BASIC_AUTH_USER=admin
# 把刚才生成的密码填这里
N8N_BASIC_AUTH_PASSWORD=dgYN71mMEbkS3uD55555
# ========== 加密密钥 ==========
# 把刚才生成的 64 位密钥填这里
N8N_ENCRYPTION_KEY=a155a297a3cf9f3a9fbb4eadf3b4be08adcb9cff7f3e152e2575b7b23f666666
# ========== 网络配置 ==========
N8N_HOST=0.0.0.0
N8N_PORT=5678
N8N_PROTOCOL=http
TZ=Asia/Shanghai
# 改成你的服务器 IP
WEBHOOK_URL=http://10.0.12.8:5678/
# ========== 数据库配置 ==========
# 用 SQLite,最简单
DB_TYPE=sqlite
# ========== 执行配置 ==========
# 生产环境别保存执行数据,省空间
EXECUTIONS_DATA_SAVE_ON_SUCCESS=none
EXECUTIONS_DATA_PRUNE=true
EXECUTIONS_DATA_MAX_AGE=168
# ========== 安全设置 ==========
# HTTP 访问时关闭安全 Cookie 检查
N8N_SECURE_COOKIE=false
# ========== 跳过账户创建 ==========
N8N_SKIP_CREATION_DURING_STARTUP=true
# ========== 禁用版本通知(可选) ==========
# 关闭后不会弹 "Your license key is on the way" 提示
N8N_VERSION_NOTIFICATIONS_ENABLED=false
N8N_DIAGNOSTICS_ENABLED=false
设置权限:
chmod 600 /root/n8n/.env
# /root/n8n/docker-compose.yml
version: '3.3'
services:
n8n:
image: docker.n8n.io/n8nio/n8n:latest
restart: unless-stopped
# 用 host 网络模式,容器直接用宿主机网络
# 好处:内网能访问,不会把端口暴露到外网
network_mode: host
volumes:
# 挂载数据目录
- ./data:/home/node/.n8n:rw
environment:
- N8N_BASIC_AUTH_ACTIVE=true
- N8N_BASIC_AUTH_USER=admin
- N8N_BASIC_AUTH_PASSWORD=${N8N_PASSWORD}
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- N8N_HOST=0.0.0.0
- N8N_PORT=5678
- N8N_PROTOCOL=http
- TZ=Asia/Shanghai
- WEBHOOK_URL=http://10.0.12.8:5678/
- DB_TYPE=sqlite
- EXECUTIONS_DATA_SAVE_ON_SUCCESS=none
- EXECUTIONS_DATA_PRUNE=true
- EXECUTIONS_DATA_MAX_AGE=168
- N8N_SECURE_COOKIE=false
- N8N_SKIP_CREATION_DURING_STARTUP=true
healthcheck:
test: ["CMD-SHELL", "wget -q --spider http://localhost:5678/healthz || exit 1"]
interval: 30s
timeout: 10s
retries: 3
container_name: n8n
这是很多人会踩的坑。
Docker 容器里的 n8n 是用 UID 1000 这个用户运行的。如果你直接把 ./data 目录挂载进去,而目录是 root:root 权限,容器就没法写东西。
# 修复权限
chown -R 1000:1000 /root/n8n/data
chmod 755 /root/n8n/data
# 启动容器
docker-compose up -d
# 等待几秒让容器启动
sleep 15
# 检查状态
docker ps | grep n8n
# 应该看到 n8n 这个容器,状态是 Up
# 检查健康状态
docker inspect --format='{{.State.Health.Status}}' n8n
# 应该输出:healthy
# 本地测试
curl -s -o /dev/null -w "%{http_code}" http://localhost:5678
# 应该返回 200
# 内网测试(把 IP 换成你的服务器 IP)
curl -s -o /dev/null -w "%{http_code}" http://10.0.12.8:5678
# 应该返回 200
打开浏览器访问:http://你的服务器IP:5678
首次访问会让你创建管理员账户,用之前在 .env 里配置的用户名密码登录即可。
最初我想用已有的 PostgreSQL 9.4:
syntax error at or near "NOT"
CREATE INDEX IF NOT EXISTS IDX_xxx
原因:n8n 用的 IF NOT EXISTS 语法,PostgreSQL 9.4 不支持。11 以上版本才支持。
解决方案:改用 SQLite,n8n 原生支持,不需要额外部署数据库。
Error: EACCES: permission denied, open '/home/node/.n8n/config'
原因:挂载目录权限是 root:root,容器里用 1000 用户跑,没权限写。
解决方案:见「第三步:修复权限」
浏览器打开时提示安全 Cookie 问题。
原因:n8n 检测到 HTTP 访问但配置了安全 Cookie。
解决方案:在环境变量加 N8N_SECURE_COOKIE=false
收到邮件说 “Your license key is on the way!”。
原因:n8n Cloud 的营销邮件。自托管版完全免费,不需要许可证。
解决方案一:禁用版本检查(推荐)
如果你不想处理邮件,直接禁用版本通知:
# 追加到 .env 文件
echo "N8N_VERSION_NOTIFICATIONS_ENABLED=false" >> /root/n8n/.env
echo "N8N_DIAGNOSTICS_ENABLED=false" >> /root/n8n/.env
# 重启
./n8n.sh restart
重启后 “Your license key is on the way” 的提示会消失。
解决方案二:激活(可选)
结论:自托管版完全免费,不影响使用。禁用版本通知最简单。
# 启动
./n8n.sh start
# 停止
./n8n.sh stop
# 重启
./n8n.sh restart
# 查看日志
./n8n.sh logs # 所有日志
./n8n.sh logs -f # 实时日志
./n8n.sh logs n8n # 只看 n8n 日志
# 查看状态
./n8n.sh status
# 更新版本
./n8n.sh update
./backup.sh
备份文件会保存在 /root/n8n/backups/ 目录下,文件名类似 n8n_backup_20260212_153321.tar.gz。
加到 crontab,每天凌晨 2 点自动备份:
crontab -e
# 添加这行
0 2 * * * /root/n8n/backup.sh > /dev/null 2>&1
# 假设有个备份文件
tar xzf /root/n8n/backups/n8n_backup_20260212_153321.tar.gz -C /tmp/
# 恢复数据
docker cp /tmp/. n8n:/home/node/.n8n/
# 重启容器
./n8n.sh restart
/root/n8n/
├── docker-compose.yml # Docker 配置
├── .env # 敏感配置(权限 600)
├── n8n.sh # 管理脚本
├── backup.sh # 备份脚本
├── README.md # 说明文档
├── data/ # SQLite 数据库文件
│ ├── database.sqlite
│ ├── database.sqlite-shm
│ └── database.sqlite-wal
└── backups/ # 备份文件
└── n8n_backup_20260212_153321.tar.gz
SQLite 数据库文件在 /root/n8n/data/database.sqlite。
直接改 .env 文件里的 N8N_BASIC_AUTH_PASSWORD,然后重启容器:
./n8n.sh restart
不能。这个密钥是用来加密存储的凭证的。如果改了,之前的凭证就解密不开了。
2026-02-11 00:00:00

Gemini 已经内置于 Chrome,但 Google 对其开启增加了繁琐的“地理围栏”和灰度测试。以下是我在macOS下的开启的步骤,做个记录。
节点伪装: 将代理切换至 美国节点 (US)。
语言环境: 进入 Chrome 设置,将 界面语言 (Display Language) 切换为 English (United States),并重启浏览器。

身份定位: 确保你的 Google 账号设置或搜索设置中的 Region 也是 US(部分情况非必须,但建议对齐)。

选择住址:

关闭chrome。
open -n -a "Google Chrome" --args --variations-override-country=us
操作完成后,打开 Chrome:
输入 chrome://components
查找 Optimization Guide On Device Model。
如果看到版本号不为 0.0.0.0,说明本地模型已下载成功。

还可以访问这个页面看关于Gemini更多的配置: <chrome://settings/ai/gemini>
开始使用,可以直接和Gemini对话了。


2026-02-10 00:00:00

最近重度使用opencode,做任务时,明明任务已经执行完了,但新开一个 Session 后,Atlas 又把之前的任务重新执行一遍。
OpenCode 用 boulder.json 文件来跟踪任务进度,文件位置在 ~/.sisyphus/boulder.json。
{
"active_plan": "/root/.sisyphus/plans/rsshub-installation.md",
"completed_at": "2026-02-12T14:40:00.000Z",
"final_status": "COMPLETED",
"session_ids": ["ses_xxx", "ses_xxx"],
"plan_name": "rsshub-installation"
}
问题出在两个方面:
n8n-docker-install.md 计划中所有任务都标记为 - [x] 完成boulder.json 的 active_plan 指向了另一个已完成的计划 rsshub-installation
# 删除 boulder.json(清除所有状态)
rm ~/.sisyphus/boulder.json
或者手动重置:
cat > ~/.sisyphus/boulder.json << 'EOF'
{
"active_plan": "/root/.sisyphus/plans/正确的计划名.md",
"final_status": "COMPLETED"
}
EOF
在 .md 计划文件末尾添加:
---
## 计划状态
**状态**: ✅ 已完成
**完成时间**: 2026-02-12
**验证方式**: 所有任务标记为 - [x]
**注意**: 此计划已完全执行,无需再次执行。
cat ~/.sisyphus/boulder.json
明确告诉 Atlas 跳过已完成的任务
不要手动修改 - [ ] 标记
~/.sisyphus/boulder.json
~/.sisyphus/plans/
~/.sisyphus/notepads/
这是一个 OpenCode 的状态管理边界问题:
| 状态 | 文件 | 问题 |
|---|---|---|
| ✅ 任务完成 | 计划 .md 文件 |
所有 - [x] 标记正确 |
| ❌ boulder 状态 | boulder.json |
指向错误的计划 |
| ⚠️ Session 连续性 | - | 新 Session 无法正确读取历史状态 |
解决方案:删除 boulder.json 或手动重置状态即可。
新开 Session 遇到任务被重复执行时,运行:
rm ~/.sisyphus/boulder.json
然后重新开始即可。
2026-02-09 00:00:00

之前用 Feedly 订阅 RSS,但很多技术博客没有原生 RSS,而且被墙的网站也无法访问。于是决定自建一个 RSS 阅读器。
mkdir -p /root/docker/miniflux
cd /root/docker/miniflux
mkdir -p data pgsql_data
version: '2.4'
services:
miniflux:
image: miniflux/miniflux:latest
container_name: miniflux
restart: unless-stopped
depends_on:
db:
condition: service_healthy
environment:
- DATABASE_URL=postgres://miniflux:changeme@miniflux-db/miniflux?sslmode=disable
- RUN_MIGRATIONS=1
- CREATE_ADMIN=1
- ADMIN_USERNAME=admin
- ADMIN_PASSWORD=changeme
- LISTEN_ADDR=:8082
volumes:
- ./data:/var/lib/miniflux
db:
image: postgres:16
container_name: miniflux-db
restart: unless-stopped
environment:
- POSTGRES_USER=miniflux
- POSTGRES_PASSWORD=changeme
- POSTGRES_DB=miniflux
volumes:
- ./pgsql_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-U", "miniflux"]
interval: 10s
timeout: 5s
retries: 5
docker-compose up -d
# 检查容器状态
docker ps | grep miniflux
# 测试 Web 访问
curl -s -o /dev/null -w "%{http_code}" http://localhost:8082
# 应返回 200
我整理了一份可访问的技术博客订阅列表:
| 名称 | RSS 地址 |
|---|---|
| Kubernetes Blog | https://kubernetes.io/feed.xml |
| AWS Blog | https://aws.amazon.com/blogs/aws/feed/ |
| Azure Blog | https://azure.microsoft.com/blog/feed/ |
| Red Hat Blog | https://www.redhat.com/en/rss/blog |
| 名称 | RSS 地址 |
|---|---|
| 美团技术团队 | https://tech.meituan.com/feed/ |
| 有赞技术团队 | https://tech.youzan.com/rss/ |
| 名称 | RSS 地址 |
|---|---|
| 胡涂说 | https://hutusi.com/feed.xml |
| 阮一峰的网络日志 | https://www.ruanyifeng.com/blog/atom.xml |
| 云风的 BLOG | https://blog.codingnow.com/atom.xml |
| 酷壳 | https://coolshell.cn/feed |
# 批量导入 RSS 源
while read url; do
curl -s -X POST \
-u admin:changeme \
-H "Content-Type: application/json" \
-d "{\"feed_url\":\"$url\"}" \
http://localhost:8082/v1/feeds
done < feeds.txt
部署完成后,成功导入 10 个订阅源,包括:
Web 界面访问地址:http://你的服务器IP:8082
初始账户:admin / changeme,建议首次登录后立即修改密码。
数据目录说明:
/root/docker/miniflux/
├── docker-compose.yml # 配置文件
├── data/ # Miniflux 应用数据
└── pgsql_data/ # PostgreSQL 数据库文件
定期备份 pgsql_data 目录即可完整保存所有订阅数据和文章缓存。
2026-02-02 00:00:00

在 WireGuard 虚拟网络环境下,我尝试 SSH 连接远程主机时,TCP 连接可以建立,但会话随即卡死。
使用 ssh -v 开启详细调试模式,发现日志停滞在密钥交换(Key Exchange)的最后一步:
debug1: SSH2_MSG_KEXINIT sent
debug1: SSH2_MSG_KEXINIT received
...
debug1: kex: algorithm: curve25519-sha256
debug1: expecting SSH2_MSG_KEX_ECDH_REPLY <-- 卡死在此处,随后超时
MTU 黑洞 (MTU Black Hole)。
SSH 在密钥交换(KEX)阶段会发送较大的数据包。WireGuard 协议本身会增加头部开销(Overhead)。当 [SSH Payload + TCP/IP Headers + WireGuard Overhead] 的总长度超过物理链路路径上的最小 MTU(通常受限于 PPPoE 拨号或中间路由策略)时,数据包会被静默丢弃。
由于中间链路可能禁用了 ICMP,导致发送端无法收到 “Fragmentation Needed” 通知,从而造成连接“假死”。
WireGuard 官方推荐将 MTU 设置为保守值 1280 以适配绝大多数网络环境。