pier.yaml
Every key of the project file — top level, environments, and each resource kind
pier.yaml declares a project: its environments and, per environment, every resource.
pier apply converges the platform to it; pier deploy also builds and rolls
(Apply and deploy). The file is found by walking up from the
current directory (CLI configuration).
version: 1
name: acme # display name
project: acme-8fb4 # slug, generated at pier config init — do not edit
vcs: # written by pier github connect
provider: github
repo: acme-inc/acme
auto_deploy: true
templates: # reusable target fragments, applied with `use:`
web:
replicas: 2
environments:
prod:
protected: true
env:
LOG_LEVEL: { value: info }
apps: [ … ]
sites: [ … ]
postgres: [ … ]
buckets: [ … ]
volumes: [ … ]
staging:
…Top level
| Key | Type | Meaning |
|---|---|---|
version | int | 1 |
name | string | display name |
project | string | the project slug; generated |
workspace | string | optional; pins the workspace when a user belongs to several |
vcs | object | provider (github), repo (owner/name), auto_deploy (default true), commit_status (default true) — GitHub |
templates | map | named fragments (replicas, autoscale, dockerfile) a target or app pulls in with use:; expanded by the CLI before anything reaches the platform |
environments | map | slug → environment; each is a complete, independent declaration (Concepts) |
Environment
| Key | Type | Meaning |
|---|---|---|
name | string | display name (default: the slug) |
protected | bool | pier dev asks before pulling this environment's secrets; the console marks it |
env | map | variables at environment scope — Environment variables |
apps, sites, postgres, buckets, volumes | lists | the resources |
Variables (env)
env:
LOG_LEVEL: { value: info }
API_KEY: { secret: true }
DATABASE_URL: { from: postgres.main.url }
OPTIONAL: { value: "1", required: false }value · secret · from (postgres.<name>.url|host|port|user|password|database,
buckets.<name>.url|name|endpoint|access_key|secret_key) · required (default true).
Allowed at environment, app/site and target scope; the narrowest wins.
App
apps:
- name: api
build_context: ./api # or targets[].image
template: node_hono # starter, instead of build_context
env: { … }
previews:
enabled: true
branch_pattern: "feat/*"
plan: app-s
ttl_hours: 72
max_targets: 5
env: { … }
targets:
- name: primary
primary: true
use: web # a template
plan: app-s
replicas: 2 # or autoscale
autoscale:
min: 1
max: 3
idle_after: 10m # with min 0: scale to zero
triggers:
- { type: cpu, target: 70 }
- { type: latency, target_ms: 250 }
image: traefik/whoami:v1.10.1 # static-image flow; exclusive with build_context
port: 80 # required with image; default 8080 for builds
dockerfile: Dockerfile.worker
deploy:
branch: main # or branch_pattern
domains:
- host: api.acme.example
volumes:
- { volume: data, mount_path: /var/lib/app }
env: { … }Trigger types: cpu, memory (target percent), rps (target requests per second per
instance), latency (target_ms). Apps · Plans.
Site
sites:
- name: www
build_context: ./www
template: site_astro
env: { … }
previews: { … } # as for apps, without plan
targets:
- name: primary
primary: true
framework: vite # vite · nextjs-static · astro · sveltekit-static · hugo · static · custom
build_command: npm run build
output_dir: dist
index_document: index.html
spa_fallback: true
routes:
redirects: [ { source: /old, destination: /new, status: 308 } ]
rewrites: [ { source: /api/*, destination: /v2/* } ]
headers: [ { path: /assets/*, headers: { Cache-Control: "public, max-age=31536000" } } ]
fallback: { type: spa, paths: [/app/*], exclude: [/api/*], document: index.html }
deploy: { branch: main }
domains: [ { host: www.acme.example } ]
env: { … }Postgres
postgres:
- name: main
targets:
- name: primary
version: "17"
plan: pg-s
replicas: 1
bootstrap_default_database: app
max_storage_gb: 50Exactly one target per environment. Postgres.
Bucket
buckets:
- name: uploads
access: private # or publicVolume
volumes:
- name: data
storage_gb: 5
durability: standard # or ha; set at creationMounted by an app target's volumes: binding. Volumes.
Validation
pier config validate checks the file without a server call: syntax, names, that every
from: names a resource in the same environment, that image and build_context are not
both set, that replicas and autoscale are not both set, one Postgres target per
environment. pier apply --dry-run shows what the platform would change.