Mix Space 是一个采用前后端分离架构的现代化个人空间后端;Shiro 则是其官方配套的高颜值前端主题,基于 Next.js 构建,以极简设计和流畅动画著称。两者搭配使用,能为你提供一个集文章写作、日常动态发布于一体,且兼具极致性能与视觉美感的个人博客系统。——Gemini
我之前都是用Hexo的,苦于更新困难+功能单一 (因为我偏好自托管,不太喜欢PaaS) 。就算更新一个字,都得打包重新上传服务器,这样使得碎片化写作变得很困难。
Mix Space和Shiro是相辅相成的,分别是后端 (Core) 和前端 (Theme),当然也有些别的前端主题 (比如 Kami ,Yun)。本篇只讲解Mix Space (后端) 和Shiro (前端)
部署后端可以直接使用官方提供的预构建Docker镜像。而Shiro前端可以使用预构建Docker镜像,也可以自构建Docker镜像。自构建可以对前端页面进行深度修改 (如果你需要悬挂两个及以上备案号,那就必须得自构建了)。
闲话少说,开整!
后端部署直接Docker Compose拉取镜像就可以跑
然后在 docker-compose.yml 写入:
JWT_SECRET 需要填写长度不小于 16 个字符,不大于 32 个字符的字符串ALLOWED_ORIGINS 需要填写被允许的域名,通常是前端的域名,如果允许多个域名访问,用英文逗号,分隔。其他可以根据需要自己修改,比如 '2333:2333' 改成 '127.0.0.1:2333:2333'
然后就可以启动Core了
推荐使用 1Panel 或者 宝塔 之类的现代面板操作,非常简单
新建网站,配置反代,URL地址 http://127.0.0.1:2333 就完事了
如果要手搓Nginx可以参考 官方Docs
去网站设置里把 前端地址 管理后台地址 API 地址 Gateway 地址 分别设置成
{Shiro地址}{后台地址}/proxy/qaqdmin{后台地址}/api/v2{后台地址}其他的就自己看着配置就行了
进入 Mix Space 后台,进入「配置与云函数」页面,点击右上角的新增按钮,在编辑页面中,填入以下内容:
shirothemeJSON以下内容根据自己需要修改
Shiro前端部署可以分为官方提供的预构建和自构建版本,按需选择
预构建适合打算快速建站使用,但是不能深度修改。还是那句话:按需选择
再提一嘴,如果你赞助的闭源版本Shiroi,那就只能选择自构建,因为官方没有提供预构建镜像
预构建就很快速了,直接拉取官方镜像配置好环境变量就可以上线了
配置环境变量
然后填入
填完就可以 docker compose up -d 了
自构建稍微麻烦点,但是如果想深度定制,那这是你唯一的选择
这里我们使用 GitHub Actions 来构建 Docker 镜像,方便快捷
前往自己的 GitHub 建一个仓库,推荐私密仓库,待会要用
确保你电脑安装了 Git
找个目录打开终端,执行以下命令。注意:不要删除 .git 文件夹,我们采用“重命名远程仓库”的方式,这样以后原作者更新了,也能同步合并
重点来了!前往并清空 .github / workflows 文件夹后新建 docker-main.yml
并在里面填写:
保存并退出
然后返回CMD窗口
推送完成后返回 GitHub 仓库查看

可以点击旁边的小黄点查看进程



构建完成后还需在服务器上配置 GitHub Token 才能拉取
read:packages 即可echo "{你的Token}" | docker login ghcr.io -u {你的GitHub ID} --password-stdin然后按照流程来就可以了
配置环境变量
然后填入
填完就可以 docker compose up -d 了
好了,现在一套流程已经走完了,具体要怎么折腾,就看各位了
随着原作者的不断更新,你可能想要同步最新的功能。不过得益于我们刚才保留了 Git 历史并配置了 upstream,更新就变得非常简单
如果在不冲突的情况下只需要三行命令就可以完成更新
如果冲突,那就到 VS Code 里"调停"一下吧(
如果你的服务器位于中国大陆,拉取 ghcr.io 镜像可能会非常缓慢甚至失败。为了解决这个问题,可以在构建完成后将镜像同步推送到阿里云 ACR
在 docker-main.yml 的 env 区域添加
然后在文件最后加上
接着,请将阿里云账户全名和 ACR 实例的独立登录密码,分别配置到仓库 Actions Secrets 的 ALIYUN_USERNAME 和 ALIYUN_PASSWORD 变量中
配置完成后,下一次推送代码时,GitHub Actions 就会自动将镜像同步到阿里云。这样一来,国内服务器就再也不会拉镜像拉半小时了
cd && mkdir -p mx-space/core && cd $_
nano 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=localhost
- JWT_SECRET=YOUR_SUPER_SECURED_JWT_SECRET_STRING
volumes:
- ./data/mx-space:/root/.mx-space
ports:
- '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:7
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
docker compose up -d
{
"footer": {
"otherInfo": {
"date": "202x-{{now}}",
"icp": {
"text": "萌 ICP 备 xxxxxxxx 号",
"link": "https://icp.gov.moe/?keyword=xxxxxxxx"
}
},
"linkSections": [
{
"name": "关于",
"links": [
{
"name": "关于本站",
"href": "/about-site"
},
{
"name": "关于我",
"href": "/about"
},
{
"name": "关于此项目",
"href": "https://github.com/innei/Shiro",
"external": true
}
]
},
{
"name": "更多",
"links": [
{
"name": "时间线",
"href": "/timeline"
},
{
"name": "友链",
"href": "/friends"
},
{
"name": "监控",
"href": "https://status.xxxxx.xxx",
"external": true
}
]
},
{
"name": "联系",
"links": [
{
"name": "写留言",
"href": "/message"
},
{
"name": "发邮件",
"href": "mailto:[email protected]",
"external": true
},
{
"name": "GitHub",
"href": "https://github.com/xxx",
"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": [
"/static/images/xxxxx.jpeg",
"/static/images/xxxxx.jpg"
],
"custom": {
"css": [],
"styles": [],
"js": [],
"scripts": []
},
"site": {
"favicon": "/xxxxx.svg",
"faviconDark": "/xxxxx-dark.svg"
},
"hero": {
"title": {
"template": [
{
"type": "h1",
"text": "xxxxxxx",
"class": "font-light text-4xl"
},
{
"type": "h1",
"text": "xxxxxxx",
"class": "font-medium mx-2 text-4xl"
},
{
"type": "h1",
"text": "👋。",
"class": "font-light text-4xl"
},
{
"type": "br"
},
{
"type": "h1",
"text": "xxxxxxx",
"class": "font-light text-4xl"
},
{
"type": "code",
"text": "<Developer />",
"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": "xxxxxxxxxxxxxxxxxxxx"
},
"module": {
"activity": {
"enable": true,
"endpoint": "/fn/ps/update"
},
"donate": {
"enable": true,
"link": "https://afdian.net/xxxxx",
"qrcode": [
"/static/images/xxxxxx.png",
"/static/images/xxxxxx.png"
]
},
"bilibili": {
"liveId": 1
}
}
}
}
mkdir shiro && cd shiro
nano docker-compose.yml
services:
shiro:
container_name: shiro
image: innei/shiro:latest
volumes:
- ./.env:/app/.env
- ./public:/app/public
restart: always
environment:
- NEXT_SHARP_PATH=/usr/local/lib/node_modules/sharp
ports:
- 2323:2323
nano .env
NEXT_PUBLIC_API_URL=https://{后端地址}/api/v2
NEXT_PUBLIC_GATEWAY_URL=https://{后端地址}
# 克隆原作者的代码
git clone https://github.com/Innei/Shiro.git
cd Shiro
# 重命名原作者的仓库为 "upstream"
git remote rename origin upstream
# 替换下面的 URL 为你自己刚创建的 GitHub 仓库地址
git remote add origin https://github.com/xxxxxx/xxxxx.git
name: Docker (Main Branch)
on:
workflow_dispatch:
push:
branches:
- main
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: path/to/hash/file.txt
TZ: Asia/Shanghai
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:
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' }}
outputs:
sha_short: ${{ steps.store.outputs.sha_short }}
branch: ${{ steps.store.outputs.branch }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
lfs: true
- name: Set up Image Name
run: |
IMAGE_ID=ghcr.io/${{ github.repository }}
IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]')
echo "IMAGE_ID=$IMAGE_ID" >> $GITHUB_ENV
- name: Login to Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- 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"
- name: Build Docker Image
run: |
docker build --build-arg TZ=Asia/Shanghai -t ${{ env.IMAGE_ID }}:${{ steps.store.outputs.sha_short }} .
- name: Push Docker Image to Github
run: |
docker push ${{ env.IMAGE_ID }}:${{ steps.store.outputs.sha_short }}
- name: Tag Docker Image as latest
run: |
docker tag ${{ env.IMAGE_ID }}:${{ steps.store.outputs.sha_short }} ${{ env.IMAGE_ID }}:latest
- name: Push Docker Image as latest
run: |
docker push ${{ env.IMAGE_ID }}:latest
# 确保分支名为 main
git branch -M main
# 提交第一次修改(可以自定义)
git add .
git commit -m "Hello World"
# 推送到你的仓库
git push -u origin main
mkdir shiro && cd shiro
nano docker-compose.yml
services:
shiro:
container_name: shiro
image: ghcr.io/{你的GitHub ID}/{你的仓库名}:latest
volumes:
- ./.env:/app/.env
- ./public:/app/public
restart: always
environment:
- NEXT_SHARP_PATH=/usr/local/lib/node_modules/sharp
ports:
- 2323:2323
nano .env
NEXT_PUBLIC_API_URL=https://{后端地址}/api/v2
NEXT_PUBLIC_GATEWAY_URL=https://{后端地址}
# 拉取原作者的最新代码
git fetch upstream
# 合并到本地
git merge upstream/main
# 推送到你自己的仓库
git push origin main
# 你的阿里云镜像仓库地址
ALIYUN_REGISTRY: crpi-xxxxxxxxx.cn-shanghai.personal.cr.aliyuncs.com
# 你的命名空间
ALIYUN_NAMESPACE: mintdesu
# 你的仓库名
ALIYUN_REPO: mint-shiroi
- name: Login to Aliyun ACR
uses: docker/login-action@v3
with:
registry: ${{ env.ALIYUN_REGISTRY }}
username: ${{ secrets.ALIYUN_USERNAME }}
password: ${{ secrets.ALIYUN_PASSWORD }}
- name: Tag & Push Image to Aliyun (Hash)
run: |
ALIYUN_IMAGE="${{ env.ALIYUN_REGISTRY }}/${{ env.ALIYUN_NAMESPACE }}/${{ env.ALIYUN_REPO }}:${{ steps.store.outputs.sha_short }}"
docker tag ${{ env.IMAGE_ID }}:${{ steps.store.outputs.sha_short }} $ALIYUN_IMAGE
docker push $ALIYUN_IMAGE
- name: Tag & Push Image to Aliyun (Latest)
run: |
ALIYUN_IMAGE_LATEST="${{ env.ALIYUN_REGISTRY }}/${{ env.ALIYUN_NAMESPACE }}/${{ env.ALIYUN_REPO }}:latest"
docker tag ${{ env.IMAGE_ID }}:latest $ALIYUN_IMAGE_LATEST
docker push $ALIYUN_IMAGE_LATEST