Chinese Version: 中文
mkBlog is a minimalist personal blog system built with Go, Gin, GORM, Vue 3, and Vite. It serves both frontend and backend from one deployment target, uses SQLite by default, and also supports MySQL and PostgreSQL. VS Code and Obsidian uploader plugins are included for a Markdown-first workflow.
- Create, update, and delete Markdown articles
- Category filtering and pagination
- Article search
- Switchable comments
- Friend links and friend link applications
- Image upload with automatic WebP conversion
- Bearer Token protection for write APIs
- Rate limiting, blacklist mode, and Bloom filter
- TLS / HTTP3 / automatic certificate control
- VS Code / Obsidian uploader workflow
- Go 1.24
- Gin
- GORM
- SQLite / MySQL / PostgreSQL
- Vue 3
- Vite
- Element Plus
- Axios
- markdown-it
- highlight.js
.
├── main.go
├── service/
├── pkg/
├── frontend/
├── plugin/vscode/
├── plugin/obsidian/
├── static/
│ ├── embed.go # Embeds frontend assets
│ └── dist/ # Frontend build output embedded in the Go binary
├── docker/
└── data/
- The server reads runtime config from
./data/config.yaml - If the file does not exist, a default config is generated automatically
- Frontend assets in
static/dist/are embedded in the Go binary and do not need to be deployed separately - Images are stored under
./data/img - An empty database is initialized with a default
Hello Worldarticle
- Go 1.24+
- Node.js 20.19+ or 22.12+ for frontend or plugin builds
- SQLite for the simplest setup
- MySQL with ngram parser for better full-text search
- PostgreSQL with zhparser for Chinese full-text search
The full build creates the frontend output, syncs it to static/dist/, and then
embeds it in a single Go executable:
make build
./build/bin/mkBlogIf you already placed the complete frontend dist/ directory under static/
(at static/dist/), run make backend-build directly.
On first start, mkBlog creates:
./data/config.yaml
./data/app.db
Update ./data/config.yaml and review these fields:
database.kind:sqlite3/mysql/postgresdatabase.host: file name for SQLite, host address for MySQL / PostgreSQLserver.portserver.imageSavePathserver.devmodeserver.http3_enabledtls.enabledauth.enabledauth.secretsite.signaturesite.aboutsite.avatarPath(relative to thedata/directory)site.bgPicturePath(relative to thedata/directory)site.comment_enabledsite.icp
When the data/ directory is created for the first time, the application writes
the embedded default avatar to data/avatar.jpg. Replace that file to switch the
avatar, or set site.avatarPath to another relative file under data/. Existing
avatar files are never overwritten.
cd frontend
npm install
npm run buildAfter building, place the complete frontend/dist/ directory under static/,
then rebuild the Go executable:
make sync-static backend-buildThe resulting build/bin/mkBlog contains all frontend assets.
Default address:
http://127.0.0.1:4801
The frontend always accesses the backend through /api on the current origin, so no separate API address is required.
- Default option
- No extra database service required
- Search falls back to LIKE queries
- Better suited for production deployments
- Search uses FULLTEXT + ngram parser
- Supported
- Chinese search requires zhparser
GET /api/siteGET /api/articlesGET /api/allarticlesGET /api/article/:titleGET /api/searchGET /api/categoriesGET /api/friendsPOST /api/friendsGET /api/commentsPOST /api/comments
PUT /api/article/:titlePUT /api/imageDELETE /api/article/:titlePOST /api/blockip
When auth.enabled: true, admin APIs require:
Authorization: Bearer <your-token>- Images are grouped by article title
- Uploaded non-WebP images are converted to
.webp - In article content, image paths can omit the extension
- If an extension is written manually, use
.webp - The plugins upload Markdown files together with images in same-name folders
docker build -f docker/Dockerfile -t mkblog:latest .
docker run -d --name mkblog -p 4801:4801 -v /etc/mkblog:/app/data mkblog:latestRuntime data inside the container is stored in /app/data.
- Deployments from
mainonly run when code-related directories, Docker files, or GitHub Actions workflows change - The deploy workflow runs
make releaseon the remote server - Publishing a GitHub Release automatically builds and pushes multi-architecture Docker images for
linux/amd64andlinux/arm64 - Changes under
docker/**or.github/workflows/**onmainalso trigger Docker image build and push
- Frontend and backend are served from the same origin
- Enable
tls.enabledif you want HTTPS - Configure
cert_controlif you want automated certificate management - Both VS Code and Obsidian plugins can connect to a remote mkBlog instance through Base URL settings


