[{"content":"Welcome to Hugo theme Stack. This is your first post. Edit or delete it, then start writing!\nFor more information about this theme, check the documentation: https://stack.jimmycai.com/\nWant a site like this? Check out hugo-theme-stack-stater\nPhoto by Pawel Czerwinski on Unsplash\n","date":"2022-03-06T00:00:00Z","image":"/p/hello-world/cover.jpg","permalink":"/p/hello-world/","title":"Hello World"},{"content":"这篇笔记整理了一台新 VPS 从买来之后到真正可用的完整流程，重点包括三部分：\n把服务器改成仅允许 SSH 密钥登录 利用脚本快速搭建 v2ray 节点 对 VPS 本身和节点线路做测试 这样整理之后，后续如果重新买服务器、迁移节点，或者帮别人重复搭建，基本都可以直接照着做。\n一、整体流程与核心思路 这套流程的目标不是单纯“把代理跑起来”，而是先把基础安全做好，再去安装节点，最后验证机器质量和线路质量。\n完整思路如下：\n购买一台海外 VPS 使用密码先登录一次服务器 在本地生成 SSH 密钥，并把公钥写入 VPS 修改 SSH 配置，只保留密钥登录 配置防火墙、修改端口、验证登录无误 使用 v2ray-agent 等脚本安装 v2ray 节点 视情况安装 BBR 或其他加速组件 在本地客户端导入节点 对 VPS 的性能、网络、IP 质量和实际速度做测试 这个顺序很重要。\n如果一开始就急着装 v2ray，而没有先把 SSH 登录方式和防火墙整理好，后面很容易出现：\n端口改错后把自己锁在门外 服务器仍然允许密码登录，暴露风险较高 cloud-init 覆盖了你的 SSH 配置 VPS 线路质量不佳，节点搭好也不稳定 所以比较稳的顺序永远是：\n1 先加固登录 -\u0026gt; 再部署节点 -\u0026gt; 最后测试效果 二、步骤一：将 VPS 设置为仅允许密钥登录 这一部分的目标是让服务器最终不再接受密码登录，只允许你用本地密钥登录。\n1. 本地生成 SSH 密钥 推荐使用 ed25519 算法，它比传统 RSA 更轻、更快，也更适合现在的新机器。\n如果你在 Windows 上，可以在命令行中执行：\n1 2 mkdir \u0026#34;G:\\VPS\\.ssh\u0026#34; ssh-keygen -t ed25519 -a 100 -C \u0026#34;vmrack\u0026#34; 执行过程中，保存路径可以指定为：\n1 G:\\VPS\\.ssh\\id_ed25519 生成完成后，你会得到两份文件：\nid_ed25519：私钥，只保留在本地 id_ed25519.pub：公钥，需要放到 VPS 上 2. 将公钥写入 VPS 先用服务器当前允许的方式登录 VPS，一般一开始还是密码登录。\n登录后执行：\n1 mkdir -p ~/.ssh \u0026amp;\u0026amp; chmod 700 ~/.ssh 然后把本地 id_ed25519.pub 里的完整内容追加到服务器的授权文件中：\n1 2 echo \u0026#34;ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA...你的完整公钥... vmrack\u0026#34; \u0026gt;\u0026gt; ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys 这里有两个权限必须注意：\n.ssh 目录应为 700 authorized_keys 应为 600 如果权限太宽，SSH 可能直接拒绝读取这份密钥。\n3. 先配置防火墙，再改 SSH 端口 很多人会先改端口，结果新端口没放行，下一次就再也连不上。正确顺序应该是先开防火墙规则，再改 SSH 配置。\n如果 VPS 是 Debian 12，可以先安装 ufw：\n1 apt update \u0026amp;\u0026amp; apt install ufw -y 假设你想把 SSH 改到 56260 端口，可以先执行：\n1 2 3 4 5 6 ufw allow 22/tcp ufw allow 56260/tcp ufw allow 80/tcp ufw allow 443/tcp ufw enable ufw status 这里暂时保留 22 端口，是为了防止配置失误时还有回退通道。\n4. 修改 SSH 主配置 编辑 SSH 主配置文件：\n1 nano /etc/ssh/sshd_config 重点修改下面几项：\n1 2 3 Port 56260 PasswordAuthentication no PermitRootLogin prohibit-password 含义分别是：\nPort 56260：SSH 改到新端口 PasswordAuthentication no：禁止密码登录 PermitRootLogin prohibit-password：允许 root 用密钥登录，但禁止 root 密码登录 如果你后续还想进一步加固，也可以把 root 登录完全禁掉，然后改成普通用户 + sudo 的方式。\n5. 检查 cloud-init 或子配置是否覆盖了主配置 在 Debian 12 或某些云厂商镜像里，sshd_config 不一定是最终生效文件，/etc/ssh/sshd_config.d/ 里的子配置可能会覆盖它。\n先检查：\n1 grep -r \u0026#34;PasswordAuthentication\u0026#34; /etc/ssh/sshd_config.d/ 如果发现类似 50-cloud-init.conf 中仍然写着：\n1 PasswordAuthentication yes 就需要打开并改掉：\n1 nano /etc/ssh/sshd_config.d/50-cloud-init.conf 改成：\n1 PasswordAuthentication no 然后检查最终生效结果：\n1 sshd -T | grep -i passwordauthentication 如果输出是：\n1 passwordauthentication no 说明配置最终生效了。\n6. 重启 SSH 并验证 重启服务：\n1 systemctl restart ssh 这一步最关键的提醒是：\n不要关闭当前已经登录着的 SSH 会话。\n正确做法是新开一个终端窗口，用新端口和密钥测试登录：\n1 ssh -i G:\\VPS\\.ssh\\id_ed25519 -p 56260 root@你的VPS_IP 确认新方式能正常登录后，再删除旧的 22 端口规则：\n1 ufw delete allow 22/tcp 7. 这一阶段常见坑 这一步最常见的问题有：\n公钥没写完整，或者写进了错误用户的 authorized_keys .ssh 和 authorized_keys 权限不对 改了新端口但忘记在防火墙放行 sshd_config.d 中有覆盖配置 还没验证新方式可用就关闭旧 SSH 会话 如果你被锁在外面，通常只能通过云厂商控制台或 VNC 救回。\n三、步骤二：利用脚本搭建 v2ray 节点 SSH 配置稳定以后，才进入节点搭建阶段。\n1. 基本思路 这里的做法不是手动一项一项拼装配置，而是直接使用成熟脚本来完成：\n安装 v2ray / xray 内核 自动写配置 自动生成节点信息 提供菜单式管理界面 其中用的比较多的一个脚本是：\nv2ray-agent 这是目前很常见的一类脚本化方案，适合个人 VPS 节点的快速搭建。\n2. 安装 v2ray-agent 如果你想直接查看这个脚本对应的说明页面，可以点击这里：\nv2ray-agent 脚本页面 直接执行：\n1 wget -P /root -N --no-check-certificate \u0026#34;https://raw.githubusercontent.com/mack-a/v2ray-agent/master/install.sh\u0026#34; \u0026amp;\u0026amp; chmod 700 /root/install.sh \u0026amp;\u0026amp; /root/install.sh 执行完成后，脚本会进入菜单式安装界面。\n后续按脚本提示完成即可，核心是根据自己的场景选择：\n有域名安装 无域名安装 使用哪类协议 是否启用 TLS 是否启用 Reality / WS / gRPC 等方案 你当前记录中更偏向使用：\n无域名安装 3. 后续管理入口 安装完成后，可以通过下面的命令再次进入脚本菜单：\n1 vasma 这个命令通常用于：\n查看当前节点信息 更新脚本 切换协议 重装某个节点方案 查看二维码或订阅信息 4. 可选：安装网络加速脚本 如果你还想在 VPS 上尝试启用 TCP 加速，可以使用下面的脚本：\n1 2 3 wget -N --no-check-certificate \u0026#34;https://raw.githubusercontent.com/chiakge/Linux-NetSpeed/master/tcp.sh\u0026#34; chmod +x tcp.sh ./tcp.sh 你原始记录里提到，常见选择是：\nBBR plus + FQ 不过这里需要实话实说：\n加速脚本不是装了就一定“肉眼飞跃”，它更多是对某些特定线路、特定拥塞场景有效。\n如果脚本安装过程中出现“是否移除 kernel”之类选项，按你原笔记中的经验，应优先选择：\n1 No 5. 本地客户端连接 VPS 节点装好之后，还需要本地客户端导入配置。\nWindows 常见客户端例如：\nv2rayN 使用方式一般是：\n从脚本输出中复制节点链接 或者扫描二维码 导入到本地客户端 打开系统代理或 TUN 模式 如果你是手机端，则可以使用对应平台的客户端，例如：\nAndroid 上常见的 v2ray / xray 客户端 iOS 上的 Shadowrocket 等 6. 这一阶段要重点关注什么 节点搭建好不代表一定可用，真正重要的是：\n协议能否成功连通 延迟是否稳定 速度是否可接受 IP 是否干净 是否容易被风控或污染 所以搭完之后，必须进入下一步测试。\n四、步骤三：对 VPS 和节点进行测试 这一部分不是可有可无，而是整个流程里很关键的一环。\n同样是“搭好了节点”，不同 VPS 的实际体验差异可能非常大，核心原因通常来自：\n机器性能 带宽 线路 丢包 IP 纯净度 1. 使用 ecs.sh 测试 VPS 综合性能 你原记录中使用的是 spiritLHLS 的融合怪测试脚本：\necs 运行命令：\n1 curl -L https://gitlab.com/spiritysdx/za/-/raw/main/ecs.sh -o ecs.sh \u0026amp;\u0026amp; chmod +x ecs.sh \u0026amp;\u0026amp; bash ecs.sh 后续如果脚本已经下载过，重新进入可以直接：\n1 bash ecs.sh 这个脚本通常能比较全面地输出：\nCPU 信息 内存信息 磁盘性能 网络测试 回程与部分线路表现 适合快速判断 VPS 大体是否值得继续使用。\n2. 测试基础网络与实际速度 最直观的方式依然是：\n直接用浏览器访问 speedtest.net 你也可以在服务器上使用命令行测速工具，但对个人使用来说，浏览器和本地客户端上的实际体验往往更有参考价值。\n推荐测试方式：\n不开代理先测本地网络 开启节点后再测 对比延迟、下载速度、网页加载体验和视频播放流畅度 如果开节点后的测速仍然很差，问题通常不一定是脚本安装错了，而更可能是：\nVPS 线路一般 本地到 VPS 的链路质量不好 VPS 出口带宽有限 3. 测试 IP 是否干净 这一点对长期使用非常重要。\n很多 VPS 提供商的 IP 早就被滥用过，可能存在：\n风控高 被流媒体限制 被社交平台注册风险标记 你原记录里用到的网站是：\nScamalytics 这个网站可以用来查看 IP 风险评分。\n实际使用时，可以关注：\n风险分是否偏高 是否被判定为代理 / VPN / abusive 某些服务是否容易触发验证 4. 测试登录失败来源 如果你已经开放 SSH，并且机器运行了一段时间，可以用下面的命令看看有没有人在爆破你的服务器：\n1 sudo lastb 这个命令会列出最近失败的登录记录。\n如果你已经改了 SSH 端口，并且禁用了密码登录，通常风险会小很多，但依然建议偶尔检查一次。\n5. 如何判断这台 VPS 值不值得继续用 一台适合长期使用的 VPS，通常至少应满足：\nSSH 延迟可接受 节点协议稳定连通 网页和视频体验稳定 速度在你的使用场景下足够 IP 风险不高 不容易被封锁或频繁抽风 如果以下问题长期存在，就要考虑更换厂商或线路：\n同样配置但速度明显不如别家 高峰时段严重掉速 丢包明显 某些网站频繁被风控 IP 风险评分很高 五、推荐的实际操作顺序 如果从零开始重新做一遍，我建议按下面顺序来：\n第一阶段：先做 SSH 安全配置 1 2 3 4 5 6 7 8 9 ssh-keygen -t ed25519 -a 100 -C \u0026#34;vmrack\u0026#34; mkdir -p ~/.ssh \u0026amp;\u0026amp; chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys apt update \u0026amp;\u0026amp; apt install ufw -y ufw allow 22/tcp ufw allow 56260/tcp ufw allow 80/tcp ufw allow 443/tcp ufw enable 然后修改：\n1 2 /etc/ssh/sshd_config /etc/ssh/sshd_config.d/50-cloud-init.conf 最后验证：\n1 2 sshd -T | grep -i passwordauthentication systemctl restart ssh 第二阶段：部署 v2ray 节点 1 wget -P /root -N --no-check-certificate \u0026#34;https://raw.githubusercontent.com/mack-a/v2ray-agent/master/install.sh\u0026#34; \u0026amp;\u0026amp; chmod 700 /root/install.sh \u0026amp;\u0026amp; /root/install.sh 后续管理：\n1 vasma 可选加速：\n1 2 3 wget -N --no-check-certificate \u0026#34;https://raw.githubusercontent.com/chiakge/Linux-NetSpeed/master/tcp.sh\u0026#34; chmod +x tcp.sh ./tcp.sh 第三阶段：测试 VPS 与线路 1 2 3 curl -L https://gitlab.com/spiritysdx/za/-/raw/main/ecs.sh -o ecs.sh \u0026amp;\u0026amp; chmod +x ecs.sh \u0026amp;\u0026amp; bash ecs.sh bash ecs.sh sudo lastb 然后再配合：\nspeedtest.net scamalytics.com 去判断这台 VPS 的实际可用性。\n六、常用命令汇总 1. SSH 密钥生成 1 ssh-keygen -t ed25519 -a 100 -C \u0026#34;vmrack\u0026#34; 作用：在本地生成一对更安全的 SSH 密钥。\n2. 设置 .ssh 权限 1 2 mkdir -p ~/.ssh \u0026amp;\u0026amp; chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys 作用：创建并锁定 SSH 授权目录与授权文件。\n3. 防火墙放行端口 1 2 3 ufw allow 56260/tcp ufw allow 80/tcp ufw allow 443/tcp 作用：放行新 SSH 端口和 Web 常用端口。\n4. 检查 SSH 最终生效配置 1 sshd -T | grep -i passwordauthentication 作用：确认密码登录是否真的已经关闭。\n5. 重启 SSH 服务 1 systemctl restart ssh 作用：让 SSH 配置重新加载。\n6. 安装 v2ray-agent 1 wget -P /root -N --no-check-certificate \u0026#34;https://raw.githubusercontent.com/mack-a/v2ray-agent/master/install.sh\u0026#34; \u0026amp;\u0026amp; chmod 700 /root/install.sh \u0026amp;\u0026amp; /root/install.sh 作用：安装并进入 v2ray-agent 脚本。\n7. 再次进入 v2ray-agent 菜单 1 vasma 作用：管理节点、查看配置、更新脚本。\n8. 安装 TCP 加速脚本 1 2 3 wget -N --no-check-certificate \u0026#34;https://raw.githubusercontent.com/chiakge/Linux-NetSpeed/master/tcp.sh\u0026#34; chmod +x tcp.sh ./tcp.sh 作用：尝试启用 BBR 或相关 TCP 优化。\n9. 测试 VPS 综合性能 1 curl -L https://gitlab.com/spiritysdx/za/-/raw/main/ecs.sh -o ecs.sh \u0026amp;\u0026amp; chmod +x ecs.sh \u0026amp;\u0026amp; bash ecs.sh 作用：测试 VPS 的 CPU、内存、磁盘和网络综合情况。\n10. 查看失败登录记录 1 sudo lastb 作用：查看是否存在 SSH 爆破或异常登录尝试。\n七、总结 把一台新 VPS 真正变成“可长期使用的自建节点”，核心并不只是装一个代理脚本，而是把三件事都做好：\nSSH 入口先加固，做到只允许密钥登录 节点安装过程尽量脚本化，减少手工出错 对 VPS 和线路做测试，确认它真的值得长期使用 如果只做第二步，不做第一步和第三步，后面往往会被这些问题反复折腾：\n登录方式不安全 配置一改就断连 节点跑起来了但线路很差 IP 风险高，长期体验不稳定 所以更稳的顺序永远是：\n1 先加固 SSH -\u0026gt; 再安装节点 -\u0026gt; 最后测试 VPS 与线路 把这个流程走顺之后，以后无论你是迁移机器、重复搭建，还是继续优化节点，都会轻松很多。\n","date":"2026-05-07T00:00:00Z","permalink":"/p/vps-v2ray-setup-and-test/","title":"购买 VPS 后如何完成密钥登录、v2ray 节点搭建与测试"},{"content":"一、整体流程和核心思路 这套方案的目标是：\n在本地编写和修改 Telegram 机器人代码 将代码 push 到 GitHub 由 GitHub Actions 自动触发部署 GitHub 通过 SSH 登录 VPS VPS 上自动拉取最新代码、更新依赖、重启机器人服务 最终可以在 Telegram 中通过命令查看 VPS 状态 这套流程的核心思路不是“让 GitHub 直接替代 VPS 运行程序”，而是把职责拆开：\n本地：负责开发和提交代码 GitHub：负责触发自动部署 VPS：负责真正运行机器人 systemd：负责守护机器人进程，确保自动重启和开机自启 为了让权限更清晰，这次最终采用的是：\nroot：只负责让 GitHub Action 通过 SSH 登录 VPS debian：负责项目目录、git pull、Python 虚拟环境、bot 运行 这样做的原因是：\n应用不直接用 root 跑，更安全 项目文件归属统一，不容易出现 Git 的 ownership 报错 部署链路和运行链路职责清晰，后续更容易维护 二、最终部署架构 最终的运行结构如下：\n本地修改代码后执行 git push origin main GitHub Actions 触发 .github/workflows/deploy-to-vps.yml Action 使用 root 的 SSH 密钥连接 VPS 登录后切换到 debian 用户执行部署脚本 debian 在 /home/debian/apps/VPS-telegram 中执行： git fetch git reset --hard origin/main 安装 Python 依赖 重启 vps-telegram-bot.service systemd 使用虚拟环境中的 Python 启动 main.py Telegram 机器人继续运行最新代码 三、本地代码改造思路 1. 凭据不能写死在代码里 最开始 main.py 中直接写了：\nAPI_TOKEN ADMIN_ID 这样虽然能跑，但不适合部署，因为：\ntoken 泄露风险高 更换 token 需要修改代码 不适合区分本地环境和 VPS 环境 因此改成了从 .env 读取：\n1 2 API_TOKEN = os.getenv(\u0026#34;API_TOKEN\u0026#34;) ADMIN_ID = os.getenv(\u0026#34;ADMIN_ID\u0026#34;) 并在代码中加入缺失检查，避免服务看似启动但实际没配置成功。\n2. 增加依赖和忽略规则 为了让 VPS 能自动安装环境，仓库中补充了：\nrequirements.txt .env.example .gitignore 其中：\n.env 不提交到 GitHub .env.example 只保留变量模板 .venv、缓存文件、流量统计文件也不提交 3. /status 输出内容扩展 状态信息最终包含：\n服务器状态 CPU 内存 Ping值 下载速度 今日流量 近三十日流量 同时输出文案统一改为了中文，方便直接在 Telegram 中查看。\n4. 下载速度测量逻辑优化 最开始下载速度依赖后台流量线程定期更新，容易显示为 0，因为：\n用户发 /status 时不一定正好处于采样更新窗口 60 秒周期过长，不适合展示瞬时速度 后来改成在执行 /status 时做一次短时实时采样：\n在很短时间内读取两次 psutil.net_io_counters().bytes_recv 根据时间差和接收字节差计算下载速度 这样更适合展示“当前时刻的大致下载速度”。\n四、GitHub 端配置思路 1. GitHub Actions 的作用 GitHub Actions 的职责是：\n在代码 push 到主分支时自动触发 通过 SSH 登录 VPS 在 VPS 上执行部署脚本 2. 使用的关键 Secrets 仓库中配置了这些 GitHub Secrets：\nVPS_HOST VPS_USER VPS_SSH_KEY VPS_PORT VPS_REPO_DIR 其中：\nVPS_USER 最终使用的是 root VPS_REPO_DIR 为 /home/debian/apps/VPS-telegram 3. 为什么不是直接用 debian SSH 登录 实际环境中只有 root 有 SSH 登录能力，因此最终采用：\nroot 负责 SSH 登录 VPS 登录后通过 sudo -u debian -H bash -lc 切换到 debian 由 debian 执行真正的更新操作 这是这次部署成功的关键调整之一。\n五、VPS 端配置思路 1. 为什么需要普通用户 debian 虽然一开始很多操作都是用 root 完成的，但应用最终不应该一直用 root 运行。\n使用 debian 的好处：\n权限更小，更安全 项目目录归属清晰 便于管理 .env、.venv、git 仓库 避免 Git 出现 dubious ownership 问题 2. 项目目录 项目最终放在：\n1 /home/debian/apps/VPS-telegram 这个目录及其内容最终应归：\n1 debian:debian 3. VPS 访问 GitHub 的 deploy key GitHub Action 能登录 VPS，不代表 VPS 自己能从 GitHub 拉代码。\n因此还需要给 debian 单独配置一把 SSH key，用于：\n1 2 git fetch git pull 这把 key 的公钥加到 GitHub 仓库的：\nSettings -\u0026gt; Deploy keys\n它的作用是：\n允许 VPS 上的 debian 用户访问 GitHub 仓库 不负责登录 VPS 4. .env 文件在哪里写 真实凭据不应该提交到 GitHub，因此 .env 应直接写在 VPS 项目目录中：\n1 /home/debian/apps/VPS-telegram/.env 例如：\n1 2 3 API_TOKEN=your_bot_token ADMIN_ID=your_telegram_id PING_TARGET=www.sjtu.edu.cn 5. Python 虚拟环境 在 VPS 上使用：\n1 python3 -m venv .venv 然后安装依赖：\n1 .venv/bin/pip install -r requirements.txt 这样 bot 的依赖和系统 Python 隔离，后续维护更稳定。\n六、systemd 服务配置思路 1. 为什么要用 systemd 因为 bot 需要长期运行，仅靠前台执行 python main.py 不够稳定。\n使用 systemd 的好处：\n开机自启 进程异常退出后自动重启 日志可以用 journalctl 查看 便于 GitHub Action 自动重启服务 2. 服务实际配置 最终服务大致为：\n1 2 3 4 5 6 [Service] User=debian WorkingDirectory=/home/debian/apps/VPS-telegram EnvironmentFile=/home/debian/apps/VPS-telegram/.env ExecStart=/home/debian/apps/VPS-telegram/.venv/bin/python /home/debian/apps/VPS-telegram/main.py Restart=always 这表示：\nbot 由 debian 用户运行 从项目目录读取 .env 使用虚拟环境中的 Python 启动 七、自动部署脚本思路 1. GitHub Actions 工作流 工作流负责：\ncheckout 仓库 通过 SSH 登录 VPS 进入项目目录 调用部署脚本 2. VPS 更新脚本 更新脚本负责：\ngit fetch origin main git checkout main git reset --hard origin/main 安装或更新依赖 systemctl restart vps-telegram-bot.service 这样每次 push 后，VPS 都会自动同步到远端最新版本。\n八、这次实际遇到的问题与解决方式 1. GitHub Action 缺少 VPS_HOST 现象：\nAction 报错 missing server host 原因：\nGitHub Secrets 中缺少 VPS_HOST 解决：\n在仓库 Secrets 中补充 VPS_HOST 2. GitHub Action 缺少 VPS_REPO_DIR 现象：\n报错 VPS_REPO_DIR secret is not set 解决：\n在仓库 Secrets 中增加 VPS_REPO_DIR=/home/debian/apps/VPS-telegram 3. Git 报 dubious ownership 现象：\n部署时 Git 认为仓库目录所有权可疑 原因：\nSSH 登录用户和项目目录拥有者不一致 解决：\n保持项目目录归 debian GitHub Action 登录后切换到 debian 执行部署 4. debian 无法从 GitHub 拉代码 现象：\ngit@github.com: Permission denied (publickey) 原因：\ndebian 用户没有配置访问 GitHub 的 deploy key 解决：\n为 debian 单独生成 GitHub key 将公钥加入仓库 Deploy keys 5. sudo systemctl restart 要密码 现象：\ndebian 执行重启服务时要求输入密码 原因：\n无人值守部署时，debian 没有免密码执行 systemctl 的权限 解决：\n在 visudo 中添加 sudoers 规则 例如：\n1 debian ALL=(ALL) NOPASSWD: /bin/systemctl 6. GitHub Action 成功但 Telegram 没显示新字段 现象：\nAction 成功，但 /status 仍显示旧内容 原因：\n仅 git fetch 了代码，没有真正切换到新版本或服务未重启 解决：\n确认执行 git reset --hard origin/main 然后重启 vps-telegram-bot.service 7. 下载速度一直为 0 原因：\n旧逻辑依赖后台周期更新，不适合瞬时显示 解决：\n改成 /status 时实时短时采样下载速度 九、最终日常使用方式 以后日常更新流程如下：\n本地更新代码 1 2 3 git add . git commit -m \u0026#34;your update\u0026#34; git push origin main GitHub 自动部署 push 后会自动：\n触发 GitHub Action SSH 登录 VPS 切换到 debian 拉取最新代码 更新依赖 重启 bot 服务 Telegram 中使用 在 Telegram 中向 bot 发送：\n1 /status 即可查看当前 VPS 状态。\n十、常用排查命令 查看服务状态 1 systemctl status vps-telegram-bot.service --no-pager 查看最近日志 1 journalctl -u vps-telegram-bot.service -n 100 --no-pager 实时查看日志 1 journalctl -u vps-telegram-bot.service -f 查看当前代码是否已更新 1 2 cd /home/debian/apps/VPS-telegram git log -1 --oneline 手动同步到最新代码 1 sudo -u debian -H bash -lc \u0026#39;cd /home/debian/apps/VPS-telegram \u0026amp;\u0026amp; git fetch origin main \u0026amp;\u0026amp; git reset --hard origin/main\u0026#39; 手动重启 bot 服务 1 sudo systemctl restart vps-telegram-bot.service 十一、这套方案的最终优点 这次搭好的方案有这些明显优点：\n代码修改后只需要 push，不需要手动登录 VPS 逐次更新 凭据从代码中分离，安全性更高 项目由普通用户 debian 运行，更安全 使用 systemd，稳定性高，支持自动重启 日志和服务状态清晰，便于维护 Telegram 中可以方便地直接查看 VPS 运行状态 十二、后续可继续优化的方向 后续如果继续增强，可以考虑加入：\n磁盘占用 系统负载 在线时长 定时日报 网络入站速度和出站速度同时展示 多个 ping 目标对比 命令白名单和更丰富的权限控制 这份笔记的目的，是把这次从零到跑通的实际过程完整记录下来，后续无论迁移服务器、重新部署，还是继续扩展功能，都可以直接以此为基准继续维护。\n","date":"2026-04-30T00:00:00Z","image":"/p/vps-telegram-bot-note/cover.png","permalink":"/p/vps-telegram-bot-note/","title":"VPS-telegram 配置笔记"},{"content":"这篇笔记完整记录了我把一个本地 Hugo 博客部署到 VPS 的全过程，包括：\n本地 Hugo 模板初始化与基础配置 GitHub 仓库与 Actions 自动部署 VPS 上的目录规划与 Nginx 配置 域名解析与 HTTPS 证书申请 常见问题排查 这个过程中经常使用的命令与含义 最终实现的效果是：本地写文章并提交到 GitHub 后，GitHub Actions 自动构建静态站点并发布到 VPS，网页内容自动更新。\n一、目标与整体思路 我想实现的目标非常明确：\n在本地维护 Hugo 博客内容 通过 git push 将更新提交到 GitHub GitHub Actions 自动构建 Hugo 站点 将生成后的静态文件自动同步到 VPS 由 Nginx 直接对外提供网页服务 配置 HTTPS，让 phyweicheng.com 和 www.phyweicheng.com 都可以安全访问 整个流程可以概括为：\n1 2 3 4 5 6 7 本地修改文章 -\u0026gt; git commit / git push -\u0026gt; GitHub Actions 自动构建 Hugo -\u0026gt; 通过 SSH/SCP 上传静态文件到 VPS -\u0026gt; VPS 切换 current 软链接到最新版本 -\u0026gt; Nginx 读取 current 目录 -\u0026gt; 网站自动更新 二、本地 Hugo 模板部署 1. 准备 Hugo 博客项目 首先需要在本地准备一个 Hugo 项目。我这里使用的是 Hugo Theme Stack 模板，并保留了标准的 Hugo 目录结构，例如：\n1 2 3 4 5 6 config/ content/ layouts/ assets/ themes/ .github/ 其中最关键的是：\ncontent/：文章内容 config/：站点配置 .github/workflows/：GitHub Actions 自动部署配置 2. 设置生产站点地址 在 Hugo 中，生产站点的域名需要通过 baseurl 指定。我在配置文件中设置为：\n1 baseurl = \u0026#34;https://www.phyweicheng.com/\u0026#34; 对应文件：\nconfig/_default/config.toml 这个值非常重要，因为 Hugo 在构建时会根据它生成页面中的链接、资源路径和 canonical URL。\n3. 保持 public/ 不提交到仓库 Hugo 构建后的静态文件默认输出到 public/。这部分不需要提交到 GitHub，而是交给 GitHub Actions 自动构建。\n因此 .gitignore 中应当忽略：\n1 public 这样可以避免把编译产物混入源码仓库。\n三、将博客与 GitHub 仓库连接 1. 初始化 Git 仓库并提交 如果项目还没有连接 GitHub，基本流程如下：\n1 2 3 4 5 6 git init git add . git commit -m \u0026#34;init hugo blog\u0026#34; git branch -M main git remote add origin \u0026lt;your-github-repo-url\u0026gt; git push -u origin main 完成后，本地博客项目就与 GitHub 仓库建立了连接。\n2. 为什么要用 GitHub Actions 这里没有选择“在 VPS 上直接 git pull 并构建 Hugo”，而是选择让 GitHub Actions 来完成构建，原因是：\nVPS 不需要安装 Hugo VPS 不需要保留完整源码仓库 构建过程统一由 GitHub 执行，结果更稳定 VPS 只保存最终静态文件，更适合静态站点部署 四、配置 GitHub Actions 自动部署 1. 自动部署工作流的目标 自动部署的核心思路是：\n当 main 分支收到新的提交 GitHub Actions 自动运行 Hugo 构建 将构建好的静态文件打包 通过 SSH 上传到 VPS 在 VPS 上解压并切换到最新版本 2. 工作流文件位置 自动部署工作流文件放在：\ndeploy-vps.yml 这个工作流主要做了下面几件事：\ncheckout 当前仓库 安装 Go 安装 Hugo Extended 执行 hugo --gc --minify 将 public/ 打成压缩包 使用 appleboy/scp-action 上传到 VPS 使用 appleboy/ssh-action 在 VPS 上切换部署版本 3. 为什么要用 releases 和 current 在 VPS 上，我采用了这样的目录结构：\n1 2 3 /var/www/phyweicheng-blog/ releases/ current -\u0026gt; /var/www/phyweicheng-blog/releases/\u0026lt;commit-sha\u0026gt; 每次发布都会：\n生成一个新的版本目录 把静态文件解压进去 将 current 软链接切换到新版本 这种方案的好处是：\n发布过程清晰 方便回滚 Nginx 永远读取固定路径 current 五、配置 GitHub Actions 所需 Secrets 在 GitHub 仓库中进入：\n1 Settings -\u0026gt; Secrets and variables -\u0026gt; Actions 然后新增或确认以下 Repository secrets：\n1. VPS_IP VPS 的公网 IP 地址。\n2. VPS_USER GitHub Actions 登录 VPS 时使用的 Linux 用户名。\n如果直接使用 root 登录，可以写：\n1 root 3. VPS_PORT SSH 登录端口。如果服务器 SSH 不是默认的 22，这里需要填实际端口，例如：\n1 6626 4. VPS_DEPLOY_PATH 站点部署目录，例如：\n1 /var/www/phyweicheng-blog 5. SSH_PRIVATE_KEY GitHub Actions 用于连接 VPS 的私钥内容。通常需要把本地或服务器生成的一对 SSH 密钥中的私钥完整粘贴到这里。\n六、VPS 上的目录准备 1. 创建部署目录 如果使用 root 部署，可以直接执行：\n1 2 sudo mkdir -p /var/www/phyweicheng-blog/releases sudo chown -R root:root /var/www/phyweicheng-blog 如果使用普通用户，例如 deploy，则应改为：\n1 sudo chown -R deploy:deploy /var/www/phyweicheng-blog 2. 检查 SSH 连通性 在正式自动部署前，最好先确认 GitHub 与 VPS 的 SSH 连通已经打通。\n如果 GitHub Actions 能顺利执行远程命令，说明：\nVPS_IP 正确 VPS_USER 正确 SSH_PRIVATE_KEY 正确 VPS_PORT 正确 七、配置 Nginx 站点 1. 站点配置文件 我的 VPS 没有使用 sites-available / sites-enabled 结构，因此采用了 conf.d 的方式。\n配置文件路径：\n1 /etc/nginx/conf.d/phyweicheng-blog.conf 2. 初始 HTTP 配置 在申请证书之前，先配置 HTTP 站点：\n1 2 3 4 5 6 7 8 9 10 11 server { listen 80; server_name phyweicheng.com www.phyweicheng.com; root /var/www/phyweicheng-blog/current; index index.html; location / { try_files $uri $uri/ =404; } } 保存后执行：\n1 2 sudo nginx -t sudo systemctl reload nginx 只要域名已经解析到 VPS，这时就应该能通过 HTTP 访问网页。\n八、域名解析配置 为了让两个域名都能访问，需要在域名服务商后台配置 DNS 解析。\n推荐至少配置两条 A 记录：\n1 2 A @ -\u0026gt; VPS公网IP A www -\u0026gt; VPS公网IP 含义如下：\n@ 表示裸域 phyweicheng.com www 表示 www.phyweicheng.com 如果只配置了 www，那么通常只有 www.phyweicheng.com 可以访问，而 phyweicheng.com 无法访问。\n九、申请 HTTPS 证书 1. 安装 Certbot 我的系统是 Debian，因此安装方式如下：\n1 2 sudo apt update sudo apt install certbot python3-certbot-nginx -y 2. 申请两个域名的证书 执行：\n1 sudo certbot --nginx -d phyweicheng.com -d www.phyweicheng.com 这个命令会完成以下工作：\n申请 Let’s Encrypt 证书 自动修改 Nginx 配置 为 phyweicheng.com 和 www.phyweicheng.com 绑定证书 可选开启 HTTP 到 HTTPS 的自动跳转 3. 证书申请后检查 申请完成后，可以执行：\n1 2 3 4 sudo nginx -t sudo systemctl reload nginx sudo ls -la /etc/letsencrypt/live/ sudo ls -la /etc/letsencrypt/live/phyweicheng.com/ 如果能看到：\nfullchain.pem privkey.pem 说明证书已经生成成功。\n十、HTTPS 访问失败时的一个真实问题 在本次部署过程中，曾经出现过一个很典型的问题：\n证书申请成功 Nginx 配置正常 但浏览器访问 https://www.phyweicheng.com 仍然失败 最后排查发现，并不是证书有问题，而是：\n1 443 端口被 xray 占用了 通过以下命令可以检查是谁占用了 443：\n1 sudo ss -tlnp | grep 443 如果看到是 xray 而不是 nginx，就说明 HTTPS 请求没有交给 Nginx 处理。\n解决方法是：\n修改 xray 配置，将其监听端口从 443 改到其他端口，例如 8443 在客户端同步修改 xray 端口 放行新的端口，例如 8443 重新启动 xray 和 nginx 相关命令例如：\n1 2 3 4 sudo systemctl restart xray sudo systemctl restart nginx sudo ufw allow 8443/tcp sudo ufw reload 十一、自动续期是否会生效 只要使用的是 Debian 上标准安装的 certbot，通常会自动配置续期机制。\n可以通过以下命令检查：\n1 2 3 systemctl list-timers | grep certbot sudo systemctl status certbot.timer sudo certbot renew --dry-run 它们的作用分别是：\n查看是否存在 Certbot 定时任务 查看续期定时器是否已启用 模拟一次证书续期，确认自动续期链路没问题 十二、首次上线后的完整发布流程 当以上步骤全部完成后，后续每次更新博客的流程就非常简单：\n1. 在本地修改文章或页面 例如新建文章、修改配置、调整样式等。\n2. 本地测试 Hugo 1 hugo server 或者正式构建检查：\n1 hugo --gc --minify 3. 提交到 GitHub 1 2 3 git add . git commit -m \u0026#34;update blog post\u0026#34; git push origin main 4. GitHub Actions 自动执行部署 推送成功后，GitHub 会自动：\n构建 Hugo 站点 上传 public/ 产物到 VPS 切换 current 到最新版本 5. Nginx 自动提供新内容 因为 Nginx 的 root 始终指向：\n1 /var/www/phyweicheng-blog/current 所以网站内容会自动更新，不需要登录 VPS 手动 git pull 或手动复制文件。\n十三、常见问题总结 1. GitHub Actions checkout 失败 可能原因：\n主题目录存在残留 submodule 信息 仓库里有 gitlink，但没有 .gitmodules 解决思路：\n不依赖错误的 submodule checkout 改用 Hugo module 的方式引入主题 2. phyweicheng.com 能否访问取决于 DNS 如果只有 www.phyweicheng.com 能访问，通常是因为只解析了 www，没有解析裸域 @。\n3. https:// 无法访问但 http:// 正常 重点检查：\nNginx 是否监听 443 443 是否被其他程序占用 防火墙是否放行 443 云厂商安全组是否放行 443 4. xray 改端口后仍然无法连接 重点检查：\nxray 是否真的监听了新端口 ufw 和云安全组是否放行新端口 客户端端口是否同步修改 十四、这个过程中常用的命令汇总 下面把整个部署过程中最常用的命令整理一下。\n1. Git 相关 1 git add . 将当前修改加入暂存区。\n1 git commit -m \u0026#34;message\u0026#34; 生成一次本地提交记录。\n1 git push origin main 将本地提交推送到 GitHub 的 main 分支，并触发自动部署。\n2. Hugo 相关 1 hugo server 在本地启动开发服务器，方便预览博客效果。\n1 hugo --gc --minify 执行正式构建：\n--gc：清理无用缓存和资源 --minify：压缩输出内容 3. Nginx 相关 1 sudo nginx -t 检查 Nginx 配置语法是否正确。\n1 sudo systemctl reload nginx 平滑重载 Nginx 配置，不中断服务。\n1 sudo systemctl restart nginx 重启 Nginx 服务，适合在重大配置变化后使用。\n4. Certbot 相关 1 sudo certbot --nginx -d phyweicheng.com -d www.phyweicheng.com 为两个域名申请证书，并自动写入 Nginx HTTPS 配置。\n1 sudo certbot renew --dry-run 模拟续期，验证自动续期逻辑是否正常。\n5. 端口与服务排查 1 sudo ss -tlnp | grep 443 检查谁正在监听 443 端口。\n1 sudo systemctl status nginx 查看 Nginx 当前状态。\n1 sudo systemctl status xray 查看 xray 当前状态。\n1 sudo journalctl -u xray -n 50 --no-pager 查看 xray 最近的日志，便于定位启动失败和配置错误。\n6. 防火墙相关 1 sudo ufw status 查看当前防火墙规则。\n1 2 sudo ufw allow 80/tcp sudo ufw allow 443/tcp 分别放行 HTTP、HTTPS 和 xray 的自定义端口。\n1 sudo ufw reload 重新加载防火墙规则。\n7. 证书文件检查 1 2 sudo ls -la /etc/letsencrypt/live/ sudo ls -la /etc/letsencrypt/live/phyweicheng.com/ 确认证书目录和文件是否存在。\n十五、总结 这次部署的关键点并不只是“把网页放上去”，而是把整套链路打通：\n本地负责内容维护 GitHub 负责自动构建与自动发布 VPS 负责稳定托管静态文件 Nginx 负责网站访问 Certbot 负责 HTTPS 当这套流程跑通以后，博客维护就会轻松很多。以后更新文章时，只需要：\n1 2 3 git add . git commit -m \u0026#34;update note\u0026#34; git push origin main 剩下的构建、上传、切换版本、网页更新，都会自动完成。\n如果以后继续扩展，还可以在这套基础上增加：\n评论系统 访问统计 备份与回滚脚本 多环境部署 自动化健康检查 但对于一个 Hugo 静态博客来说，这样的部署方案已经足够稳定、清晰，也非常适合长期维护。\n","date":"2026-04-28T00:00:00Z","image":"/p/hugo-vps-auto-deploy/cover.png","permalink":"/p/hugo-vps-auto-deploy/","title":"Hugo 博客自动部署到 VPS 的完整流程记录"},{"content":"This article offers a sample of basic Markdown syntax that can be used in Hugo content files, also it shows whether basic HTML elements are decorated with CSS in a Hugo theme.\nHeadings The following HTML \u0026lt;h1\u0026gt;—\u0026lt;h6\u0026gt; elements represent six levels of section headings. \u0026lt;h1\u0026gt; is the highest section level while \u0026lt;h6\u0026gt; is the lowest.\nH1 H2 H3 H4 H5 H6 Paragraph Xerum, quo qui aut unt expliquam qui dolut labo. Aque venitatiusda cum, voluptionse latur sitiae dolessi aut parist aut dollo enim qui voluptate ma dolestendit peritin re plis aut quas inctum laceat est volestemque commosa as cus endigna tectur, offic to cor sequas etum rerum idem sintibus eiur? Quianimin porecus evelectur, cum que nis nust voloribus ratem aut omnimi, sitatur? Quiatem. Nam, omnis sum am facea corem alique molestrunt et eos evelece arcillit ut aut eos eos nus, sin conecerem erum fuga. Ri oditatquam, ad quibus unda veliamenimin cusam et facea ipsamus es exerum sitate dolores editium rerore eost, temped molorro ratiae volorro te reribus dolorer sperchicium faceata tiustia prat.\nItatur? Quiatae cullecum rem ent aut odis in re eossequodi nonsequ idebis ne sapicia is sinveli squiatum, core et que aut hariosam ex eat.\nBlockquotes The blockquote element represents content that is quoted from another source, optionally with a citation which must be within a footer or cite element, and optionally with in-line changes such as annotations and abbreviations.\nBlockquote without attribution Tiam, ad mint andaepu dandae nostion secatur sequo quae. Note that you can use Markdown syntax within a blockquote.\nBlockquote with attribution Don\u0026rsquo;t communicate by sharing memory, share memory by communicating.\n— Rob Pike1\nTables Tables aren\u0026rsquo;t part of the core Markdown spec, but Hugo supports supports them out-of-the-box.\nName Age Bob 27 Alice 23 Inline Markdown within tables Italics Bold Code italics bold code A B C D E F Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus ultricies, sapien non euismod aliquam, dui ligula tincidunt odio, at accumsan nulla sapien eget ex. Proin eleifend dictum ipsum, non euismod ipsum pulvinar et. Vivamus sollicitudin, quam in pulvinar aliquam, metus elit pretium purus Proin sit amet velit nec enim imperdiet vehicula. Ut bibendum vestibulum quam, eu egestas turpis gravida nec Sed scelerisque nec turpis vel viverra. Vivamus vitae pretium sapien Code Blocks Code block with backticks 1 2 3 4 5 6 7 8 9 10 \u0026lt;!doctype html\u0026gt; \u0026lt;html lang=\u0026#34;en\u0026#34;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;Example HTML5 Document\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;p\u0026gt;Test\u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; Code block indented with four spaces \u0026lt;!doctype html\u0026gt; \u0026lt;html lang=\u0026quot;en\u0026quot;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026quot;utf-8\u0026quot;\u0026gt; \u0026lt;title\u0026gt;Example HTML5 Document\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;p\u0026gt;Test\u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; Diff code block 1 2 3 4 5 [dependencies.bevy] git = \u0026#34;https://github.com/bevyengine/bevy\u0026#34; rev = \u0026#34;11f52b8c72fc3a568e8bb4a4cd1f3eb025ac2e13\u0026#34; - features = [\u0026#34;dynamic\u0026#34;] + features = [\u0026#34;jpeg\u0026#34;, \u0026#34;dynamic\u0026#34;] One line code block 1 \u0026lt;p\u0026gt;A paragraph\u0026lt;/p\u0026gt; List Types Ordered List First item Second item Third item Unordered List List item Another item And another item Nested list Fruit Apple Orange Banana Dairy Milk Cheese Other Elements — abbr, sub, sup, kbd, mark GIF is a bitmap image format.\nH2O\nXn + Yn = Zn\nPress CTRL + ALT + Delete to end the session.\nMost salamanders are nocturnal, and hunt for insects, worms, and other small creatures.\nThe above quote is excerpted from Rob Pike\u0026rsquo;s talk during Gopherfest, November 18, 2015.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2023-09-07T00:00:00Z","permalink":"/p/markdown-syntax-guide/","title":"Markdown Syntax Guide"},{"content":"Hugo theme Stack supports the creation of interactive image galleries using Markdown. It\u0026rsquo;s powered by PhotoSwipe and its syntax was inspired by Typlog.\nTo use this feature, the image must be in the same directory as the Markdown file, as it uses Hugo\u0026rsquo;s page bundle feature to read the dimensions of the image. External images are not supported.\nSyntax 1 ![Image 1](1.jpg) ![Image 2](2.jpg) Result Photo by mymind and Luke Chesser on Unsplash\n","date":"2023-08-26T00:00:00Z","image":"/p/image-gallery/2.jpg","permalink":"/p/image-gallery/","title":"Image gallery"},{"content":"For more details, check out the documentation.\nBilibili video Tencent video YouTube video Generic video file Your browser doesn't support HTML5 video. Here is a link to the video instead. GitLab Quote Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n― A famous person, The book they wrote Photo by Codioful on Unsplash\n","date":"2023-08-25T00:00:00Z","image":"/p/shortcodes/cover.jpg","permalink":"/p/shortcodes/","title":"Shortcodes"},{"content":"Stack has built-in support for math typesetting using KaTeX.\nIt\u0026rsquo;s not enabled by default side-wide, but you can enable it for individual posts by adding math: true to the front matter. Or you can enable it side-wide by adding math = true to the params.article section in config.toml.\nInline math This is an inline mathematical expression: $\\varphi = \\dfrac{1+\\sqrt5}{2}= 1.6180339887…$\n1 $\\varphi = \\dfrac{1+\\sqrt5}{2}= 1.6180339887…$ Block math $$ \\varphi = 1+\\frac{1} {1+\\frac{1} {1+\\frac{1} {1+\\cdots} } } $$ 1 2 3 $$ \\varphi = 1+\\frac{1} {1+\\frac{1} {1+\\frac{1} {1+\\cdots} } } $$ $$ f(x) = \\int_{-\\infty}^\\infty\\hat f(\\xi)\\,e^{2 \\pi i \\xi x}\\,d\\xi $$ 1 2 3 $$ f(x) = \\int_{-\\infty}^\\infty\\hat f(\\xi)\\,e^{2 \\pi i \\xi x}\\,d\\xi $$ ","date":"2023-08-24T00:00:00Z","permalink":"/p/math-typesetting/","title":"Math Typesetting"}]