Build
aria.yaml reference: every field
The complete aria.yaml (version 1) reference: services, databases, env, references between services, backups, limits, and two worked examples.
In this page
- What does a whole file look like?
- What are the top-level fields?
- What can a service say?
- What can a database say?
- What can a variable say?
- How does one service or database reference another?
- What can the backup block say?
- What are registries and resources?
- What are the limits?
- Example 1: three services, Postgres and Redis
- Example 2: a web app on self-hosted Supabase
- How do I check the file?
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?
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: 200Only 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. |
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. |
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. |
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. |
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 and Self-hosted 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.
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.
| 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.
- 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.
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:
webreads the API throughapi.internal_url, so it starts after the API.apirunsnode migrate.jsbefore each new version takes traffic.- The database schema comes from
db/migrations, applied in order, each once, anddb/seed.sqlruns once on a new database. EXTERNAL_API_KEYisrequiredandsecret, so the worker waits until you enter it witharia env add EXTERNAL_API_KEY.PUBLIC_SITE_NAMEisbuild: truebecause 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.
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 markedbuild: trueand 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 theconfig.tomlthat Supabase reads.GITHUB_SECRETturns on GitHub sign-in once it has a value.- Supabase’s own folder (
supabase/withmigrations/,seed.sql,functions/andconfig.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 and Connect a coding agent.