Canonical deployment guide for hosting this Quartz site and its live dev environment on 178.18.250.170.
Deployment Architecture
Utilizes Nginx as a reverse proxy (TLS, HTTP routing, WS upgrades) and systemd user services for background Node.js processes.
1. Nginx Configuration & Port Split Design
HMR WebSockets share domains and SSL certificates by proxying upgrades inside HTTPS server blocks:
- Tinker Bench (Dev): Port
3002 ssl-> Local WS3003. - Quartz q5vault Sandbox: Port
3005 ssl-> Local WS3004. - Udytor q5vault Wiki: Port
3008 ssl-> Local WS3008.
Private services are secured via basic auth (/etc/nginx/.htpasswd_q5vault).
A. Main Site (/etc/nginx/sites-available/quartz.loca.zone)
server {
server_name quartz.loca.zone;
root /var/www/quartz-loca/current;
location / { try_files $uri $uri.html $uri/ =404; }
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/quartz.loca.zone/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/quartz.loca.zone/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}B. Tinker Bench (/etc/nginx/sites-available/dev.quartz.loca.zone)
server {
server_name dev.quartz.loca.zone;
auth_basic "Private Tinker Bench";
auth_basic_user_file /etc/nginx/.htpasswd_q5vault;
satisfy any;
allow 127.0.0.1;
allow 178.18.250.170;
deny all;
location /api/deploy {
proxy_pass http://127.0.0.1:8084;
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;
}
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;
}
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/quartz.loca.zone/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/quartz.loca.zone/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}
server {
listen 3002 ssl;
server_name dev.quartz.loca.zone;
ssl_certificate /etc/letsencrypt/live/quartz.loca.zone/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/quartz.loca.zone/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
allow 127.0.0.1;
allow 178.18.250.170;
deny all;
location / {
proxy_pass http://127.0.0.1:3003;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}C. Independent Sandbox (/etc/nginx/sites-available/q5vault.quartz.loca.zone)
server {
server_name q5vault.quartz.loca.zone;
location / {
proxy_pass http://127.0.0.1:8087;
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;
auth_basic "Private Vault";
auth_basic_user_file /etc/nginx/.htpasswd_q5vault;
}
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/quartz.loca.zone/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/quartz.loca.zone/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}
server {
listen 3005 ssl;
server_name q5vault.quartz.loca.zone;
ssl_certificate /etc/letsencrypt/live/quartz.loca.zone/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/quartz.loca.zone/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
location / {
proxy_pass http://127.0.0.1:3004;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
auth_basic "Private Vault WS";
auth_basic_user_file /etc/nginx/.htpasswd_q5vault;
}
}D. Udytor q5vault Wiki (/etc/nginx/sites-available/wiki.udyt.loca.zone)
server {
server_name wiki.udyt.loca.zone;
location / {
proxy_pass http://127.0.0.1:8083;
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;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/udyt.loca.zone/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/udyt.loca.zone/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}
server {
listen 3008 ssl;
server_name wiki.udyt.loca.zone;
ssl_certificate /etc/letsencrypt/live/udyt.loca.zone/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/udyt.loca.zone/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
location / {
proxy_pass http://127.0.0.1:3008;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}2. Letβs Encrypt Certificates
sudo certbot --nginx -d quartz.loca.zone -d dev.quartz.loca.zone -d q5vault.quartz.loca.zone3. systemd Services
Configured in ~/.config/systemd/user/.
A. Tinker Bench: quartz-tinker.service (Port 8081)
[Unit]
Description=Quartz Tinker Bench Server
After=network.target
[Service]
Type=simple
WorkingDirectory=/home/loca/dev/quartz/quartz
Environment=PATH=/home/loca/dev/quartz/quartz/node-v22.16.0-linux-x64/bin:/usr/local/bin:/usr/bin:/bin
ExecStart=/home/loca/dev/quartz/quartz/node-v22.16.0-linux-x64/bin/npm run serve:dev
Restart=on-failure
RestartSec=5
[Install]
WantedBy=default.targetB. Deployer API: quartz-deployer.service (Port 8084)
[Unit]
Description=Quartz Deploy API
After=network.target
[Service]
Type=simple
WorkingDirectory=/home/loca/dev/quartz/quartz
Environment=PATH=/home/loca/dev/quartz/quartz/node-v22.16.0-linux-x64/bin:/usr/local/bin:/usr/bin:/bin
ExecStart=/home/loca/dev/quartz/quartz/node-v22.16.0-linux-x64/bin/node scripts/workflowz/deploy-api.js
Restart=on-failure
RestartSec=5
[Install]
WantedBy=default.targetC. Standalone Vault: quartz-q5vault.service (Port 8087)
[Unit]
Description=Quartz Private Vault
After=network.target
[Service]
Type=simple
WorkingDirectory=/home/loca/dev/quartz/quartz
Environment=PATH=/home/loca/dev/quartz/quartz/node-v22.16.0-linux-x64/bin:/usr/local/bin:/usr/bin:/bin
ExecStart=/home/loca/dev/quartz/quartz/node-v22.16.0-linux-x64/bin/npm run serve:q5vault
Restart=on-failure
RestartSec=5
[Install]
WantedBy=default.targetD. Udytor q5vault Wiki: udyt-q5vault.service (Port 8083)
[Unit]
Description=Udytor Private Vault
After=network.target
[Service]
Type=simple
WorkingDirectory=/home/loca/dev/udytor
Environment=PATH=/home/loca/dev/quartz/quartz/node-v22.16.0-linux-x64/bin:/usr/local/bin:/usr/bin:/bin
ExecStart=/home/loca/dev/quartz/quartz/node-v22.16.0-linux-x64/bin/npm run serve:q5vault
Restart=on-failure
RestartSec=5
[Install]
WantedBy=default.targetManagement Commands:
systemctl --user daemon-reload
systemctl --user enable --now quartz-tinker quartz-deployer quartz-q5vault udyt-q5vault
systemctl --user restart quartz-tinkerDomain Responsibilities & Subdomain Map
| Domain / Subdomain | Primary Purpose / Responsibility | Port | Backend Service / Path |
|---|---|---|---|
quartz.loca.zone | Production homelab Quartz site (Static) | 443 | Static /var/www/quartz-loca/current |
dev.quartz.loca.zone | Quartz Homelab Live Tinker Bench (Dev) | 443 / 3002 | quartz-tinker.service (Node port 8081 / WS 3003) |
q5vault.quartz.loca.zone | Independent Sandbox for q5vault (Dev) | 443 / 3005 | quartz-q5vault.service (Node port 8087 / WS 3004) |
tree.loca.zone | Python Knowledge Tree application | 443 | knowledge-tree.service (Python port 8080) |
wiki.tree.loca.zone | Knowledge Tree Quartz static wiki | 443 | tree-wiki.service (static files served on port 8085) |
wiki.less.loca.zone | Paperless-ngx Quartz static wiki | 443 | less-wiki.service (static files served on port 8095) |
wiki.trlm.loca.zone | Trilium Notes Quartz static wiki | 443 | trlm-wiki.service (static files served on port 8105) |
wiki.surf.loca.zone | Surfboard Quartz static wiki | 443 | surf-wiki.service (static files served on port 8205) |
wiki.khoj.loca.zone | Khoj Quartz static wiki | 443 | khoj-wiki.service (static files served on port 8305) |
wiki.yuan.loca.zone | Yuan Quartz static wiki | 443 | yuan-wiki.service (static files served on port 8405) |
wiki.opno.loca.zone | Open WebUI Quartz static wiki | 443 | opno-wiki.service (static files served on port 8415) |
wiki.allm.loca.zone | AnythingLLM Quartz static wiki | 443 | allm-wiki.service (static files served on port 8425) |
udyt.loca.zone | Production Udytor editor SPA | 443 | Static /home/loca/dev/udytor/dist |
udytor.loca.zone | Production Udytor editor SPA (Alias) | 443 | Static /home/loca/dev/udytor/dist |
wiki.udyt.loca.zone | Udytor q5vault live dev/serve server | 443 / 3008 | udyt-q5vault.service (Node port 8083 / WS 3008) |
wiki.omp.loca.zone | OMP Single Source of Truth Wiki | 80 | Quartz serve process (port 8088 / WS 3006) |
omp.loca.zone | OMP local proxy / console dashboard | 80 | OMP dashboard process (port 8086 - offline) |
os.loca.zone | Operating System main landing app | 443 | OS backend service (port 44100) |
api.os.loca.zone | OS API service | 443 | OS API service (port 44100) |
app.os.loca.zone | OS client application wrapper | 443 | OS app wrapper (port 44100) |
dev.os.loca.zone | OS development environment | 443 | OS dev service (port 44100) |
s3.os.loca.zone | OS MinIO S3 console/API | 443 | MinIO S3 service (port 34900) |
site.os.loca.zone | OS static site / portal | 443 | OS portal service (port 44100) |
Static & Wiki Workflows
- Static SPAs / Sites:
udyt.loca.zone/udytor.loca.zoneserve files directly from/home/loca/dev/udytor/dist.quartz.loca.zoneserves static HTML from/var/www/quartz-loca/current.
- Live Quartz Dev Servers:
wiki.udyt.loca.zonerunsnpx quartz build --serveon port 8083 (watchesdev/udytor/q5vault, WS port 3008).dev.quartz.loca.zoneruns dev wiki on port 8081 (WS port 3003).
- Static Wikis (Served via Systemd):
wiki.tree.loca.zoneserved vianpx serve -s quartz-dist-tree -l 8085. Built usingdev/udytor/scripts/quartz-tree-build.sh.
Subdomain Diagnostics
A. dev.wiki.udyt.loca.zone 404/403 Root Cause
- Symptom:
dev.wiki.udyt.loca.zoneHTTP returns 404; HTTPS returns 403. - Root Cause: Domain is missing from Nginx config.
- HTTP hits default port 80 server block (proxies to Docker on 4173 which 404s).
- HTTPS falls back to first loaded SSL block, triggering local-only ACL access blocks (403).
- Resolution: Targeted HMR dev script to
wiki.udyt.loca.zonedirectly.
B. Subdomain Port Conflict
- Symptom: Conflict on port
8083betweenwiki.udyt.loca.zoneandq5vault.quartz.loca.zone. - Resolution:
- Remap
q5vault.quartz.loca.zoneto proxy to local port8087(WS3004). - Maintain
wiki.udyt.loca.zoneon port8083(WS3008). - Run separate systemd units:
udyt-q5vault.service(port 8083) andquartz-q5vault.service(port 8087).
- Remap
C. wiki.tree.loca.zone Directory Listing Issue
- Symptom: Server returned directory index/file list instead of rendered wiki.
- Root Cause: Quartz build ignored
q5vault-tree/due to.gitignorerules, resulting in an empty build directory. - Resolution: Updated
quartz-tree-build.shto temporarily stripq5vault-tree/from.gitignoreduringnpx quartz build, outputting proper HTML before restartingtree-wiki.service.