A self-hosted repository blog system with profiles, project showcases, a message board, and friendly links, using a static frontend and a local C/SQLite backend.
This repository is a reusable self-hosted template for a split deployment:
- static frontend: any static host or CDN
- API backend: local C service exposed through your preferred proxy or tunnel
- data storage: local SQLite + exported JSON mirror + local avatar files
Each page includes a language switcher so visitors can move between the three versions.
This version has been sanitized for public release:
- removed project-specific tunnel UUIDs, hostnames, and local paths
- removed personal admin account data and replaced it with template values
- replaced public API domain examples with placeholders
- replaced example admin credentials with generic starter values that you should change immediately
This repository is derived from the original project by LinusTrevian.
The upstream project and author are already credited in the site "About" pages and remain credited in this public release.
web/static frontend filesbackend/C backend (libmicrohttpd+sqlite3+jansson+curl+openssl)data/warehouse-blog.sqlite3local SQLite databasedata/export/site-state.jsonJSON mirror exportdata/uploads/avatars/local avatar filestools/api_health_logger.pyhelper logger / health checkerbackend/deploy/cloudflared/config.api.example.ymlexample tunnel config
sudo dnf install -y gcc make pkgconf-pkg-config unzip curl ca-certificates libmicrohttpd-devel sqlite-devel jansson-devel libcurl-devel openssl-devel python3sudo apt-get update
sudo apt-get install -y build-essential pkg-config unzip curl ca-certificates libmicrohttpd-dev libsqlite3-dev libjansson-dev libcurl4-openssl-dev libssl-dev python3cd backend
make
cd ..cp ./backend/scripts/server.env.example ./backend/scripts/server.env
set -a
source ./backend/scripts/server.env
set +a
./backend/warehouse-blog-serverOpen local test page:
The frontend uses the following API base resolution order:
window.WAREHOUSE_API_BASEdata-api-baseon the root HTML element- same-origin for
localhost/IP - fallback placeholder:
https://your-api-domain.example
Before production deployment, replace the placeholder with your real API origin.
Upload the contents of web/ to your static hosting provider of choice.
This repository can also be served directly from the server without a tunnel.
Recommended topology:
- Public site: your public domain
- Local backend only:
http://127.0.0.1:8080 - Shared data: the same SQLite database and upload directory in this repository
Run the deployment helper on the server:
bash ./deploy_nginx.sh --domain your-site.exampleAfter DNS for your domain points to the server, enable HTTPS:
sudo certbot --nginx -d your-site.exampleOr let the helper run certbot:
bash ./deploy_nginx.sh --domain your-site.example --certbotFor later updates on the server:
bash ./update_dual_sites.sh your-site.exampleThe local backend port is the internal address Nginx proxies to. The default is 127.0.0.1:8080; visitors do not access this port directly.
- Install
cloudflared cloudflared tunnel logincloudflared tunnel create your-blog-api- Bind
your-api-domain.exampleto that tunnel - Use the example config from
backend/deploy/cloudflared/config.api.example.yml
Example:
cloudflared tunnel route dns your-blog-api your-api-domain.example
cloudflared --config ~/.cloudflared/config.yml tunnel run your-blog-api- For local HTTP testing keep
WB_COOKIE_SECURE=0 - For production HTTPS, change it to
WB_COOKIE_SECURE=1 - Update
WB_ALLOWED_ORIGINSso it only contains your real frontend origins - Leave
WB_COOKIE_DOMAIN=''unless you explicitly want to widen cookie scope
Registration now requires a 6-digit email verification code. Configure SMTP before enabling public registration:
WB_SMTP_URL='smtps://smtp.example.com:465'
WB_SMTP_USERNAME='smtp-user'
WB_SMTP_PASSWORD='smtp-password'
WB_SMTP_FROM='no-reply@example.com'
WB_SMTP_FROM_NAME='Warehouse-Blog'python3 ./tools/api_health_logger.py --local http://127.0.0.1:8080/api/health --public https://your-api-domain.example/api/health --profile https://your-api-domain.example/api/site-profile?lang=enThe logger writes to ./logs/api-health.log by default.