如何优雅地部署自己的个人博客主页 🚀

2026 年 4 月 3 日 星期五
4
1

如何优雅地部署自己的个人博客主页 🚀

作为一个沉迷代码无法自拔审美在线的理科生,假期在 GitHub 闲逛时,一眼就被优美的 Shiro 主题 俘获了芳心,于是立马拍桌决定:我也要搞一个!

为了 装X 为了便于展示自己的项目和介绍之类的~

  • 个人配置:MacBook Pro + 1Panel 服务器(阿里云买的轻量级 2vCPU/1GiB)
  • 部署路线:后端采用 Docker 部署,前端则是利用 Github Action 构建 Shiro 然后推送到服务器上。

💳 准备工作(氪金环节)

万丈高楼平地起,首先你得拥有:

  1. 一台自己的服务器:如果只是为了搞一个网页 ,可以像我一样搞一个很小规模的那种。
  2. 一个自己喜欢的域名:这可是你的门面!

苯人的上述工作都是在阿里云购买完成的。


🛠️ 安装 1Panel

如果你并非购买的应用镜像(也就是服务器是“毛坯房”),则需要进行这一步骤。

参考官网的 1Panel官方安装文档 步骤即可,一步一步慢慢设置即可~


⚙️ 后端 Core 的部署(给博客安个“发动机”)

这里推荐用 Docker 去进行后端的部署,一般来讲如果是 1Panel 面板会自动安装下载好,干净又卫生。

1. 检查并安装 Docker

可以先在 1Panel 的 「容器」 菜单中检查下 Docker 是否安装好再进行下一步。

2. 拉取 mx-space 文件并修改配置

可使用命令行直接拉取文件,也可以使用 1Panel 面板终端进行拉取,存放路径可以自行修改(但要记住在哪!)。

# 回到根目录并创建 mx-space/core 的文件夹,并跳转至 core 文件夹下
cd && mkdir -p mx-space/core && cd $_ 

# 拉取 docker-compose.yml 文件
wget https://fastly.jsdelivr.net/gh/mx-space/core@master/docker-compose.yml

vim 或者自己习惯的方式修改 docker-compose.yml 内的部分内容(见注释):

services:
  app:
    container_name: mx-server
    image: innei/mx-server:latest
    environment:
      - TZ=Asia/Shanghai
      - NODE_ENV=production
      - DB_HOST=mongo
      - REDIS_HOST=redis
      # 填写自己博客的域名,多个用 , 相连,这里可以额外加上本机地址允许他们自己访问自己
      - ALLOWED_ORIGINS=blog.lolita.best,localhost:*,127.0.0.1:* 
      # 填写你自己生成的JWT密钥,16≤密钥长度≤32 个字符(务必保存好自己的密钥不要泄露)
      - JWT_SECRET=xxxxx 
      # 是否开启加密的相关配置,这边默认为false与空即可,如需加密可以参考官方配置文档
      - ENCRYPT_ENABLE=false
      - ENCRYPT_KEY=
    volumes:
      - ./data/mx-space:/root/.mx-space
    ports:
      # -'2333:2333' 修改了docker的端口映射,不开放端口,只对本机监听
      - '127.0.0.1:2333:2333' 
    depends_on:
      - mongo
      - redis
    networks:
      - mx-space
    restart: unless-stopped
    healthcheck:
      test: ['CMD', 'curl', '-f', 'http://127.0.0.1:2333/api/v2/ping']
      interval: 1m30s
      timeout: 30s
      retries: 5
      start_period: 30s

  mongo:
    container_name: mongo
    image: mongo
    volumes:
      - ./data/db:/data/db
    networks:
      - mx-space
    restart: unless-stopped

  redis:
    image: redis:alpine
    container_name: redis
    volumes:
      - ./data/redis:/data
    healthcheck:
      test: ['CMD-SHELL', 'redis-cli ping | grep PONG']
      start_period: 20s
      interval: 30s
      retries: 5
      timeout: 3s
    networks:
      - mx-space
    restart: unless-stopped

networks:
  mx-space:
    driver: bridge

3. 启动 Core

在上述操作都没问题后,直接在当前的终端目录下启动启动!

# 后续如果需要更新后端或者yaml配置修改更新容器也是一样的指令
docker compose pull && docker compose up -d

启动成功后你可以在 1Panel 的侧栏容器内看到 mx-serverredismongo 这三个容器的运行情况(都是绿色 已启动)。至此你已经成功布置好了后端。

4. 反向代理(打通“任督二脉”)

后端是支棱起来了,但现在它还只是个“局域网巨星”,外面的人访问不到。我们需要设置反向代理,打通它和外界的“任督二脉”。

可以参考官方安装设置反向代理的操作!

(根据自身购买的域名的特点,结合 AI 老师搞一下即可)

官方进行反向代理的教学

设置完毕后,这时候你就可以通过 https://你的域名/qaqdmin 访问后台管理地址了,根据指示填写对应内容即可。在设定的网站设置内填写对应的地址(看准,是你自己的!!!)。


✨ 前端部署(给博客搞个“精装修”)

苯人是用的是 Shiro 的开源版本,并通过 Github Action 的方式自动部署。

1. Fork 仓库并修改 Workflows 文件

仓库地址: GitHub - innei-dev/shiroi-deploy-action

Fork 到本地后打开,修改 workflows 下的 deploy.yml 文件 (要改的地方有中文注释)。

# deploy.yml 的完整内容
name: Build and Deploy

on:
  workflow_dispatch: # 增加了工作流的手动运行
  push:
    branches:
      - main
  # schedule:
  # - cron: '0 3 * * *'

  repository_dispatch:
    types: [trigger-workflow]

permissions: write-all
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

env:
  PNPM_VERSION: 9.x.x
  HASH_FILE: build_hash

jobs:
  prepare:
    name: Prepare
    runs-on: ubuntu-latest
    if: ${{ github.event.head_commit.message != 'Update hash file' }}

    outputs:
      hash_content: ${{ steps.read_hash.outputs.hash_content }}

    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Read HASH_FILE content
        id: read_hash
        run: |
          content=$(cat ${{ env.HASH_FILE }}) || true
          echo "hash_content=$content" >> "$GITHUB_OUTPUT"
  check:
    name: Check Should Rebuild
    runs-on: ubuntu-latest
    needs: prepare
    outputs:
      canceled: ${{ steps.use_content.outputs.canceled }}

    steps:
      - uses: actions/checkout@v4
        with:
          repository: innei-dev/shiroi # 填写你需要访问的仓库,如开源版为innei/shiro
          token: ${{ secrets.GH_PAT }} # `GH_PAT` is a secret that contains your PAT
          fetch-depth: 0
          lfs: true

      - name: Use content from prev job and compare
        id: use_content
        env:
          FILE_HASH: ${{ needs.prepare.outputs.hash_content }}
        run: |
          file_hash=$FILE_HASH
          current_hash=$(git rev-parse --short HEAD)
          echo "File Hash: $file_hash"
          echo "Current Git Hash: $current_hash"
          if [ "$file_hash" == "$current_hash" ]; then
            echo "Hashes match. Stopping workflow."
            echo "canceled=true" >> $GITHUB_OUTPUT
          else
            echo "Hashes do not match. Continuing workflow."
          fi

  build:
    name: Build artifact
    runs-on: ubuntu-latest
    needs: check
    if: ${{needs.check.outputs.canceled != 'true'}}

    strategy:
      matrix:
        node-version: [20.x]
    outputs:
      sha_short: ${{ steps.store.outputs.sha_short }}
      branch: ${{ steps.store.outputs.branch }}
    steps:
      - uses: actions/checkout@v4
        with:
          repository: innei-dev/shiroi # 填写你需要访问的仓库,如开源版为innei/shiro
          token: ${{ secrets.GH_PAT }} # `GH_PAT` is a secret that contains your PAT
          fetch-depth: 0
          lfs: true

      - name: Checkout LFS objects
        run: git lfs checkout
      - uses: pnpm/action-setup@v2
        with:
          version: ${{ env.PNPM_VERSION }}
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'pnpm'
      - uses: jongwooo/next-cache@v1
      - name: Install dependencies
        run: pnpm install
      - name: Build project
        run: |
          sh ./ci-release-build.sh

      - name: Cache Build Artifacts
        id: cache-primes
        uses: actions/cache/save@v4
        with:
          path: assets
          key: ${{ github.run_number }}-release

      - name: Store artifact commit version
        shell: bash
        id: store
        run: |
          sha_short=$(git rev-parse --short HEAD)
          branch_name=$(git rev-parse --abbrev-ref HEAD)
          echo "sha_short=$sha_short" >> "$GITHUB_OUTPUT"
          echo "branch=$branch_name" >> "$GITHUB_OUTPUT"

  deploy:
    name: Deploy artifact
    runs-on: ubuntu-latest
    needs: build
    steps:
      - name: Restore cached Build Artifacts
        id: cache-primes-restore
        uses: actions/cache/restore@v4
        with:
          path: |
            assets
          key: ${{ github.run_number }}-release
      - name: Move assets to root
        run: mv assets/release.zip release.zip

      - name: copy file via ssh password
        uses: appleboy/scp-action@v0.1.7
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USER }}
          password: ${{ secrets.PASSWORD }}
          key: ${{ secrets.KEY }}
          port: ${{ secrets.PORT }}
          source: 'release.zip'
          target: '/tmp/shiro'

      - name: Exec deploy script with SSH
        uses: appleboy/ssh-action@master
        with:
          command_timeout: 5m
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USER }}
          password: ${{ secrets.PASSWORD }}
          key: ${{ secrets.KEY }}
          port: ${{ secrets.PORT }}
          # script_stop: true 移除了改行指令,因为该指令已经停止使用了会提示但不影响,不改也行
          script: |
            set -e
            export NVM_DIR=~/.nvm # 定义.nvm的安装目录,否则终端运行时明明安装了但找不到命令
            source ~/.nvm/nvm.sh # 识别nvm命令,否则可能会报command not found
            source $HOME/.bashrc 
            basedir=$HOME/mx-space/shiro # 填写你自己希望的路径,因为我的后端文件在mx-space下,所以我希望我的shiro也在mx-space文件夹下,方便管理
            workdir=$basedir/${{ github.run_number }}
            mkdir -p $workdir
            mkdir -p $basedir/.cache
            mv /tmp/shiro/release.zip $workdir/release.zip
            rm -r /tmp/shiro
            cd $workdir
            unzip -qq -o $workdir/release.zip
            rm -rf $workdir/standalone/.env
            ln -s $HOME/mx-space/shiro/.env $workdir/standalone/.env # 你的.env文件路径,我存放在mx-space/shiro文件夹内,方便管理
            export NEXT_SHARP_PATH=$(npm root -g)/sharp
            # copy workdir ecosystem.config.js to basedir if not exists
            if [ ! -f $basedir/ecosystem.config.js ]; then
              cp $workdir/standalone/ecosystem.config.js $basedir/ecosystem.config.js
            fi
            # https://github.com/Unitech/pm2/issues/3054
            # symlink workdir node entry file to basedir
            ln -sf $workdir/standalone/server.js $basedir/server.js
            mkdir -p $workdir/standalone/.next
            rm -rf $workdir/standalone/.next/cache
            ln -sf $basedir/.cache $workdir/standalone/.next/cache
            cd $basedir
            pm2 reload ecosystem.config.js --update-env
            rm $workdir/release.zip
            pm2 save
            echo "Deployed successfully"

      - name: After deploy script
        run: |
          hash=${{ needs.build.outputs.sha_short }}
          # curl -X "POST" "https://mx.innei.in/api/v2/fn/shiro/new-version-hook" -H 'Content-Type: application/json' -d "{\"hash\": \"$hash\", \"key\": \"\"}"
          ${{ secrets.AFTER_DEPLOY_SCRIPT }}
  store:
    name: Store artifact commit version
    runs-on: ubuntu-latest
    needs: [deploy, build]
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          persist-credentials: false
          fetch-depth: 0
      - name: Use outputs from build
        env:
          SHA_SHORT: ${{ needs.build.outputs.sha_short }}
          BRANCH: ${{ needs.build.outputs.branch }}
        run: |
          echo "SHA Short from build: $SHA_SHORT"
          echo "Branch from build: $BRANCH"
      - name: Write hash to file
        env:
          SHA_SHORT: ${{ needs.build.outputs.sha_short }}
        run: echo $SHA_SHORT > ${{ env.HASH_FILE }}
      - name: Commit files
        run: |
          git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git config --local user.name "github-actions[bot]"
          git add ${{ env.HASH_FILE }}
          git commit -a -m "Update hash file"
      - name: Push changes
        uses: ad-m/github-push-action@master
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          branch: ${{ github.ref }}

2. 设置工作流的 Secrets

为了让 GitHub Actions 能够安全连接到你的服务器,需要在仓库的 Settings -> Secrets and variables -> Actions 中设置以下密钥:

  • HOST: 服务器地址
  • USER: 服务器用户名
  • PASSWORD: 服务器密码
  • PORT: 服务器 SSH 端口
  • KEY: 服务器 SSH Key(可选,密码 key 二选一)
  • GH_PAT: 可访问之前 repository 内填写仓库的 Github Token

至于如何获取 KEYGH_PAT 可以自行 google 或者 AI(一步步操作),这是基础~

3. 配置 .env 文件

返回到 1Panel,在你的工作流内指定好的地方(就是找到 /shiro 所在的地方)创建 .env 文件并将以下内容复制修改成自己的内容。如上方参考中是 root/mx-space/shiro

(在终端中熟练掌握 cdls 两个指令有助于你迅速找到相应内容)

# 后端API地址
NEXT_PUBLIC_API_URL=https://blog.lolita.best/api/v2
# 后端网关地址
NEXT_PUBLIC_GATEWAY_URL=https://blog.lolita.best
# TMDB信息获取
TMDB_API_KEY=填写你的TMDB-API
# GitHub token,用来获取前端版本哈希
GH_TOKEN=填你自己的token

4. 服务器安装必要环境

要在服务器的终端处下载以下一系列帅气的助手:Node.js, npm, pnpm, pm2, sharp。

可以在 1Panel 的终端内输入以下指令进行直接安装。

(温馨提示:如果内存不够,可以尝试 swap 白嫖一点空间

#!/bin/bash

echo "🚀 开始安装 Node.js、npm、pnpm、pm2、sharp..."

# 确认 Homebrew 是否已安装
if ! command -v brew &> /dev/null
then
    echo "📦 未检测到 Homebrew,开始安装..."
    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
    echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
    eval "$(/opt/homebrew/bin/brew shellenv)"
fi

# 更新 brew
brew update

# 安装 Node.js(自带 npm)
echo "📦 安装 Node.js 和 npm..."
brew install node

# 安装 pnpm
echo "📦 安装 pnpm..."
npm install -g pnpm

# 安装 pm2
echo "📦 安装 pm2..."
npm install -g pm2

# 安装 sharp 所需依赖
echo "📦 安装 sharp 依赖..."
brew install pkg-config cairo pango libpng jpeg giflib librsvg

# 安装 sharp
echo "📦 安装 sharp..."
npm install -g sharp

# 输出版本信息
echo "✅ 安装完成!版本信息如下:"
node -v
npm -v
pnpm -v
pm2 -v
node -e "console.log('sharp version:', require('sharp').version)"

5. 启动 Workflow

在 Github 内找 Actions,然后手动 Run workflow 并等待工作流跑完,不报错、显示绿色就成功了。

6. 管理后台配置 Shiro 主题云函数

提示云函数的配置修改更新并非立刻刷新,需要时间请耐心等待或者多次刷新!!!

Shiro 主题还要配合云函数才可以部署,进入管理后台 /附加功能/配置与云函数 内,先导入相关文件。

右上角第二个下载 —— 弹出弹窗选择下边的导入shiro

再点击右上角的新增按钮,在编辑页面中,填入以下设置:

  • 名称shiro
  • 引用theme
  • 数据类型JSON
  • 数据:右侧复制以下示例,更改为自己的信息。数据内容参考:配置 - Mix Space 文档
{
  "footer": {
    "otherInfo": {
      "date": "2019-{{now}}",
      "icp": {
        "text": "萌ICP备20248012号",
        "link": "https://icp.gov.moe/?keyword=20248012"
      }
    },
    "linkSections": [
      {
        "name": "关于",
        "links": [
          { "name": "关于本站", "href": "/about-site" },
          { "name": "关于我", "href": "/about", "external": true }
        ]
      },
      {
        "name": "更多",
        "links": [
          { "name": "时间线", "href": "/timeline" },
          { "name": "友链", "href": "/friends", "external": true }
        ]
      },
      {
        "name": "联系",
        "links": [
          { "name": "写留言", "href": "/message", "external": true },
          { "name": "发邮件", "href": "mailto:mine.sumika@gmail.com", "external": true },
          { "name": "GitHub", "href": "https://github.com/Sumikanya", "external": true }
        ]
      }
    ]
  },
  "config": {
    "color": {
      "light": ["#33A6B8", "#FF6666", "#26A69A", "#fb7287", "#69a6cc", "#F11A7B", "#78C1F3", "#FF6666", "#7ACDF6"],
      "dark": ["#F596AA", "#A0A7D4", "#ff7b7b", "#99D8CF", "#838BC6", "#FFE5AD", "#9BE8D8", "#A1CCD1", "#EAAEBA"]
    },
    "bg": [
      "https://github.com/Innei/static/blob/master/images/F0q8mwwaIAEtird.jpeg?raw=true",
      "https://github.com/Innei/static/blob/master/images/IMG_2111.jpeg.webp.jpg?raw=true"
    ],
    "custom": { "css": [], "styles": [], "js": [], "scripts": [] },
    "site": {
      "favicon": "https://cn.cravatar.com/avatar/08d99bea604c306b51ebe162e93412f1?s=36",
      "faviconDark": "https://cn.cravatar.com/avatar/08d99bea604c306b51ebe162e93412f1?s=36"
    },
    "hero": {
      "title": {
        "template": [
          { "type": "h1", "text": "Hi, I'm ", "class": "font-light text-4xl" },
          { "type": "h1", "text": "星野纯夏", "class": "font-medium mx-2 text-4xl" },
          { "type": "h1", "text": "💕。", "class": "font-light text-4xl" },
          { "type": "br" },
          { "type": "h1", "text": "一个喜欢科技数码的Lo娘 ", "class": "font-light text-4xl" },
          { "type": "code", "text": "🦋<INFP>🦋", "class": "font-medium mx-2 text-3xl rounded p-1 bg-gray-200 dark:bg-gray-800/0 hover:dark:bg-gray-800/100 bg-opacity-0 hover:bg-opacity-100 transition-background duration-200" },
          { "type": "span", "class": "inline-block w-[1px] h-8 -bottom-2 relative bg-gray-800/80 dark:bg-gray-200/80 opacity-0 group-hover:opacity-100 transition-opacity duration-200 group-hover:animation-blink" }
        ]
      },
      "description": "希望在这个世界上可以留下属于我的痕迹",
      "Hitokoto": { "random": true, "custom": "自定义一言" }
    },
    "module": {
      "activity": { "enable": true, "endpoint": "/fn/ps/update" },
      "donate": {
        "enable": false, "link": "https://afdian.net/@Innei",
        "qrcode": ["https://cdn.jsdelivr.net/gh/Innei/img-bed@master/20191211132347.png", "https://cdn.innei.ren/bed/2023/0424213144.png"]
      },
      "bilibili": { "liveId": 19781 }
    }
  }
}

7. 设置 PM2 服务在重启之后还原进程

pm2 startup
pm2 save

如何手动启动 Shiro?可以进入 shiro 的前端路径并运行以下代码(默认 github 的工作流配置内已经启动过了):

pm2 start ecosystem.config.js

可以使用以下指令查看正在运行的进程:

pm2 list

status 显示 绿色的 online 即可~


🎉 部署完毕

前后端都部署成功后你就可以直接访问你的网页查看到当前效果了。


📚 参考链接(感谢各位大佬带飞)

十分感谢官方以及其他搭建的相关教程:

使用社交账号登录

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...