gio build standalone
Package a GioJS app into one directory - the server binary, a bundled worker.js and the static assets - that runs anywhere Node is installed, with no node_modules.
npx gio build standalonepnpm exec gio build standaloneyarn gio build standalonebunx gio build standalonegio build standalone [--out <dir>] [--target <platform>]
node standalone/run.mjsReference
| Option | Type | Default | Description |
|---|---|---|---|
--out <dir> | path | ./standalone | Where the build goes, relative to the current directory. The directory is emptied first, so it must be one this command owns (see The output directory). |
--target <platform> | string | this machine | Build for another platform: linux-x64, linux-x64-musl, linux-arm64, win32-x64, darwin-x64 or darwin-arm64. The binary comes from @gio.js/server-<target>, which must be installed (npm i @gio.js/server-<target> --force). linux-arm64 has no published package yet. |
-h, --help | boolean | - | Print the usage and exit with 0. |
Environment variables
| Variable | Effect |
|---|---|
GIO_APP_DIR | The app directory (default ./app); the project root is its parent. |
GIO_STANDALONE_SERVER_BIN | The server binary to copy, overriding --target and the installed package. |
GIO_SERVER_BIN | Without --target: the binary to copy instead of the installed platform package. |
GIO_PUBLIC_* | Inlined into the client chunks and worker.js at build time, from the environment and the project's .env.production.local, .env.local, .env.production and .env. Changing one needs a rebuild. |
GIO_ENV_FILES | 0 / false loads no .env files for the build, like [env] files = false in gio.toml; 1 / true loads them even when gio.toml turns them off. |
Behavior
- Loads the project's production
.envfiles (always the production set). - Discovers the routes exactly as the server does, and fails on the same route conflicts. A project with no page and no
route.tsis an error. - Builds the route stylesheets and the client chunks in production mode. The stylesheets follow
[css] minifyin the project'sgio.tomlat build time. - Bundles the whole Node side - your pages, layouts,
route.tsfiles,middleware.ts,gio.config.ts, React and every dependency - into oneworker.jswith esbuild.tsxandesbuildare needed only for the build. - Empties
--outand writes the output below.
standalone/
server the Rust binary (server.exe for win32-x64)
worker.js the whole Node side, bundled
run.mjs the launcher: node run.mjs
static/ prebuilt client chunks and route stylesheets
public/ a copy of public/, when the project has one
gio.toml a copy of gio.toml, when the project has one
package.json {"name":"giojs-standalone-app","private":true,"type":"module"}
.gio/manifest.json routes, handlers, chunk and worker hashes (feeds the deployment id)
.gio/routes.d.ts the typed routes and .gio/css-modules.d.ts, when the project has themrun.mjs starts server with the output directory as its working directory, NODE_ENV=production unless the environment sets NODE_ENV, and the paths to worker.js and static/. It passes its own arguments to the server (which takes only --check-config), forwards SIGINT / SIGTERM (except on Windows), and exits with the server's code. Like gio, it holds the server's stdin pipe, so killing the launcher also stops the server.
The output directory
The build deletes --out before writing, so it refuses, without touching anything:
- the project directory or any directory that contains it (
--out .,--out ..); - a directory inside
app/; - a path that exists and is not a directory;
- a non-empty directory that is not a previous standalone build (one holding
run.mjsand.gio/manifest.json).
Examples
Build and run
$ npx gio build standalone
gio build standalone
app: /home/me/my-app/app
server: /home/me/my-app/node_modules/@gio.js/server-linux-x64/bin/giojs-server
out: /home/me/my-app/standalone
env: .env.production (GIO_PUBLIC_* inlined at build time)
routes: /, /about, /blog, /posts/:id
standalone build complete: /home/me/my-app/standalone
server, worker.js, run.mjs, static/, .gio/
deploy the directory to a server that has Node, then:
node standalone/run.mjsCopy the folder to the server and run node run.mjs inside it, or node standalone/run.mjs from its parent. Server-side variables are read at runtime: set them in the deploy environment, or put .env files in the output directory.
Check the configuration on the target machine
node standalone/run.mjs --check-configrun.mjs passes its arguments on, so this runs --check-config with the deployed gio.toml, .env files and environment.
Cross-build for Linux from a Mac
npm i @gio.js/server-linux-x64 --force
npx gio build standalone --target linux-x64 --out dist-linux--force lets npm install a package for another OS. Without the package the build stops before writing anything:
gio build standalone: platform package @gio.js/server-linux-x64 is not installed.
Cross-target builds need it present: npm i @gio.js/server-linux-x64 --forceIn a Dockerfile
FROM node:22-slim AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build && npx gio build standalone --out standalone
FROM node:22-slim
WORKDIR /app
ENV GIO_HOST=0.0.0.0 PORT=3000
COPY --from=build /app/standalone ./
# The server writes its page cache and IPC sockets under .gio/
RUN mkdir -p .gio && chown -R node:node .gio
USER node
EXPOSE 3000
CMD ["node", "run.mjs"]The Docker guide has the complete image, with a health check and the settings for a graceful stop; gio add docker writes it for you.
Good to know
- The output carries no
.envfiles and nonode_modules.GIO_PUBLIC_*values are frozen in; every other variable is read when the server starts. - Data your app reads from the project at runtime (a SQLite file, migrations, uploaded files) is not copied: ship it next to
run.mjsyourself. - Modules evaluate as they do from source:
route.tsfiles at startup, pages and layouts on first use. A module that throws when imported fails only the URLs that import it (500), not the whole worker. - A usage error - an unknown argument, a missing value, an unknown
--target- exits with code2, as everygiousage error does. A build that fails exits with1. - Options take their value as the next argument or after
=:--out dist/appand--out=dist/appare the same. - The deployment id is derived from
.gio/manifest.json, which includes a hash ofworker.js, so a rebuild of changed code never serves the old build's cached pages.
Related
- Standalone Deploys - the guide
- Docker and Deploying
gio buildandgio startgio exportfor static hosts
Version history
| Version | Changes |
|---|---|
v0.1.0-beta.8 | Refuses an --out it could not safely empty (before, --out . deleted the project). Usage errors exit with 2 instead of 1, and options also take --option=value. Loads .env files and inlines GIO_PUBLIC_*. Ships CSS imports and CSS Modules, following [css] minify. Modules evaluate lazily, so one failing module no longer stops the worker. |
v0.1.0-beta.7 | Introduced. |