本文总结基于 Memos 自建私有知识库服务的完整部署流程,包括 Docker Compose 编排(MySQL / SQLite 两种模式)、环境变量配置以及自定义样式与脚本的优化技巧。
为什么选择 Memos
- 轻量开源 — Go 语言编写,单二进制文件,资源占用极低
- 私有化部署 — 数据完全存储在自己的服务器上,不依赖第三方云服务
- 博客式知识管理 — 支持标签、过滤、搜索,适合碎片化知识整理
- Markdown 支持 — 原生支持 Markdown 语法,书写体验流畅
Docker Compose 部署(MySQL 模式)
当笔记数量超过 50,000 条时,建议使用 MySQL 作为存储后端以获得更好的性能表现。
创建编排文件
touch docker-compose.ymldocker-compose.yml
services:
memos:
image: neosmemo/memos:0.29
container_name: memos
ports:
- "5230:5230"
environment:
MEMOS_DRIVER: mysql
MEMOS_DSN: "root:xK9mP2qL5nR7@tcp(mysql:3306)/memos?charset=utf8mb4&parseTime=True&loc=Local"
volumes:
- ./data:/var/opt/memos
depends_on:
mysql:
condition: service_healthy
restart: unless-stopped
mysql:
image: mysql:8.4.3
container_name: memos-mysql
environment:
MYSQL_ROOT_PASSWORD: xK9mP2qL5nR7
MYSQL_DATABASE: memos
volumes:
- ./mysql:/var/lib/mysql
ports:
- "3306:3306"
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
timeout: 5s
retries: 10
restart: unless-stopped⚠️ 注意:
MEMOS_DSN中的数据库账号密码需与 MySQL 容器的MYSQL_ROOT_PASSWORD保持一致,生产环境请务必替换为强密码。
Docker Compose 部署(SQLite 模式)
当笔记数量 小于 50,000 条时,SQLite 模式更为简单,无需额外数据库容器。
services:
memos:
image: neosmemo/memos:0.29
container_name: memos
ports:
- "5230:5230"
volumes:
- ./data:/var/opt/memos
environment:
# 如需公网访问建议补充
# MEMOS_INSTANCE_URL: "https://your-memos-domain.com"
restart: unless-stoppedSQLite 模式无需任何数据库连接变量,Memos 会自动启用 SQLite 作为存储后端。
启动服务
docker compose up -d启动后访问 http://<服务器IP>:5230 即可进入 Memos Web 界面。
初始配置
1. 注册管理员账号
首次访问 Web 地址时,注册第一个账号,该账号自动成为管理员。
2. 禁止用户注册
为防止未授权用户注册,部署完成后应立即关闭注册功能:
- 点击左下角 账号
- 进入 系统 设置
- 滚动到最底部,打开 禁止用户注册 开关
自定义样式与脚本
Memos 支持通过系统设置注入自定义 CSS 和 JavaScript,以下为两个实用优化方案。
自定义 CSS:编辑区域宽度自适应
默认编辑区域较窄,添加以下 CSS 可让编辑区域宽度自适应浏览器屏幕:
.max-w-2xl {
max-width: none !important;
padding-inline: 10px;
}
#memos-custom-toggle-btn:hover {
background-color: var(--card-bg);
border-radius: 6px;
}自定义脚本:日历面板折叠控制
添加以下 JavaScript 脚本,可在侧边栏添加一个折叠/展开按钮,控制日历面板的显示与隐藏:
(function () {
const wrapSelector = '#root > div:nth-child(1) > div > header > div:nth-child(1)';
const panelSelector = '#root > div:nth-child(1) > main > section > div:nth-child(1)';
const STORAGE_KEY = 'memos_sidebar_panel_state';
const btnId = 'memos-custom-toggle-btn';
let initLock = false;
function buildToggleButton() {
if (document.getElementById(btnId)) return;
const wrapEl = document.querySelector(wrapSelector);
if (!wrapEl) {
setTimeout(buildToggleButton, 280);
return;
}
const panelEl = document.querySelector(panelSelector);
if (!panelEl) {
setTimeout(buildToggleButton, 280);
return;
}
const allLinks = wrapEl.querySelectorAll(':scope > a');
const lastA = allLinks[allLinks.length - 1];
const toggleA = document.createElement('a');
toggleA.id = btnId;
toggleA.href = 'javascript:void(0)';
toggleA.style.cssText = `
display:flex;align-items:center;margin-left:8px;cursor:pointer;
color:var(--text-color);padding:4px;
`;
const svgExpand = `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>`;
const svgCollapse = `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>`;
let isShow = localStorage.getItem(STORAGE_KEY) !== 'hidden';
function syncUI() {
panelEl.style.display = isShow ? 'block' : 'none';
toggleA.innerHTML = isShow ? svgCollapse : svgExpand;
localStorage.setItem(STORAGE_KEY, isShow ? 'show' : 'hidden');
}
syncUI();
toggleA.onclick = function (e) {
e.preventDefault();
e.stopPropagation();
isShow = !isShow;
syncUI();
};
if (lastA) {
lastA.after(toggleA);
} else {
wrapEl.appendChild(toggleA);
}
}
let lastHref = location.href;
const observer = new MutationObserver(() => {
if (location.href !== lastHref) {
lastHref = location.href;
const oldBtn = document.getElementById(btnId);
if (oldBtn) oldBtn.remove();
setTimeout(buildToggleButton, 350);
}
});
observer.observe(document.body, { subtree: true, childList: true });
buildToggleButton();
})();脚本通过 MutationObserver 监听 SPA 路由变化,确保页面切换后按钮仍能正常工作,折叠状态通过 localStorage 持久化保存。
存储方案选择建议
| 笔记数量 | 推荐存储 | 说明 |
|---|---|---|
| < 50,000 条 | SQLite | 部署简单,无需额外数据库容器 |
| ≥ 50,000 条 | MySQL | 性能更优,适合大规模数据场景 |
总结
Memos 是一款轻量、开源、私有化的知识管理工具,通过 Docker Compose 可以快速部署。结合自定义 CSS 与 JavaScript,还能进一步优化使用体验,打造属于自己的私有知识库。
评论区