When you create an app, dply inspects your repository, works out which framework it uses, and fills in the build command, output directory and runtime mode. This page lists everything dply recognizes, the runtime versions it uses, and how to change what it chose.
How detection works
After you pick a repository and branch on the Create an app page, dply reads the files at the repository root (or at the package you chose in a monorepo) and shows the result, for example astro · Site.
dply checks for these files, and when several match, the more specific one wins:
composer.json: a PHP app.Gemfile: a Ruby app, unless it namesjekyllorgithub-pages(then it's a Jekyll site).package.json: a Node.js app. The dependencies decide the framework.- A static site generator config file, or an
index.htmlat the root: a static site.
PHP and Ruby apps often ship a package.json for their frontend assets. Because their own manifest takes precedence when it names a framework, a Laravel app with Vite is detected as Laravel, not as a Vite site. dply builds its frontend assets as part of the container image.
The same rules apply whether dply reads your files through the GitHub API or clones the repository (GitLab, Bitbucket, or anything the API path can't read), so a project is always detected the same way.
From the framework, dply recommends Site or App on the create page:
- A PHP, Ruby or Node HTTP server is an App that runs as a Container.
- A Next.js app that renders on the server (no
output: 'export') or a Keel app is an App that runs as Worker SSR. - Other JavaScript frameworks that render on the server are recommended as Hybrid (under Advanced), because running them as Worker SSR needs a Cloudflare adapter in your project. Choose App if yours has one.
- Anything else that builds files is a Site and runs as Static.
Container apps
These run on Cloudflare Containers. If your repository has a Dockerfile at its root, dply builds that and routes traffic to the port in its EXPOSE line (8080 when there isn't one). Otherwise dply generates a Dockerfile for the stack below.
Container apps are available on every plan. See Container apps.
| Framework | Detected from | Runtime | How it serves |
|---|---|---|---|
| Laravel | laravel/framework in composer.json |
PHP | php-fpm behind nginx |
| Laravel with Octane | laravel/octane in composer.json |
PHP | Swoole, or RoadRunner when a spiral/roadrunner package is required |
| Symfony | symfony/framework-bundle in composer.json |
PHP | php-fpm behind nginx |
| Other PHP | composer.json |
PHP | php-fpm behind nginx |
| Rails | rails in Gemfile |
Ruby | Puma |
| Sinatra and other Rack apps | Gemfile |
Ruby | rackup |
| NestJS | @nestjs/core in package.json |
Node.js | npm start |
| Express, Fastify, Koa | express, fastify or koa in package.json |
Node.js | npm start |
| Play and other Scala apps | build.sbt |
Scala (JVM) | The sbt-assembly jar with java -jar, or the sbt-native-packager start script. See Scala. |
| Django, Flask, FastAPI and other Python apps | pyproject.toml, requirements.txt, Pipfile or setup.py |
Python | Gunicorn (Django, Flask) or Uvicorn (FastAPI) |
| Gin, Echo, Fiber, Chi and other Go apps | go.mod |
Go | The compiled binary |
| Axum, Actix, Rocket, Warp, Poem and other Rust apps | Cargo.toml |
Rust | The release binary |
| Spring Boot, Micronaut and other Java apps | pom.xml, build.gradle or build.gradle.kts |
Java | The built jar with java -jar |
| ASP.NET Core and other .NET apps | a .csproj |
.NET | dotnet <assembly>.dll |
| Phoenix and other Elixir apps | mix.exs |
Elixir | The mix release binary |
| Fresh and other Deno servers | deno.json or deno.jsonc |
Deno | deno task start |
| Elysia and other Bun servers | package.json with a Bun server |
Bun | bun run start |
Node container apps install with pnpm, yarn or npm depending on which lockfile the repository has, and start with the matching start command. The app must listen on the PORT environment variable, which is set to 8080.
PHP versions
| Supported | 8.2, 8.3, 8.4, 8.5 |
| Chosen from | config.platform.php in composer.json, then require.php |
| Open-ended constraints | Get the newest supported version up to 8.4. ^8.2 builds with 8.4. |
| Newer versions | Name them explicitly. ^8.5 or 8.5.* builds with 8.5. |
PHP extensions your composer.json or composer.lock require are installed into the image before composer install runs.
Ruby versions
dply reads the major and minor version from .tool-versions, .ruby-version or the ruby line in your Gemfile, in that order, and uses 3.3 when none of them pins one. It builds Ruby 4.0, 3.4, 3.3 and 3.2. A pin older than 3.2 builds on 3.2, and one newer than 4.0 builds on 4.0. Ruby 3.2 no longer gets security fixes upstream, so move off it when you can.
Node.js versions
| Supported | 18, 20, 22, 24 |
| Default | 22 |
| Chosen from | engines.node in package.json, then .nvmrc, then .node-version, then packageManager |
Any other version is moved to a supported one: below 18 builds with 18, above 24 with 24, and a version in between with the next supported one up. The same rule picks the Node.js version for static, hybrid and Worker SSR builds.
Scala
dply builds sbt projects (build.sbt at the app root) with the sbt launcher on Eclipse Temurin JDK 21. The launcher uses the sbt version in project/build.properties. The project needs one of these plugins in project/plugins.sbt:
| Plugin | Build | Runs |
|---|---|---|
| sbt-assembly | sbt assembly |
The fat jar, with java -jar |
| sbt-native-packager (Play includes it) | sbt stage |
The staged start script, with -Dhttp.port=$PORT |
Without either, the build fails with "dply builds Scala apps that use sbt-assembly or sbt-native-packager (Play has it). Add one to project/plugins.sbt, or add a Dockerfile." The app listens on port 8080 (PORT). For another JDK version, add a Dockerfile.
A Play app won't start in production without an application secret. Set APPLICATION_SECRET under Environment and read it in conf/application.conf with play.http.secret.key = ${?APPLICATION_SECRET}.
JavaScript frameworks
These build in a Node.js image and serve from the edge.
| Framework | Detected from | Build command without a build script |
Output directory | Default mode |
|---|---|---|---|---|
| Next.js | next |
npm run build |
out |
Site with output: 'export', otherwise App (Worker SSR) |
| Nuxt | nuxt |
npm run generate (always) |
.output/public |
Site |
| Astro | astro |
npm run build |
dist |
Static |
| SvelteKit | @sveltejs/kit |
npm run build |
build |
Hybrid |
| Remix | remix or any @remix-run/* package |
npm run build |
build/client |
Hybrid |
| Hono | hono |
npm run build |
dist |
Hybrid |
| Keel | @shaferllc/keel |
npm run css:build --if-present |
public |
Hybrid |
| Vite | vite |
npm run build |
dist |
Static |
| Gatsby | gatsby |
npm run build |
public |
Static |
| Eleventy | @11ty/eleventy |
npx @11ty/eleventy |
_site |
Static |
| VitePress | vitepress |
npm run docs:build |
docs/.vitepress/dist |
Static |
| Docusaurus | @docusaurus/core |
npm run build |
build |
Static |
Other Node.js project with a build script |
package.json |
npm run build |
dist |
Static |
When the project's package.json defines a build script, dply runs npm run build instead of the command in the table. Nuxt is the exception: dply always builds it with npm run generate (or npx nuxi generate when there's no generate script), because a plain nuxt build makes a server build with no complete static output.
A Next.js project is a static site only when next.config.js, next.config.mjs, next.config.ts or next.config.cjs sets output: 'export' (or the build script runs next export). Otherwise it renders on the server.
To check or change the build command and output directory before you deploy, open Advanced on the create page.
Important
Hybrid needs an Origin URL: a server you already run that handles your app's server routes. dply serves the static files and proxies the rest. See Static & hybrid sites.
Worker SSR
Worker SSR runs your app's server code as a Cloudflare Worker, with no origin to run. It's what App means for a server-rendered JavaScript framework. Next.js and Keel need nothing extra; the others need the framework's Cloudflare adapter in your project. It needs a paid plan.
| Framework | Adapter | What dply runs |
|---|---|---|
| Next.js | None needed | npx @opennextjs/cloudflare build, in place of your build command |
| SvelteKit | @sveltejs/adapter-cloudflare |
Your build command |
| Astro | @astrojs/cloudflare |
Your build command |
| Remix | @remix-run/cloudflare |
Your build command |
| Keel | None needed | A Wrangler dry-run bundle of your Worker |
Worker SSR sites have no per-site fee; they bill for their Workers CPU time and delivery usage like any other app. See Server rendering (SSR) and Plans & pricing.
Static sites
| Site | Detected from | Build command | Output directory |
|---|---|---|---|
| Plain HTML | index.html at the repository root |
None | . (the repository root) |
Hugo and Jekyll sites are detected (from hugo.toml or a config.toml with Hugo keys, and from _config.yml or a Jekyll Gemfile), but dply can't build them yet: builds run in a Node.js image without Hugo or Ruby. The create page says so and doesn't let you deploy. Commit the built site (public or _site) to a repository and deploy that as a plain HTML site, or use a Node.js generator such as Eleventy or Astro.
Not supported
dply stops you on the create page when it detects one of these, and shows why.
| Stack | Detected from |
|---|---|
| WordPress | wp-config.php, or roots/wordpress or johnpbloch/wordpress in composer.json |
A repository that looks like a framework's own source or a monorepo root, rather than a single app, is also stopped. Pick the app's package directory instead.
Override the detected settings
In the dashboard
In your app, open Build.
| Setting | What it does |
|---|---|
| Build command | The command that builds the app |
| Output directory | The directory the build writes files to, relative to the repository root |
| Deploy on push | Deploy every push to the production branch |
| Advanced → Repository root | A monorepo subdirectory to build from. Auto-deploy only runs for changes under it. |
| Advanced → SPA fallback | Serve index.html for unknown paths after a 404 |
Choose Save, then redeploy. Changes apply to the next deploy.
In your repository
A dply.yaml, dply.yml or dply.json file at the repository root overrides the dashboard's build settings on every deploy:
build:
command: npm run build
output: dist
root: apps/web
When the file sets build keys, Build shows it under Repo config. See Configuration files for every key.
Container apps
Commit a Dockerfile at the repository root to control the image completely. dply builds it as it is.