[{"content":"这是我在本机写的第一篇文章，然后一键发布上来的！\n","permalink":"https://blog.lbfamous.xyz/posts/third-post/","summary":"\u003cp\u003e这是我在\u003cstrong\u003e本机\u003c/strong\u003e写的第一篇文章，然后一键发布上来的！\u003c/p\u003e","title":"本地写作测试"},{"content":" 一台 1核1G 的海外 VPS，从 SSH 都连不上，到最后拥有自己的网站、博客、监控和评论系统。这篇文章记录完整过程，以及所有踩过的坑。\n一、缘起：SSH 连不上 第一次登录服务器时，为了安全禁用了 root 登录（PermitRootLogin no），结果把自己也锁在了外面：\nssh: connect to host ... port 2222: Connection timed out 排查方法：用 ssh -vvv 看完整日志——TCP 能连上、密钥协商正常、主机密钥校验通过，但认证阶段\u0026quot;密钥认了又被拒\u0026quot;，再配合密码也被拒（Permission denied, please try again），基本可以锁定是 PermitRootLogin no。\n解决：通过 VPS 服务商控制台（VNC）进去，改回 PermitRootLogin yes 并重启 sshd：\nsed -i \u0026#39;s/^#\\?PermitRootLogin.*/PermitRootLogin yes/\u0026#39; /etc/ssh/sshd_config systemctl restart sshd 教训：安全加固前先确保留好退路（比如先配好密钥再禁密码）。\n二、基础：安装 Docker apt update apt install -y docker.io docker-compose-v2 systemctl enable --now docker 两个核心概念贯穿始终：\n镜像（Image）：打包好的软件模板（只读） 容器（Container）：镜像运行起来的实例，删了重建很方便 三、第一个网站：nginx 容器 docker run -d --name myweb -p 80:80 -v /opt/myweb:/usr/share/nginx/html nginx 两个最重要的参数：\n-p 80:80：端口映射，把容器内 80 端口暴露到服务器 80 端口 -v 宿主机目录:容器目录：卷挂载，网站文件放宿主机 /opt/myweb，改文件刷新即生效，不用动容器 四、域名与 DNS 国内平台（阿里云）买域名流程：\n注册账号 + 实名认证（支付宝最快） 搜索购买（.com 约 ¥70/年） 等待「注册局审核」（几小时） 不需要 ICP 备案——服务器在国外就绕开了备案 DNS 解析 = 一本分级的通讯录：\n输入 your-domain.com → 问 .xyz 总目录（注册局）：这域名归谁管？ → 得到：归 dns31/dns32.hichina.com 管 → 问 dns31.hichina.com：IP 是多少？ → 得到：你的服务器IP 注意：新域名即使审核通过，注册局\u0026quot;发布\u0026quot;到全球 DNS 还要等一段时间（我那次等了几小时）。可以用 nslookup 域名 8.8.8.8 轮询检测。\n五、HTTPS 小绿锁 HTTPS = 加密 + 身份证明。证书就是\u0026quot;带公证章的身份证明\u0026quot;，Let\u0026rsquo;s Encrypt 是免费公证处，certbot 是跑腿工具。\n申请证书（webroot 方式）：\ndocker run --rm \\ -v /opt/certbot-webroot:/var/www/certbot \\ -v /etc/letsencrypt:/etc/letsencrypt \\ certbot/certbot certonly --webroot -w /var/www/certbot \\ -d your-domain.com -d www.your-domain.com \\ --email you@example.com --agree-tos --no-eff-email nginx 配置 443 + 强制跳转：\nserver { listen 80; server_name your-domain.com; location /.well-known/acme-challenge/ { root /var/www/certbot; } location / { return 301 https://$host$request_uri; } } server { listen 443 ssl; server_name your-domain.com; ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem; location / { root /usr/share/nginx/html; index index.html; } } 自动续期（证书 90 天有效）——cron 每天凌晨 3 点检查续期：\ncrontab -e # 加入一行： 0 3 * * * docker run --rm -v /opt/certbot-webroot:/var/www/certbot -v /etc/letsencrypt:/etc/letsencrypt certbot/certbot renew --quiet \u0026amp;\u0026amp; docker exec myweb nginx -s reload 六、Docker Compose 化 把一长串 docker run 变成配置文件，以后一句 docker compose up -d 搞定：\nservices: web: image: nginx container_name: myweb restart: unless-stopped ports: - \u0026#34;80:80\u0026#34; - \u0026#34;443:443\u0026#34; volumes: - /opt/myweb:/usr/share/nginx/html - /opt/nginx-conf:/etc/nginx/conf.d:ro - /opt/certbot-webroot:/var/www/certbot - /etc/letsencrypt:/etc/letsencrypt:ro 日常命令：docker compose ps（状态）、logs（日志）、restart（重启）、up -d（按配置重建）。\nrestart: unless-stopped 很重要：服务器重启后容器自动恢复。\n七、多服务 + nginx 反向代理 加新服务的模式固定是四步：\nDNS：阿里云加 A 记录 服务名.域名 → 服务器IP compose：加一个服务（不暴露端口，只活在 Docker 内部网络） nginx：conf.d/ 下新建独立 .conf，按 server_name 区分、proxy_pass 转发 证书：certbot --expand 把新域名加进证书 nginx 反代核心（Kuma 为例）：\nserver { listen 443 ssl; server_name kuma.your-domain.com; ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem; location / { proxy_pass http://uptime-kuma:3001; 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_pass http://uptime-kuma:3001 —— compose 内部网络里用服务名当主机名，Docker 自动解析。\n八、Hugo 博客 Hugo 是静态网站生成器：Markdown 文章 → 一次构建 → 一堆 HTML，nginx 直接托管，0 内存常驻。\n基本流程：\n# 生成骨架 docker run --rm -u root -v /opt/hugo-blog:/hugo -w /hugo ghcr.io/gohugoio/hugo:latest new site . # 装主题（Stack 或 PaperMod） git clone --depth 1 https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod # 写文章：content/posts/xxx.md # 构建 docker run --rm -u root -v /opt/hugo-blog:/hugo -w /hugo ghcr.io/gohugoio/hugo:latest build 配置踩坑（TOML 语法）：\n# 错：先 [menu.main] 再 [[menu.main]] 会冲突 # 对：直接用数组元素 [[menus.main]] name = \u0026#34;首页\u0026#34; url = \u0026#34;/\u0026#34; weight = 1 本地写作工作流：在 Windows 本地写 md → 一键脚本同步 + 构建：\n#!/bin/bash set -e scp -r content/* your-server:/opt/hugo-blog/content/ scp -r static/* your-server:/opt/hugo-blog/static/ ssh your-server \u0026#39;docker run --rm -u root -v /opt/hugo-blog:/hugo -w /hugo ghcr.io/gohugoio/hugo:latest build\u0026#39; 九、评论系统 Waline（踩坑最多的一关） 为什么自托管：不依赖 GitHub/第三方，数据在自己手里，国内访问快。\nWaline 正确的 compose 配置（v3 新版变量名）：\nwaline: image: lizheming/waline:latest container_name: waline restart: unless-stopped environment: - TZ=Asia/Shanghai - SQLITE_PATH=/app/data - JWT_TOKEN=你的随机密钥 - SITE_NAME=我的博客 - SITE_URL=https://blog.your-domain.com - SECURE_DOMAINS=blog.your-domain.com - AUTHOR_EMAIL=you@example.com volumes: - /opt/waline-data:/app/data 四个连环坑，一个都不能少：\n挂载别盖程序目录：数据目录挂 /app/data，不是 /app（会盖掉程序报 MODULE_NOT_FOUND） 环境变量版本不同：旧版 WALINE_* 前缀已废弃，新版用 SQLITE_PATH（指目录）+ JWT_TOKEN（必填） Waline 不自动建表：必须下载官方模板放到数据目录： curl -L -o /opt/waline-data/waline.sqlite \\ https://raw.githubusercontent.com/walinejs/waline/main/assets/waline.sqlite 前端脚本要用 UMD 版：@waline/client@v3 的 dist/waline.js 是 ES Module，普通 \u0026lt;script\u0026gt; 会报 Unexpected token 'export'，要改用 dist/waline.umd.js 博客接入（PaperMod 的 extend 钩子，插到每篇文章后）：\n\u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;https://cdn.jsdelivr.net/npm/@waline/client@v3/dist/waline.css\u0026#34; /\u0026gt; \u0026lt;div id=\u0026#34;waline\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;script src=\u0026#34;https://cdn.jsdelivr.net/npm/@waline/client@v3/dist/waline.umd.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script\u0026gt; Waline.init({ el: \u0026#39;#waline\u0026#39;, serverURL: \u0026#39;https://comment.your-domain.com\u0026#39;, lang: \u0026#39;zh-CN\u0026#39; }); \u0026lt;/script\u0026gt; 十、访问统计：不蒜子 一行脚本（PaperMod 页脚钩子 extend_footer.html）：\n\u0026lt;script async src=\u0026#34;https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;span\u0026gt;总访问量 \u0026lt;span id=\u0026#34;busuanzi_value_site_pv\u0026#34;\u0026gt;\u0026lt;/span\u0026gt; 次 · 总访客 \u0026lt;span id=\u0026#34;busuanzi_value_site_uv\u0026#34;\u0026gt;\u0026lt;/span\u0026gt; 人\u0026lt;/span\u0026gt; 最终全家桶 子域名 服务 用途 your-domain.com 主站 跳转博客 blog.your-domain.com Hugo 博客 写作 + 评论 + 统计 kuma.your-domain.com Uptime Kuma 监控所有服务 memos.your-domain.com Memos 碎片笔记 comment.your-domain.com Waline 评论后端 全部 HTTPS，证书自动续期，Docker Compose 一键管理。\n经验总结 安全加固先留退路：改配置前想好怎么回去 数据永远在宿主机：容器随便删，数据通过卷挂载保住 文档要看对版本：环境变量、配置格式会随版本变 静态网站最省资源：1G 内存跑一堆服务还绰绰有余 踩坑是最好的学习：这篇文章里的每个坑，都是亲手踩出来的 ","permalink":"https://blog.lbfamous.xyz/posts/server-journey/","summary":"\u003cblockquote\u003e\n\u003cp\u003e一台 1核1G 的海外 VPS，从 SSH 都连不上，到最后拥有自己的网站、博客、监控和评论系统。这篇文章记录完整过程，以及所有踩过的坑。\u003c/p\u003e\n\u003c/blockquote\u003e","title":"自建服务器全记录：网站、博客、监控、评论"},{"content":"你好，世界！这是我在自己的服务器上部署的博客。\n","permalink":"https://blog.lbfamous.xyz/posts/first-post/","summary":"\u003cp\u003e你好，世界！这是我在自己的服务器上部署的博客。\u003c/p\u003e","title":"我的第一篇文章"}]