这是一篇真正按顺序执行的建站操作手册。目标不是把所有程序都塞进一台小服务器,而是用一台 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、账号、密码或密钥,可以作为自己的建站模板继续修改。
先看操作顺序:照着做就不乱
- 第 0 步:准备账号、域名、VNC 和密码管理器;
- 第 0.5 步:理解每个组件的职责,此时不修改任何服务器设置;
- 第 1 步:在供应商 VNC 修改 root 密码,确认救援入口;
- 第 2 步:在本地创建 SSH 密钥,并用第二个窗口验证登录;
- 第 3 步:确认密钥可用后,才关闭公网 SSH 密码认证;
- 第 4 步:更新系统、添加 Swap、开启 UFW、Fail2ban 和自动更新;
- 第 5 步:安装 1Panel 与 Docker,并由用户亲自设置面板账号、密码和安全入口;
- 第 6 步:在 Cloudflare 添加 DNS 记录,先灰云排障;
- 第 7 步:配置 OpenResty 和源站 HTTPS,确认域名可访问;
- 第 8 步:部署 Directus 与 PostgreSQL,确认容器健康;
- 第 9 步:创建 R2 Bucket,接入 Directus 图片存储;
- 第 10 步:创建文章、分类和公开权限模型;
- 第 11 步:开发 Astro 静态前端并在本地构建;
- 第 12 步:连接 GitHub,部署 Cloudflare Pages 和主域名;
- 第 13 步:用 Directus Flow 连接 Pages Deploy Hook;
- 第 14 步:全部域名验证后,关闭 18080 等公网管理端口;
- 第 15 步:建立每日备份并做恢复演练;
- 第 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/Secret | Directus 上传图片 | 服务器环境文件,不进 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 代输密码。
- 打开 VPS 供应商后台,进入 VNC、Web Console 或网页终端。
- 用供应商发放的初始
root密码登录。 - 执行:
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 亲自回答安装提示:
- 安装目录可用
/opt; - 面板端口可设置为
18080; - 选择让安装器安装 Docker;
- 面板用户名由用户自己设置,不要沿用 root;
- 面板密码由用户自己输入,8–30 位并使用允许的字符;
- 安全入口使用随机字母、数字或下划线,保存到密码管理器。
安装成功后,终端会显示:
http://VPS_IPV4:18080/安全入口
临时登录时,只在云防火墙/UFW 中允许自己的公网 IP 访问 18080。不要长期对全世界开放。忘记入口时,用户可以在 VNC 自己执行:
1pctl user-info
它会显示敏感入口信息,不要截图发到聊天中。修改 1Panel 账户只能在用户明确要求时进行:
1pctl update username
1pctl update password
第二次密码输入是确认新密码,不是验证旧密码。
第 6 步:先把域名指到正确的位置
进入 Cloudflare → 你的域名 → DNS → Records,添加:
| 类型 | 名称 | 目标 | 初始状态 |
|---|---|---|---|
| A | admin | VPS_IPV4 | 灰云排障,成功后橙云 |
| A | panel | VPS_IPV4 | 灰云排障,成功后橙云 |
| CNAME | www | 主域名或 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 个人设置中完成。
在 1Panel 的“数据库”页面看不到 PostgreSQL 是正常的:它由 Compose 管理。应在 1Panel → 容器中看到 directus-directus-1 和 directus-database-1。不要再安装第二套 PostgreSQL。
第 9 步:给图片准备单独仓库(R2)
这一步由用户在 Cloudflare 控制台操作:
- 进入 R2 Object Storage → Create bucket;
- 创建例如
site-media的 Bucket; - 创建只允许该 Bucket 读写的 API Token;
- 复制 Account ID、Access Key ID、Secret Access Key;
- 回到 VNC,用
nano /opt/directus/.env写入,绝不发到聊天; - 进入 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)
- Cloudflare → Workers & Pages → Create;
- 连接 GitHub,选择 Astro 仓库;
- Production branch 选择
main; - Build command 填
npm run build; - Output directory 填
dist; - Node 版本与本地保持一致,例如 22;
- 环境变量设置
DIRECTUS_URL=https://admin.example.com; - 第一次部署成功后进入 Custom domains,添加
example.com。
第 13 步:让文章发布后自动更新网站
Cloudflare Pages → Settings → Builds & deployments → Deploy hooks,创建 Production Hook。这个 URL 等同密码,不要放进 GitHub 或聊天。
Directus → Settings → Flows → Create Flow:
- 名称:内容变更后重建网站;
- 状态:Active;
- Trigger:Event Hook;
- 类型选择 Action,不阻塞内容保存;
- Scope 选择
items.create、items.update、items.delete; - Collections 选择 posts、categories、series、projects、site_settings;
- 添加 Request URL operation;
- Method 选择 POST;
- URL 由用户亲自粘贴 Deploy Hook;
- 保存并启用。
完整测试:
- 创建一篇草稿,确认主站看不到;
- 把状态改为 published 并保存;
- Flow 日志应显示 request 2xx;
- Pages 出现新部署;
- 一两分钟后文章 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 数据库里没有 PostgreSQL | Compose 数据库在“容器”中,不由 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、写完文章、点击发布。