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.json must be at the repository root and its name must be exactly ploy-web.
  • astro must be installed as a dependency, not only available globally.
  • astro.config.mjs must be at the repository root and committed at HEAD.
  • The config must default-export a plain defineConfig({...}) object literal.
Minimum package contract
{
  "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

PackageSupported major
astro6.x
@astrojs/cloudflare13.x
@astrojs/react5.x
@astrojs/sitemap3.x
@astrojs/mdx5.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.

Patchable Astro config shape
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 your astro.config.mjs sets trailingSlash: "always" as a plain string, your published site uses html_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, or Expires header are sent with Cache-Control: private, so they are not cached. To control caching, set your own cache header. Responses that set one keep it.
  • _redirects entries whose destination ends in /index or /index.html are rejected. Redirect to the public route instead.
  • node:fs is shimmed in the Workers build, including during prerender. Use import.meta.glob for build-time file discovery instead of filesystem reads.
Static Astro route
---
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:

  1. Provision a blank Astro site in Ploy and clone its code with ploy site git clone or through Code Sync.
  2. Move the existing repository's src/ and public/ directories into the starter.
  3. Replace framework-specific routes with thin .astro shells. Add export const prerender = true to every route that should be static.
  4. Run bun install, then test locally with bunx wrangler dev --local.
  5. Push to the ployspace remote and run ploy site git sync, or push to GitHub main and run Code Sync. Then publish.
Recommended repository migration
# 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 --wait

A public tenant-starter-astro template and bunx create-ploy-site command are not available yet. Today you get the exact starter by provisioning a Ploy site and cloning it with ploy site git clone or through Code Sync. Both need a paid plan.

Import with the CLI

You can seed a new site directly from a compatible local checkout:

Seed a new site from a local repository
ploy site init --from /path/to/repo --wait

This 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.

  1. Create or select the site, for example with ploy site init --wait.
  2. Run ploy site git clone <directory>. The clone has a Git remote named ployspace.
  3. Pull the latest code with git pull --rebase ployspace main.
  4. Commit your changes and push them with git push ployspace HEAD:main.
  5. Run ploy site git sync to bring the pushed commits into the site in Ploy.
Push changes to an existing site
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 sync

Ploy'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.json is named ploy-web and all required packages use the supported majors.
  • Confirm astro.config.mjs is committed and uses a plain object literal with patchable literal fields.
  • Confirm every static route exports prerender = true.
  • Replace build-time node:fs lookups with import.meta.glob.
  • Test the Workers runtime locally before Code Sync and publish.

Keep going