Search the site
SHIPPING
Run the generated backend as a web server, a job worker or a cron process.
Deploy it with dartvel deploy, or provision your own servers with dartvel infra.
ON THIS PAGE
Serve each page with its data and head tags
Run as web, worker or cron
dartvel build web-server writes build/server: one executable with your backend, the native server, the web app and the admin dashboard inside it.
The server reads each bundled asset when a request needs it. Compressed files are sent in that encoding when the browser accepts it; other requests use a decoded copy. Public files have ETags and byte ranges. Studio files stay private and are never kept in the asset cache.
DARTVEL_CACHE_DIR moves the disposable asset cache. Linux builds can also load deferred backend code from inside the executable on first use. Other hosts compile one code unit; the app uses the same page render path on every host.
$ dartvel build web-server
$ scp build/server you@host:/srv/shop/
$ ssh you@host "cd /srv/shop && ./server"Copy code to clipboard
With no DATABASE_URL, the first run creates /srv/shop/dartvel_data/data.db next to the binary, with your models' tables in it. Back up that one folder.
DARTVEL_DATA_DIR moves dartvel_data somewhere else, such as a mounted volume.
Set DATABASE_URL to use PostgreSQL or MySQL. The binary does not migrate those on start: run dartvel db migrate.
With dartvel.admin.enabled on, the binary serves the admin at /__studio. dartvel.admin.path moves it.
Nobody can open the admin until you grant access. Being signed in is not enough, because every customer who signs up is signed in. Grant access to your own account, by the user id you sign in as, in the database the binary uses:
$ dartvel admin grant <user-id> --database /srv/shop/dartvel_data/data.db
$ dartvel admin list --database /srv/shop/dartvel_data/data.db
$ dartvel admin revoke <user-id> --database /srv/shop/dartvel_data/data.dbCopy code to clipboard
With DATABASE_URL set, leave out --database. Add --tenant when the account signs in on a tenant other than default.
Studio signs people in at /__studio/login, against your app's accounts, and does not need your app's own /login page. Signed in without a grant, it tells them the account may not open Studio; its files and API answer a stranger as a page that does not exist.
If your app already knows who its operators are, register your own rule instead: DV.Auth.authorization.registerAction('Studio.access', (caller, _) => ...). It replaces the grants.
A --profile development build serves the admin to anyone who can reach it, for local work. Never deploy one.
Build it on the kind of machine it runs on
build/server runs on the operating system and CPU it was built on. CI builds and runs it on Linux x64 and arm64, macOS arm64 and x64, and Windows x64 and arm64, where the file is build/server.exe. To deploy to a Linux arm64 server, build on Linux arm64.
macOS signing
The macOS binary has only the ad-hoc signature the Dart compiler gives it. CI runs a copy of it on the Mac that built it. A copy downloaded through a browser has not been tested.
The server resolves the route, loads the page's data and writes the title, description, image, canonical link and JSON-LD before the app starts, so crawlers and link previews see the real page.
A hidden or unpublished record answers 404, and one the visitor may not see answers 401, without its data.
Page data can be awaited, cached for a while, served stale and refreshed, or left to the app. With a shared cache such as Redis, every instance serves what one of them resolved.
Public GET pages with compiled content keep the rendered HTML after the first request. Cookies, authorization, guarded routes and runtime data bypass this cache. Studio saves and publishes and successful OTA updates purge it. Set cache: false on a DVRoute in route config to opt out.
Safe pages send an ETag and require revalidation. Browsers and CDNs can reuse the same document with a 304 response, while a content change reaches the next request. The cache is bounded and local to each server process.
Partial
Spec section: Web Server Rendering
Planned work and implementation limits
Widgets are not rendered to HTML on each request. Crawlers get the text, links and headings the build captured from each page, and for a record's page the head tags and text written from its data.
The generated backend reads its role and port from the environment.
DARTVEL_ROLE
Effect: web, worker or cron. With none, it serves HTTP and runs schedules
DARTVEL_PORT
Effect: The HTTP port. Else the host's PORT, else dartvel.backendPort
DARTVEL_QUEUE
Effect: The queues a worker works, comma separated
DARTVEL_HEALTH_PORT
Effect: /healthz for a worker or cron process
DATABASE_URL
Effect: The shared job queue and schedule leases
A value it cannot use, such as a port that is not a number, stops the process at startup.
dartvel deploy --target web --provider vercel
dartvel deploy --target web --provider firebase-hosting
dartvel deploy --target server --environment staging
dartvel deploy --functions --function-target cloud-runCopy code to clipboard
firebase-hosting
Runs: firebase deploy, from the firebase CLI
vercel
Runs: vercel --prod
netlify
Runs: netlify deploy --prod --dir=build/web
cloudflare
Runs: wrangler pages publish build/web
custom
Runs: Nothing. It builds, and you ship build/ yourself
--target is web, server or all. server builds web-server first.
It builds first, unless you pass --no-build.
Secrets the environment requires must resolve before anything ships.
--functions writes a deployment artifact per backend function into build/deploy: lambda, cloud-run, container, edge, fly, railway or bare-metal.
Uploads to Google Play, the App Store, TestFlight and Firebase App Distribution use dartvel deploy --store. See App stores.
Built
Spec section: Deployment
Implementation notes
dartvel deploy calls each host's own CLI. It holds no cloud credentials itself.
There is no plan or rollback step for dartvel deploy yet.
# pubspec.yaml
dartvel:
infra:
production:
hosts: [app1.example.com, app2.example.com]
ssh: { user: deploy, knownHosts: infra/known_hosts }
proxy: { adapter: caddy }
tls:
domains: [example.com]
acme: { email: ops@example.com }
services:
backend: { instances: 2 }
workers: { queues: [default, mail], instances: 1 }
cron: { enabled: true }
database:
adapter: postgres
backup: { schedule: "0 2 * * *", retain: 30d }Copy code to clipboard
dartvel infra plan production --out plan.json
dartvel infra provision production --plan plan.json
dartvel infra check productionCopy code to clipboard
It connects over ssh and sets up Caddy, a firewall and a systemd unit per backend instance, worker and cron process.
An unknown key under dartvel.infra is refused.
provision asks before it removes anything, unless you pass --confirm-destructive.
Partial
Spec section: Server Provisioning
Planned work and implementation limits
It does not install the backend binary. Services stay stopped until something puts the server on the host.
Only the ssh adapter works, and it has not been run against a real host.
FSL-1.1-MIT licensed. Built with Dartvel.
Dartvel is made by
To the bottom