Deploying
The same buildApp() deploys three ways: a Cloudflare Worker, an AWS Lambda or a container running the native server, selected by your [build] target in plumekit.toml. One command builds and ships it.
plumekit deploy
$plumekit deploy # the [build] default target$plumekit deploy --target aws # a specific target$plumekit deploy --target all # every target in [build] targetsdeploy runs, in order: migrate → seed → build → deploy. Migrations and seeders are controlled by [deploy] in plumekit.toml (migrate = true, seed = false by default), and overridable per run:
$plumekit deploy --skip-migrations # don't migrate$plumekit deploy --seed # also run seeders$plumekit deploy --skip-seed # don't seedWhat each target does:
- cloudflare: migrate the remote D1, build the Worker, deploy. Everything (D1, module upload, assets, durable-object migrations, cron schedules, queue consumers, custom domains) goes over the Cloudflare API — no wrangler or Node. Auth comes from
CLOUDFLARE_API_TOKEN, the token stored byplumekit login, or an activewrangler loginsession (reused while valid).plumekit tokenopens the dashboard's create-token page with the needed permissions pre-selected — Workers Scripts/KV/R2/D1/Queues/Routes edit plus Zone read. - aws: migrate the configured database, build the Lambda bundle,
aws lambda update-function-code. See Deploying to AWS Lambda. - native: migrate, then
docker buildthe container image (push it to your registry / platform yourself).
Cloudflare configuration
Everything Cloudflare-specific lives in plumekit.toml's [targets.cloudflare]: the account, compatibility date/flags, custom domains, crons, [vars] (as [targets.cloudflare.vars]), resource-name overrides (database_name, queue_name, bucket_name) and the pinned resource ids. plumekit build --target cloudflare emits a deployable bundle in dist/cloudflare/ — the app.wasm module, a dependency-free worker.mjs, your Public/ directory as ./public (served by the [assets] block; see Static files) and a generated wrangler.toml, so wrangler dev/wrangler tail and a manual npx wrangler deploy keep working against the bundle. Settings plumekit doesn't model go in a root wrangler.extra.toml, appended to the generated file verbatim. (Projects with a user-owned root wrangler.toml from earlier versions are migrated automatically: its values are absorbed into plumekit.toml and the file is renamed to wrangler.toml.bak.)
The first deploy provisions what the manifest declares: the D1 database, KV namespaces, R2 bucket and queue are looked up by name and created when missing, and fresh ids are pinned back into plumekit.toml. Ids are a pin, not a requirement: in CI the writeback is discarded and resolution by name keeps working, so nothing needs to commit from CI. Existing resources are adopted, never recreated; nothing is ever deleted or renamed. Secrets are the one manual step: plumekit secret set NAME after the first deploy.
$plumekit build --target cloudflare$plumekit deploy # or `cd dist/cloudflare && npx wrangler deploy`Static files (Public/)
Scaffolded apps have a Public/ directory, and your app references each asset by the same URL path on every target; only who serves it changes:
- native: the server serves files under
Public/directly (path-traversal-safe,Content-Typeby extension, aCache-Controlheader); a GET miss falls through to your routes. - cloudflare:
Public/→dist/cloudflare/public, served by the[assets]block inwrangler.toml(above). - aws:
plumekit build --target awscopiesPublic/→dist/aws/public. Upload it to S3 and front it with CloudFront (routing dynamic paths to the Lambda); the generateddist/aws/README.mdhas the exactaws s3 synccommand and CloudFront setup. See Deploying to AWS Lambda.
The regenerated Plume bundle (Public/app.) is gitignored; your own Public/ files are tracked. See Portability for the whole picture, and Storage.serve for runtime* uploads (not static files).
Containers (the native server)
Scaffolded apps include a multi-stage Dockerfile that builds the native Server and runs it on 0.0.0.0:8080. Deploy it anywhere that runs containers (Fly.io, Render, ECS, a VPS, Kubernetes):
$docker build -t bookmarks .$docker run -p 8080:8080 bookmarks$# or: plumekit deploy --target nativeCI
Generate CI that tests on pull requests and deploys on push to main:
$plumekit generate ci --provider github # or gitlab | forgejoThis writes a test workflow (swift test on PRs) and a deploy workflow (./plumekit deploy on push to main, so migrations run on deploy), with the toolchain set up for your default target and ${{ secrets.* }} placeholders to fill in. Because CI calls the committed ./plumekit wrapper, it needs nothing installed but a Swift toolchain.