这是一篇真正按顺序执行的建站操作手册。目标不是把所有程序都塞进一台小服务器,而是用一台 2 核 2G VPS 提供自己的内容后台,再让 Astro 把文章生成静态网页,交给 Cloudflare Pages 和 CDN 承担访问。

完成后,你只需要打开 Directus 写文章并点击发布,网站就会自动重新构建。正文访问不经过 VPS,图片走 R2,服务器只负责后台、数据库和必要的动态接口。

先看最后效果:我们要做成什么样

访客 → example.com → Cloudflare Pages → Astro 静态网页

作者 → admin.example.com → OpenResty → Directus
                                      ↓
                                  PostgreSQL

图片 → img.example.com → Cloudflare R2

运维 → panel.example.com → OpenResty → 1Panel

发布文章 → Directus Flow → Pages Deploy Hook → 自动构建

推荐只保留四个域名:

  • example.com:Astro 主站;
  • admin.example.com:Directus 后台,同时也是 Astro 构建时读取内容的 API;
  • panel.example.com:1Panel 管理面板;
  • img.example.com:R2 图片域名。

本文中的 example.com、VPS_IPV4、YOUR_... 都是占位符,必须替换成自己的值。不要直接照抄示例密码。

先把建站助手下载好(可选)

我把整套流程整理成了一个可被 Agent 调用的 Skill。它会按阶段推进,并在 root 密码、1Panel/Directus 账号、MFA、VNC、Cloudflare 授权和破坏性操作前强制暂停,让用户亲自完成敏感步骤。

下载 build-astro-directus-content-hub.zip

压缩包内包含:

build-astro-directus-content-hub/
├── SKILL.md
└── agents/
    └── openai.yaml

使用方法:解压后把整个 build-astro-directus-content-hub 文件夹放进 Agent 的 Skills 目录。以 Codex 为例,放入 $CODEX_HOME/skills/,重新开启会话后调用:

$build-astro-directus-content-hub

不同 Agent 的 Skills 目录可能不同,请以对应产品文档为准。Skill 不包含任何真实域名、IP、账号、密码或密钥,可以作为自己的建站模板继续修改。

先看操作顺序:照着做就不乱

  1. 第 0 步:准备账号、域名、VNC 和密码管理器;
  2. 第 0.5 步:理解每个组件的职责,此时不修改任何服务器设置;
  3. 第 1 步:在供应商 VNC 修改 root 密码,确认救援入口;
  4. 第 2 步:在本地创建 SSH 密钥,并用第二个窗口验证登录;
  5. 第 3 步:确认密钥可用后,才关闭公网 SSH 密码认证;
  6. 第 4 步:更新系统、添加 Swap、开启 UFW、Fail2ban 和自动更新;
  7. 第 5 步:安装 1Panel 与 Docker,并由用户亲自设置面板账号、密码和安全入口;
  8. 第 6 步:在 Cloudflare 添加 DNS 记录,先灰云排障;
  9. 第 7 步:配置 OpenResty 和源站 HTTPS,确认域名可访问;
  10. 第 8 步:部署 Directus 与 PostgreSQL,确认容器健康;
  11. 第 9 步:创建 R2 Bucket,接入 Directus 图片存储;
  12. 第 10 步:创建文章、分类和公开权限模型;
  13. 第 11 步:开发 Astro 静态前端并在本地构建;
  14. 第 12 步:连接 GitHub,部署 Cloudflare Pages 和主域名;
  15. 第 13 步:用 Directus Flow 连接 Pages Deploy Hook;
  16. 第 14 步:全部域名验证后,关闭 18080 等公网管理端口;
  17. 第 15 步:建立每日备份并做恢复演练;
  18. 第 16 步:进入日常发布和每月巡检。
关键依赖不能颠倒:先验证 SSH 密钥,再关密码登录;先验证 HTTPS 反代,再关闭 18080;先让 Astro 改用新接口并构建成功,再停旧接口;先备份并恢复演练,再考虑升级或删除。

第 0 步:先把账号、域名和工具准备好

开始前准备:

  • Ubuntu 24.04 VPS,建议至少 2C2G、30GB;
  • 一个已经接入 Cloudflare 的域名;
  • GitHub 账号和 Cloudflare 账号,并开启 MFA;
  • Windows 本地电脑,安装 Git、Node.js 22、代码编辑器;
  • 服务器供应商提供的 VNC/网页控制台;
  • 密码管理器。

你会遇到的 7 类凭据

凭据用途应该放在哪里
服务器 root 密码供应商 VNC 应急登录密码管理器,不发给 Agent
SSH 私钥及口令本地远程连接服务器只留在本地电脑
1Panel 用户名和密码登录服务器管理面板由用户在安装提示中亲自设置
Directus 管理员密码登录内容后台由用户在 VNC 中写入受保护配置
PostgreSQL 密码Directus 连接数据库服务器的 600 权限环境文件
R2 Access Key/SecretDirectus 上传图片服务器环境文件,不进 Git
Pages Deploy Hook触发网站构建Directus Flow 内,按密码对待

重要原则:Agent 可以替你检查、部署和验证,但密码、MFA、恢复码和密钥必须由你在 VNC 或官方控制台亲自设置。任何 Agent 都不应该擅自重置你的账号密码。

第 0.5 步:花 3 分钟认识这些工具(先别动服务器)

第一次看见 1Panel、Docker、Directus、PostgreSQL、OpenResty、Astro、R2 和 Cloudflare,很容易觉得是在安装一堆重复的软件。其实它们并不抢同一份工作,每一个都只负责一个位置。把它理解成一间杂志社会更直观:

组件通俗理解它实际负责什么你平时什么时候会碰它
1Panel服务器的物业管理处查看容器、磁盘、日志、证书和网站运行情况服务器维护、查看资源、排障
Docker一个个独立的应用盒子把 Directus 和 PostgreSQL 隔离运行,避免互相污染通常只看容器状态,不直接改内部文件
Directus编辑部后台写文章、分类、标签、封面、权限和“发布”按钮这是你日后最常使用的后台
PostgreSQL上锁的资料库保存文章正文、账号、分类、系统配置和权限日常不直接操作,只负责备份与恢复
OpenResty大楼前台和分流员接收域名 HTTPS 请求,再转给 Directus 或 1Panel配置域名、反代、HTTPS、排障
Astro排版和印刷工厂把 Directus 中已发布的内容编译成静态 HTML 页面改网站样式、布局和功能时
Cloudflare Pages全球发行网络把 Astro 生成的网页放到 CDN,让访客就近访问查看构建是否成功、绑定主域名
Cloudflare R2图片仓库存封面和正文图片,不占 VPS 的硬盘与带宽上传图片、管理 Bucket 和图片域名

先分清最容易混淆的三件事

1Panel 不是 Directus。1Panel 管的是服务器;Directus 管的是网站内容。前者相当于管理员后台,后者相当于作者后台。写文章只进 Directus,不需要打开 1Panel。

Directus 不是 PostgreSQL。Directus 给你一个好用的可视化编辑界面;PostgreSQL 是它背后真正保存数据的数据库。你在 Directus 点击“保存文章”,实际是 Directus 把数据写入 PostgreSQL。就像微信不是通讯录本身,而是管理通讯录的应用。

Astro 不是 Cloudflare Pages。Astro 负责把内容“做成网页文件”,Cloudflare Pages 负责把这些文件“发给全世界”。Astro 是生产,Pages 是分发。

为什么访客不直接访问你的数据库

如果每个访客打开一篇文章都要经过 Directus 和 PostgreSQL,小 VPS 很快会把 CPU、内存和带宽用在重复查询上。现在的方案是在发布时一次性生成静态页面:

你在 Directus 点击发布
        ↓
文章保存到 PostgreSQL
        ↓
Directus Flow 通知 Cloudflare Pages
        ↓
Astro 读取已发布文章并生成 HTML
        ↓
访客从 CDN 直接获取 HTML

因此一万人阅读同一篇文章,主要压力由 Cloudflare CDN 承担;VPS 不需要一万人次访问数据库。这就是“静态优先”的意义。

为什么 1Panel 里会看到两个容器

你会在 1Panel → 容器中看到:

directus-directus-1   → Directus 编辑后台
directus-database-1   → PostgreSQL 数据库

它们是一套系统的两个零件:停止 Directus,后台打不开;停止 PostgreSQL,Directus 无法读取或保存内容。不要删除“已使用”的镜像、容器和数据卷。只有明确标记“未使用”、且没有任何容器引用的旧镜像,才可以清理来释放空间。

以后做不同事情,该去哪里

  • 写文章、上传封面、设置分类:Directus;
  • 改网页视觉、组件、代码:本地 Astro 项目 → GitHub;
  • 检查服务器 CPU、容器和证书:1Panel;
  • 改 DNS、R2、Pages、WAF:Cloudflare;
  • 救援登录或改 root 密码:供应商 VNC;
  • 恢复数据:备份脚本和 PostgreSQL 恢复流程。

第 1 步:先在 VNC 里改掉 root 初始密码

这一步必须由用户自己完成,不要让 Agent 代输密码。

  1. 打开 VPS 供应商后台,进入 VNC、Web Console 或网页终端。
  2. 用供应商发放的初始 root 密码登录。
  3. 执行:
passwd root

第一次输入新的 root 密码,第二次再次输入同一个密码确认。Linux 终端不会显示星号和字符,这是正常现象。看到类似 password updated successfully 才算成功。

建议密码长度 20 位以上,包含大小写字母、数字和符号,并保存到密码管理器。这个密码用于供应商 VNC 应急救援;后面会关闭公网 SSH 密码认证,但 VNC 通常仍然可以用它登录。

不要关闭 VNC,后面的 SSH 密钥验证完成前,它是你的退路。

第 2 步:让电脑用密钥登录服务器

在本地 PowerShell 执行:

ssh-keygen -t ed25519 -a 64 -f $env:USERPROFILE\.ssh\content_hub

系统询问 passphrase 时,建议设置一个私钥口令并保存到密码管理器。它保护的是本地私钥,不是服务器 root 密码。

会生成两个文件:

  • content_hub:私钥,永远不要发送;
  • content_hub.pub:公钥,可以安装到服务器。

在 PowerShell 查看公钥:

Get-Content $env:USERPROFILE\.ssh\content_hub.pub

在 VNC 里把公钥放进服务器

下面四条命令必须逐条执行,避免粘贴后命令连在一起:

mkdir -p /root/.ssh
chmod 700 /root/.ssh
nano /root/.ssh/authorized_keys
chmod 600 /root/.ssh/authorized_keys

在 nano 中粘贴完整的一行公钥,按 Ctrl+O、回车保存,再按 Ctrl+X 退出。不要粘贴私钥。

回到本地,打开一个新的 PowerShell 测试:

ssh -i $env:USERPROFILE\.ssh\content_hub root@VPS_IPV4

必须在“第二个新窗口”成功登录,才能继续关闭 SSH 密码认证。原来的 VNC 和 SSH 窗口先不要关。

连不上时,按这个顺序检查

在 VNC 执行:

sshd -T | grep -E 'pubkeyauthentication|passwordauthentication|permitrootlogin|authorizedkeysfile'
tail -n 1 /root/.ssh/authorized_keys
ls -ld /root/.ssh
ls -l /root/.ssh/authorized_keys

需要看到公钥认证开启、目录权限 700、文件权限 600。遇到 File exists 时先检查是否把 mkdir、chmod、echo 粘成了一整行,而不是反复执行。

第 3 步:确认密钥能用后,再关闭 SSH 密码登录

只有新窗口已经通过密钥登录后,才执行以下配置。建议创建独立配置文件,避免直接破坏系统主配置:

nano /etc/ssh/sshd_config.d/99-content-hub-hardening.conf

写入:

PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password
MaxAuthTries 3
LoginGraceTime 30
X11Forwarding no

保存后检查并重载:

sshd -t
systemctl reload ssh
sshd -T | grep -E 'pubkeyauthentication|passwordauthentication|permitrootlogin'

再次打开第三个 PowerShell 验证密钥登录。不要用 restart 冒险断开当前会话。供应商 VNC 仍能使用 root 密码,这是独立的控制台通道,不代表 SSH 密码认证没有关闭。

第 4 步:让小服务器更稳定、更不容易被入侵

apt update
apt full-upgrade -y
timedatectl set-timezone Asia/Shanghai
free -h
df -h

如果 free -h 显示 Swap 为 0,2GB 内存服务器建议添加 2GB:

fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
free -h

配置 UFW 前,先允许 SSH:

apt install -y ufw fail2ban unattended-upgrades
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw limit 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
ufw status numbered

如果 SSH 不是 22 端口,把命令里的 22 换成实际端口。不要在未添加允许规则时启用 UFW。

启用基础服务:

systemctl enable --now fail2ban
systemctl enable --now unattended-upgrades

第 5 步:安装服务器管理面板和运行环境(1Panel + Docker)

以官方文档当前的 1Panel v2 交互式安装命令为准:

bash -c "$(curl -sSL https://resource.fit2cloud.com/1panel/package/v2/quick_start.sh)"

这一步建议用户盯着 VNC 亲自回答安装提示:

  1. 安装目录可用 /opt;
  2. 面板端口可设置为 18080;
  3. 选择让安装器安装 Docker;
  4. 面板用户名由用户自己设置,不要沿用 root;
  5. 面板密码由用户自己输入,8–30 位并使用允许的字符;
  6. 安全入口使用随机字母、数字或下划线,保存到密码管理器。

安装成功后,终端会显示:

http://VPS_IPV4:18080/安全入口

临时登录时,只在云防火墙/UFW 中允许自己的公网 IP 访问 18080。不要长期对全世界开放。忘记入口时,用户可以在 VNC 自己执行:

1pctl user-info

它会显示敏感入口信息,不要截图发到聊天中。修改 1Panel 账户只能在用户明确要求时进行:

1pctl update username
1pctl update password

第二次密码输入是确认新密码,不是验证旧密码。

参考:1Panel v2 官方在线安装文档。

第 6 步:先把域名指到正确的位置

进入 Cloudflare → 你的域名 → DNS → Records,添加:

类型名称目标初始状态
AadminVPS_IPV4灰云排障,成功后橙云
ApanelVPS_IPV4灰云排障,成功后橙云
CNAMEwww主域名或 Pages 目标橙云

主域名不要随便手工猜 A 记录。等 Pages 项目创建后,在 Workers & Pages → 项目 → Custom domains 中添加 example.com,让 Cloudflare 自动给出记录。

进入 SSL/TLS → Overview,把模式改成 Full (strict)。这意味着浏览器到 Cloudflare、Cloudflare 到 VPS 都使用有效 HTTPS。

第 7 步:给后台和面板加上域名与 HTTPS(OpenResty)

可在 1Panel 第一次进入“网站”功能时安装它推荐的 OpenResty 稳定版。安装后用 OpenResty 做统一入口:

admin.example.com → http://127.0.0.1:8055
panel.example.com → http://127.0.0.1:18080

申请证书前,确保 80 和 443 已开放。使用 Certbot Webroot 的典型流程:

apt install -y certbot
mkdir -p /var/www/acme
certbot certonly --webroot -w /var/www/acme \
  -d admin.example.com -d panel.example.com

如果橙云导致验证困难,暂时切灰云,签发完成后再切回橙云。OpenResty 配置至少包括:

  • 80 端口保留 /.well-known/acme-challenge/,其余跳到 HTTPS;
  • 443 配置证书和 TLS 1.2/1.3;
  • 反代设置 Host、X-Real-IP、X-Forwarded-For、X-Forwarded-Proto;
  • Directus 和 1Panel 增加 WebSocket Upgrade 头;
  • 上传限制可设为 100MB;
  • 每次修改后先运行 openresty -t,成功才 reload。

确认 https://panel.example.com 能自动到达安全入口后,从 UFW 和云防火墙删除公网 18080,只保留 OpenResty 从本机访问。

第 8 步:把写作后台和资料库装到服务器上(Directus + PostgreSQL)

先创建目录:

mkdir -p /opt/directus/postgres
cd /opt/directus
umask 077
nano .env

这里是第二个必须由用户在 VNC 亲自操作的密码关口。把以下模板写入 /opt/directus/.env,所有尖括号内容由用户自己生成并保存:

POSTGRES_DB=directus
POSTGRES_USER=directus
POSTGRES_PASSWORD=<数据库随机强密码>

DIRECTUS_SECRET=<至少64位随机值>
ADMIN_EMAIL=<你的管理员邮箱>
ADMIN_PASSWORD=<你自己选择的Directus密码>

R2_ACCOUNT_ID=<稍后填写>
R2_ACCESS_KEY_ID=<稍后填写>
R2_SECRET_ACCESS_KEY=<稍后填写>
R2_BUCKET=<Bucket名称>

可以在 VNC 中用 openssl rand -base64 36 生成随机值,但不要把输出发给 Agent。然后:

chmod 600 /opt/directus/.env
nano /opt/directus/docker-compose.yml

Compose 核心模板如下。部署时先核对 Directus 当前稳定版本,并固定版本号,不使用 latest:

services:
  database:
    image: postgres:16-alpine
    restart: unless-stopped
    env_file: .env
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - ./postgres:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
      interval: 10s
      timeout: 5s
      retries: 10

  directus:
    image: directus/directus:11.15.4
    restart: unless-stopped
    env_file: .env
    depends_on:
      database:
        condition: service_healthy
    ports:
      - "127.0.0.1:8055:8055"
    environment:
      SECRET: ${DIRECTUS_SECRET}
      ADMIN_EMAIL: ${ADMIN_EMAIL}
      ADMIN_PASSWORD: ${ADMIN_PASSWORD}
      PUBLIC_URL: https://admin.example.com
      DB_CLIENT: pg
      DB_HOST: database
      DB_PORT: 5432
      DB_DATABASE: ${POSTGRES_DB}
      DB_USER: ${POSTGRES_USER}
      DB_PASSWORD: ${POSTGRES_PASSWORD}
      WEBSOCKETS_ENABLED: "true"

启动并检查:

docker compose config
docker compose up -d
docker compose ps
docker logs --tail 100 directus-directus-1
curl -I http://127.0.0.1:8055/server/health

浏览器访问 https://admin.example.com/admin,使用用户刚才设置的 ADMIN_EMAIL 和 ADMIN_PASSWORD 登录。第一次启动成功后,可以从 Compose 环境中移除 ADMIN_PASSWORD,因为它只负责首次创建管理员;之后改密码应在 Directus 个人设置中完成。

参考:Directus Docker 官方指南和配置选项。

在 1Panel 的“数据库”页面看不到 PostgreSQL 是正常的:它由 Compose 管理。应在 1Panel → 容器中看到 directus-directus-1 和 directus-database-1。不要再安装第二套 PostgreSQL。

第 9 步:给图片准备单独仓库(R2)

这一步由用户在 Cloudflare 控制台操作:

  1. 进入 R2 Object Storage → Create bucket;
  2. 创建例如 site-media 的 Bucket;
  3. 创建只允许该 Bucket 读写的 API Token;
  4. 复制 Account ID、Access Key ID、Secret Access Key;
  5. 回到 VNC,用 nano /opt/directus/.env 写入,绝不发到聊天;
  6. 进入 Bucket → Settings → Custom Domains,绑定 img.example.com。

生产环境使用自定义域名,不使用开发用 r2.dev 地址。参考:Cloudflare R2 Public Buckets。

Directus 的 S3 兼容配置需要包括 driver、endpoint、bucket、region 和上述密钥。配置完成后上传一张无关紧要的测试图片,确认能访问,再删除测试图,验证读写权限都正常。

第 10 步:在后台准备文章、分类和发布权限

登录 Directus → Settings → Data Model,按顺序创建:

先创建“文章”这张内容表

  • status:draft / published / archived;
  • title:必填标题;
  • slug:必填、唯一,作为永久网址;
  • excerpt:60–120 字摘要;
  • content:Rich Text HTML 正文;
  • published_at:发布时间;
  • cover_image:关联 Directus Files;
  • category:多对一关联 categories;
  • series:多对一关联 series;
  • tags:Tags/JSON;
  • reading_time:整数;
  • featured:布尔值;
  • accent:lime / blue / orange / pink;
  • sort:排序。

再创建 categories、series、projects 和单例 site_settings。

进入 Settings → Access Policies,给 Public 角色添加只读规则:

  • 只允许读取 status = published 的 posts;
  • 只开放前端使用的字段;
  • 不要公开管理员、token、状态控制和系统敏感字段。

无登录浏览器测试:

https://admin.example.com/items/posts

应该只能看到已发布文章。

第 11 步:把内容变成好看的静态网站(Astro)

本地电脑执行:

npm create astro@latest
npm install
npm run dev

创建环境示例文件:

DIRECTUS_URL=https://admin.example.com
DIRECTUS_STATIC_TOKEN=

公开文章不需要管理员 Token。构建代码在服务器端请求:

GET /items/posts?filter[status][_eq]=published

用每篇文章的 slug 生成:

/notes/[slug]/index.html

至少实现首页、文章列表、文章详情、404、RSS、Sitemap、SEO、移动端导航。完成后:

npm run build

必须零错误,再推送 GitHub。不要提交 .env、私钥、R2 密钥或管理员 Token。

第 12 步:让网站上线到全球 CDN(Cloudflare Pages)

  1. Cloudflare → Workers & Pages → Create;
  2. 连接 GitHub,选择 Astro 仓库;
  3. Production branch 选择 main;
  4. Build command 填 npm run build;
  5. Output directory 填 dist;
  6. Node 版本与本地保持一致,例如 22;
  7. 环境变量设置 DIRECTUS_URL=https://admin.example.com;
  8. 第一次部署成功后进入 Custom domains,添加 example.com。

参考:Cloudflare Pages 自定义域名。

第 13 步:让文章发布后自动更新网站

Cloudflare Pages → Settings → Builds & deployments → Deploy hooks,创建 Production Hook。这个 URL 等同密码,不要放进 GitHub 或聊天。

Directus → Settings → Flows → Create Flow:

  1. 名称:内容变更后重建网站;
  2. 状态:Active;
  3. Trigger:Event Hook;
  4. 类型选择 Action,不阻塞内容保存;
  5. Scope 选择 items.create、items.update、items.delete;
  6. Collections 选择 posts、categories、series、projects、site_settings;
  7. 添加 Request URL operation;
  8. Method 选择 POST;
  9. URL 由用户亲自粘贴 Deploy Hook;
  10. 保存并启用。

完整测试:

  1. 创建一篇草稿,确认主站看不到;
  2. 把状态改为 published 并保存;
  3. Flow 日志应显示 request 2xx;
  4. Pages 出现新部署;
  5. 一两分钟后文章 URL 返回 200。

如果曾经更换 API 域名,必须同时检查 Pages 环境变量。环境变量会覆盖代码默认值,是“本地构建成功、云端构建失败”的常见原因。

第 14 步:确认一切正常后,关闭多余的公网端口

确认以下地址均正常:

https://admin.example.com/admin
https://admin.example.com/items/posts
https://panel.example.com
https://example.com

然后删除公网 18080 规则。最终 UFW 应只开放:

22/tcp  LIMIT
80/tcp  ALLOW
443/tcp ALLOW

Directus 的 8055 仍只监听 127.0.0.1;PostgreSQL 的 5432 不映射到宿主机。不要为了“方便连接数据库”把 5432 开到公网。

第 15 步:每天备份,并亲自验证真的能恢复

数据库备份核心命令:

docker exec directus-database-1 \
  pg_dump -U directus -d directus | gzip \
  > /opt/backups/directus/database-$(date -u +%Y%m%dT%H%M%SZ).sql.gz

备份目录设为 root 700,备份文件 600;每天执行,保留 14 天。还要备份 Compose 配置,但排除或加密包含密钥的 .env。

至少做一次恢复演练:创建临时数据库 → 解压导入 → 检查文章数量 → 确认成功 → 只删除这个临时数据库。没有恢复演练的备份,只能算“可能有用”。

第 16 步:以后更新网站时,该去哪里操作

  • 写文章、分类、封面、首页文案:Directus;
  • 改布局、颜色、组件、功能:本地 Astro → GitHub;
  • 看容器、证书、磁盘、日志:1Panel;
  • 改 DNS、Pages、R2、WAF:Cloudflare;
  • 服务器救援和密码恢复:供应商 VNC;
  • 数据库:日常不要直接修改,使用 Directus 和备份脚本。

遇到问题时,先看这里

现象优先检查
SSH 公钥失败公钥是否完整、权限是否 700/600、PubkeyAuthentication 是否开启
VNC 能用 root 密码,SSH 不行正常;两者是不同通道,SSH 已禁密码
1Panel 改密后仍失败用户名大小写、无痕窗口、是否混用了 root/Directus 密码
Cloudflare 重定向循环是否误用 Flexible,改 Full (strict),检查源站 HTTPS
Directus 能开但登录失败确认是首次 ADMIN_PASSWORD 还是已有用户;不要只改环境变量
本地 Astro 构建成功,Pages 失败Pages 环境变量是否仍指向旧 API、Node 版本是否一致
1Panel 数据库里没有 PostgreSQLCompose 数据库在“容器”中,不由 1Panel 数据库模块管理
旧镜像占空间只删除标记“未使用”且没有容器引用的镜像

上线前最后检查一遍

  • 供应商 VNC root 密码由用户自己掌握;
  • SSH 新会话密钥登录成功,公网密码认证关闭;
  • UFW 只有 22、80、443;
  • Fail2ban 和自动安全更新运行;
  • Directus、PostgreSQL 容器健康;
  • 8055 只监听 127.0.0.1,5432 不公开;
  • Cloudflare 使用橙云和 Full (strict);
  • admin、panel、img、主域名 HTTPS 正常;
  • Public API 只能读取已发布内容;
  • Directus 发布可触发 Pages 新构建;
  • 备份每天执行,恢复演练成功;
  • Cloudflare、GitHub、Directus、1Panel 已开启 MFA;
  • 任何聊天、仓库和文章中都没有真实密码、私钥与 Hook。

最后:以后只需写文章和点击发布

2C2G 并不妨碍你搭出一套认真可用的内容系统,关键是不要让小服务器承担所有访问。把后台和数据库留在 VPS,把静态网页交给 Pages,把图片交给 R2,再用严格的权限、HTTPS、备份和恢复演练兜底。

第一次搭建真正困难的不是某条命令,而是分清“谁该做什么”:用户掌握账号与恢复入口,Agent 负责可验证的部署工作,Cloudflare 负责边缘分发,Directus 负责内容,Astro 负责生成。边界清楚以后,日常发布只剩下打开 Directus、写完文章、点击发布。