GioJSdocs
On this page

giojs-server

The Rust server binary and its launcher bin: start the server with the caller's NODE_ENV, or validate the configuration with --check-config and print a JSON report.

npx giojs-server --check-config
bash
giojs-server                  # start the server; NODE_ENV is the caller's
giojs-server --check-config   # validate .env files + gio.toml, print JSON, exit

Reference

Two programs share the name. @gio.js/server installs a giojs-server bin (bin/giojs-server.js), a launcher, and the platform package @gio.js/server-<platform> holds the Rust binary it runs.

ParameterTypeDefaultDescription
--check-configflag-Validate and exit instead of serving (see below). Recognized only on its own.
other argumentsstring-Passed through by the launcher and refused by the binary: it prints unexpected argument and exits with 2 (a usage error, as for gio) without starting. Everything else is configured with gio.toml and environment variables, and gio --version prints the versions.

The launcher

giojs-server (the bin) starts the server with no command parsing, as projects scaffolded with create-giojs do from their scripts:

package.json
{
  "scripts": {
    "dev": "cross-env NODE_ENV=development giojs-server",
    "start": "cross-env NODE_ENV=production giojs-server"
  }
}
  • It finds the binary like gio does (GIO_SERVER_BIN, else the platform package) and exits with 1 and the package to install when there is none.
  • It keeps the caller's NODE_ENV: development runs dev mode, anything else (or unset) production.
  • It tells the server where the Node worker's entry and tsx are (GIO_NODE_SCRIPT, GIO_TSX_PKG), holds the server's stdin pipe so the server exits if the launcher dies (GIO_EXIT_ON_STDIN_EOF=1), forwards SIGINT / SIGTERM on Unix, and exits with the server's exit code.
  • Unlike gio dev / gio start, it prints no ready banner, takes no --port / --host (set GIO_PORT / GIO_HOST) and does not set NODE_ENV.

The binary

At startup the Rust binary, in order:

  1. loads the .env files for its mode, unless GIO_ENV_FILES=0 or [env] files = false (variables already set win);
  2. parses gio.toml strictly (unknown keys are errors, with the line and the closest valid key; so is every invalid value, rule and [security] entry, all listed together) and applies GIO_HOST / GIO_PORT / PORT;
  3. runs the startup validation: the page cache directory's placement, [security], the revalidation token, local [[fonts]] files and the TLS certificate and key - every problem is printed, not just the first;
  4. logs one warning per protection gio.toml turns off or loosens;
  5. spawns the Node worker(s), waits for the first to be ready, and only then binds the port.
text
$ npx giojs-server
giojs-server: configuration error: gio.toml:2: unknown key `server.prot` - did you mean `server.port`?
$ echo $?
1

SIGINT and SIGTERM start a graceful shutdown: up to 8 seconds to drain in-flight requests, then up to 6 seconds for each worker to run its plugin onShutdown hooks (the workers stop in parallel). Give a process manager at least 14 seconds before it kills the server.

Environment variables

The binary reads these itself; app variables (GIO_SESSION_SECRET, GIO_PUBLIC_*, your own) also reach the Node worker. The full list is on Environment Variables.

VariableEffect
NODE_ENVdevelopment runs dev mode; anything else production. The worker runs in the same mode.
GIO_HOST, GIO_PORT, PORTThe listen address, over [server] host / port (GIO_PORT before PORT).
GIO_APP_DIRThe app/ directory (default app); gio.toml, public/ and the .env files are read from its parent.
GIO_PUBLIC_DIRThe public/ directory, when it is not next to app/.
GIO_ENV_FILES0 / false loads no .env files, 1 / true loads them whatever [env] files says. Any other value is a startup error.
GIO_SESSION_SECRETSigns sessions; required by require_session guards in production.
GIO_REVALIDATE_TOKENThe on-demand revalidation token, over [revalidate] token (at least 32 bytes).
GIO_CACHE_DIRThe page cache directory, over [cache] disk_path.
GIO_DEPLOYMENT_IDPins the deployment id (up to 64 characters) instead of deriving it from the code - for several instances of one release.
GIO_LOG_FORMATjson or text, over [logging] format.
RUST_LOGThe log filter (default info).
GIO_NODE_SCRIPT, GIO_TSX_PKGThe worker entry and the tsx package; the launchers set them.
GIO_EXIT_ON_STDIN_EOF1: shut down when stdin reaches end of file (the launcher died). The launchers set it.

giojs-server --check-config

Loads the .env files and gio.toml exactly as startup does, runs the same validation, prints one line of JSON on stdout and exits: 0 when the server would start, 1 when it would refuse. It never binds a port and never starts a worker, so it is safe in CI and on a production host next to a running server.

bash
npx giojs-server --check-config
NODE_ENV=development npx giojs-server --check-config   # the dev configuration
node standalone/run.mjs --check-config                 # a standalone build

Report

FieldTypeDefaultDescription
okboolean-Whether the server would start.
errorsstring[]-Every refusal, worded as startup prints it after configuration error:, in line order: every unknown key and section, every invalid value, rules that cannot be enforced, [i18n] mistakes, and the other checks startup makes. Rules (or [i18n] locales) holding a misspelled or invalid key are checked once it is fixed. A required key that is missing, or whose value is invalid (path = 3, or a [[rate_limits]] path that is not a valid pattern), ends the list there, and the last entry says which checks did not run.
warningsstring[]-Protections the file turns off or loosens and ignored [dev] allowed_hosts entries - the same lines startup logs.
modestring-development or production, from NODE_ENV.
envFilesstring[]-The .env files loaded, by name, highest precedence first.
envFilesDisabledBystring | null-What turned .env loading off: GIO_ENV_FILES or [env] files.
configFilestring | null-The gio.toml read, or null when there is none (defaults apply).
listenobject-{ host, port, portSource, tls }. portSource is GIO_PORT, PORT, gio.toml or default. IPv6 hosts are bracketed.
trustedProxiesnumber-Entries in [server] trusted_proxies.
proxyHeadersstring-[server] proxy_headers: x-forwarded or forwarded.
rateLimitRulesnumber-[[rate_limits]] entries.
sessionGuardsnumber-[[guards]] in gio.toml with require_session = true (middleware.ts guards are not counted).
sessionSecretstring-unset, valid or invalid for GIO_SESSION_SECRET - never its value.
sessionSecretErrorstring | null-Why the secret is invalid.
cacheDirstring-The page cache directory, absolute.

When gio.toml cannot be parsed, the report holds only ok, errors, mode, envFiles, envFilesDisabledBy and configFile. When a .env file cannot be parsed, only ok, errors and configFile.

Worker boot errors

The Node worker loads gio.config.ts, discovers the routes and loads middleware.ts before it reports ready. When it cannot - an unknown key in gio.config.ts, two files that answer the same URL, a page whose export const revalidate is a literal the server cannot use, a middleware.ts that throws or holds a rule that cannot be enforced - it exits, and the server prints the worker's own error after the worker's log lines. A standalone build reports the same errors (its middleware.ts and gio.config are loaded at boot too, after the worker can report them):

text
giojs-server: the Node worker exited before it was ready (exit status: 1):
  /srv/shop/middleware.ts failed to load: GIO_SESSION_SECRET is not set
  • Production exits 1 at once, without a backtrace: the server never serves with the app's routes or rules half loaded.
  • Development prints the error and waiting for a file change to start the worker again, binds no port, and starts the worker again on the next save ([dev] watch = false exits instead). A worker that breaks after startup is respawned on the next save, and answers 503 meanwhile.

Examples

A valid configuration with warnings

gio.toml
[server]
port = 8080
max_connections = 0

[security.csrf]
enabled = false

[[guards]]
path = "/admin/*rest"
require_session = true
redirect_to = "/login"
json
{
  "cacheDir": "/srv/shop/.gio/cache/pages",
  "configFile": "gio.toml",
  "envFiles": [".env"],
  "envFilesDisabledBy": null,
  "errors": [],
  "listen": { "host": "0.0.0.0", "port": 8080, "portSource": "gio.toml", "tls": false },
  "mode": "production",
  "ok": true,
  "proxyHeaders": "x-forwarded",
  "rateLimitRules": 0,
  "sessionGuards": 1,
  "sessionSecret": "invalid",
  "sessionSecretError": "GIO_SESSION_SECRET secret #1 is 5 bytes; at least 32 are required",
  "trustedProxies": 0,
  "warnings": [
    "[security.csrf] enabled = false: any website can send form posts and other unsafe requests to this server with your visitors' cookies - prefer listing origins in [security.csrf] trusted_origins, or public endpoints in [security.csrf] exempt",
    "[server] max_connections = 0: concurrent connections are unlimited - a connection flood can exhaust file descriptors and memory"
  ]
}

The output is one line; it is shown formatted here. ok is true although the session secret is invalid: the server starts and denies every require_session request instead. gio doctor turns that into an error.

A refused configuration

Every unknown key and section is reported in one run:

text
$ npx giojs-server --check-config
{"configFile":"gio.toml","envFiles":[],"envFilesDisabledBy":null,"errors":["gio.toml:2: unknown key `server.prot` - did you mean `server.port`?","gio.toml:7: unknown key [image] - did you mean [images]?"],"mode":"production","ok":false}
$ echo $?
1

A syntax error is reported by line and column, without quoting the line (it may hold a token):

json
{"configFile":"gio.toml","envFiles":[".env"],"envFilesDisabledBy":null,"errors":["cannot parse gio.toml:1:8: invalid table header; expected `.`, `]`"],"mode":"production","ok":false}

In CI

bash
npx giojs-server --check-config > check.json || { cat check.json; exit 1; }
node -e 'const r = require("./check.json"); if (r.warnings.length) { console.log(r.warnings.join("\n")); process.exit(1); }'

The second line also fails the job on any warning, for a pipeline that allows no loosened protections.

Good to know

  • The report never carries a secret: not the session secret, not tokens, not .env values. Errors name a key or a position, never a quoted line.
  • gio dev, gio start, gio doctor, gio info, gio cache explain and gio bench run --check-config to learn the listen address and validate the configuration, so no JavaScript re-implements the gio.toml rules. An installed platform package of another version is not asked (it may predate the flag); a GIO_SERVER_BIN or repository build always is. Without an answer, they read gio.toml leniently and validate nothing.
  • --check-config does not load gio.config.ts, middleware.ts or your modules: the Node side is checked when the worker boots, and a worker that cannot boot stops startup with its own error (see Worker boot errors).
  • The binary has no --help or --version; use gio --help and gio --version. Run it through a launcher (gio, the giojs-server bin, a standalone run.mjs): started bare, it needs GIO_NODE_SCRIPT to find the worker.
  • Exit codes: 0 after a graceful shutdown or a passing check, 1 for a configuration error, a failed check or a failed startup (a worker that cannot boot included), and 2 for any argument other than --check-config (a usage error; nothing starts).

Version history

VersionChanges
v0.1.0-beta.8--check-config introduced. The server loads .env files, decides the worker's mode from NODE_ENV, rejects unknown gio.toml keys, reports every startup refusal at once (every unknown key in one run), and logs a warning per loosened protection. Any other argument (--version, --port) is a usage error, exit 2, where it used to be ignored and the server started. The bin became a separate launcher: gio got commands, giojs-server kept starting the server.
v0.1.0-beta.1Introduced, as a second name for the gio bin.