The initial goal was simple: build a personal site for long-term writing using the Yohaku theme and Mix Space Core.
Final Setup
Here's the current structure:
The image omits two things deliberately kept separate: content is always maintained in the Mix Space admin, while infrastructure and source code are managed via Git and CI. This way, writing articles doesn't require rebuilding the image; only frontend changes trigger the release process.
Here's the current structure:
8cat.life: Yohaku frontend, running in a Docker container on a Tencent Cloud Lighthouse server.api.8cat.life: Mix Space Core, reverse-proxied by Caddy to the Core service.- Core dependencies: PostgreSQL, Redis, and COS object storage.
- Frontend image: CNB builds the Docker image and pushes it to Tencent Cloud's personal image registry TCR.
- Auto-deployment: CNB uses Tencent Cloud TAT to invoke the automation agent on the Lighthouse server, which pulls the specified image and rebuilds the frontend container.
The key here is separating "building the image" from "running the image": CNB handles the build, and the server only pulls and runs the already-built version.
The Road Taken
1. Initially Tried Deploying Directly to EdgeOne Pages
Yohaku is a Next.js SSR project. EdgeOne Pages can recognize Next.js, but during the actual build, Next.js 16's Turbopack/webpack process was forcibly terminated by the platform's memory limit: the logs show SIGKILL and a memory cap of about 6 GiB.
Even switching the build from Turbopack to webpack still resulted in termination; additionally, EdgeOne's runtime had compatibility issues with the project's middleware/proxy. So I didn't continue hard-coding routes on the production branch to accommodate the platform.
Trade-off: EdgeOne is great for static sites and lightweight frameworks, but the current Yohaku SSR build is too heavy for that path. Keep the test branch; switch production to Docker.
2. GitHub Actions Can Build, but Isn't Suitable as the Final Release Pipeline
GitHub Actions successfully built Yohaku, confirming that the source code, submodules, and the Next.js build itself have no fundamental issues.
But after putting the image on GHCR, pulling a cross-border image on the Tencent Cloud server was slow; and having a GitHub Runner directly SSH into the server for deployment would bring unfamiliar IP alerts and long-term credential management issues.
Trade-off: Keep GitHub as the main source repository; move image building and server deployment into the Tencent Cloud ecosystem.
3. CNB, TCR, and TAT for Automated Deployment
Finally, I used CNB for Docker builds, pushing the artifacts to a private TCR. The image uses both an immutable commit SHA tag and latest; the server actually deploys the SHA tag, making it easy to track "which commit the site is running." Deployment isn't about "recompiling code on the server" but about getting an already-verified, finished image.
This step also hit a few pitfalls:
- When CNB used a domestic npm mirror, some packages weren't yet synced on the mirror site, resulting in 404s; those packages needed to fall back to the official npm registry.
- The
git: not founderror in the Docker build came from hooks during the installation phase; the container build doesn't have a.gitdirectory, so it's harmless noise, not a build failure. - TAT initially used the wrong CVM-style instance ID; the Lighthouse server actually requires an instance ID like
lhins-.... - TAT CAM permissions, besides
RunCommand, also need permissions to query invocations and task status; missing one results in "command sent but pipeline still fails."
Finally, after CNB completes the build, it calls TAT to have the server: pull the image for the specified SHA, update the compose service, and rebuild the yohaku container.
Content and Theme Configuration
"Pages" in Mix Space are regular CMS content, not Yohaku theme files. So the initial 404s for the /about, /about-site, and /message links in the footer were straightforward: the corresponding pages hadn't been created yet.
Currently created:
- About Me:
/about - About This Site:
/about-site - Messages:
/message
Yohaku's homepage and footer visual configuration live in the theme/shiro JSON under "Code Snippets" in the admin, not in the theme.json at the project root. This is where you configure the homepage title, intro, hitokoto, favicon, and footer links.
Images, Favicon, and Email
- Images use Tencent Cloud COS. Core currently uses a single S3 connection configuration; blog post images and comment images are isolated via different object path prefixes; the access key is granted only the minimum permissions needed for that bucket.
- The favicon was migrated from the old site; the frontend provides a 512px original for the browser to scale. When the theme retains the old path, the frontend gracefully falls back to this high-resolution resource.
- Email subscriptions use Resend. The sending domain is
updates.8cat.life, e.g., the sender addressnewsletter@updates.8cat.life. The API key is stored only in the Core admin configuration, not in the frontend or the code repository.
Limitations to Keep in Mind
Google Comment Login
Google OAuth browser authorization can be completed, but the server also needs to access oauth2.googleapis.com to exchange the authorization code for a token. The Core container on the Tencent Cloud server times out connecting to port 443 on that address, so it ultimately shows invalid_code.
This isn't a missing Google callback address. To enable Google login, you'd need to configure an HTTPS proxy for Core that can reliably reach Google; for now, keeping the working GitHub login is more appropriate.
import { createServer } from 'node:http'
const server = createServer() // [!code highlight]
server.listen(3000)
console.log('ready')