Add Quartz service for serving Obsidian notes at /notes/
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 2m28s

Replaces the custom Go/Vue notes system with Quartz v4, a polished
static site generator for Obsidian vaults. Mounts OBSIDIAN_DIR as the
Quartz content directory and serves it at /notes/ with hot-reload via
`npx quartz build --serve`.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-06 13:27:10 +01:00
parent 7e8e50f80a
commit b6623de23a
7 changed files with 163 additions and 2 deletions

View File

@@ -31,6 +31,7 @@ services:
- icecast2
- gitea
- hasura
- quartz
networks:
- app-network
ports:
@@ -115,6 +116,19 @@ services:
ports:
- "${LIQUIDSOAP_HARBOR_PORT:-8001}:${LIQUIDSOAP_HARBOR_PORT:-8001}"
quartz:
build:
context: ./quartz
dockerfile: Dockerfile
container_name: "${QUARTZ_HOST}"
restart: always
networks:
- app-network
env_file:
- ./.env
volumes:
- ${OBSIDIAN_DIR}:/quartz/content:ro
gitea-runner:
image: gitea/act_runner:latest
container_name: "${GITEA_RUNNER_HOST}"

View File

@@ -12,12 +12,12 @@ if [ "$DEV_MODE" = "true" ]; then
-out "$CERT_DIR/fullchain.pem" \
-subj "/CN=localhost" 2>/dev/null
fi
envsubst '${DOMAIN} ${BACKEND_HOST} ${BACKEND_PORT} ${BACKEND_ENDPOINT} ${ICECAST_HOST} ${ICECAST_PORT} ${GITEA_HOST} ${GITEA_PORT} ${HASURA_HOST} ${HASURA_PORT}' \
envsubst '${DOMAIN} ${BACKEND_HOST} ${BACKEND_PORT} ${BACKEND_ENDPOINT} ${ICECAST_HOST} ${ICECAST_PORT} ${GITEA_HOST} ${GITEA_PORT} ${HASURA_HOST} ${HASURA_PORT} ${QUARTZ_HOST} ${QUARTZ_PORT}' \
</etc/nginx/nginx_dev.conf.template \
>/etc/nginx/nginx.conf
elif [ -f "/etc/letsencrypt/live/$DOMAIN/fullchain.pem" ] && [ -f "/etc/letsencrypt/live/$DOMAIN/privkey.pem" ]; then
echo "Certificates found. Using production nginx config."
envsubst '${DOMAIN} ${BACKEND_HOST} ${BACKEND_PORT} ${BACKEND_ENDPOINT} ${ICECAST_HOST} ${ICECAST_PORT} ${GITEA_HOST} ${GITEA_PORT} ${HASURA_HOST} ${HASURA_PORT}' \
envsubst '${DOMAIN} ${BACKEND_HOST} ${BACKEND_PORT} ${BACKEND_ENDPOINT} ${ICECAST_HOST} ${ICECAST_PORT} ${GITEA_HOST} ${GITEA_PORT} ${HASURA_HOST} ${HASURA_PORT} ${QUARTZ_HOST} ${QUARTZ_PORT}' \
</etc/nginx/nginx.conf.template \
>/etc/nginx/nginx.conf
else

View File

@@ -217,6 +217,21 @@ http {
proxy_set_header Connection "upgrade";
}
location /notes {
return 301 /notes/;
}
location /notes/ {
proxy_pass http://$QUARTZ_HOST:$QUARTZ_PORT/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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;
}
}
}

View File

@@ -144,6 +144,21 @@ http {
proxy_set_header Connection "upgrade";
}
location /notes {
return 301 /notes/;
}
location /notes/ {
proxy_pass http://$QUARTZ_HOST:$QUARTZ_PORT/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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;
}
}
server {
@@ -253,6 +268,21 @@ http {
proxy_set_header Connection "upgrade";
}
location /notes {
return 301 /notes/;
}
location /notes/ {
proxy_pass http://$QUARTZ_HOST:$QUARTZ_PORT/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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;
}
}
}

18
quartz/Dockerfile Normal file
View File

@@ -0,0 +1,18 @@
FROM node:22-slim
RUN apt-get update \
&& apt-get install --yes git gettext-base \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /quartz
ARG QUARTZ_VERSION=v4.4.0
RUN git clone --depth 1 --branch ${QUARTZ_VERSION} \
https://github.com/jackyzha0/quartz.git . \
&& npm ci
COPY quartz.config.ts.template /quartz/quartz.config.ts.template
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

8
quartz/entrypoint.sh Normal file
View File

@@ -0,0 +1,8 @@
#!/bin/sh
set -e
envsubst '${DOMAIN}' \
< /quartz/quartz.config.ts.template \
> /quartz/quartz.config.ts
exec npx quartz build --serve --port "${QUARTZ_PORT:-8080}"

View File

@@ -0,0 +1,76 @@
import { QuartzConfig } from "./quartz/cfg"
import * as Plugin from "./quartz/plugins"
const config: QuartzConfig = {
configuration: {
pageTitle: "Notes",
pageTitleSuffix: "",
enableSPA: true,
enablePopovers: true,
analytics: null,
locale: "en-GB",
baseUrl: "www.${DOMAIN}/notes",
ignorePatterns: ["private", "templates", ".obsidian"],
defaultDateType: "modified",
theme: {
fontOrigin: "googleFonts",
cdnCaching: true,
typography: {
header: "Schibsted Grotesk",
body: "Source Sans Pro",
code: "IBM Plex Mono",
},
colors: {
lightMode: {
light: "#faf8f8",
lightgray: "#e5e5e5",
gray: "#b8b8b8",
darkgray: "#4e4e4e",
dark: "#2b2b2b",
secondary: "#284b63",
tertiary: "#84a98c",
highlight: "rgba(143, 159, 169, 0.15)",
textHighlight: "#fff23688",
},
darkMode: {
light: "#161618",
lightgray: "#393639",
gray: "#646464",
darkgray: "#d4d4d4",
dark: "#ebebec",
secondary: "#7b97aa",
tertiary: "#84a98c",
highlight: "rgba(143, 159, 169, 0.15)",
textHighlight: "#b3aa0288",
},
},
},
},
plugins: {
transformers: [
Plugin.FrontMatter(),
Plugin.CreatedModifiedDate({ priority: ["frontmatter", "filesystem"] }),
Plugin.SyntaxHighlighting(),
Plugin.ObsidianFlavoredMarkdown({ enableInHtmlEmbed: false }),
Plugin.GitHubFlavoredMarkdown(),
Plugin.TableOfContents(),
Plugin.CrawlLinks({ markdownLinkResolution: "shortest" }),
Plugin.Description(),
Plugin.Latex({ renderEngine: "katex" }),
],
filters: [Plugin.RemoveDrafts()],
emitters: [
Plugin.AliasRedirects(),
Plugin.ComponentResources(),
Plugin.ContentPage(),
Plugin.FolderPage(),
Plugin.TagPage(),
Plugin.ContentIndex({ enableSiteMap: true, enableRSS: true }),
Plugin.Assets(),
Plugin.Static(),
Plugin.NotFoundPage(),
],
},
}
export default config