ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

个人主页部署Hetzner服务器:选型、配置与运维全攻略

个人主页部署Hetzner服务器:选型、配置与运维全攻略 个人主页这东西做起来容易放哪儿却是个反复纠结的问题。我最早的版本挂在共享虚拟主机上一年几十块钱除了慢点也没什么毛病后来页面里塞了自建的小接口、统计脚本、还有一个定时抓数据的任务共享主机立刻就不够用了PHP 版本锁死、没有 root、装不了常驻进程。折腾过一阵静态托管平台构建确实省心但我想在同一台机器上跑点自己的东西时又被平台边界卡住了。最后我把整站迁到了 Hetzner 的一台小实例上从开机到 HTTPS 自动续期到后面一年多断断续续的维护踩的坑不算少。这篇就把这套个人主页部署到 Hetzner 服务器的完整过程拆开讲清楚包括选型时怎么算账、开机后第一小时该做什么、Nginx 怎么配才不别扭、部署链路用哪种最省心。不管你是刚买了云服务器的新手还是想把自己那台闲置实例重新利用起来的老手下面这些内容应该都能直接用。1. 先把需求摆清楚个人主页到底需不需要一台独立服务器1.1 个人主页的流量画像和资源画像先做个老实的需求盘账。绝大多数个人主页的访问模式是白天零星几个访问搜索引擎爬虫偶尔来一趟某个文章被转发时突然涌进来几百个 IP之后又归于平静。这种流量曲线有两个特征一是峰值和日均差距极大二是绝大部分请求都是静态文件HTML、CSS、JS、字体、图片加上偶尔的接口调用。按这个画像去推资源需求结论会很反直觉CPU 几乎不重要1 核 2 核都够内存才是关键因为 Nginx、系统服务、可能还有一个小数据库或者 Node 进程2GB 是比较舒服的底线1GB 在构建站点时容易 OOM磁盘 20GB 起步站点本身撑死几百 MB真正吃空间的是日志、系统更新缓存和你随手丢上去的备份文件带宽反而是个人站最容易高估的指标一篇 2MB 的文章页被 500 个人同时打开也就 1GB 流量一个月跑下来可能连 50GB 都用不掉。所以真正的需求不是多强的算力而是一台能长期稳定在线、有公网 IP、我能完全控制、价格不会让我心疼的 Linux 机器。理解这一点之后选型就不会被各种高性能云服务器的宣传带着跑了。1.2 PaaS、共享主机、VPS 三方对照我把当时考虑过的三类方案列成一张表方便你直接对照自己的情况维度静态托管 PaaS共享虚拟主机独立 VPS上手成本极低Git 一推就完事低面板点点点中需要懂 Linux控制权几乎为零受限很多目录碰不了完整 root能否跑常驻进程不能或受限一般不能可以自定义端口与服务不行不行随便成本量级免费到每月几美元每年几十元起每月几美元起长期维护负担平台代管平台代管全靠自己数据迁移自由度低低高打包就走结论很明确如果你的主页纯静态、没有任何后端需求PaaS 是最省事的答案没必要为了折腾而折腾。但只要出现下面任意一条VPS 的性价比就会迅速反超——想跑一个自己的 API、想做定时任务、想装某种常驻监控、想让某个端口对外提供服务、想自己掌握数据库文件。我当时就是因为最后几条选的 VPS。代价也认了系统更新、证书续期、日志清理、备份全得自己盯着。这个代价是可以被流程化的后面几节讲的就是怎么把它压到最低。1.3 Hetzner 在这张牌桌上的位置选 Hetzner 的理由很朴素同等配置下价格明显更低而且按小时计费、按月封顶开一台试玩一周的成本几乎可以忽略。它的产品线里和放个人主页直接相关的是 Cloud 实例虚拟机、Storage Box大容量存储适合放备份、以及对象存储。机房分布上欧洲有纽伦堡、法尔肯施泰因、赫尔辛基美国有阿什本和希尔斯伯勒亚洲有新加坡。这里必须讲一个很多人忽略的点机房位置直接决定访问延迟。物理距离带来的光信号往返是无法绕开的欧洲机房对亚洲访客来说往返延迟通常在两百毫秒级别而新加坡机房能压到几十毫秒。你可以用ping和curl -w分别测一下目标机房的实测值再决定别只看带宽大不大。另一个要提前想清楚的是免费的 IPv4 时代早就结束了现在开实例默认给的是 IPv6需要 IPv4 的话要额外付费大概每月不到一欧元以官网为准。对个人主页来说 IPv4 基本是必须的因为相当一部分访客网络环境对 IPv6 支持还不完整所以这笔钱别省。2. 实例创建阶段最容易踩的三个坑2.1 机房位置与机型选择ARM 便宜但要注意什么Hetzner 的 Cloud 实例大致分几档共享 vCPU 的 Intel 系列、共享 vCPU 的 AMD 系列、基于 ARM 架构的系列以及独占 vCPU 的系列。对个人主页这种负载ARM 那档的性价比非常夸张两核四核、四 GB 内存起步的价格比同规格 x86 便宜一大截。但便宜有前提这一点必须说清楚ARM 架构意味着你用的所有镜像和二进制都得有 aarch64 版本。绝大多数主流软件早就支持了Nginx、Node、Python、Docker 官方源都没问题。但如果你要用某个只发布了 x86 预编译包的商业软件或者依赖某个小众的闭源二进制就会卡住。更麻烦的是你往往是在部署到一半的时候才发现。我的建议是分两步走如果你确定自己的技术栈全是主流开源组件直接上 ARM省钱又安静如果技术栈里有任何不确定的第三方组件先用 x86 那档跑通全流程确认没问题再考虑迁移。迁移本身很简单把数据打包、DNS 切过去就行不用一开始就赌。还有一种情况要提醒小内存实例在构建阶段特别容易假死。我见过 1GB 内存的机器上一跑前端构建就 SSH 卡住free -h一看 swap 已经打满。这个问题的解法在 3.3 节会讲。2.2 镜像、SSH Key 与首次登录创建实例时官方会提供一批现成镜像Ubuntu、Debian、Fedora、Rocky 等等。个人主页场景我倾向于选 Ubuntu LTS 或 Debian 稳定版理由不是性能而是遇到问题搜得到答案——几乎所有教程、报错讨论、软件官方安装脚本都以这两个发行版为主。第二个要命的选项是认证方式。控制台会让你二选一密码登录或者导入 SSH 公钥。一定要选公钥。如果你勾了密码登录机器开机之后几分钟内就会开始收到暴力破解尝试日志里密密麻麻全是陌生 IP 的失败记录。公钥认证从机制上就杜绝了这条路。公钥怎么生成如果你本地还没有ssh-keygen -t ed25519 -C homepage-deploy生成的默认在~/.ssh/id_ed25519私钥和~/.ssh/id_ed25519.pub公钥。把.pub的内容整段粘贴到控制台的 SSH Key 里注意是pub 那个粘错私钥等于把钥匙照片发给了别人。开好机器后第一次登录ssh -i ~/.ssh/id_ed25519 root你的实例IP第一次连接会提示指纹确认输入 yes 即可。如果你懒得每次带-i可以在本地~/.ssh/config里加一段别名后面所有命令都能简写Host hp HostName 你的实例IP User deploy IdentityFile ~/.ssh/id_ed25519 ServerAliveInterval 60ServerAliveInterval这一行很实用它让客户端定期发心跳避免你挂在那儿十分钟不动就被断开。2.3 计费方式、额度与几个容易被忽略的限制Hetzner Cloud 是按小时计费的但月度有封顶价也就是说你开满一个月不会比标价更贵。这对个人站特别友好临时开一台高配机器做构建或者迁移跑两小时就删掉成本几乎为零。流量方面欧洲和美国机房的额度相当宽松个人主页正常跑完全用不到零头。但有一个坑要记住超出额度的部分是要额外计费的所以如果你的主页里挂了大量图片、视频这类大文件最好走对象存储或者 CDN别全压在实例带宽上。还有一个操作层面的坑实例删除是不可逆的快照是单独计费的删实例不会自动帮你留一份。我有一次手滑删掉了一台测试机第二天想起来里面有份配置文件没备份只能重新写。后来我给自己定了个规矩任何要删的实例先拍快照确认一周内没用到再删快照。另外控制台的重建Rebuild功能会用选定镜像覆盖系统盘数据全丢。它和重置密码是两个完全不同的按钮别点错。3. 开机后第一小时把一台裸机改成能长期跑的状态3.1 普通用户、SSH 加固与 fail2ban第一件事别用 root 干活。创建一个日常账户并给它 sudo 权限adduser deploy usermod -aG sudo deploy然后把 root 那边的 SSH 公钥复制过去这样新用户不用重新生成密钥mkdir -p /home/deploy/.ssh cp ~/.ssh/authorized_keys /home/deploy/.ssh/ chown -R deploy:deploy /home/deploy/.ssh chmod 700 /home/deploy/.ssh chmod 600 /home/deploy/.ssh/authorized_keys这个顺序不能反。一定先确认新用户能用密钥登录成功了再去改 sshd 配置禁用 root 和密码认证否则一旦出问题你就被锁在门外只能去控制台开救援模式。确认新用户能登录后编辑/etc/ssh/sshd_configPermitRootLogin no PasswordAuthentication no PubkeyAuthentication yes改完先别急着重启用sshd -t检查语法通过之后systemctl reload sshd。用 reload 不用 restartreload 不会断开你当前的连接restart 有可能。接着装 fail2ban它的作用是监控日志把反复登录失败的 IP 临时封掉apt install fail2ban -y cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local然后在jail.local里把 sshd 那段的enabled改成true顺手把bantime调长一点。装完之后fail2ban-client status sshd能看到当前封禁数量心里会踏实很多。3.2 两层防火墙控制台防火墙与主机防火墙Hetzner 提供控制台层面的云防火墙规则在网页上配独立于系统内部。它的好处是即使系统里的防火墙规则被误改控制台这层还在挡着属于兜底。我的习惯是两层都开但只在控制台配一次最粗的规则允许 22、80、443 入站其余全部拒绝。系统内部用 ufwufw default deny incoming ufw default allow outgoing ufw allow 22/tcp ufw allow 80/tcp ufw allow 443/tcp ufw enableufw enable之前一定要先放行 22这是最常见的自锁操作。执行完之后用ufw status verbose确认一遍规则列表再另开一个终端测试登录确认通了再关掉原来的窗口。有个细节值得注意如果你之后要给某个服务开新端口比如一个管理面板优先考虑用 Nginx 反代到本机端口而不是直接对外开端口。原因是反代能统一走 443、统一上 TLS、统一做访问控制而裸端口暴露出去之后你还要额外考虑它的认证、日志、更新管理成本翻倍。3.3 交换文件、时区与自动安全更新小内存实例必须配交换文件这不是可选项。做法fallocate -l 2G /swapfile chmod 600 /swapfile mkswap /swapfile swapon /swapfile echo /swapfile none swap sw 0 0 /etc/fstab最后那行写进/etc/fstab是为了开机自动挂载漏了这一步的话下次重启 swap 就没了你会在某次构建时莫名其妙地再次 OOM。配好之后swapon --show确认一下free -h也能看到总量变化。关于 swap 的调优参数vm.swappiness默认值往往偏激进个人站可以调到 10 左右让系统更倾向于留在内存里。方法是在/etc/sysctl.conf里加一行vm.swappiness10然后sysctl -p。时区这件事看着小其实影响很大。服务器默认多半是 UTC如果你不调日志时间、定时任务执行时间、证书续期记录全部会和你本地的直觉差好几个小时。查问题时对着日志一算时差很容易把自己绕晕。设成你习惯的时区timedatectl set-timezone Asia/Shanghai timedatectl最后是自动安全更新。手动apt upgrade这件事一开始你会记得三个月后就忘了。装unattended-upgrades让它自己处理apt install unattended-upgrades -y dpkg-reconfigure --prioritylow unattended-upgrades注意自动更新建议只放行安全源。全量自动升级有可能在半夜悄悄换掉某个组件的版本第二天服务起不来而你还不知道发生了什么。4. Nginx 配置个人主页时的取舍4.1 站点目录结构与 server 块骨架先说目录约定这决定了后面部署脚本怎么写。我不喜欢把文件直接丢在/var/www/html而是用这个结构/var/www/homepage/ ├── releases/ │ ├── 20240501-1200/ │ └── 20240520-0930/ └── current - releases/20240520-0930每个版本一个独立目录current是指向当前生效版本的软链接。这样做的好处在第 5.3 节讲回滚时会体现得非常明显。Nginx 的站点配置放在/etc/nginx/sites-available/下然后软链到sites-enabled/。一个够用的server块骨架长这样server { listen 80; listen [::]:80; server_name example.com www.example.com; root /var/www/homepage/current; index index.html; access_log /var/log/nginx/homepage.access.log; error_log /var/log/nginx/homepage.error.log; location / { try_files $uri $uri/ /index.html; } }改完配置先nginx -t验证通过了再systemctl reload nginx。这个习惯能帮你避免 90% 的改完配置网站直接 502。4.2 SPA 与静态站的路径回退如果你用的是前端框架构建出来的站路由是前端自己管的那就必须处理直接访问子路径直接 404的问题。try_files $uri $uri/ /index.html;这一行就是解法先试着找同名文件找不到目录最后统统回退到index.html交给前端路由处理。但要注意这个回退只对小写、无后缀的路径生效静态资源不应当被回退。所以更好的写法是把资源路径单独拿出来location /assets/ { expires 1y; add_header Cache-Control public, immutable; try_files $uri 404; } location / { try_files $uri $uri/ /index.html; }try_files $uri 404;的意思是找不到就直接返回 404不要回退。这样做的好处是资源缺失会明确报 404而不是返回一坨 HTML 让浏览器在控制台报语法错误。我在早期项目里因为这个坑排查了半天index.html被当成 JS 返回浏览器报的是Unexpected token 看着像语法问题其实是部署路径错了。4.3 缓存策略与响应头让回访变快缓存策略的核心原则只有一句带内容哈希的文件长缓存入口 HTML 绝不缓存。前端构建工具通常会给产物文件名加哈希文件名一变浏览器自然重新拉index.html不带哈希一旦被缓存你更新了内容用户却还看到旧版本会非常难查。location /index.html { add_header Cache-Control no-cache, must-revalidate; } location ~* \.(js|css|woff2|png|jpg|svg)$ { expires 30d; add_header Cache-Control public, immutable; }no-cache不是不缓存它表示可以存但每次必须回源校验。配合 ETag命中时会返回 304既省流量又保证新鲜。这个区别经常被误读值得记一下。安全响应头我个人会加这几条成本很低add_header X-Content-Type-Options nosniff; add_header X-Frame-Options SAMEORIGIN; add_header Referrer-Policy strict-origin-when-cross-origin;再加 gzip 压缩文本类资源体积能砍掉六七成。Nginx 默认就有 gzip 模块在nginx.conf的 http 块里确认gzip on;并补上类型列表即可。压缩级别不建议开到 9CPU 换来的那点体积收益不划算5 到 6 是常见平衡点。5. 把构建产物送上去三种部署链路的对比与实现5.1 本机 rsync最短路径与 --delete 的杀伤力最简单的方式本地构建完直接同步rsync -avz --delete \ --exclude.git \ --excludenode_modules \ ./public/ hp:/var/www/homepage/current/-a保持权限和时间戳-v看进度-z传输压缩。--delete是关键也是危险所在它会删除目标端在源端不存在的文件。用得好它能让服务器和本地完全一致不会留下旧版本的孤儿文件用错一个路径它能把你整个目录清空。我有一次把源路径末尾的斜杠漏了rsync ./public hp:/var/www/current/结果public这个目录本身被复制进去服务器上变成了current/public/而 Nginx 还指向current直接白屏。源路径末尾加斜杠表示复制目录内容不加表示复制目录本身这个区别必须刻在脑子里。更保险的做法是先用-n做一次演练rsync -avzn --delete ./public/ hp:/var/www/homepage/current/-n是 dry run只打印会做什么不动文件。看到输出符合预期再去掉n真跑。这个习惯至少帮我省过两次事故。最后别忘了权限。rsync 过去之后文件属主可能是你的部署用户而 Nginx 通常以www-data运行读不到文件就 403。统一处理ssh hp chown -R www-data:www-data /var/www/homepage/current目录 755、文件 644 是安全且通用的组合。5.2 Git 裸仓库加钩子推代码即上线rsync 的问题是它依赖你本地环境换台电脑就没法部署了。如果你的站点内容是用 Markdown 写的或者本身就在 Git 里用裸仓库加钩子是更优雅的方案。在服务器上建一个裸仓库mkdir -p /srv/repos/homepage.git cd /srv/repos/homepage.git git init --bare然后写钩子/srv/repos/homepage.git/hooks/post-receive#!/bin/bash set -e TARGET/var/www/homepage/current GIT_WORK_TREE$TARGET git checkout -f main chown -R www-data:www-data $TARGET echo deployed at $(date)给执行权限chmod x /srv/repos/homepage.git/hooks/post-receive本地加个 remote 推上去git remote add deploy hp:/srv/repos/homepage.git git push deploy main推完之后钩子自动执行文件就落到站点目录了。GIT_WORK_TREE这个变量是核心它告诉 Git工作区在别处否则 checkout 会试图在裸仓库里展开文件。这个方案有个坑要注意git checkout -f不会删除目标目录里已有的、不在仓库中的文件。如果你希望每次部署都是干净的得在钩子里先清空目录或者干脆结合 5.3 的原子发布方式每次检出到一个新目录。5.3 原子发布与软链接回滚前面 4.1 节提到的 releases 结构配合一个简单的部署脚本就能实现原子发布和秒级回滚。核心思路是每次部署检出到一个带时间戳的新目录构建和权限都处理好之后再把current软链接指过去。#!/bin/bash set -euo pipefail STAMP$(date %Y%m%d-%H%M%S) ROOT/var/www/homepage NEW$ROOT/releases/$STAMP mkdir -p $NEW GIT_WORK_TREE$NEW git --git-dir/srv/repos/homepage.git checkout -f main chown -R www-data:www-data $NEW ln -sfn $NEW $ROOT/current nginx -s reload /dev/null 21 || true # 只保留最近 5 个版本 cd $ROOT/releases ls -1t | tail -n 6 | xargs -r rm -rf echo released $STAMPln -sfn的-n参数很关键它避免在目标已经是软链接时把链接建到链接里面去。这个坑我第一次写的时候踩了结果是current指向current/current越指越深最后 404。回滚就变成一句话ln -sfn /var/www/homepage/releases/20240501-1200 /var/www/homepage/current出问题的时候几秒钟切回上一个版本服务不用重启用户几乎无感。这个能力在半夜出故障时的价值远超你为它多写的那二十行脚本。5.4 自动化到什么程度才合适有人会接着上 CI比如在代码仓库里配一个工作流push 之后自动构建、rsync 到服务器。这当然可以而且体验很好。但对个人主页来说要先想清楚两个问题。第一是构建环境一致性。前端构建对 Node 版本敏感本地能跑通不代表 CI 能跑通。如果本地和 CI 版本不一致你会遇到本地好好的CI 报错的经典问题。解决办法是把 Node 版本固定在项目配置文件里两边都读它。第二是密钥管理。CI 要往服务器推文件就得有私钥。私钥放在仓库的 secrets 里谁有仓库写权限谁就能拿到。个人项目问题不大但要知道这个风险的存在。更稳妥的做法是给部署专门生成一对密钥只允许它做必要的操作出了事可以单独吊销。我的实际选择是内容型站点用裸仓库钩子代码型站点用本地构建加 rsyncCI 只用来跑检查和构建不直接部署。这样既省心又把最危险的那把钥匙留在了自己手里。6. HTTPS 与证书续期那些证书明明签了却没生效的情况6.1 证书签发方式怎么选现在有 Lets Encrypt 这类免费证书流程已经很成熟。装完 certbot 之后最省事的是 Nginx 插件模式apt install certbot python3-certbot-nginx -y certbot --nginx -d example.com -d www.example.com它会自动读你的 Nginx 配置、自动加证书路径、自动配 80 到 443 的跳转。原因是它走的是 http-01 验证也就是在 80 端口放一个临时文件让验证服务器来读。所以签发之前必须保证 80 端口是通的且域名已经解析到这台机器。如果 80 端口因为某些原因不方便开放可以用 DNS 验证方式通过在域名服务商那边加一条 TXT 记录来完成验证。这种方式的好处是支持泛域名证书比如*.example.com一张就够还能在证书里包含多台机器很适合有多台服务的场景。缺点是续期时如果用了通配符需要保留某种凭证来做自动验证配置起来稍微绕一点。我个人的经验单机单域名就用插件模式多机或者泛域名再考虑 DNS 方式。前者五分钟搞定后者要半小时但后者一次配置长期受益。6.2 自动续期到底有没有真的生效签完之后第一件要做的事不是庆祝而是验证自动续期。certbot 会自己装一个 systemd 定时器检查一下它在不在systemctl list-timers | grep certbot然后手动跑一次演练certbot renew --dry-run这个命令会模拟一次完整的续期过程但不写入真实证书。它是唯一能让你在证书到期前发现问题的办法。很多人装完就忘了八十天后突然收到一封证书即将过期的邮件才慌起来那时距离过期可能只剩两三天而问题如果是端口或者 DNS 引起的排查时间会很紧张。我一般还会加一个提醒给自己设一个日历在证书签发后第 60 天看一眼续期日志。听起来土但确实管用。注意如果你在域名前面套了 CDN 之类的代理服务http-01 验证请求会被代理拦截有可能导致验证失败。这种情况下的稳妥做法是先临时关闭代理、签发完成后再打开或者直接改用 DNS 验证。6.3 页面里的混合内容与 HSTS 的开启时机证书装好之后浏览器地址栏绿了但控制台可能还在报错。最常见的是混合内容问题HTTPS 页面里引用了 HTTP 的资源浏览器会拦截。这种问题往往来自硬编码在模板里的图片地址或者第三方脚本地址要找出来一条条改。可以用浏览器控制台的 Network 面板筛一下把所有 http:// 开头的资源揪出来。全部修干净之后才能考虑上 HSTS。Strict-Transport-Security这个响应头的作用是告诉浏览器以后这个站只用 HTTPS 访问。听起来很好但它有不可逆的副作用一旦浏览器记住了这条指令在有效期内即使你把证书撤掉、退回 HTTP用户也访问不了。所以正确的姿势是先不带preload跑一段时间确认所有子域名的证书都没问题再逐步延长max-age。不要一上来就写个一年加 preload那样出问题的时候会非常难收场。add_header Strict-Transport-Security max-age86400 always;先用一天做观察期稳了再往上加。这个always参数也别忘了它保证错误响应里也带这个头。7. 上线只是开始日志、备份与成本的日常7.1 日志怎么看才有用上线之后你会积累三种日志Nginx 的访问日志和错误日志、系统日志、以及应用自己的日志。全看是不现实的得挑重点。Nginx 错误日志优先看级别告警以上的条目grep -E \[error\]|\[crit\]|\[alert\] /var/log/nginx/homepage.error.log | tail -50访问日志更有意思能看出很多运营层面的问题。比如用一行命令统计访问最多的路径awk {print $7} /var/log/nginx/homepage.access.log | sort | uniq -c | sort -rn | head -20我看这份统计时通常关注三类异常某个不存在路径被反复请求可能是坏链接或有扫描器某个资源请求量异常高可能是被外链或者写错循环以及搜索引擎爬虫的抓取频率是否合理。搜索引擎对个人站的 SEO 影响很大而它们的抓取行为在日志里是清清楚楚的。日志要不要轮转答案是必须。Ubuntu 上的 Nginx 默认就有 logrotate 配置但你要确认一下保留天数和是否压缩。磁盘被日志撑满是个非常经典的低级故障而且发现时通常服务已经挂了。7.2 监控能简就简个人站不需要上一整套 Prometheus 加一堆 exporter那是给多机集群准备的。我自己的配置非常朴素一个轻量的状态检测工具每分钟探一次首页状态码不是 200 或者响应时间超过阈值就通知我再加一个每周发一封邮件报磁盘和内存使用情况的脚本。如果你确实想搭监控面板也建议把监控服务和被监控服务分开部署。同一台机器上跑的监控机器宕机时它自己也挂了等于没监控。可以放在另一台最便宜的实例上或者用第三方的外部探测服务成本很低。磁盘监控的阈值建议设在 80%而不是 95%。原因是留出处理时间看到 80% 的告警你有几天时间从容清理等到 95% 才告警往往是某个服务写不了文件已经报错了。7.3 备份与快照的组合拳备份这件事我的原则是分成两层整机快照和关键数据备份。整机快照适合在重大变更前拍一张比如换系统版本、改分区、装某个侵入性强的服务。它的好处是恢复快坏处是占空间且依赖服务商。关键数据备份则是必须自己能掌控的至少包括站点源码、数据库导出文件、Nginx 配置、证书目录、以及你的部署脚本。自动化的话写个脚本打包关键目录用 rsync 推到另一台机器或者对象存储上加进 crontab 每天跑一次#!/bin/bash STAMP$(date %Y%m%d) tar czf /tmp/backup-$STAMP.tar.gz \ /var/www/homepage/current \ /etc/nginx/sites-available \ /etc/letsencrypt \ /srv/repos rsync -az /tmp/backup-$STAMP.tar.gz remote:/backups/ rm -f /tmp/backup-$STAMP.tar.gz这里有个细节/etc/letsencrypt一定要备份。如果证书目录丢了虽然可以重新签发但频繁重新签发会触发证书颁发机构的速率限制导致你短时间内签不出来只能干等。还有一条经验备份脚本要定期做恢复演练。我遇到过备份文件能生成但解不开的情况原因是打包时磁盘写满导致文件截断。一个没验证过的备份和一个不存在的备份可靠性是一样的。7.4 个人站的成本账最后算一下账因为这也是选 Hetzner 的核心理由之一。一台能舒服跑个人主页的小实例月成本大致在几美元量级加上 IPv4 附加费一年下来可能还没一顿饭贵。Storage Box 用来放备份价格更低容量却很大。省钱有几个实用操作不用的实例及时删掉Hetzner 是按小时计的一台机器开着一星期不用就是浪费用快照代替长期开机需要时从快照恢复大文件走对象存储或者 CDN别让它们占着实例的流量额度。但更值得算的是时间成本。这套流程搭起来大概花我两个晚上之后每月维护时间不到十分钟看一眼磁盘、看一眼续期日志、偶尔更新系统。前期把流程做扎实后期的时间收益是持续的这也是我最终没有选择更省事的平台方案的原因——平台方案省的是部署时间但每次想加点东西都要跟边界较劲长期看反而更费。我在实际运维这台机器的过程中最大的体会是个人主页的部署难点从来不在怎么把文件传上去而在半年后你还记不记得当时怎么传的。所以那些看着多余的东西——带时间戳的发布目录、能随时回滚的软链接、写在脚本里的部署命令、备份里的证书目录——它们的价值都要在出问题的那一天才会真正显现。真到那天你会庆幸自己当初多写了那几行。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进