apps.yml reference
apps.yml tells a sandbox how to run your product: the toolchain, the backing services, and each app’s setup, startup, routing, and health checks.
Where it lives
The project Configure page stores the shared baseline for each repository. Changes made by an agent inside a thread are saved as workspace-only overrides, so an app can be configured and tested alongside branch-only code without changing other threads. Promote the reviewed document in project settings when the code is available project-wide.
A complete example
tools:
- nodejs_22
- pnpm@10.32.1
env:
NODE_ENV: development
setup:
- pnpm install
- pnpm db:migrate
services:
postgres:
image: postgres:16
environment:
POSTGRES_PASSWORD: postgres
ports:
- "5432:5432"
connectionEnv:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/app
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
retries: 5
apps:
web:
dir_path: apps/web
when:
exists: apps/web/package.json
port: 3000
hmr_port: 30003
routes:
- path: /
startup: pnpm dev --host 0.0.0.0
secrets:
MAPS_API_KEY: "Maps console > Credentials > Server key"
health:
- endpoint: /
api:
dir_path: apps/api
port: 4000
routes:
- path: /api
startup: pnpm start
health:
- endpoint: /healthzTop-level fields
toolsToolchain packages, as name or name@version — e.g. nodejs_22, pnpm@10.32.1, postgresql_16.
envRepo-level environment variables injected into all apps and services.
setupRepo-level shell commands run once when the workspace is built, before per-app setup — installs, migrations, seeds.
secretsSecret variable names shared by every app in this repository, each with an optional where-to-get hint. Names only; add values in Gentlybot.
onepasswordBind a 1Password item as the source of repo-level secrets: source_type (note or fields) plus source_item_id.
servicesNamed backing services (Postgres, Redis, …) run as containers next to your apps.
appsNamed applications — the processes Gentlybot starts, routes, and health-checks.
App fields (apps.<name>)
startupCommand(s) that launch the app. The only required app field.
portHTTP port the app listens on. Required for endpoint health checks and URL routing.
hmr_portSeparate HMR/WebSocket port (e.g. Vite). Upgrade requests are routed here instead of port.
rewrite_hostSend Host: localhost:<port> to the app, with the public preview host in X-Forwarded-Host. Only for apps that reject unknown hostnames. Requires port.
dir_pathSubdirectory of the repo the app lives in (monorepos). Defaults to the repo root.
when.existsOptional repo-relative sentinel. The app is skipped in workspaces where this path does not exist.
routesHow the app is exposed on the sandbox URL: each entry is exactly one of path (/api), prefix (a subdomain label), or subdomain.
envApp-level environment variables; override repo-level values.
setupApp-specific setup commands, run after repo-level setup.
healthReadiness checks — all must pass before the app counts as running. Each is one of endpoint: /path (2xx/3xx on port) or exists: relative/file.
secretsSecret variable names only this app needs, each with an optional where-to-get hint. Names only; add values in Gentlybot.
onepasswordApp-level 1Password binding, same shape as the repo-level one.
Service fields (services.<name>)
imageContainer image, e.g. postgres:16 or redis:7.4.
environmentEnvironment variables for the service container.
portsPort mappings, e.g. "5432:5432".
connectionEnvConnection strings exported to your apps’ environment, e.g. DB_URL.
healthcheckContainer health probe: test (command array) plus optional interval, timeout, start_period, retries.
optionsRuntime tuning: memory and shmSize (up to 8g), cpus (up to 4), ulimits, and capAdd (allowlisted — IPC_LOCK, SYS_NICE, SYS_RESOURCE plus Docker defaults). Privileged containers are not available.
Add secrets in Gentlybot
API keys, tokens and signing secrets never go in gently.apps.yml or your repository. The config declares which names each app needs; the values are stored encrypted in Gentlybot, can't be read back, and reach every new sandbox for the project.
Declare the names
Put a name under top-level secrets: when every app in the repository shares one value, such as a server and a background worker from the same codebase. Put it under apps.<name>.secrets when apps need different values, such as apps in separate folders of a monorepo. Names are uppercase server-side variables: names exposed to the browser (VITE_…, NEXT_PUBLIC_… and similar) and names the sandbox sets (PORT, GENTLY_…) are refused.
secrets: # shared by every app in this repository
SIGNING_SECRET: "Generate with: openssl rand -hex 32"
apps:
api:
startup: bin/rails server
secrets: # this app only
PAYMENTS_API_KEY: "Payments dashboard > API keys (test mode)"
worker:
startup: bundle exec sidekiqAdd the values
- On the Configure page: each repository has its own Secrets section, above Tools and Services, with one group per app. A repository with missing secrets opens by default. Use Add value or Replace on a row, Add secret for a new name, or the ⋯ menu to remove a value. Project admins can change values; other members see names and whether each is saved.
- Paste .env: paste
NAME=valuelines to add or replace several secrets at once. Nothing is removed, and browser-exposed or reserved names are skipped with a note. - Edit all: saved values show as
NAME=***. Leave***to keep a value, type over it to replace it, delete a line to remove it, or add lines. You confirm removals before anything is saved, and every change lands together or not at all. - In a thread: when setup needs a value, Gentlybot asks whether you want to enter it now. If you do, a secure form opens; in a project with several repositories you choose the repository it belongs to. Project admins see Use in every new thread for this project, on by default; unchecked, the value reaches that thread only. A thread-only value can be moved to the project later from the thread's Environment tab.
Each value belongs to one app, or to all apps, in one repository. Two repositories can use the same variable name with different values, so app names must be unique across a project's repositories. Values reach the app's environment and its .env file, not repository-level setup: commands. New sandboxes receive them automatically; a running sandbox picks up a change after you rerun setup or restart the app.
Optional: use 1Password for secrets
If your team already keeps development secrets in 1Password, Gentlybot can load them instead of values added above. Keep secret values in 1Password, not in gently.apps.yml or your repository. Gentlybot loads the selected items into the sandbox environment for your apps.
- Create a 1Password service account with read access to the vault containing your development secrets, and copy its service account token.
- On the project’s Configure page, expand 1Password. Paste the token and select Save; then choose the vault and select its Save button. This vault is shared by the project’s item bindings.
- Create an item in that vault using one of the formats below. Copy its item ID (the item’s unique identifier), then expand the repository on Configure and add a
onepasswordblock in itsgently.apps.ymleditor. Save the config and start or refresh the sandbox.
Option 1 · Note body
Put KEY=value lines in the item’s free-text note body, like a .env file. Choose source_type: note. Custom fields on the same item are not read in this mode.
API_TOKEN=your-value
PRIVATE_KEY="your-multiline-value"Option 2 · Item fields
Add a separate Text or Concealed field for each variable. The field label is the variable name (for example API_TOKEN) and the field value is the secret. Choose source_type: fields. The note body is not read in this mode.
Add the block at the top level to share values across the repository’s apps, or under apps.<name> for one app. You can use both with different items. Replace the example IDs with IDs from the selected vault; item titles also work if unique, but IDs avoid ambiguity.
onepassword:
source_type: note
source_item_id: your-repo-item-id
apps:
web:
startup: pnpm dev
onepassword:
source_type: fields
source_item_id: your-web-item-id