Configuration
All GioJS configuration lives in gio.toml at the project root. Every field is optional - defaults are production-ready.
Full reference
toml
[app]
name = "my-app"
router = "app" # "app" | "pages"
[server]
host = "0.0.0.0" # bind address
port = 3000
http2 = true # HTTP/2 support
max_body_bytes = 2097152 # request body limit (2 MiB)
[server.tls]
enabled = false # set true to terminate TLS in GioJS directly
cert_path = "/path/to/cert.pem"
key_path = "/path/to/key.pem"
[[fonts]] # self-hosted fonts, repeat per font file
family = "Inter"
url = "/fonts/inter.woff2"
weight = 400 # default 400
style = "normal" # default "normal"
[images]
allowed_widths = [16, 32, 48, 64, 96, 128, 256, 384, 640, 750, 828, 1080, 1200, 1920, 2048, 3840]
quality = 75 # 1-100
disk_max_bytes = 536870912 # on-disk image cache cap (512 MiB)
max_remote_bytes = 20971520 # max fetched remote source size (20 MiB)
[[images.remote_patterns]]
protocol = "https" # default "https"
hostname = "images.example.com"
pathname = "/photos/*" # optional; exact match, or prefix with trailing *
[css]
enabled = true # CSS pipeline
minify = true
critical_extraction = true
[websocket]
enabled = true
max_connections = 1000
ping_interval_secs = 30
[[rate_limits]] # repeat per path rule; /_gio/image honors these too
path = "/api/*"
per_ip = 100 # requests per window (default 100)
window_seconds = 60 # default 60
burst = 20 # default 20
key_header = "x-api-key" # optional: key on a header value instead of IP
[[redirects]] # evaluated in Rust before routing (see Middleware)
from = "/old-blog/:slug"
to = "/posts/:slug"
status = 301 # 301/302/307/308, default 302
[[rewrites]] # serve another route without changing the URL
from = "/latest"
to = "/posts/newest"
[[headers]] # stamp response headers on matching paths
path = "/api/*"
[headers.headers]
x-frame-options = "DENY"
[[guards]] # cookie gate: redirect when the cookie is absent
path = "/admin/*"
require_cookie = "session"
redirect_to = "/login"
[i18n]
locales = ["en", "de"] # empty = i18n disabled
default_locale = "en"
detect_from = ["path", "accept-language", "cookie"]
[metrics]
enabled = false # expose /_gio/metrics (Prometheus); off when this section is absent
token = "" # require "Authorization: Bearer <token>" when set
ip_allowlist = [] # restrict by client IP, e.g. ["10.0.0.5"]Health & metrics
GioJS serves two built-in observability endpoints directly from the Rust layer - no Node round-trip, so they stay responsive even under load:
| Endpoint | Default | Description |
|---|---|---|
/_gio/health | always on | Liveness probe - always returns 200 with a JSON body: {status, http2, tls, deploymentId, nodeReady, cacheEntries, uptimeSecs}. nodeReady is false while the Node SSR worker is respawning (cached and static content still serves) - readiness probes should check that field. |
/_gio/metrics | off | Prometheus exposition (request counts, latency histograms, cache hit ratio, IPC timing). Returns 404 until enabled via [metrics]. |
Metrics are opt-in so you never expose them by accident. Turn them on, and lock them down for anything beyond localhost:
toml
[metrics]
enabled = true # serve /_gio/metrics
# Secure it for production - use either or both:
token = "a-long-random-secret" # require Authorization: Bearer <token>
ip_allowlist = ["10.0.0.5", "10.0.0.6"] # only allow these client IPsbash
# Scrape with a token:
curl -H "Authorization: Bearer a-long-random-secret" \
http://localhost:3000/_gio/metricsIn production (
NODE_ENV not development), GioJS logs a warning at startup when neither token norip_allowlist is set - unauthenticated metrics are fine on localhost but should never face the public internet.Environment variables
A few runtime knobs live in the environment rather thangio.toml (the listen host and port are configured in[server], not via env):
| Variable | Description | Default |
|---|---|---|
GIO_APP_DIR | Path to the app/ directory; gio.toml is loaded from its parent | app |
GIO_DEPLOYMENT_ID | Pin the deployment ID across pods (otherwise derived from the build content) | content-derived |
GIO_SOCKET_PATH | Rust-to-Node IPC path; the server passes the resolved value to the Node worker | per-instance .gio/ipc-<pid>-<rand>.sock (Unix), unique named pipe (Windows) |
GIO_SITE_URL | Absolute base URL for sitemap.xml during gio export | unset |
NODE_ENV | development enables dev mode (file watcher, dev endpoints) | unset |
RUST_LOG | Rust log filter (info/debug/trace) | info |
Static page caching
Export revalidate from any page module to control caching:
typescript
// Cache forever (ISR: never revalidate)
export const revalidate = false;
// Cache for 60 seconds, then revalidate
export const revalidate = 60;
// Never cache (default when not set)
// (omit the export)revalidate = false maps to a one-year TTL (31536000 seconds) in the Rust cache layer - the standard sentinel for "cache indefinitely."