Skip to main content

Static site

The files a build writes, served by nginx with nothing running behind it

A static site is the files a build writes — Astro, Hugo, Eleventy, Jekyll, a Vite or React single-page application — served by nginx, with nothing running behind it: no PHP, no server of your own, no database. Choose Static site as the application when you order, and the machines run nginx alone. In front of it is the platform's proxy, which terminates TLS and routes the hostname, and the CDN where the environment has one.

What it serves

The release's public/ directory, read-only:

  • a path is a file, a directory's index.html, or the page of that name: /about serves about.html or about/index.html;
  • any other path is a 404, with your own 404.html where public/ has one;
  • styles, scripts, images and fonts go out with a year's Expires and Cache-Control: public, so build them with hashed names, as every bundler does. NGINX_STATIC_EXPIRES shortens it.

A generator that writes somewhere else — dist/ for Vite and Astro, _site/ for Jekyll and Eleventy, out/ for a Next.js export — has its last build step move it there: mv dist public.

A static site

vallic.yaml at the root of the repository:

version: 1
type: static

runtime:
  nginx: '1.31'

services:
  - node: '26'

build:
  steps:
    - name: Site
      image: node
      run: npm ci && npm run build && rm -rf public && mv dist public && rm -rf node_modules

health:
  path: /

The build runs in Node.js, a build tool that never runs beside the site. A site committed as files needs no build at all: put them in public/ and leave build out.

No cache, and node_modules removed. Whatever is in the checkout when the build ends is the release, copied to every machine, and nothing in a static site's release runs its node_modules. npm's downloads are kept between builds for you, so npm ci installs from them without going to the network.

runtime.nginx is the version of nginx that serves it, matched to the current build of it the way a language version is. There is no start and no port: nginx is the server, on its own port.

A redirect, a header or a location of your own goes in .vallic/nginx/server.conf: see Rules of your own.

A single-page application

An application that routes in the browser — React Router, Vue Router — wants every path that is not a file to load its index.html and answer 200, so that the router draws the page. Say so in the server's settings:

services:
  - static:
      version: '1.31'
      environment:
        NGINX_HTML_FALLBACK: index.html

Every entry under services needs a version, and which nginx runs is still runtime.nginx, so keep the two the same. Without the setting, a path with no file is a 404.

Deploy steps, jobs and the shell

Deploy steps, scheduled jobs and vallic ssh run in nginx's container, which has sh, curl and wget and nothing to build with. That is enough to empty the CDN after a release, which is what a static site's deploy usually does: see Clearing the cache from your code. A release goes live without nginx restarting, so no request is dropped.

No database, cache, search engine, queue or mail relay is offered to a static site: nothing behind nginx would ever reach one. A site that needs any of them is an application of its own — Node.js, PHP or another — rather than a static site. The type is chosen when the project is bought, and changing it once a release is serving is a migration rather than a setting; see Configuration.

Versions

Version Status
1.31 Supported, and the default
1.30 Supported

Pin the version, not the build: name 1.31 and the platform matches it to the current build, so a security rebuild reaches you without anybody editing a repository.

What you can change

In vallic.yaml — what each one does is on Service settings:

services:
  - static:
      version: '1.31'
      environment:
        NGINX_KEEPALIVE_TIMEOUT: …
        NGINX_GZIP_COMP_LEVEL: …
        NGINX_STATIC_EXPIRES: …
        NGINX_ERROR_LOG_LEVEL: …
        NGINX_INDEX_FILE: …
        NGINX_HTML_FALLBACK: …

Anything not on this list refuses the deploy, naming the variable — rather than being accepted and quietly ignored.