Configuration
Complete instancez.yaml schema reference. Every key, type, default, and example.
instancez.yaml is the single source of truth for your project. On boot, the server diffs it against the live database and applies migrations automatically.
Env vars are interpolated using ${VAR} or ${VAR:-default}. They are resolved at load time; references are never stored in the database.
Editor schema
Section titled “Editor schema”A JSON Schema for this file is published at https://docs.instancez.ai/schema/instancez.json. Add this as the first line of instancez.yaml for completion and inline errors in editors that use the YAML language server (VS Code YAML extension, Neovim, JetBrains):
# yaml-language-server: $schema=https://docs.instancez.ai/schema/instancez.jsoninz init writes the line for you. Numbers and booleans also accept a ${VAR} string.
version
Section titled “version”| Key | Type | Required | Description |
|---|---|---|---|
version |
integer |
yes | Schema version. Currently 1. |
project
Section titled “project”| Key | Type | Default | Description |
|---|---|---|---|
project.name |
string |
— | Display name shown in the dashboard. |
project.description |
string |
— | Optional project description. |
project.cloud.project_id |
string |
— | Cloud project ID. Written automatically by inz cloud deploy --new; not meant to be hand-edited. |
database
Section titled “database”| Key | Type | Default | Description |
|---|---|---|---|
database.pool.max |
integer |
20 |
Maximum connections in the request pool. |
database.pool.min |
integer |
5 |
Minimum idle connections. |
database.pool.idle_timeout |
duration |
300s |
How long idle connections are held before closing. |
server
Section titled “server”| Key | Type | Default | Description |
|---|---|---|---|
server.port |
integer |
8080 |
HTTP listen port. |
server.max_body_size |
string |
1MB |
Maximum request body size for non-upload endpoints. Cannot raise Instancez Cloud’s ~6MB request cap. |
server.max_limit |
integer |
1000 |
Most rows one REST table read, setof RPC, or top-level has-many embed returns (PostgREST db-max-rows). Applies with or without limit/Range; Content-Range shows the capped range. -1 disables the cap. |
server.cors
Section titled “server.cors”Methods (GET, POST, PUT, PATCH, DELETE, OPTIONS), headers, credentials, and
preflight caching are fixed platform defaults — origins is the only knob,
matching Supabase’s own gateway. Changes to origins and server.max_body_size
apply on config reload (inz dev file watch, dashboard save) without a restart.
| Key | Type | Default | Description |
|---|---|---|---|
server.cors.origins |
string[] |
[] |
Allowed origins. Use ["*"] to allow all. |
server.timeouts
Section titled “server.timeouts”| Key | Type | Default | Description |
|---|---|---|---|
server.timeouts.request |
duration |
25s |
Read/write deadline for /rest/v1 and /auth/v1 requests. Storage, functions, admin and dashboard routes are exempt. |
server.timeouts.db_query |
duration |
10s |
statement_timeout for every API query (all roles), applied per transaction. Prefer: statement-timeout=<ms> can lower it for one request. 0 or empty disables it. |
server.timeouts.upload |
duration |
5m |
Deadline for file upload requests. |
server.timeouts.shutdown |
duration |
30s |
Graceful shutdown window. |
Duration strings use Go format: 30s, 5m, 1h.
providers
Section titled “providers”providers.email
Section titled “providers.email”| Key | Type | Default | Description |
|---|---|---|---|
providers.email.type |
string |
— | Email provider type. Currently "resend" or "ses". |
providers.email.api_key |
string |
— | Provider API key. Supports ${VAR}. |
providers.email.default_from_email |
string |
— | Default sender address. |
Set providers.email: null to disable email sending.
providers.storage
Section titled “providers.storage”| Key | Type | Default | Description |
|---|---|---|---|
providers.storage.type |
string |
— | "local", "s3" or "gcs". |
providers.storage.path |
string |
— | (local only) Directory for local file storage. |
providers.storage.bucket |
string |
— | (s3 and gcs) Bucket name. Required. |
providers.storage.region |
string |
— | (s3 only) AWS region. |
providers.storage.access_key_id |
string |
— | (s3 only) AWS access key. Supports ${VAR}. |
providers.storage.secret_access_key |
string |
— | (s3 only) AWS secret key. Supports ${VAR}. |
providers.storage.endpoint |
string |
— | (s3 only) Custom endpoint URL for S3-compatible stores. |
Auth is always provisioned, even if auth: is omitted entirely; the block only overrides defaults.
| Key | Type | Default | Description |
|---|---|---|---|
auth.jwt_expiry |
duration |
15m |
Access token lifetime. |
auth.refresh_token_expiry |
duration |
7d |
Refresh token lifetime. Refresh tokens are always issued. |
auth.allow_signup |
boolean |
true |
Allow public sign-up: POST /auth/v1/signup, first-time signInWithOtp, and first-time OAuth / ID-token sign-in. Set to false for invite-only; blocked sign-ups get 422 signup_disabled, except first-time signInWithOtp, which returns an empty 200. |
auth.allow_anonymous |
boolean |
false |
Allow anonymous sign-in (empty-body signup). Off by default; set true to enable. |
auth.redirect_urls |
string[] |
[] |
Allowlist of origins for post-auth redirects (OAuth, email verification). The server’s own origin is always allowed. |
auth.refresh_tokens is deprecated and ignored — refresh tokens are always issued.
auth.email
Section titled “auth.email”| Key | Type | Default | Description |
|---|---|---|---|
auth.email.verify_email |
boolean |
false |
Require email verification before the user can sign in. Requires a configured email provider. |
auth.email.templates |
map |
— | Override built-in email templates by name (e.g. confirm, recovery). |
auth.email.templates.<name>.subject |
string |
— | Email subject line. |
auth.email.templates.<name>.body |
string |
— | Inline HTML/text body. |
auth.email.templates.<name>.body_file |
string |
— | Path to a file containing the body (alternative to body). |
auth.oauth.<name>
Section titled “auth.oauth.<name>”OAuth provider configuration. Providers are keyed by name under auth.oauth; the
name (google, github, apple, …) selects the built-in provider implementation.
| Key | Type | Default | Description |
|---|---|---|---|
auth.oauth.<name>.client_id |
string |
— | OAuth client ID. Supports ${VAR}. |
auth.oauth.<name>.client_secret |
string |
— | OAuth client secret. Supports ${VAR}. Optional for google and apple (only signInWithIdToken works without it; the redirect flow needs it), required for other providers. |
auth.oauth.<name>.redirect_url |
string |
— | OAuth callback URL registered with the provider. |
apple takes a Services ID plus optional iOS bundle IDs and an ES256 JWT client_secret; see Auth.
tables
Section titled “tables”Tables map to Postgres tables in the public schema by default. The migrator diffs this block against the live database on each boot.
tables: posts: schema: public # optional; default "public" rls_enabled: true # recommended; see RLS page fields: - name: id type: bigserial primary_key: true - ... indexes: - ... rls: - ...tables.<name>.fields
Section titled “tables.<name>.fields”| Key | Type | Default | Description |
|---|---|---|---|
fields[].name |
string |
required | Column name. |
fields[].type |
string |
required | Postgres type (e.g. text, bigint, uuid, timestamptz, text[]). |
fields[].primary_key |
boolean |
false |
Mark as primary key. Mark several fields for a composite key. The key’s columns can’t change once the table exists, except for dropping a single key column and adding one new key column in the same change. |
fields[].required |
boolean |
false |
Add NOT NULL constraint. |
fields[].unique |
boolean |
false |
Add UNIQUE constraint. |
fields[].default |
any |
— | Column default. Supported: literal values, now(), current_date, current_time. The shorthand uuid_v7() and uuid_v4() are normalized to gen_random_uuid(). |
fields[].enum |
string[] |
— | Restrict values to this list (creates a CHECK constraint). |
fields[].pattern |
string |
— | Regex pattern for a CHECK constraint. |
fields[].min |
number |
— | Minimum numeric value (inclusive). |
fields[].max |
number |
— | Maximum numeric value (inclusive). |
fields[].check |
string |
— | Raw SQL CHECK expression. |
fields[].foreign_key.references |
string |
— | table.column or schema.table.column. table.column is public; use auth.users.id for auth users. |
fields[].foreign_key.on_delete |
string |
restrict |
cascade, restrict, or set_null. Defaults to restrict when omitted. |
fields[].auto_updated_at |
boolean |
false |
Set the column to now() on every UPDATE. Only on timestamp/timestamptz fields. |
fields[].immutable |
boolean |
false |
Reject any UPDATE that changes the column (error 23514, HTTP 400), for every role including the secret key. INSERT is unaffected; an upsert that hits a conflict counts as an update. |
No columns are injected automatically. Every column, including primary keys, must be declared.
A composite primary key marks each of its fields:
tables: memberships: fields: - name: team_id primary_key: true foreign_key: references: teams.id - name: user_id type: uuid primary_key: trueTo reference one column of a composite key from another table’s foreign_key, that column must be unique on its own: add a unique index on it (indexes: [{columns: [serial], unique: true}]). unique: true on an existing column adds no constraint. See Schema.
tables.<name>.rls_enabled
Section titled “tables.<name>.rls_enabled”| Key | Type | Default | Description |
|---|---|---|---|
rls_enabled |
boolean |
inferred: true if rls has policies, else false |
Turn row-level security on or off for the table. true with no policies denies anon/authenticated everything. false with policies is a validation error. Leaving it unset produces a validation warning. See RLS. |
tables.<name>.timestamps
Section titled “tables.<name>.timestamps”| Key | Type | Default | Description |
|---|---|---|---|
timestamps |
boolean |
false |
Appends created_at (timestamptz, required, default now()) and updated_at (same, plus auto_updated_at: true) after the declared fields. Declaring either column as well is a validation error. Removing timestamps later drops both columns, so it needs --allow-destructive. A dashboard save writes the expanded fields, not the shorthand. |
tables.<name>.indexes
Section titled “tables.<name>.indexes”| Key | Type | Default | Description |
|---|---|---|---|
indexes[].columns |
string[] |
required | Columns to index. |
indexes[].unique |
boolean |
false |
Create a unique index. |
indexes[].where |
string |
— | Partial index condition (SQL expression). |
indexes[].method |
string |
btree |
btree, hash, gin, gist, brin or spgist. unique needs btree. Changing it rebuilds the index. |
tables.<name>.rls
Section titled “tables.<name>.rls”RLS is the only authorization layer. Declare policies here; instancez creates them automatically. Whether RLS itself is on is controlled by rls_enabled, not by whether this list is empty.
| Key | Type | Default | Description |
|---|---|---|---|
rls[].operations |
string[] |
required | One or more of select, insert, update, delete. |
rls[].using |
string |
conditional | SQL boolean expression selecting which existing rows the operation can see or target. |
rls[].with_check |
string |
conditional | SQL boolean expression deciding what a written row is allowed to look like. |
rls[].type |
string |
permissive |
permissive or restrictive. |
rls[].owner |
string |
— | Shorthand for <column> = auth.uid() in using and with_check (whichever the operations read). operations defaults to all four. The column must be uuid. Cannot be combined with using/with_check. A dashboard save writes the expanded form. |
Useful SQL helpers available in RLS expressions:
auth.uid()— UUID of the authenticated user (nullfor anonymous).auth.is_authenticated()— true when the request carries a valid user JWT.
storage
Section titled “storage”Bucket definitions. Buckets cannot be created, modified, or deleted at runtime; only instancez.yaml changes take effect. Bucket names follow the identifier rules, and public, sign, authenticated, info, upload, list, move, copy and v1 are reserved because they’re storage route segments.
storage: avatars: public: false max_size: 5MB types: - image/png - image/jpeg rls: - operations: [select] using: "true"| Key | Type | Default | Description |
|---|---|---|---|
storage.<name>.public |
boolean |
false |
Allow anyone to download via /storage/v1/object/public/..., which skips RLS. Grants no select; listing and writes still follow rls. |
storage.<name>.max_size |
string |
50MB |
Maximum file size per upload (e.g. 5MB, 1GB). Does not lift the ~4.5MB cap on server-proxied uploads on Instancez Cloud; use POST /storage/<bucket>/sign for larger files. |
storage.<name>.types |
string[] |
[] |
Allowed MIME types. Wildcards like image/* are accepted. Empty means all types allowed. |
storage.<name>.rls |
RLSPolicy[] |
[] |
Same policy shape as table RLS, applied to storage.objects. |
Postgres stored procedures exposed at /rest/v1/rpc/<name>.
rpc: search_posts: description: Full-text search auth_required: false language: plpgsql # sql | plpgsql (default: plpgsql) volatility: stable # volatile | stable | immutable (default: volatile) security: invoker # invoker | definer (default: invoker) args: - name: query type: text required: true returns: type: "setof posts" body: | SELECT * FROM posts WHERE body @@ plainto_tsquery(query);| Key | Type | Default | Description |
|---|---|---|---|
rpc.<name>.description |
string |
— | Documentation string. |
rpc.<name>.auth_required |
boolean |
false |
Reject unauthenticated callers. |
rpc.<name>.language |
string |
plpgsql |
sql or plpgsql. |
rpc.<name>.volatility |
string |
volatile |
volatile, stable, or immutable. Only stable/immutable can be called with GET. |
rpc.<name>.security |
string |
invoker |
invoker or definer. |
rpc.<name>.args[].name |
string |
required | Argument name. |
rpc.<name>.args[].type |
string |
required | Postgres type. |
rpc.<name>.args[].required |
boolean |
false |
Return 404 PGRST202 (with a hint naming the argument) if absent. |
rpc.<name>.args[].default |
any |
— | Postgres DEFAULT for optional args. null or NULL emits DEFAULT NULL. |
rpc.<name>.returns.type |
string |
— | Return type: void, record, a scalar or composite type, setof <type>, or table(col type, …). |
rpc.<name>.set |
map |
— | Map of search_path, statement_timeout, lock_timeout, work_mem, emitted as SET on the function. |
rpc.<name>.body |
string |
required | Function body (PL/pgSQL or SQL). |
functions
Section titled “functions”JavaScript code functions served at /functions/v1/<name>. Distinct from rpc: (which declares Postgres stored procedures).
functions: send_notification: runtime: node file: functions/send_notification.js auth_required: true timeout: 15s env: WEBHOOK_URL: https://hooks.example.com/notify API_KEY: ${INSTANCEZ_ENV_NOTIFY_API_KEY}| Key | Type | Default | Description |
|---|---|---|---|
functions.<name>.runtime |
string |
required | "node". |
functions.<name>.file |
string |
required | Path to the JS handler file, relative to the config root. |
functions.<name>.auth_required |
boolean |
false |
Require a valid JWT. Returns 401 otherwise. |
functions.<name>.timeout |
duration |
30s |
Per-request timeout. Exceeded requests return 504. |
functions.<name>.env |
map |
{} |
Env values injected as ctx.env. Values may be string literals or ${INSTANCEZ_ENV_*} references. |
INSTANCEZ_ENV_* references are resolved from the process environment. Plain ${VAR} references (without the INSTANCEZ_ENV_ prefix) are not supported in env: — use them elsewhere in the config file for other values.
include
Section titled “include”Split a large config across files. Only the main instancez.yaml may use it.
version: 1include: - tables/*.yaml - rpc.yaml- Paths are relative to the main file’s directory. Glob characters (
*,?,[...]) are allowed only in the last path element, there is no**, and each pattern must match at least one file.${VAR}and absolute paths are rejected. - An included file may only contain
tables,storage,rpcandfunctions(empty files are fine). Nestedincludeis an error. - The same table, bucket, rpc or function name in two files is an error.
functions.<name>.filestays relative to the project root. - Every match must resolve inside the project directory, symlinks included. The main file is skipped if a glob matches it.
- Errors in a fragment name the file and line. Line numbers in later validation errors refer to the merged document.
inz devreloads when the main file or any included file changes, including a new file that matches a glob.- The dashboard cannot save a config that uses
include:(GET /_admin/config/statusreportsread_only: trueand the save returns 409config_read_only); edit the YAML files instead. inz validate,inz vet,inz gen,inz devandinz serveread the merged config. S3 and bundle config sources do not supportinclude;inz bundle,inz cloud deployandinz validate --cloudsend one merged file without theinclude:key; configs withoutinclude:are sent as written. Shorthands (timestamps,owner) are not expanded by the CLI, so cloud support for them needs a platform release.- A glob that matches only the main file logs a warning. Dotfiles and editor temp files (
.x.swp,x~,#x#) are skipped by globs.
functions_bundle
Section titled “functions_bundle”Bundle pointer for self-hosted deployments. inz bundle produces the value (e.g. s3://bucket/key#sha256 or gcs://bucket/key#sha256) and writes it here; the managed cloud stamps it server-side from uploaded sources. inz cloud deploy does not write this field.
Full example
Section titled “Full example”version: 1
project: name: My App description: Example instancez project
server: port: 8080 max_body_size: 5MB max_limit: 500 cors: origins: ["https://app.example.com"] timeouts: request: 30s db_query: 10s upload: 5m shutdown: 30s
database: pool: max: 20 min: 5 idle_timeout: 300s
providers: email: type: resend api_key: ${RESEND_API_KEY} default_from_email: noreply@example.com storage: type: s3 bucket: my-app-storage region: us-east-1 access_key_id: ${AWS_ACCESS_KEY_ID} secret_access_key: ${AWS_SECRET_ACCESS_KEY}
auth: jwt_expiry: 1h refresh_token_expiry: 30d allow_signup: true allow_anonymous: false redirect_urls: - https://app.example.com email: verify_email: true
tables: profiles: rls_enabled: true fields: - name: id foreign_key: references: auth.users.id on_delete: cascade primary_key: true - name: display_name type: text - name: avatar_url type: text rls: - operations: [select] using: "true" - operations: [insert] with_check: "auth.uid() = id" - operations: [update] using: "auth.uid() = id" with_check: "auth.uid() = id"
storage: avatars: public: true max_size: 2MB types: [image/png, image/jpeg, image/webp] rls: - operations: [select] using: "auth.uid() IS NOT NULL" - operations: [insert] with_check: "name LIKE auth.uid()::text || '/%'" - operations: [delete] using: "name LIKE auth.uid()::text || '/%'"
rpc: profile_search: language: sql volatility: stable args: - name: q type: text required: true returns: type: "setof profiles" body: | SELECT * FROM profiles WHERE display_name ILIKE '%' || q || '%';
functions: resize_avatar: runtime: node file: functions/resize_avatar.js auth_required: true timeout: 20s env: MAX_WIDTH: "512"