GioJSdocs
On this page

.env files

Environment variables loaded from .env, .env.local and their per-mode variants in the project root when the server starts.

.env.local
DATABASE_URL=postgres://localhost:5432/acme
GIO_SESSION_SECRET=replace-me-with-32-random-bytes
# Only GIO_PUBLIC_* variables reach browser code.
GIO_PUBLIC_SITE_NAME=Acme

Reference

Files and precedence

The files sit in the project root, next to app/. They are read in this order, and the first one that sets a variable wins:

OrderFileTypical use
1.env.development.local / .env.production.localMachine-specific overrides for one mode. Never committed.
2.env.localLocal secrets for every mode. Never committed.
3.env.development / .env.productionShared defaults for one mode.
4.envShared defaults for every mode.
  • Real environment variables always win. A variable already set in the environment that starts the server is never overridden by a file.
  • The mode is development when NODE_ENV=development (as gio dev sets it) and production otherwise, NODE_ENV=test or unset included. There is no .env.test.
  • When. Once, at server startup, before gio.toml is read, so the files can set GIO_PORT and the other variables that override it. The Node worker inherits the result. Editing a file needs a restart, in development too: the watcher ignores dotfiles.

Syntax

.env
# A comment
PLAIN=value
QUOTED="two words"
export WITH_EXPORT=allowed            # the export prefix is ignored
INLINE=value # a comment after a space
MULTILINE="line 1
line 2"
DERIVED=${PLAIN}-suffix              # value-suffix
LITERAL='${PLAIN} is not expanded'  # single quotes keep it as written

${VAR} expands a variable from the environment, from an earlier line, or from a file with higher precedence; an unknown one expands to nothing. The parser is dotenvy's, and the Node tools use a port of it, so a file means the same thing everywhere.

Errors and warnings

  • A file that cannot be parsed stops startup, naming the file and line but never the value: cannot load .env.local: invalid syntax on line 2.
  • NODE_ENV in a file is ignored, with a warning: the mode was already decided from the real environment. Set it where the server is started.
  • A candidate that is not a regular file (python -m venv .env makes a directory) is skipped.
  • The startup log lists the files it read, never their values: loaded .env files mode="production" files=.env.local, .env.production, .env.

Turning it off

SettingEffect
[env] files = false in gio.tomlNo file is loaded.
GIO_ENV_FILES=0 (or false)No file is loaded, whatever gio.toml says.
GIO_ENV_FILES=1 (or true)Files are loaded, whatever gio.toml says.

Any other GIO_ENV_FILES value stops startup. Platforms that inject their own environment (containers, PaaS) often turn the files off so a stray .env in the image cannot apply.

Examples

Read a variable on the server

app/status/page.tsx
import React from 'react';
import type { GetServerSideProps, InferPageProps } from '@gio.js/core';

export const getServerSideProps: GetServerSideProps<{ region: string }> = async () => {
  // Server-only: DEPLOY_REGION is never sent to the browser unless returned as a prop.
  return { props: { region: process.env.DEPLOY_REGION ?? 'local' } };
};

export default function Status({ region }: InferPageProps<typeof getServerSideProps>) {
  return <p>Served from {region}</p>;
}

A public variable in browser code

components/Footer.tsx
import React from 'react';

export function Footer() {
  // Inlined into the client bundle when it is built.
  return <footer>{process.env.GIO_PUBLIC_SITE_NAME}</footer>;
}

Good to know

  • Only GIO_PUBLIC_* reaches the browser. Those variables are inlined into the client bundles when they are built (at worker start, gio export or gio build standalone, which freeze the values). Every other process.env.X is undefined in the browser. The prefix is fixed, so a secret cannot leak by a renamed setting. Never put a secret in a GIO_PUBLIC_ variable.
  • Commit .env and .env.{mode} only if they hold no secrets, and keep the .local files out of git. The create-giojs starter ignores .env*.local and ships a .env.example to copy.
  • gio export, gio build standalone, gio routes, gio typegen and the testing kit load the same files with the same rules and switches. giojs-server --check-config loads them too, and reports which ones it read.
  • Unlike Next.js, .env.local also applies when NODE_ENV=test. Set GIO_ENV_FILES=0 in tests that must not see local values.

Version history

VersionChanges
v0.1.0-beta.8Introduced: .env files with Next.js precedence for the server, gio export, standalone builds and the CLI; GIO_PUBLIC_* inlining; [env] files and GIO_ENV_FILES.