Bring your own repo
Last updated
Move an existing Astro site to Ploy without discovering the publish contract one failed build at a time.
Use the public Ploy site skill (ploy-site) with your coding agent. If the source is already a Ploy site, follow the workspace transfer guide instead.
Read this contract before importing code. Ploy validates and patches the tenant repository during publish, so a project that builds on another host can still fail here when its package versions or config shape differ.
Repository contract
package.jsonmust be at the repository root and itsnamemust be exactlyploy-web.astromust be installed as a dependency, not only available globally.astro.config.mjsmust be at the repository root and committed atHEAD.- The config must default-export a plain
defineConfig({...})object literal.
{
"name": "ploy-web",
"dependencies": {
"astro": "^6.0.0",
"@astrojs/cloudflare": "^13.0.0",
"@astrojs/react": "^5.0.0",
"@astrojs/sitemap": "^3.0.0"
}
}If the site uses MDX, also install @astrojs/mdx 5.x.
Match the Ploy toolchain
| Package | Supported major |
|---|---|
astro | 6.x |
@astrojs/cloudflare | 13.x |
@astrojs/react | 5.x |
@astrojs/sitemap | 3.x |
@astrojs/mdx | 5.x, when MDX is used |
Ploy wraps the tenant Astro config with its own config, which imports these packages from the tenant's node_modules. A repository on Astro 7, or one missing the Cloudflare adapter, can therefore fail with an unrelated-looking module not found error.
The sandbox install is a plain bun install. Ploy deletes package-lock.json before installing, so do not rely on npm-only lockfile behavior.
Keep astro.config.mjs patchable
At publish time Ploy AST-patches four fields: site, build.assets, cacheDir, and vite.cacheDir. Each must be represented with plain literals in the exported object. Do not use environment lookups, computed values, helper-returned config, or spread objects for these fields.
import cloudflare from "@astrojs/cloudflare";
import react from "@astrojs/react";
import sitemap from "@astrojs/sitemap";
import { defineConfig } from "astro/config";
export default defineConfig({
site: "https://example.com",
output: "server",
build: { assets: "_astro" },
cacheDir: "./node_modules/.astro",
vite: { cacheDir: "./node_modules/.vite" },
adapter: cloudflare(),
integrations: [react(), sitemap()],
});site must specifically be a string literal. Ploy forces output: "server" for the Workers build even if your local config says otherwise.
Cloudflare Workers runtime
- Every route that should ship as a static page needs
export const prerender = true. - Static assets are served with
html_handling: "drop-trailing-slash". If yourastro.config.mjssetstrailingSlash: "always"as a plain string, your published site useshtml_handling: "force-trailing-slash"instead. A computed value counts as unset. In the preview, routes open with or without the trailing slash. - Server responses that set no
Cache-Control,CDN-Cache-Control,Cloudflare-CDN-Cache-Control, orExpiresheader are sent withCache-Control: private, so they are not cached. To control caching, set your own cache header. Responses that set one keep it. _redirectsentries whose destination ends in/indexor/index.htmlare rejected. Redirect to the public route instead.node:fsis shimmed in the Workers build, including during prerender. Useimport.meta.globfor build-time file discovery instead of filesystem reads.
---
export const prerender = true;
---Recommended migration path
The safest path is to preserve Ploy's starter shell and move the portable parts of your existing site into it:
- Provision a blank Astro site in Ploy and clone its code with
ploy site git cloneor through Code Sync. - Move the existing repository's
src/andpublic/directories into the starter. - Replace framework-specific routes with thin
.astroshells. Addexport const prerender = trueto every route that should be static. - Run
bun install, then test locally withbunx wrangler dev --local. - Push to the
ployspaceremote and runploy site git sync, or push to GitHubmainand run Code Sync. Then publish.
# 1. Start from a Ploy-provisioned Astro starter checkout
# 2. Move your existing source and public files into it
cp -R /path/to/existing-repo/src ./src
cp -R /path/to/existing-repo/public ./public
# 3. Add thin .astro route shells and opt static routes into prerendering
# export const prerender = true;
# 4. Install and test with the same runtime shape Ploy uses
bun install
bunx wrangler dev --local
# 5. Connect the repository, sync main, and publish
ploy site code-sync init
ploy site code-sync sync
ploy site publish --waitA public
tenant-starter-astrotemplate andbunx create-ploy-sitecommand are not available yet. Today you get the exact starter by provisioning a Ploy site and cloning it withploy site git cloneor through Code Sync. Both need a paid plan.
Import with the CLI
You can seed a new site directly from a compatible local checkout:
ploy site init --from /path/to/repo --waitThis validates the repository contract above and creates a new Ploy site every time. It is an initial import, not an incremental push. To send later changes to the same site, use ploy site git, which is available on paid plans only and does not need GitHub.
Send later changes with ploy site git
You need a paid plan, the CLI installed, to be signed in, and the Member role or higher in the workspace.
- Create or select the site, for example with
ploy site init --wait. - Run
ploy site git clone <directory>. The clone has a Git remote namedployspace. - Pull the latest code with
git pull --rebase ployspace main. - Commit your changes and push them with
git push ployspace HEAD:main. - Run
ploy site git syncto bring the pushed commits into the site in Ploy.
ploy site init --wait
ploy site git clone my-site
cd my-site
git pull --rebase ployspace main
git push ployspace HEAD:main
ploy site git syncPloy's Agent can edit the same code, so always pull before you push. Syncing does not publish. Publish as a separate step. See the CLI reference for both commands.
Before publishing
- Confirm
package.jsonis namedploy-weband all required packages use the supported majors. - Confirm
astro.config.mjsis committed and uses a plain object literal with patchable literal fields. - Confirm every static route exports
prerender = true. - Replace build-time
node:fslookups withimport.meta.glob. - Test the Workers runtime locally before Code Sync and publish.
Keep going
- Check the complete CLI contract for global options, environment variables, saved state, and exit codes.
- Bring an existing Astro repository into Ploy using the supported toolchain and publish contract.
- Manage workspaces and sites, including domains, routing, publishing, and Code Sync.
- Move a site between workspaces with dependency checks, destination validation, traffic cutover, and separate cleanup.
- Equip local agents with Ploy skills and inspect a site's design-system context.
- Run the CLI from remote agents and CI with API tokens and non-interactive commands.
