# aria.yaml reference: every field

> The complete aria.yaml (version 1) reference: services, databases, env, references between services, backups, limits, and two worked examples.

Source: https://getaria.cloud/docs/aria-yaml (updated 1 November 2026)

`aria.yaml` is the one file that tells Aria what your app needs to run: its services, its databases, and which variable goes to which service. Put it at the root of your repository (or tell Aria where it is). Aria AI or your coding agent usually writes it, Aria checks it with line numbers, and Aria applies it on the first launch and on every push. Unknown keys are refused, so a typo is caught before anything is deployed.

## What does a whole file look like?

```yaml
version: 1
name: my-app
services:
  web:
    type: web
    path: apps/web
    port: 3000
    healthcheck: /
    domain: "@"
databases:
  main:
    type: postgres
    migrations: db/migrations
env:
  DATABASE_URL:
    services: [web]
    from: main.url
resources:
  users: 200
```

Only `version` and `services` are required.

## What are the top-level fields?

| Field | Type | Default | What it does |
|---|---|---|---|
| `version` | `1` | required | The schema version. |
| `name` | text, up to 60 characters | none | A label for people. |
| `services` | map of name to service | required | What runs. At least one, at most 12. |
| `databases` | map of name to database | none | Up to 6. |
| `env` | map of NAME to variable | none | Up to 200 variables. |
| `email` | `{ enabled, from }` | off | A test inbox that catches the mail your app sends over SMTP so you can read it in Aria. It delivers to nobody. To send real email, turn on [Aria Mail](https://getaria.cloud/docs/mail). |
| `registries` | map of name to login | none | Logins for private image registries. |
| `backup` | backup settings | none | Your own backup schedule, retention and hooks. |
| `resources` | sizing hints | none | Traffic, people at once, data region. |

Names of services, databases, volumes and registries use lowercase letters, digits and dashes, start with a letter, and have at most 31 characters. A service cannot be called `supabase`, `inbox` or `email`, or end in `-supabase`. A database cannot share a service's name. The file is up to 64 KB.

## What can a service say?

`services.<name>` describes one thing that runs.

| Field | Type | Default | What it does |
|---|---|---|---|
| `type` | `web`, `api`, `worker`, `static` | required | `web`, `api` and `static` are public and have a port. A `worker` has neither. A `static` site is built once, then served. See [Deploy a website](https://getaria.cloud/docs/deploy-website). |
| `path` | folder in the repository | `.` | The service's own folder. A push rebuilds the service when files under it change. |
| `context` | folder | same as `path` | The build context, for a monorepo that builds from its root (`.`). Install, build and start commands then run from it. |
| `dockerfile` | path from the repository root | none | Build with this Dockerfile. Aria does not look for one, so set it whenever the service has one. It must sit inside the build context. `install`, `build` and `start` are then ignored. |
| `image` | `registry/name:tag` | none | Run a prebuilt image instead of building. The tag can hold `${commit}`, the full commit SHA Aria deploys. Not with `path`, `context`, `dockerfile`, `install`, `build` or `start`, and not on `static`. |
| `command` | list of arguments | none | Replaces the image's own command. Needs an `image` or a `dockerfile`. Not with `start`. |
| `runtime` | text, up to 40 | none | A hint for sizing. |
| `framework` | text, up to 40 | none | A hint for sizing. A Next.js build needs more memory than a Go one. |
| `install`, `build`, `start` | one-line command | Aria's detection | Used when there is no Dockerfile. One line, up to 1,000 characters, with no backticks, single quotes or `$( )`. |
| `port` | 1 to 65535 | 3000 for `web`, `api`, `static`; none for a worker | The port the service listens on. Aria sets `PORT` and `HOST=0.0.0.0` for every service. |
| `healthcheck` | path | `/` | Where Aria asks whether the service is up. A plain path with no query string, answered quickly without a login, with no 4xx or 5xx. Set it when `/` redirects or needs a login. |
| `public` | true or false | true, false for a worker | Whether the service has an address on the internet. A worker cannot be public. |
| `domain` | `@`, a name, or a full host | none | Its address on your own domain. `@` is the domain itself, `api` is `api.yourdomain.com`. Public services only, and two services cannot share one. See [Domains](https://getaria.cloud/docs/domains). |
| `realtime` | true or false | false | The service holds long connections such as WebSockets or live audio. Sizing counts connections, not page views. Not on `static`. |
| `migrate` | one-line command | none | Your app's own migration command, such as `prisma migrate deploy`. It runs once per deploy in the newly built image, with the service's variables, before the new version takes traffic. If it fails, the old version keeps serving. It also runs before restarts, so it must be safe to run again. Put it on one service. |
| `volumes` | map of name to absolute folder | none | Folders that keep their files across every deploy and restart, up to 8 per service. A new volume starts with what the image has at that folder. Not on `static`. |
| `depends_on` | list of service names | none | Services that must start first. Aria already orders services by what they read, so you rarely need it. Circular dependencies are refused. |
| `stop_grace_period` | 1 to 900 seconds | the platform's | How long the service gets to finish when it is stopped or replaced. |
| `memory` | MB (`1536`) or text (`"1.5G"`, `"768M"`) from 128M to 64G | twice Aria's estimate | The service's memory limit. It replaces Aria's estimate when sizing the server, so the plan must have room for it. |
| `shm_size` | MB or text from 64M to 32G | none | Shared memory for Chromium or desktop containers. It cannot exceed `memory`. |
| `gpu` | `true`, or `{ memory, count }` | none | The service needs an NVIDIA GPU. `memory` is GPU memory with a unit such as `"24G"` (1G to 192G), `count` is 1 to 8. A stack that asks for a GPU only fits a GPU plan, and GPU servers may be available on request rather than straight away. Leave it out for code that only calls a hosted AI API. |
| `pids_limit` | 32 to 65536 | none | The most processes and threads at once. |
| `access` | `public`, `owner` | `public` | With `owner`, Aria's sign-in sits in front of the service and only the workspace's Owners and Admins get through. |
| `access_exceptions` | up to 20 paths | none | With `access: owner`, paths that skip the sign-in because your app checks them itself. `/hooks/*` matches everything under it. Other paths match exactly. `/.aria/*` is reserved. |

A static site cannot use `image`, `volumes`, `gpu`, `stop_grace_period`, `shm_size`, `realtime` or `migrate`. A worker cannot be public or have a `domain`.

## What can a database say?

`databases.<name>` adds one database. Aria runs Postgres, Redis and Supabase.

| Field | Type | Default | What it does |
|---|---|---|---|
| `type` | `postgres`, `redis`, `supabase` | required | Which database. |
| `version` | text | latest Aria offers | Postgres `16` or `17`, Redis `7`. Leave it out unless you need to pin. Not for Supabase. |
| `path` | folder | `supabase` | Supabase only. The folder laid out like the Supabase CLI's: `migrations/`, `seed.sql`, `functions/`, `config.toml`. Two Supabase databases each need their own. |
| `migrations` | folder | none | Postgres only. A folder of `.sql` files applied in the order of their leading number, each once. Only names that start with digits and an underscore count, like `0001_init.sql`. |
| `seed` | `.sql` file, or `false` | none | Postgres: a file that runs once, on a new database. Supabase: only `false`, which stops `seed.sql` from running. |
| `auth` | `{ smtp }` | none | Supabase only. Real sign-up and password-reset email through your SMTP provider. See [Self-hosted Supabase](https://getaria.cloud/docs/supabase). |
| `domain` | `@`, a name, or a full host | `api`, or `supabase` if a service is named `api` | Supabase only. The Supabase gateway's address on your domain. |
| `extensions` | list | none | Postgres only. Any of `vector`, `pg_trgm`, `btree_gin`, `btree_gist`, `fuzzystrmatch`, `citext`, `pgcrypto`, `uuid-ossp`, `hstore`, `unaccent`. `vector` gives you pgvector. |
| `names` | list of names | none | Postgres only. More databases on the same server, each owned by a login of the same name. |

More on each kind is on [Databases](https://getaria.cloud/docs/databases) and [Self-hosted Supabase](https://getaria.cloud/docs/supabase).

## What can a variable say?

`env.<NAME>` decides which services get a variable and where its value comes from. A variable reaches only the services it lists. With no `services` it reaches every service, so always give a secret a `services` list.

| Field | Type | Default | What it does |
|---|---|---|---|
| `services` | list of service names | every service | Who gets it. A Supabase database's name delivers it to that Supabase's edge functions. |
| `from` | `name.attribute` | none | A value Aria knows, such as a database or service address. See the table below. |
| `template` | text with `${name.attribute}` | none | A value built from references and kept current when addresses change, like `wss://${api.public_host}/live`. |
| `generate` | `secret`, `password`, `uuid` | none | Aria makes the value once and keeps it. |
| `value` | text | none | A fixed setting that is not secret, like `production`. Put numbers and `true` in quotes. |
| `required` | true or false | false | The services that get it wait until it has a value. |
| `secret` | true or false | true for a value only a person has, and for a generated one | Masks the value everywhere. Not allowed together with `value`. |
| `build` | true or false | true for `NEXT_PUBLIC_`, `VITE_`, `PUBLIC_`, `REACT_APP_`, `NUXT_PUBLIC_`, `EXPO_PUBLIC_`, `GATSBY_` | The value is needed while building, not only at runtime. |
| `description` | text, up to 300 | none | Shown to the person who has to enter it. |
| `database` | a name from a Postgres `names` list | none | With `from: <postgres>.url`, points at one of its databases, with that database's own login. |

Use only one of `from`, `template`, `generate` and `value` per variable. Everything built into a browser bundle is public: never wire a credential into a variable with a browser prefix. The full story is on [Environment variables](https://getaria.cloud/docs/environment-variables).

## How does one service or database reference another?

`from: name.attribute` and `${name.attribute}` read a value Aria knows. `name` is a key under `databases` or `services`, or the word `email`. Aria works out every address before it builds anything.

| Kind | Attributes you can read |
|---|---|
| Postgres | `url`, `host`, `port`, `user`, `password`, `database` |
| Redis | `url`, `host`, `port`, `password` |
| Supabase | `url`, `public_host`, `internal_url`, `anon_key`, `service_role_key`, `db_url`, `jwt_secret` |
| A service | `url`, `public_host`, `internal_url`, `host`, `port` |
| `email` | `smtp_url`, `host`, `port`, `from` (needs the `email` block on) |
| `backup` | `status_url` (needs a `backup` block) |

`public_host` is the public address without `https://`, handy for building a `wss://` URL in a template. `url` and `public_host` need a public service. From another service, read `internal_url`. Aria orders services by what they read, so a frontend that reads an API's address waits for the API.

## What can the backup block say?

Without a `backup` block, Aria already backs up your databases and volumes every night. Add one to choose the schedule and what is kept. See [Backups](https://getaria.cloud/docs/backups).

| Field | Default | What it does |
|---|---|---|
| `schedule` | once a night | A five-field cron in UTC, at most once an hour, like `"0 3 * * *"`. |
| `retention` | 14 daily, 8 weekly, 6 monthly | The newest set of each of the last N days (1 to 90), weeks (0 to 52) and months (0 to 36). |
| `volumes` | every volume | Volume names to include. Write `service/name` when two services use the same name. |
| `exclude` | none | Paths inside volumes to leave out. `**` matches any folders, `*` matches within a name. |
| `hooks` | none | Commands to run in a running service before (`pre`) and after (`post`) the copy, with a `timeout` of 1 to 600 seconds. A failing `pre` hook fails the backup. |
| `encryption` | Aria's workspace key | `recipient_secret` names a variable holding your own age public key. Aria then cannot read your backups, and a restore asks for your key file. |

## What are registries and resources?

`registries.<name>` logs in to a private registry so Aria can pull an `image`. It takes `host` (like `ghcr.io`), `username`, and `password_secret`, the name of a secret you enter in Aria. That secret is used only to pull images, never given to a service, and cannot also appear under `env`.

`resources` gives Aria hints:

| Field | What it does |
|---|---|
| `traffic` | `low`, `medium` or `high`. |
| `users` | People using the app at the same time at its busiest, 1 to 1,000,000. Sizes memory and checks the processor. |
| `users_next_year` | The same, a year from now. Aria places the app where it can grow. |
| `data_region` | `europe`, `north-america` or `asia-pacific`: where your data must stay. Aria marks server locations outside it. |
| `notes` | Text for people. Nothing reads it. |

## What are the limits?

- Up to 12 services, 6 databases and 200 variables per project, and up to 5 projects on one server by default. Every limit is on [Limits](https://getaria.cloud/docs/limits).
- Databases are Postgres (16 or 17), Redis (7) and Supabase. There is no MySQL or MongoDB.
- Up to 8 volumes per service.
- Commands are one line, with no backticks, single quotes or `$( )`.
- Supabase needs about 4 GB of memory on its own.

## Example 1: three services, Postgres and Redis

This stack has a web front end built from a Dockerfile at the repository root, an API built from its folder that runs its own migrations, and a worker that waits for a key only you can give.

```yaml
version: 1
name: stack-demo
services:
  web:
    type: web
    path: apps/web
    context: .
    dockerfile: apps/web/Dockerfile
    port: 3000
    healthcheck: /
  api:
    type: api
    path: apps/api
    start: node server.js
    migrate: node migrate.js
    port: 8080
    healthcheck: /health
  worker:
    type: worker
    path: apps/worker
    context: .
    dockerfile: apps/worker/Dockerfile
databases:
  main:
    type: postgres
    migrations: db/migrations
    seed: db/seed.sql
  cache:
    type: redis
email:
  enabled: true
  from: hello@example.com
env:
  PUBLIC_SITE_NAME:
    services: [web]
    value: Stack demo
    build: true
  API_URL:
    services: [web]
    from: api.internal_url
  DATABASE_URL:
    services: [api]
    from: main.url
  REDIS_URL:
    services: [api, worker]
    from: cache.url
  SESSION_SECRET:
    services: [api]
    generate: secret
  SMTP_URL:
    services: [api]
    from: email.smtp_url
  MAIL_FROM:
    services: [api]
    from: email.from
  EXTERNAL_API_KEY:
    services: [worker]
    required: true
    secret: true
    description: Any value. The worker refuses to start without it.
```

What to notice:

- `web` reads the API through `api.internal_url`, so it starts after the API.
- `api` runs `node migrate.js` before each new version takes traffic.
- The database schema comes from `db/migrations`, applied in order, each once, and `db/seed.sql` runs once on a new database.
- `EXTERNAL_API_KEY` is `required` and `secret`, so the worker waits until you enter it with `aria env add EXTERNAL_API_KEY`.
- `PUBLIC_SITE_NAME` is `build: true` because the web build reads it.

## Example 2: a web app on self-hosted Supabase

One web service on Supabase. The app receives Supabase's address and keys through variables, and one extra variable reaches an edge function.

```yaml
version: 1
name: supabase-demo
services:
  web:
    type: web
    path: .
    dockerfile: Dockerfile
    port: 3000
    healthcheck: /
databases:
  main:
    type: supabase
env:
  NEXT_PUBLIC_SUPABASE_URL:
    services: [web]
    from: main.url
    build: true
  NEXT_PUBLIC_SUPABASE_ANON_KEY:
    services: [web]
    from: main.anon_key
    build: true
  SUPABASE_INTERNAL_URL:
    services: [web]
    from: main.internal_url
  SUPABASE_SERVICE_ROLE_KEY:
    services: [web]
    from: main.service_role_key
  DATABASE_URL:
    services: [web]
    from: main.db_url
  DEMO_FUNCTION_SECRET:
    services: [main]
    generate: secret
  GITHUB_SECRET:
    services: [main]
    required: true
    secret: true
    description: The GitHub OAuth app's client secret, for signing in with GitHub.
```

What to notice:

- The two `NEXT_PUBLIC_` variables are public by design, so they are marked `build: true` and are safe in a browser bundle. The service-role key is not, and goes only to server code.
- `services: [main]` names the Supabase database, not a service. That delivers the variable to the database's edge functions and to the `config.toml` that Supabase reads. `GITHUB_SECRET` turns on GitHub sign-in once it has a value.
- Supabase's own folder (`supabase/` with `migrations/`, `seed.sql`, `functions/` and `config.toml`) lives in the repository. Aria applies it on every launch and push.

## How do I check the file?

Your coding agent can call the `validate_stack_file` tool, which returns problems with line numbers and warnings about things that are allowed but probably not what you meant. In the browser, Aria checks the file as you paste it. Problems stop a file from being saved. Warnings never block anything. Aria AI runs the same check as it writes the file. See [Aria AI](https://getaria.cloud/docs/aria-ai) and [Connect a coding agent](https://getaria.cloud/docs/agents).

Next: [Environment variables and secrets](https://getaria.cloud/docs/environment-variables)
