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. This replaces the need to commit wrangler.json just to wire static
assets.
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.
monorepo
Type: boolean
Required: No
Default: false
Enables monorepo mode. When enabled, Ploy will:
- Look for
package.jsonin the repository root - Install dependencies from the root
- Execute build commands from the base directory
monorepo: true
base: apps/webTypical monorepo configuration:
kind: static
monorepo: true
base: apps/frontend
build: npm run build
out: distHow it works:
- Ploy installs dependencies from the repository root (where the main
package.jsonis) - Changes directory to the
basepath - Runs the
buildcommand from the base directory - Looks for output in
{base}/{out}
When to use monorepo: true: - You have a monorepo with shared packages -
Your root package.json contains workspace configuration - Dependencies are
managed at the repository root
Per-App Configuration in Monorepos
In a monorepo, you can place ploy.yaml directly in each app's subdirectory (the base directory) instead of — or in addition to — the repository root. During builds, Ploy automatically discovers and uses the ploy.yaml closest to your app:
- If a
ploy.yamlexists in the base directory (e.g.,apps/web/ploy.yaml), it is used - Otherwise, Ploy falls back to the root
ploy.yaml
This lets each app in a monorepo define its own build configuration independently:
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
monorepo: true
build: pnpm build --filter webapps/docs/ploy.yaml:
kind: nextjs
base: apps/docs
monorepo: true
build: pnpm build --filter docsThe per-app ploy.yaml is a complete, self-contained configuration — it is
not merged with the root ploy.yaml. Make sure to include all required fields
in each app's config file.
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.
Complete 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
monorepo: true
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 - Set
monorepo: truefor monorepos - Ensures proper dependency installation - 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
- Ensure
monorepo: trueis set inploy.yaml - Verify your root
package.jsonhas workspace configuration - Check that the
basepath points to the correct app directory - Ensure build commands are run from the base directory
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