Configuration
Configure your Ploy projects with ploy.yaml
Configuration
Ploy uses a ploy.yaml file in the root of your repository to configure build settings and deployment options. This file provides fine-grained control over how your project is built and deployed.
Quick Start
Create a ploy.yaml file in your repository root:
kind: static
build: npm run build
out: distCommit and push the file to trigger a deployment with your custom configuration.
Configuration Options
kind
Type: "static" | "nextjs" | "dynamic" | "worker"
Required: No (auto-detected if omitted)
Specifies the project type:
static: Static site generation (React, Vue, Angular, HTML, etc.)nextjs: Next.js applications with server-side rendering supportdynamic: Worker-based applications and full-stack runtimesworker: Alias fordynamic
kind: staticIf kind is set to static, the build command is required.
Example - Static React App:
kind: static
build: npm run build
out: distExample - Next.js App:
kind: nextjs
build: npm run build
out: .nextExample - Cloudflare Vite / TanStack Start App:
kind: dynamic
build: pnpm build
out: distbuild
Type: string
Required: Yes (when your project needs a build step)
The command to run to build your project. This is typically a script defined in your package.json.
build: npm run buildCommon examples:
# npm
build: npm run build
# pnpm
build: pnpm build
# yarn
build: yarn build
# Custom script
build: npm run build:productionThe build command is required for static apps and most dynamic apps,
including Vite, TanStack Start, and worker bundles built during deployment.
assets
Type: object
Required: No
Configures uploaded static assets for dynamic projects such as Cloudflare Vite
apps. Set static asset behavior here in ploy.yaml.
assets:
binding: ASSETS
not_found_handling: single-page-application
run_worker_first:
- /api/*
- "!/api/docs/*"Fields:
binding: Optional binding name exposed to your worker. Must be uppercase with underscores.not_found_handling: One ofnone,404-page, orsingle-page-application.run_worker_first: Eithertrue,false, or an array of route patterns.
Use run_worker_first when your worker should handle some requests before the
asset layer. In the example above, /api/* stays worker-first, while
/api/docs/* is served from static assets.
out
Type: string
Required: No (auto-detected if omitted)
Default: Framework-specific (e.g., dist, build, .next)
The relative path to the output directory containing your built files.
out: distValidation rules:
- Must be a relative path (not absolute)
- Cannot start with
/ - Cannot contain
..(path traversal prevention) - Trailing slashes are automatically removed
Common output directories:
| Framework | Default Output |
|---|---|
| Vite | dist |
| Create React App | build |
| Next.js | .next |
| Angular | dist/project-name |
| SvelteKit | build |
| Astro | dist |
Example:
kind: static
build: npm run build
out: distInvalid paths: - out: /dist ❌ (absolute path) - out: ../dist ❌ (path
traversal) - out: dist/ ✅ (trailing slash auto-removed)
base
Type: string
Required: No
Default: Repository root
The base directory where your project is located within the repository. This is useful for monorepos where your deployable project is in a subdirectory.
base: apps/webValidation rules:
- Must be a relative path (not absolute)
- Cannot start with
/ - Cannot contain
..(path traversal prevention) - Trailing slashes are automatically removed
Monorepo example:
my-monorepo/
├── apps/
│ ├── web/ ← Your Next.js app
│ └── api/
├── packages/
│ └── ui/
└── ploy.yamlkind: nextjs
base: apps/web
build: npm run build
out: .nextWhen using base, all paths (like out) are relative to the base directory,
not the repository root.
Monorepos
Ploy detects monorepos automatically — there is no monorepo field. A repository is treated as a monorepo when it contains workspace indicators (pnpm-workspace.yaml, package.json with workspaces, turbo.json, etc.) or when more than one ploy.yaml is present in the repository.
When a monorepo is detected and a project has a base field, Ploy installs dependencies from the repository root (so workspace dependencies resolve) and runs the build command from base.
my-monorepo/
├── apps/
│ ├── web/
│ │ └── ploy.yaml ← Config for web app
│ └── docs/
│ └── ploy.yaml ← Config for docs app
├── packages/
│ └── ui/
└── package.jsonapps/web/ploy.yaml:
kind: nextjs
base: apps/web
build: pnpm build --filter webapps/docs/ploy.yaml:
kind: nextjs
base: apps/docs
build: pnpm build --filter docsEach app's ploy.yaml is a complete, self-contained configuration — it is not
merged with any root config. Include all required fields in each file.
To exclude directories that happen to contain a ploy.yaml (e.g., examples/, templates/, fixtures) from monorepo detection, add a ploy-workspace.yaml at the repository root:
# ploy-workspace.yaml
exclude:
- examples/**
- templates/**node_modules/ is always excluded.
agentSDK
Type: boolean
Required: No
Default: false
Enables the Agent SDK bindings. When set to true, Ploy automatically configures all bindings required by @meetploy/agent-sdk:
ai: true(AI gateway)state: { PLOY_AGENT_STATE: ploy_agent_state }(durable key-value storage)fs: { PLOY_AGENT_FILES: ploy_agent_files }(file storage)workflow: { PLOY_AGENT_WORKFLOW: ploy_agent_run }(durable workflow)timer: { PLOY_AGENT_SCHEDULER: ploy_agent_scheduler }(scheduled tasks)
kind: worker
agentSDK: trueYou can still add extra bindings alongside agentSDK: true — they will be merged with the defaults.
Running ploy dev will warn you if @meetploy/agent-sdk is installed but
agentSDK: true is not set. Running ploy types will automatically add it.
agentRules
Type: boolean
Required: No
Default: true
When ploy dev detects an AI coding agent, it creates or refreshes version-matched instructions in the project's AGENTS.md or CLAUDE.md. These instructions direct the agent to documentation bundled with the installed @meetploy/cli version.
Disable this behavior for a project with:
agentRules: falseskew_protection
Type: boolean
Required: No
Default: true
Controls Skew Protection, which pins a client's
framework-managed requests to the deployment that served its page. Enabled by
default; set to false to opt out.
kind: nextjs
skew_protection: falseComplete Examples
Static Vite + React App
kind: static
build: npm run build
out: distNext.js App
kind: nextjs
build: pnpm build
out: .nextMonorepo with Turborepo
Project structure:
my-app/
├── apps/
│ ├── web/ # Next.js frontend
│ └── docs/ # Documentation site
├── packages/
│ └── ui/ # Shared UI components
├── package.json # Root with workspaces
├── turbo.json
└── ploy.yamlploy.yaml for deploying the web app:
kind: nextjs
base: apps/web
build: npm run build
out: .nextCustom Output Directory
kind: static
build: npm run build:prod
out: public/distSPA with Custom Base Path
kind: static
build: npm run build
out: build
base: packages/clientConfiguration Priority
Ploy uses the following priority order for configuration:
ploy.yaml(highest priority) - Explicit configuration file- Dashboard settings - Manual overrides in project settings
- Auto-detection (lowest priority) - Framework detection
Settings in ploy.yaml will override dashboard settings and
auto-detection. This ensures your repository configuration is the source of
truth.
Validation Errors
Common validation errors and how to fix them:
"Build command is required when kind is 'static'"
# ❌ Missing build command
kind: static
out: dist
# ✅ Fixed
kind: static
build: npm run build
out: dist"Out path must be relative, not absolute"
# ❌ Absolute path
kind: static
build: npm run build
out: /dist
# ✅ Fixed - relative path
kind: static
build: npm run build
out: dist"Out path cannot contain '..'"
# ❌ Path traversal
kind: static
build: npm run build
out: ../dist
# ✅ Fixed
kind: static
build: npm run build
out: dist"Base path must be relative, not absolute"
# ❌ Absolute path
kind: static
base: /apps/web
build: npm run build
# ✅ Fixed
kind: static
base: apps/web
build: npm run buildBest Practices
- Commit
ploy.yamlto version control - Keep configuration in sync with code - Use
kind: staticfor most frameworks - Only usenextjsfor Next.js apps requiring SSR - Use relative paths - Never use absolute paths or
..for security - Match your framework's output directory - Check your framework's documentation for the correct
outpath
Troubleshooting
My build is failing
- Check that your
buildcommand works locally:npm run build - Verify the
outdirectory exists after building - Review deployment logs for specific error messages
My monorepo isn't building correctly
- Verify your root
package.jsonhas workspace configuration (or apnpm-workspace.yamlexists) - Check that the
basepath points to the correct app directory - Ensure build commands are run from the base directory
- To exclude unrelated
ploy.yamlfiles (e.g., inexamples/), add aploy-workspace.yamlwith anexclude:list at the repo root
Configuration changes aren't taking effect
- Ensure
ploy.yamlis committed to your repository - Push your changes to trigger a new deployment
- Check deployment logs to see which configuration was used
- Verify there are no YAML syntax errors in your file
Next Steps
- Review the Quick Start Guide for deployment basics
- Learn about Self-Hosting to deploy Ploy on your infrastructure
- Check out example projects for inspiration
How is this guide?
Last updated on
Introduction to Ploy
Ploy is an open-source, self-hostable serverless deployment platform. Deploy from GitHub, run serverless workers with databases, queues, and durable workflows, and host it all on your own infrastructure.
CLI Overview
The Ploy CLI for local development, type generation, and building workers.