PloaDocs

Web: site and web app

The web is one more platform of your app, beside iOS and Android. Ploa hosts it at a free address on ploa.app, and every version goes to testers, through Ploa's review and live, the way a build goes through the stores. A web app can have its own server side: an API, scheduled jobs and a database.

What to ask your agent

Add a showcase site for this app with Ploa, and send the first version to testers. Build the web app and let dana@example.com test it. Release it tonight at 22:00 once I say it's good. Connect www.example.com to the site and tell me the DNS records to add. Make a server for this web app in Ploa's cloud, deploy the API, and run the database migrations on every release.

The tools behind these: Every MCP tool › The web and The app's server.

Who does what

What it does
Your agentAsks you which address and which kind of web, adds it, builds each version with the project's own framework and sends it, sets who tests it, connects your domain, and makes and deploys the server side.
PloaNumbers each version, checks it on upload and again on release, puts it at its own testing address, hosts the site with its certificate, keeps the last 10 versions, and runs the server in Google Cloud.
YouChoose the address and what the web is, approve each release, add the DNS records for a domain you own, give the server's secrets, and choose where the server runs.

What the web of an app is

You pick one of three, and can change it later:

KindWhat it is
Showcase sitePages about the app: what it does, screenshots, links to its store apps.
Web appThe app itself in the browser, built from the same project.
BothThe showcase pages at the address, and the web app under /app.
  • Installable (PWA) is a switch. On, people can add it to their home screen, and Ploa checks its manifest, icons and service worker in every version.
  • An app in both stores doesn't need the PWA on phones: people should get the store apps. Leave it off, or turn it on for computers and point phones at the stores (Ploa's check says what's missing).
  • An app can be on the web alone, with no store apps at all.

The address, and a domain you own

Every site gets a free address, <name>.ploa.app, such as myapp.ploa.app. You pick the name; your agent asks you rather than choosing it.

Ploa sells no domains. You can connect a domain you already own, on plans that include one (see Room by plan):

  1. Add the domain on the app's Web page, with ploa web domain example.com, or through your agent.
  2. Ploa gives you the DNS records to add where the domain's DNS is managed: a TXT record that proves it's yours, and the record that points it at Ploa (A records for a bare domain, a CNAME for a subdomain such as www). For a bare domain, remove the other A and AAAA records on @.
  3. If the domain's DNS is at Cloudflare, the domain's card has Set it up in Cloudflare: you sign in to Cloudflare, pick the account the domain is in, see every record Ploa will add, change or remove, and Ploa writes them for you, DNS only, then checks the domain. Ploa uses that sign-in only for this and gives it back to Cloudflare right after. Or add the records yourself, with Proxy status set to DNS only. Otherwise Ploa doesn't change your DNS, and your agent doesn't either unless you ask it to.
  4. Ploa checks the records by itself every few minutes, and says what's still missing. DNS changes usually show within minutes, sometimes hours. Once they're right, the domain serves the site with its own certificate, made automatically.

A domain is connected to one workspace at a time.

Any web framework

ploa web build builds the project with its own framework's command, then sends the folder that build made. It knows:

Expo (web), Next.js with output: "export", Vite, Astro, SvelteKit with adapter-static, Nuxt (nuxi generate), React Router in SPA mode, Angular, Gatsby, Create React App, Vue CLI, Eleventy, Docusaurus, VitePress, Hugo, Jekyll, MkDocs, Flutter (flutter build web), any project with a build script, and plain HTML that needs no build.

  • Missing packages are installed first, with the package manager the project's lockfile names.
  • --no-build sends what's built already.
  • A Flutter app needs its web/ folder: add it once with flutter create --platforms=web .

Hosting serves static files. A project set up to run a server (Next.js without output: "export", Astro's server output, a SvelteKit server adapter, React Router or Remix with SSR) is refused before it builds, with how to make it static. An API, jobs and a database live in the app's server: see The web app's server side.

Versions

Every upload is a version. Its number is the version in the project (app.json, package.json, or pubspec.yaml for Flutter), so it lines up with iOS and Android, plus a build number Ploa counts: 1.4.1 (13). --version sets another version.

An upload runs like a build: the app's Web page shows its steps as they happen (upload, number, Ploa's review, up at its testing address), with Stop… while it's going up. A stopped version never reaches testers. Files like .env, .git and node_modules are never sent. Ploa keeps the files of the last 10 versions.

From built to live

StepWhat happens
BuiltThe version is uploaded and checked.
TestersIt's up at its own testing address, <site>--<build>.ploa.app.
Ploa's reviewPloa checks it again when it's released.
LiveVisitors get it at the site's address and your domains.

Testers

Each version's testing address is its own, and search engines never index it. You choose who can open it:

  • Testers: people you add by email sign in with a code sent to them, once per device for 30 days. The people of your workspace open it too.
  • Password: anyone with the address and the password.
  • Anyone with the link.

Write what to test, and testers see it when they sign in.

Ploa's review

Ploa checks every version on upload, and again on release. Anything unsafe stops it; the rest are notes. It checks that the site opens and every file it asks for is there, that it holds no keys, .env or private files, that no form sends a password to another site, the site's rules, that it fits in the plan's room, that it reads well on a phone, a title and a share image, installability when the PWA switch is on, and links to the store apps.

Release and going back

  • You release it yourself, as with App Review and production, and it takes the App Review and production permission. On the Web page, Release… asks what and when, then Yes, release. From the CLI or an agent, it asks you, and you approve it in Approvals on ploa.dev. An autonomous agent's release goes at once.
  • Now or at a time within the next 30 days: ploa web release 13 --at 2026-11-06T09:00.
  • Going back is at once, with no review, to any of the last 10 versions that was live: Go back to this on an earlier version, or ploa web rollback.

Site rules (ploa.json)

A version can carry its own rules in ploa.json, in the built folder or beside the project's package.json. The CLI and your agent add it to the upload, and it stays out of your build, so a deploy elsewhere never serves it.

{
  "rewrites":  [{ "from": "/he/*", "to": "/he.html" }],
  "redirects": [{ "from": "/old", "to": "/new", "status": 301 }],
  "headers":   [{ "path": "/*", "set": { "Content-Security-Policy": "default-src 'self'" } }],
  "proxy":     [{ "from": "/api/*", "to": "https://api.example.com", "strip": true }]
}
  • Paths are exact (/he) or a prefix ending in /*. The first proxy, then redirect, then rewrite that fits a request decides it; every headers rule that fits adds its headers. Up to 50 of each. A redirect from a prefix to an address ending in /* keeps the rest of the path.
  • Headers a site may set: Content-Security-Policy (and its Report-Only), Cache-Control, Permissions-Policy, Referrer-Policy, the three Cross-Origin policies, X-Frame-Options, X-Robots-Tag, Access-Control-Allow-Origin, Link, Content-Type and Strict-Transport-Security (a year or more). Never Set-Cookie.
  • A proxy sends a path to the site's own API over https, by name only, to a domain your workspace connected and proved. Cookies, the body and the answer pass through as they are, and the API hears the visitor's own address. WebSockets don't pass yet.
  • /.well-known/apple-app-site-association and assetlinks.json, when the site has them, are served as JSON, as Apple and Google read them.

The web app's server side

A web app can have its own server: an API, scheduled jobs and a Postgres database, run on Google Cloud and driven through Ploa. It's part of the app's web, not something you buy: there are no server packages to pick.

In Ploa's cloudIn your own Google Cloud
PlansPro and Team, one for each app, with the room the plan givesEvery plan, Free included
Who pays GooglePloa; it's in the planYou, directly
WhereGoogle Cloud in Tel AvivYour project, in the region you pick
DatabaseYour own database and role on a database server Ploa shares between serversYour own Cloud SQL instance (Postgres 16, the smallest size to start, backups on), which you can grow in Google Cloud
Setting it upNothing: it's made at oncePloa prints a few commands you run once in Cloud Shell to let it in, then it carries on

The room each app's server has in Ploa's cloud:

ProTeam
The API0.25 vCPU and 512 MB, sleeps when idle (the first request after a quiet spell waits for it to start)0.25 vCPU and 512 MB, always ready
Postgres512 MB, 5 connections1 GB, 10 connections
Traffic out a month2 GB5 GB
Scheduled jobs13
  • Any language. With a Dockerfile in the server's folder, Ploa builds that. Otherwise it builds Node, Python, Go, Java, Ruby, PHP and .NET as they are (a start script, a Procfile…). The server listens on the PORT variable.
  • Deploying sends the server's folder from your project, or has Ploa take it from the app's linked GitHub repository (a commit, or the branch's head). In a git checkout only the files git keeps are sent; elsewhere the folder without dependencies, .env files and keys. Then the release command runs, if you set one (database migrations, say: npx prisma migrate deploy), and the new version goes live. If it fails, the version doesn't. The one before stays for going back.
  • Variables: plain ones show; secrets (keys, passwords) are kept in Ploa's vault and Google's Secret Manager and never shown again. DATABASE_URL is set by Ploa. Setting or removing one restarts the live version. Your agent never sets a secret you didn't give it.
  • Scheduled jobs run a command with the live version, on cron's five fields in UTC, at most every 5 minutes.
  • Logs show what the server and its jobs printed, requests and crashes, newest first.
  • A command once (a seed script, a one-off fix) runs with the live version, its variables and database. There's no SSH: a server is containers that come and go, and a change made by hand inside one is gone with it.
  • The SQL console runs as the server's own role, so it reaches that database only. It's read-only unless you ask to write.
  • Stopping a server closes it to everyone and keeps its database. Deleting is an owner's, in the browser, typing the app's name: in Ploa's cloud its database goes too; in your own project, the database instance goes only if you tick it.

An agent creates, deploys, and sets variables and jobs with the draft level. Running a command, writing to the database, stopping and going back need the production level. See Access and approvals.

On the dashboard and from the terminal

On the dashboard, each app's App settings › Web page has what the web is, the PWA switch, a zip to upload, versions with their steps, testers, release, going back and domains, and under it the web app's server side: where it stands, its versions, variables, jobs, logs and the SQL console. Settings › Hosting shows how much room the plan has, what your sites use, every site and every domain you connected.

ploa web build                       # build, send a new version to testers
ploa web status                      # sites, addresses, versions
ploa web release                     # the newest tested version, live
ploa web rollback                    # back to the version live before
ploa web domain example.com          # connect a domain you own

ploa server                          # the app's server: where it stands
ploa server create --ploa            # in Ploa's cloud (Pro, Team); or --own <project>
ploa server deploy                   # the server's folder; --github from the repository
ploa server env KEY=value --secret   # a secret variable
ploa server logs
ploa server jobs --name cleanup --schedule "0 3 * * *" --command "node cleanup.js"
ploa server run node seed.js         # a command once
ploa server sql "select count(*) from users"

Your agent stops a version that's still going up with ploa_release_stop. Every tool for the web and the server: Every MCP tool › The web.

Room by plan

Every plan comes with its own hosting; plans differ in how much.

FreeStarterProTeam
Websites1, the app's1One per appOne per app
Storage100 MB5 GB10 GB50 GB
Traffic a month1 GB50 GB100 GB500 GB
Domains you own, connected013Unlimited
The web app's server sideIn your own Google CloudIn your own Google CloudOne per app in Ploa's cloud, or your ownOne per app in Ploa's cloud, with more room, or your own
"Made with Ploa" badgeYesNoNoNo

On Pro and Team, each app past the plan's count adds its share (3 GB and 30 GB on Pro, 2.5 GB and 25 GB on Team). A version that doesn't fit the storage is refused, unless an owner turned on extra usage. Plans and prices: Workspace, team and plans.

Not yet

  • Ploa adding the files that make links open the store app (apple-app-site-association, assetlinks.json) and a smart app banner. You can add those files yourself.
  • A new web version on every GitHub push.
  • Releasing to a share of visitors at a time.
  • Your own domain for the server, a live shell, and Redis or MongoDB. A server's traffic out isn't counted against its room yet.