Preface
If you've ever tinkered with self-hosting a blog, you know the feeling: tutorial after tutorial, version after version. By the time you've typed your way through some 2024 tutorial, you realize the API moved from v2 to v3 ages ago, your reverse proxy is missing a line of WebSocket config, and the admin panel is a blank white screen.
This article documents my complete from-scratch deployment of Mix Space (Core backend) + Yohaku (official frontend theme) on 1Panel — including the pitfalls I hit and the definitive fix for each. Unlike most "follow-along" tutorials out there, I've written it as a document structured well enough to feed directly to an AI agent for execution: if you use tools like Codex or Claude Code that support long contexts and command-line operations, in theory you only need to feed it this entire article, fill in the basics like the server IP and domain, and it will methodically set up your blog for you.
If you just want to understand the deployment logic and do it by hand, skip the "execution protocol" section and go straight to the architecture and the phase-by-phase steps; if you plan to hand it to an AI to complete fully automatically, read the next section.
How to Use This Article
This article can be read two ways:
As reading material: start from "Overall Architecture" and read downward to understand how the Mix Space + Yohaku + 1Panel combo runs, what problem each step solves, and what pitfalls it can hit. All commands and config files in this article are verified working — you can follow them and run manually.
As a Codex execution script: if you want an AI agent to handle the deployment fully automatically, send it the whole article along with the parameter sheet below (provide sensitive info like passwords temporarily in the conversation; don't write them into any file that would be committed to a repository):
deployment:
mode: fresh
server_ip: "<SERVER_IP>"
ssh_user: root
ssh_port: 22
ssh_password_or_key: "<SECRET_OR_KEY_PATH>"
domain: "<BLOG_DOMAIN>"
install_root: /opt
backend_port: 2333
frontend_port: 2323
upload_limit_mb: 600
timezone: Asia/Shanghai
panel_url: "http://<SERVER_IP>:<PANEL_PORT>/<PANEL_ENTRY>"
panel_username: "<PANEL_USERNAME>"
panel_password: "<SECRET>"
certificate_already_issued: true
github_deploy_repo: "<OWNER>/yohaku-deploy-action"
github_logged_in: true
panel_logged_in: true
owner:
username: "<LOGIN_USERNAME>"
password: "<SECRET>"
nickname: "<DISPLAY_NAME>"
email: "<VALID_EMAIL>"
introduction: "<SHORT_INTRODUCTION>"
avatar_url: "" # 可留空,后续在后台修改
bootstrap_note:
create: true
title: "欢迎来到<DISPLAY_NAME>"
slug: hello-world
The "One-Shot Prompt Template" at the end is the complete instruction I actually used to trigger Codex. Copy it, fill in the parameters, and send.
Security note: this article does not store — and should not be used to write — server passwords, 1Panel passwords, GitHub PATs, database passwords, cookies, or webhook secrets. Every sensitive field in this article is a placeholder.
Overall Architecture: One Domain, Two Services
Mix Space uses a separated frontend/backend architecture: Core handles the "invisible" parts — data, API, authentication, webhooks; Yohaku is the official Next.js frontend theme that renders pages. The two are deployed and updated independently and are stitched into one domain-facing service by a reverse proxy — visitors can't tell that two processes are actually working together behind it.
There are a few deliberate trade-offs in this design worth clarifying up front:
- Core is managed with Docker Compose; Yohaku is not. Core is a stateful service (database, cache), so Compose fits it better; Yohaku is a stateless Next.js app, which is lighter when managed by 1Panel's Node.js runtime and saves you from installing a Node build environment on the server.
- Yohaku's source compilation happens in GitHub Actions; the server only receives the artifacts. This way the server doesn't need pnpm or long build jobs, and every release naturally carries its version number (
releases/<run_number>), making rollback easy. - The database and cache expose no public ports — only Core can access them inside the Docker network.
- Both services use fixed local ports: backend
127.0.0.1:2333, frontend127.0.0.1:2323, and all external traffic enters through the OpenResty reverse proxy.
The fixed directory layout looks like this:
/opt/mix-space/
docker-compose.yml
.env # 600
data/mx-space/
data/postgres/
data/redis/
/opt/yohaku/
.env # 600
.cache/
releases/<run_number>/
current -> releases/<run_number>/standalone/apps/web
server.js -> current/server.js
/opt/1panel/www/conf.d/<domain>.conf
/opt/1panel/www/sites/<domain>/ssl/fullchain.pem
/opt/1panel/www/sites/<domain>/ssl/privkey.pem
/opt/1panel/runtime/node/yohaku/
The routing rules are equally straightforward — except for a few explicit backend paths, everything else goes to the frontend:
| Path | Upstream | Notes |
|---|---|---|
/ and all other regular paths | 127.0.0.1:2323 | Yohaku frontend |
/api/v3 | 127.0.0.1:2333 | Core API |
/ws/ | 127.0.0.1:2333 | Current Core WebSocket |
/socket.io | 127.0.0.1:2333 | Compatibility for older clients |
/render | 127.0.0.1:2333 | Core rendering endpoint |
/proxy | 127.0.0.1:2333 | Static proxy for Core admin backend |
/qaqdmin | 127.0.0.1:2333/proxy/qaqdmin | Short path to the admin panel |
Before You Begin: Verify the Upstream Sources
Mix Space and Yohaku are both under continuous development, and the thing tutorials fear most is being "right then, wrong now." So whether you're doing it yourself or handing it to an AI, the first step isn't typing commands — it's confirming the sources below haven't gone stale:
https://mx-space.js.org/llms.txthttps://mx-space.js.org/llms-full.txthttps://mx-space.js.org/docs/deploy/dockerhttps://mx-space.js.org/docs/deploy/reverse-proxyhttps://mx-space.js.org/docs/deploy/ssl