vela
v0.29.1
Published
A CLI for creating and updating SvelteKit projects
Maintainers
Readme
vela
Full-stack SvelteKit, without the setup.
vela scaffolds a SvelteKit app with a real backend already wired up — database, auth, payments, typed data access — and keeps generating the boring parts as you build. Everything it writes is ordinary source in your project. No runtime to learn, no lock-in, nothing hidden behind a flag.
npm create vela my-app
cd my-app
npm run devThat's a running app with a database behind it. No separate PocketBase install, no Docker, no config.
What you get
- A backend on day one. PocketBase, migrated and running locally, with types synced from your schema as it changes.
- Auth, payments (Stripe, Shopify or WhatsApp), a cart, teams, notifications, email, i18n — one command each, generated into your project as code you own.
- CRUD you didn't write.
vela generate scaffold post title:text body:editorgives you the model, schema, forms, and pages.vela generate shopgives a cart its product listing and product pages, in Stripe's, Shopify's or WhatsApp's own terms. - Describe it instead. Pass
--ai "a blog post with tags and a cover image"and review the collection it designs before anything is written. - shadcn-svelte components on tap, and fixtures and seeds for realistic data while you work.
- The app's name in code. Every project keeps its name and public URL in
src/lib/site.ts; canonical links, Open Graph images and feeds are built from it, andvela dev/vela deployhand the name to PocketBase asAPP_NAMEfor the emails it sends. Nothing is written into its settings behind the admin panel's back. - Themed templates.
vela create --template broadsheetpulls a finished blog design from the template registry;--templatelists what is available, grouped by category. - Linked from the start. Logged in to velastack.dev,
vela createlinks the new project there (.vela/project.json), sovela deployreports to it and a CMS-ready template such ashearthreads from the project's free hosted CMS right away.vela loginfirst if you are not;vela linkdoes the same for a project you already have.
vela create my-site --template hearth # linked, CMS endpoint written into src/lib/site.ts
vela create my-site --template hearth --link none # keep it local; fill in `cmsEndpoint` later
vela create my-site --template hearth --cms https://cms.example/v1/projects/<id>/cms
CI=1 VELA_API_KEY=… vela create my-site --template hearth --link new --no-installOff a terminal nothing is created on velastack.dev unless --link new or --link <project-id> says so (--team <id> picks the team for a new one; the personal team is the default).
The shape of a day
vela enable auth # sign-in, sessions, OAuth scaffold
vela enable whatsapp # sign in with a code on WhatsApp, next to email
vela generate scaffold post title:text body:editor # model, forms, list and detail pages
vela migrate up # schema, versioned and in git
vela ui add button card dialog # shadcn-svelte components, into your project
vela ui add data-table multiselect # vela's own components, same command
vela ui list # what's installed, what vela ships, what the registry offers
vela ui style nova # switch shadcn-svelte style, re-adding its components
vela ui base zinc # base palette; `vela ui theme blue` for an accentChanged your mind? vela destroy and vela disable take it back out.
Ship it
Deploys go to a server you own, over SSH. Prepare the box once, then deploy as often as you like.
vela provision root@your-server # Caddy, Node, PocketBase, systemd, TLS
vela deploy --server root@your-server --domain example.comAny SvelteKit project deploys this way, PocketBase or not — npx sv create my-app
straight into vela deploy works. The deploy looks at the project rather than at a
config: a server needs @sveltejs/adapter-node, so a project still on adapter-auto
(what sv create gives you) is switched to it on the first deploy, the package
installed, and you are asked to commit the change. At a terminal the deploy asks before
it edits the config file and package.json; in CI it goes ahead. A project on
adapter-static or a hosting platform's adapter is left alone and told why. The server
installs dependencies with npm, so a pnpm, yarn or bun project should commit a
package-lock.json for a reproducible deploy; the deploy warns when it finds only theirs.
Nothing on the server assumes a database: run vela enable backend whenever you want
one and deploy again, and the same instance gains its PocketBase.
That first deploy binds production to the server, so nothing after it names a
machine again. Every command takes the same selector — -t local, -t production
(or prod), or any name you choose such as -t staging:
vela targets # what exists, and where
vela env set STRIPE_SECRET_KEY -t production
vela admin create -t production # a login for the admin panel
vela seeds load -t production # the records in data/seeds, where missingEach deploy uploads an immutable release, runs migrations, restarts the app and health-checks it. A release that does not come up healthy is rolled back before the command exits, migrations included.
Once the release is live, the deploy loads the project's seeds (data/seeds) into the target's database. Seeds are the records the app needs to function, such as plans and roles, and loading only ever adds: a record whose id is already there is left as it is, so an edit made in the admin panel survives every later deploy and a changed seed file does not overwrite it. Each record needs an id, and vela seeds save writes them, leaving fixtures out. Read data/seeds before committing it, because every copy of the app gets what is in it. An account in a seed file is created with a random password for its owner to reset. A seed file the database refuses does not undo the deploy: the command exits non-zero, and vela seeds load -t production loads the rest once the file is fixed. Pages are prerendered before seeds load, so one that renders a record seeded for the first time shows it from the next deploy.
One deploy runs per target at a time. A second one started from another machine — CI and a laptop, say — waits for the first to finish (up to --lock-wait seconds, 300 by default; 0 gives up at once), and a release that is older than the one already live is dropped rather than put back in front of it. Release ids are stamped from the server's clock so every machine agrees on which is newer.
The build sees exactly what the target will run with: once a target has a database, the build renders against it over the same SSH connection (--no-remote-db to use a throwaway local one instead), and it runs with the target's environment rather than your .env. A key set only in .env is blanked for the build, and a $env/static import of one fails the deploy before anything is uploaded.
vela status # what is running, on which release
vela logs -f # journald, tailed
vela rollback # previous release, with its down migrationsvela destroy deployment -t staging removes a copy from its server. Its database and uploads stay behind unless you pass --purge, which snapshots them into /var/lib/vela/trash on the server (kept two weeks) before deleting. Removing production, or purging anything but a preview, asks you to type the app's name; from a script, pass --confirm <app-name> — --yes alone is not accepted for either.
These default to production; vela admin and vela seeds load default to local,
because that is the copy you are usually standing in. vela env has no default — see below.
Environment
Everything that differs between environments is an environment variable, for the
app and for PocketBase alike. Locally that is .env, with .env.example
documenting what the app reads. On a server a target's environment is three
layers, lowest to highest, and a key in a higher layer wins:
vela env set VELASTACK_API_KEY -t all # every target, on every server the app is deployed to
vela env set WHATSAPP_MODE -t preview # every preview
vela env set STRIPE_SECRET_KEY -t production # one target (or -t staging, -t preview:<branch>)A value is secret by default: written, never read back, shown as ••••. Pass
--public for one that should read back (vela env get, values in vela env
list); PUBLIC_* keys are public without asking, since SvelteKit ships them to
the browser anyway. Setting a key again with or without --public moves it.
Values are always prompted for, never taken as an argument, so none lands in
shell history; a secret is typed without echo, a public value with. Without -t
the command asks which environment; vela env list with no -t shows every
scope at once.
PocketBase reads its own settings from the same environment — APP_NAME (from
src/lib/site.ts), APP_URL (the deploy's origin unless you say otherwise),
PB_SMTP_*, PB_S3_*, PB_BACKUPS_S3_*, and the WhatsApp plugin's
WHATSAPP_* / VELASTACK_* — and shows them locked in the admin panel. Nothing
in the repo carries environment but .env.example, and there is no project
config file.
Same thing from CI with velastack/action.
Already have a project?
vela enable backend # just PocketBase and the server test harness
vela bless # the full upgrade: Tailwind, shadcn-svelte, vela's layout and routes, the backendBoth work in place on a vanilla SvelteKit 3 project. A project that is already deployed keeps deploying to the same instance, now with a database.
On SvelteKit 2? npx vela@^0.15 migrate sveltekit-3 runs SvelteKit's own migration and
then the vela fix-ups it doesn't know about. Or stay on [email protected], which keeps
working with SvelteKit 2: a project created by it pins vela ^0.14, so it isn't moved
to 0.15 by accident.
Much of vela needs neither. In a plain npx sv create project, with no setup:
vela generate schemaandvela generate form, the form in plain HTMLvela generate scaffoldonce there is a backend: a plain HTML table, detail page and formsvela enable i18n,ai,analytics,content-negotiationandcmsvela legalandvela routesvela deploy,env,status,logsandrollback
vela ui, vela enable blog, vela enable auth and what builds on it, vela enable payments
and ecommerce, and vela generate scaffold --remote write shadcn-svelte markup. Without it they refuse before
changing anything and name the setup: npx sv add tailwindcss, then
npx shadcn-svelte@latest init.
It stays your code
Generated files are yours to edit — no framework wrapping your app, no magic you can't read. Each project pins the CLI version it was created with, so builds match across your team and in CI no matter what's installed on any one machine.
Requirements
Node 22.22+ and SvelteKit 3. That's it.
Docs
Full documentation, every command and flag: velastack.dev/docs
License
MIT
