Notes/note

How this site is built

This site is intentionally small. Most of what it publishes is static content, so the architecture treats it that way.

Static by default

Astro produces the site at build time. What deploys is a directory of HTML, CSS and images. There is no database, CMS, or application runtime in the request path; the site’s current requirements don’t need them.

The production host reflects that. It runs nginx, sshd and the base system, with no node, npm, git, docker, php or database binaries installed. A prebuilt site is uploaded into timestamped release directories; deployment switches an atomic current symlink, and nginx serves that release. The host serves the site; it does not build it.

The browser side follows the same rule. One same-origin script provides a theme toggle and a search dialog over a static JSON index; everything else is HTML and CSS. Every page is readable and navigable with that file blocked. The limited browser surface also allows a restrictive default-src 'none' content security policy without complicating normal site behaviour. The same constraint is why videos are linked rather than embedded — a third-party player frame would mean relaxing that policy on every page it appears on, which is not worth the functionality it adds here.

Dynamic only where necessary

One capability genuinely needs a server: the contact form has to accept a POST and deliver it somewhere. That requirement is isolated to the contact path rather than introducing a runtime for the rest of the site. Submissions post to a small Worker that validates them and forwards to a delivery provider, which keeps the destination address out of the page source and makes changing providers a secret rotation rather than a rebuild.

The site renders the form only when a real endpoint is configured. An unconfigured build ships no form rather than one that fails on submit.

Small operational surface

The consequences are ordinary. There is no application dependency tree to patch on the server, because there is no application. The static serving path requires no application credentials; the credentials associated with deployment and the contact service remain outside it. Rolling back is repointing a symlink at the previous release directory, which is still on disk. Operating cost is small and predictable: a fixed hosting instance, DNS, and the isolated contact service.

This is not the same as no maintenance. The operating system still takes updates, TLS certificates still renew, and the Worker is a service that can fail independently of the site.

What it costs

The design has two notable operational tradeoffs.

The social preview image is generated — an SVG rendered to PNG during the build — and has to be regenerated by hand when the headline changes. Nothing enforces that it was, so it can drift out of date without any visible signal.

The contact path is the one component that is not a file on a disk. It has its own runtime, its own deploy step and its own failure mode, and it can break without the site appearing to change.

What would change the architecture

Authenticated users, server-side state that changes between builds, per-visitor personalisation, or content volume large enough that a full rebuild per change stops being reasonable. Those requirements would justify a different architecture. Until they exist, the additional infrastructure would add operational overhead without serving a current need.

Sources

  1. astro.config.mjs
  2. infra/nginx/vtchevalier.com.conf
  3. worker/contact-form/src/index.js
  4. docs/deployment.md
ESC
↑↓ navigate↵ open