Built with Chinaski

Deploy to nginx

A production recipe for putting a Chinaski-built site behind nginx: serve the static output, proxy the few dynamic routes back to the CMS daemon, add TLS, and let deploys keep everything in sync.

Prerequisites

  • A running CMS instance (the chinaski@<N> unit) with the daemon bound to loopback.
  • A built output directory at /var/www/chinaski<N>/output/<lang> (or output/ for a monolingual site with no language prefix).
  • The repo’s example vhost (deploy/chinaski.nginx.example for a minimal static site; chinaski.nginx.conf.example for the fuller per-instance pattern).

The three values to replace

In the copied vhost, set:

  • YOUR_DOMAIN: the site’s public hostname.
  • OUTPUT_PATH: the per-language output directory from above.
  • CMS_PORT: the port matching LISTEN_URL in /etc/chinaski<N>/env (3001 for instance 1 by default).

Activation:

ln -s /etc/nginx/sites-available/YOUR_DOMAIN /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx

Then add TLS:

certbot --nginx -d YOUR_DOMAIN

The two proxy blocks you must not omit

A “static” Chinaski site has exactly two URL families that have to reach the CMS daemon:

  1. /forms/: form placeholders embed into built pages, but submissions POST back to the CMS. Without this block every form submission returns 404.
  2. /documents/: document downloads are served by the CMS (tracked download counts, publish windows). Without this block every document URL returns 404.

Both blocks forward the standard headers (Host, X-Real-IP, X-Forwarded-For, X-Forwarded-Proto) so the CMS sees the real client, needed for its rate limiting and logging to work per visitor.

Denote admin traffic separately: the admin interface lives on its own vhost (or the CMS’s own loopback port if you don’t expose it publicly). The checklist pattern proxying from loopback keeps the CMS’s trusted-proxy logic simple: no extra CHINASKI_TRUSTED_PROXIES setting is needed because the connection comes from loopback. If your architecture makes nginx reach the daemon from a non-loopback address (e.g. a container bridge), add that network to CHINASKI_TRUSTED_PROXIES.

Caching and headers

The example vhost sets cache lifetimes tuned to Chinaski’s publish rhythm:

  • HTML: short cache (revalidate quickly after each build).
  • sw.js: no-cache, must-revalidate.
  • Fonts: year-long immutable caching.
  • Other assets: one-day cache.
  • Security headers (X-Frame-Options, nosniff, Referrer-Policy, Permissions-Policy, HSTS 1y), belt-and-braces alongside the headers the app already sets.

Language vhosts

For multilingual sites, duplicate the server block per language: each language’s subdomain or path vhead serves its own output directory (e.g. es.example.com → /var/www/chinaski1/output/es), each with its own /forms/ block pointed at the same upstream.

The deploy-time root sync

deploy/deploy.sh sweeps nginx configs after each deploy: it finds the root directives that point at this instance’s output tree and rewrites them to whatever the current language mode requires (output/ for monolingual sites, output/<default language>/ otherwise), runs nginx -t, and reloads on success, with a warning (no reload) when the config test fails so the error can be fixed by hand. If you are following the example layout, much of nginx upkeep becomes zero-touch on each deploy.

Read this site in