Self-Hosted Hugo Comments with Private Gitea
Most static sites rely on third-party comment systems. While convenient, they introduce external dependencies, tracking, and long-term uncertainty.
Instead, I implemented a self-hosted comment system for Hugo backed by a private Gitea instance. The result is lightweight, secure, and fully under my control.
Architecture Overview
Key points:
- Hugo renders static content
- Only
/comments/is proxied to the bridge - Gitea remains on a private VLAN
- The public site never communicates directly with Gitea
How It Works
Each post slug maps to a Gitea issue.
If the issue does not exist, the bridge creates one automatically via the Gitea API:
| |
New comments are appended to that issue. Gitea effectively becomes the comment database — without introducing a separate database stack.
The Bridge Service
A small Flask application handles:
GET /comments/<slug>→ fetch commentsPOST /comments/<slug>→ create comment
It runs in Docker and communicates with Gitea using a scoped API token. No credentials are ever exposed client-side.
Nginx Isolation
Only comment traffic is proxied:
| |
Everything else remains static Hugo output, keeping the attack surface minimal.
Front-End Integration
A Hugo shortcode renders the comment UI and posts data using fetch():
| |
The layout injects the comment block automatically, so comments appear on all posts without modifying each Markdown file.
Security Model
The system is designed with containment in mind:
- Gitea runs on an internal VLAN
- No public repository exposure
- Limited-scope API token
- Reverse proxy isolation
- No external SaaS dependencies
Moderation happens directly inside Gitea by editing or deleting issue comments.
Why This Approach Works
This setup avoids:
- Third-party tracking
- External comment providers
- Database management overhead
- Over-engineering
It reuses infrastructure already present in a homelab:
- Git
- Issues
- Docker
- Nginx
Static where possible.
Dynamic only where necessary.
Private by default.
Comments