A fast blogging platform with Go, Nuxt, Caddy and Docker.
I built Scrib.ee, a clean blogging platform with Go, Nuxt, Caddy, Docker, Redis, MySQL, and Meilisearch. It focuses on simple publishing, SEO-ready blogs, and Cleo, an AI reading assistant without AI bloat.

Martin Binder
ID @martin
AI without the bloat
Scrib.ee also includes Cleo, an AI reading assistant inside blog posts.
But I don’t want Scrib.ee to become another AI content generator.
The goal is not to generate endless content.
I want AI to help only where it is actually useful: helping readers understand posts better, asking questions about the article, and getting more value from what they are already reading.
Tech stack
The current stack is:
-
Go backend
-
Nuxt dashboard
-
Nuxt blog frontend
-
Caddy reverse proxy
-
Docker
-
Docker Compose
-
MySQL
-
Redis
-
Meilisearch
-
GitHub Actions CI/CD
-
VPS deployment
The app is split into multiple services:
app/
├── server/ # Go API
├── client/ # Nuxt dashboard
├── blogclient/ # Nuxt public blog frontend
├── sitemap/ # XML sitemap generator
├── dbadmin/ # database admin utility
└── docker/
└── caddy/ # reverse proxy and routing
Architecture
Scrib.ee has one main Go API and two Nuxt frontends.
The client app is the dashboard where users manage their blog.
The blogclient app renders the public blogs.
Both frontends talk to the same Go backend.
Caddy sits in front of everything and handles routing, domains, subdomains, and HTTPS.
User
↓
Caddy
↓
┌──────────────┬────────────────┐
│ Dashboard │ Public Blogs │
│ Nuxt client │ Nuxt blogclient │
└──────┬───────┴───────┬────────┘
↓ ↓
Go API
↓ ↓ ↓
MySQL Redis Meilisearch
Docker-native deployment
Everything runs with Docker and Docker Compose.
For production, I can start the app with:
docker compose up -d
For development, I use a separate compose file:
make dev
The Makefile also includes commands for common tasks:
env:
@cp -n .env.example .env || true
dev:
@docker compose -f compose-dev.yaml up --build
prod:
@docker compose up -d
deploy:
@git pull origin main
@APP_ENV=prod docker compose up -d --build
@docker compose exec sitemap ./sitemap-worker generate
@docker image prune -f
logs:
@docker compose logs -f $(SERVICE)
ps:
@docker compose ps
down:
@docker compose down
test:
@go test -C ./server ./...
lint:
@cd ./server && golangci-lint run
fmt:
@cd ./server && go fmt
@cd ./client && pnpm run format
@cd ./blogclient && pnpm run format
CI/CD with GitHub Actions
I use GitHub Actions to build and push Docker images to GitHub Container Registry.
The workflow checks which folders changed and only rebuilds the affected services.
For example:
-
changes in
server/**rebuild the Go API -
changes in
client/**rebuild the dashboard -
changes in
blogclient/**rebuild the public blog frontend -
changes in
sitemap/**rebuild the sitemap service
There is also a manual force_rebuild option if I want to rebuild everything from scratch.
After the images are built, the workflow connects to my VPS over SSH and runs:
cd /srv/app
git pull
docker compose pull
APP_ENV=prod APP_VERSION=<short-sha> docker compose up -d
docker image prune -a -f
It is simple, but it works well for a bootstrapped project.
SEO and sitemaps
SEO is an important part of Scrib.ee.
Each blog can generate its own sitemap. The sitemap service is written in Go and creates XML sitemap files for published posts and tags.
It supports splitting large sitemaps into multiple files, using the standard limit of 50,000 URLs per file.
A simplified version looks like this:
const maxURLsPerFile = 50000
For each blog with sitemap enabled, the service:
-
loads the blog domain or subdomain
-
fetches published posts
-
fetches tags
-
writes sitemap XML files
-
writes a sitemap index
The generated URLs include posts like:
https://example.com/posts/my-post
And topics like:
https://example.com/topic/go
Themes with CSS variables
For blog customization, I use CSS variables.
This keeps themes simple and flexible without needing a complex theme engine.
A blog can have different colors, styles, and visual settings, while the frontend still stays fast and maintainable.
The goal is to let users customize their blog without turning Scrib.ee into a huge website builder.
Local development
For local development, Scrib.ee uses subdomain-based routing.
I add local domains to /etc/hosts:
127.0.0.1 scribee.local
127.0.0.1 watches.scribee.local blog.scribee.local
Then I can open:
http://scribee.local
for the dashboard, and:
http://watches.scribee.local
for a local test blog.
Current stage
Scrib.ee is still early, but already has the main structure working:
-
dashboard
-
public blog frontend
-
Go backend
-
Docker deployment
-
CI/CD
-
sitemap generation
-
caching
-
themes
-
Markdown posts
-
Cleo AI reading assistant
I’m preparing for a bigger launch later, but first I want to collect feedback from developers, creators, bloggers, and indie makers.
What I would love feedback on
I would really appreciate feedback on:
-
the idea
-
the technical architecture
-
the UX
-
the positioning
-
the SEO features
-
the AI reading assistant
-
what would make you use a blogging platform today
Final thoughts
I don’t want Scrib.ee to become a bloated website builder or another AI content machine.
I want it to be a clean, fast place where people can write, publish, and share ideas — with AI used only where it actually helps the reader.
Thanks for reading.
Comments
No comments yet.