基于 Debian + Docker,结合 Nginx + Cloudflare Tunnel 的服务器,用于部署 Astro 静态博客。
在家中的闲置 Win10 旧笔记本上重装了 Debian Linux 系统,用于搭建家庭轻量级服务器,并在新购域名下重新构建与部署了个人的 Astro 博客。
目录
目录
架构
手头有一台闲置的戴尔游匣 14 7000,虽然性能不再适合日常高负载,但用于运行轻量级家庭服务依旧绰绰有余。
Debian:底层操作系统。我安装了 XFCE 桌面。
Docker:多阶段容器化隔离。统一管理 Nginx、Cloudflare Tunnel 等组件。
Nginx:静态资源反向代理服务器。承接容器内的 HTTP 请求,分发静态文件。
Cloudflare Tunnel:内网穿透通道。与 Cloudflare 边缘节点连接,避免内网端口暴露到公网。
Astro:静态站点生成框架(SSG)。采用“群岛架构(Islands Architecture)”。
整套方案不依赖公网 IP、不修改光猫与家庭路由器配置,兼顾了自建基础设施的灵活性与边缘网络的安全保护,赞。
一、底层系统初始化
1. 准备
前往 Debian 官网下载Debian。
下载并使用 Rufus,选择下载的 debian-xxx.iso,
分区方案选 GPT,目标系统选 UEFI,点击开始写入 U盘。
2. BIOS
在开机界面连续按下 F12 以跳转到 Boot Mode 界面,选择 BIOS Setup,
SATA Controller / Operation Mode 切换为 AHCI,
Secure Boot 切换为 Disable,
插入 启动U盘 后,保存并退出。
3. 系统安装
按照引导菜单自行选择,
Root password 留空,
如果想保留 window10 系统,给磁盘分区,
安装XFCE桌面,
勾选 SSH server 和 standard system utilities,
完成后拔出 U盘,重启电脑。
4. 禁用合盖休眠
打开终端,执行以下命令:
sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target
别忘了按需配置基础环境,但这里就略过了。
二、 Astro
在已有基于 Astro 的本地项目的情况下,
将 Astro 项目送入容器打包前,
我遇到了以下问题:
| 现象 | 原因 | 解决方案 |
|---|---|---|
| CSS/JS 失效 | 设置了 base: '/Limo_Blog',导致生成的资源链接失效 | 在 astro.config.mjs 中修改为 base: '/' |
| Prefetch 拦截 | site 写成了 http://,HTTPS 网页严禁调用 HTTP 资源 | 修正为 https://,在 Cloudflare 开启 Always Use HTTPS |
| 特定图片失效 | 源码组件中残留了我手写的硬编码路径(/Limo_Blog/images/) | 修正为绝对路径 /images/即可 |
配置检查完毕后,在项目根目录创建 .dockerignore:
node_modules
.git
dist
.vscode
三、 容器化多阶段工程
1. 编写 Nginx 站点配置
在项目根目录新建 nginx.conf,启用 Gzip 压缩与长效缓存策略:
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
gzip on;
gzip_types text/plain text/css application/javascript application/json image/svg+xml;
location / {
try_files $uri $uri/ =404;
}
location ~* \.(?:css|js|jpg|jpeg|gif|png|ico|svg|woff2?)$ {
expires 30d;
add_header Cache-Control "public, no-transform";
}
}
2. 编写多阶段构建 Dockerfile
针对跨境镜像拉取不稳定的情况,先拉取国内镜像源打标:
docker pull m.daocloud.io/docker.io/library/node:22-alpine
docker tag m.daocloud.io/docker.io/library/node:22-alpine node:22-alpine
docker pull m.daocloud.io/docker.io/library/nginx:alpine
docker tag m.daocloud.io/docker.io/library/nginx:alpine nginx:alpine
在项目根目录新建 Dockerfile:
# 第一阶段:编译静态文件
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# 第二阶段:装配静态分发容器,丢弃构建依赖
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
四、 网络发布
1. 域名与 DNS
注册域名后,前往 Cloudflare 控制台 点击Add a site输入域名。
进入域名注册商后台,将 Name Server 记录修改为 Cloudflare 分配的两条权威服务器地址(*.ns.cloudflare.com),等待解析生效。
2. 创建 Cloudflare Tunnel
在左侧导航栏依次选择Networks -> Tunnels,点击Create a tunnel,
选择Cloudflared部署方式并命名。
在配置页面选择Docker环境,复制命令中附带的专属 TUNNEL_TOKEN。
在博客项目根目录下创建 docker-compose.yml:
services:
astro-blog:
build: .
container_name: astro-blog
restart: always
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
restart: always
command: tunnel --no-autoupdate run
environment:
- TUNNEL_TOKEN=你的Cloudflare_Tunnel_Token
depends_on:
- astro-blog
在终端执行一键启动命令:
docker compose up -d --build
3. 映射 Public Hostname
回到 Cloudflare Zero Trust 控制台 的 Tunnel 配置页,切换至Public Hostname标签:
点击Add a public hostname;
Subdomain:按需输入子域名(如 blog,主域则留空);
Domain:选择你在第一步绑定的域名;
Type:选择 HTTP;
URL:填写 astro-blog:80(直接使用 Compose 服务名解析,无需暴露宿主机端口)。
保存配置后,Cloudflare 会自动完成全球 CDN 节点与本地笔记本容器的端到端安全连接。
五、 博客更新流程图
[ 开发端 ]
│ 1. 本地修改代码/更新文章
│ 2. git add .
│ 3. git commit -m ""
│ 4. git push origin main
▼
[ GitHub 仓库 ]
│ 5. 推送至 GitHub 仓库
▼
[ 服务器 ]
│ 6. cd ~/.../LimoBlog
│ 7. git pull --rebase origin main
▼
[ Docker ]
│ 8. docker compose up -d --build
│ 9. docker image prune -f
▼
[ 公网(https://limovane.top)]
## 结语 除此之外,我还做了三件事:
Tailscale:
为服务器与开发端建立了安全的 P2P 加密隧道,实现 SSH 与 xrdp 远程桌面控制;
GParted:
从盘中划出 154GB 的独立 ext4 分区挂载为 /data,将 Docker 的存储中枢(data-root)完整平移。
Autocutsel:
由于 Linux 历史遗留的双重剪贴板机制,我无法跨系统剪贴,这个工具通过让 Linux 双剪贴板同步,解决了应用层粘贴阻抗的问题。
不过受限于篇幅,就不作过多展开了。
收工。
整个工程闭环比我预期的更为流畅,
前后仅耗费一天半。