Hosting on loca.zone
This is the canonical deployment guide for hosting this Quartz site and its live dev environment on 178.18.250.170. The configuration below has been successfully deployed.
Deployment Architecture
1. Nginx Configuration
The file /etc/nginx/sites-available/quartz.loca.zone contains:
# HTTP redirects will be handled by certbot automatically
server_name quartz.loca.zone;
root /var/www/quartz-loca/current;
# Static files pattern
location / {
try_files $uri $uri.html $uri/ =404;
}
}
server {
server_name dev.quartz.loca.zone;
# Proxy HTTP to local Quartz server
location / {
proxy_pass http://127.0.0.1:8081;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}2. Let’s Encrypt Certificates
Certificates were generated using:
sudo certbot --nginx -d quartz.loca.zone -d dev.quartz.loca.zone3. Stream Block for Hot-Reload (WebSockets)
Certbot doesn’t configure stream blocks for WebSockets over TLS, so the following was appended to /etc/nginx/nginx.conf (outside the http { ... } block):
stream {
server {
listen 3002 ssl;
ssl_certificate /etc/letsencrypt/live/quartz.loca.zone/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/quartz.loca.zone/privkey.pem;
# Proxy to local Quartz WebSocket port (3003)
proxy_pass 127.0.0.1:3003;
}
}4. Quartz Dev Command
npm run serve:dev
# HTTP :8081 (proxied by Nginx)
# WebSocket server binds :3003 (--wsPort)
# Browser connects to wss://dev.quartz.loca.zone:3002 (--wsPublicPort via Nginx stream)Alternative Hosting Options
Alternative A — Local-only DNS
Use /etc/hosts or Pi-hole/dnsmasq on your LAN. TLS would be HTTP-only or require mkcert.
For hot reload, omit --remoteDevHost and connect to ws://localhost:3002.
Not chosen: No trusted public HTTPS.
Alternative B — Split-horizon DNS
Internal resolver returns LAN IP, public returns none. Not chosen: Public reference would be unreachable without a VPN.
Alternative C — Tailscale / WireGuard MagicDNS
Use the mesh hostname (no public A record). Set --remoteDevHost to your MagicDNS name.
Not chosen: Limits access to the VPN mesh.
Alternative D — Cloudflare Tunnel
Run cloudflared to :8081. You would need a separate TCP ingress for the WebSocket port 3002.
Not chosen: More moving parts than using Nginx on an owned VPS.
Alternative E — Docker-only
Use the repo’s Dockerfile to run npx quartz build --serve on 8080.
Not chosen: Lack of persistent homelab Nginx integration.
Alternative F — Static-only
Skip the dev subdomain and systemd service completely; run local npm run serve:docs only when needed.
Not chosen: The goal is a persistent live tinker bench.