Write and publish documentation
Documentation source lives in docs/; the Astro Starlight site, CSS, header, and container live in site/. The content loader reads Markdown and MDX directly from the repository-level documentation folder.
Content structure
docs/├── index.md├── user-guide/│ ├── index.md│ └── nextcloud.md├── infrastructure/│ ├── index.md│ ├── platform.md│ └── nextcloud.md└── admin-guide/ ├── index.md ├── bootstrap.md ├── nextcloud.md └── nextcloud-upgrades.mdA new deployed service gets the same slug in each folder. Use its overview for purpose, access, user workflows, and official manuals; infrastructure for component paths, state, and failure boundaries; administration for prerequisites, exact manifest inputs, identity/integrations, and recovery.
Keep shared workflows in the administrator section and link to them from app pages. Put complex application-specific procedures, such as Nextcloud upgrades, beside the main app guide in that same section.
Author a page
Start with block-style frontmatter:
---title: Example service · Usedescription: What the service does, how to start using it, and where to find its official manual.---Use task-based headings, concise introductions, and sequential steps with expected outcomes. Keep current versions in the shared variables reference instead of embedding a historical upgrade as a permanent instruction. Discover pod names in examples; never retain a pod name copied from an incident.
Shared reading paths and runbook links are configured in site/astro.config.mjs. Service groups live in site/src/navigation.mjs; serviceNavigation() adds the same service slugs to all three perspectives. Add a service to that registry once, create its three pages, and add its row to docs/user-guide/services.md.
Section landing pages are task-based reading paths, not repeated application inventories. Keep the directory as the complete list. Each service page includes a guide-switcher labeled Use, Architecture, and Operate, linking to the same slug in all three folders.
User pages should explain access, one useful first workflow, what to report when it fails, and official product documentation. Keep pod names, database elections, and infrastructure repair commands in the architecture or admin guide. Distinguish the product’s own manual from a community plugin’s documentation, as in Obsidian LiveSync.
Admin pages should state prerequisites, exact configuration ownership, deployment order, manual integration work, and the data needed for recovery. Link to official installation and upgrade guidance for the selected version. Describe checked-in configuration as desired state, and date any separate operational observation.
Diagrams that render without a runtime
Architecture diagrams use semantic HTML figures styled by site/src/styles/antalos.css. The three columns describe an entry/declaration boundary, a serving/controller boundary, and the dependencies or consumers. They become a vertical flow on small screens and inherit the light/dark theme. The labels remain selectable text and readable without JavaScript.
<figure class="architecture-diagram" aria-label="Example service request path"> <div class="diagram-heading">Example service · request path</div> <ol class="diagram-flow" role="list"> <li class="diagram-stage"> <span class="diagram-label">Entry</span> <ul><li>HTTPS ingress</li></ul> </li> <li class="diagram-stage"> <span class="diagram-label">Application</span> <ul><li>Two web replicas</li></ul> </li> <li class="diagram-stage"> <span class="diagram-label">Persistence</span> <ul><li>Primary / standby database</li></ul> </li> </ol> <figcaption>Requests reach a healthy web replica; writes use the database service.</figcaption></figure>In Markdown, leave a blank line before and after the complete HTML block, with no blank lines inside it. Keep topology-specific details in both the diagram and accompanying prose. A third voter must be labeled separately from a data replica.
No Mermaid rendering integration is installed. A mermaid fence would display source text, so use the existing diagram pattern or deliberately add a renderer as a separately reviewed site change. See Starlight’s content-authoring reference for supported Markdown features.
Code examples
Use fenced code blocks with the actual language and an informative title="…" attribute. Expressive Code, included with Starlight, supplies syntax highlighting and copy controls. Keep commands copyable by omitting shell prompts and placing expected output in a separate block.
Use block-style YAML, quote shell variables, state the working directory, and make placeholders obvious. Never use a code block merely to highlight a hostname or file path when inline code is clearer. Keep credentials out of examples and write private outputs outside Git.
Layout and theme
Starlight owns the left sidebar, article column, responsive breakpoints, and On this page placement. The custom stylesheet sets color, readable article width, typography, cards, tables, and diagram appearance. It does not pin the TOC to the viewport edge or override its container width.
Adjust supported tokens such as --sl-content-width instead of adding competing .main-pane or .right-sidebar positioning rules. See Starlight CSS customization. Preserve keyboard focus, reduced-motion support, and light/dark contrast when changing the theme.
site/src/components/Header.astro keeps the antalos logo, centered search, GitHub link, and light/dark theme button visible in the header. Avoid overriding the content layout just to align a header element.
Develop locally
Use the Node version family and package-manager version declared by the site build inputs. From the repository root:
cd sitepnpm install --frozen-lockfilepnpm devThe local URL is printed by Astro. The loader watches content from ../docs. Builds and previews are available when validation is authorized for the task:
cd sitepnpm run buildpnpm previewThe build runs astro check before astro build. Do not commit node_modules or dist. Follow repository instructions about checks and publication; a documentation edit does not automatically authorize running every command in this guide.
Publish through the existing pipeline
.github/workflows/docs-image.yaml builds the repository-root Docker context using site/Dockerfile. GitHub Actions publishes the main-branch image and an immutable commit tag. apps/docs/ deploys the selected image with NGINX, a Service, ingress, disruption budget, and certificate.
For a manual local container build, supply the actual shared variable values rather than copying an old tag:
DOCS_BUILD_NODE_TAG='REPLACE_WITH_DOCS_NODE_IMAGE_TAG'DOCS_BUILD_NGINX_TAG='REPLACE_WITH_DOCS_NGINX_IMAGE_TAG'
docker build \ --build-arg "NODE_IMAGE_TAG=$DOCS_BUILD_NODE_TAG" \ --build-arg "NGINX_IMAGE_TAG=$DOCS_BUILD_NGINX_TAG" \ --tag antalos-docs:local \ --file site/Dockerfile \ .Deploy a published immutable tag through DOCS_IMAGE_TAG for repeatable releases. The checked-in rollout uses one surge pod and zero unavailable replicas, so sufficient eligible placement is required. /healthz reports NGINX health; it does not validate page content.
Preserve old links
When moving a page, update current navigation and internal links, then add its old URL to redirects in site/astro.config.mjs. Static Astro output provides a redirect page. Existing matching exact locations in site/nginx.conf provide HTTP 301 redirects for the container deployment, including old URLs with and without a trailing slash.
Keep these mappings together when moving a route again. Remove the old source only after its content has a destination, so it does not shadow the redirect with a duplicate page.
