Deploy natively
You end with the app built into one program, running against a real database.
On this page
jadpo build turns your app into one program, with nothing else to
install beside it, that you run on any Linux or macOS server with a
PostgreSQL database. This guide builds the book catalogue from
Build an API, applies its migrations to a real
database and runs it.
You need PostgreSQL that the server can reach, and jadpo on the
machine you build on. You start from the catalogue's code:
Name the database
jadpo dev and jadpo test start databases of their own. A deployed
app is told where its database is, through a connection called main,
the store every entity is kept in unless it says otherwise:
app.jadpo, changed. The main connection is new.
application { connections: { main: { driver: Driver.postgres url: config.database_url } }}
config.jadpo. The database_url setting is new.
config { /// The PostgreSQL database the app keeps its data in. database_url: PostgresUrl}
PostgresUrl is a database address, such as
postgres:/. It holds a password, so it
is always a secret, and it has no default, so the program won't start
without it. On your machine, jadpo dev and jadpo test still start
their own database while DATABASE_URL isn't set.
Build the program
jadpo buildbinary: build/target/app/app build: b_bd8f8b62b3ed commit: none settings: DATABASE_URLalert: Built from files git doesn't track: no commit explains its eventsbuilt: app, native in 101.0 snext: jadpo migrate apply it starts only once its database has every migration
The program is build/. The first build fetches the Rust
compiler it needs, so it takes a few minutes. build: is an id for this
exact build, which every line the program logs carries. The alert says
the project isn't in git, so the build can't name its commit; commit
your project and it will. settings: lists the variables the program
reads.
Put the settings in a file
The program reads its settings from its environment, never from
.env.local. Don't type them on the command line, where your shell's
history keeps them: put them in a file outside your project that only
you can read, with your editor.
~/:
DATABASE_URL=postgres://postgres@localhost:5433/bookschmod 600 ~/books-app.envUse your own database's address. On a real server, set the same variables wherever your host keeps secrets.
Apply the migrations
Before the program first runs, and before each new build that brings new migrations, apply them. This loads the file into the environment of one command:
(set -a; . ~/books-app.env; jadpo migrate apply)Applied 1 migration to `main` 20261020T1400_initial applied
The program refuses to start against a database that is missing one of its migrations.
Run it
(set -a; . ~/books-app.env; build/target/app/app){"address":"0.0.0.0:8080","build":"b_bd8f8b62b3ed","commit":null,"compiler":"0.1.0","deployable":"app","event":"runtime.started","modified":true,"target":"native","trigger":"native","unbuilt":[],"workers":10}It writes one line of JSON for each thing worth logging, and listens on
port 8080; the variables PORT and HOST change that. A setting it
needs and doesn't have stops it at once, naming the setting but never
printing a value:
{"build":"b_bd8f8b62b3ed","commit":null,"compiler":"0.1.0","deployable":"app","event":"runtime.startup_failed","modified":true,"problems":[{"message":"`DATABASE_URL` isn't set: `database_url` is required, so the app can't start without it.","problem":"setting_missing","setting":"database_url","variable":"DATABASE_URL"}],"target":"native"}Every deployed app answers a health check, for your load balancer or host to poll:
curl -i http://localhost:8080/health/liveHTTP/1.1 200 OKcontent-type: application/jsoncache-control: no-storex-request-id: req_d246e6a6b13b1c94content-length: 17date: Sat, 31 Oct 2026 11:03:38 GMT{"status":"live"}
The routes answer as they do under jadpo dev. Ctrl-c, or the SIGTERM
a service manager sends, stops it cleanly: it finishes the requests it
has, then exits. On a real server, run it under a service manager such
as systemd, which restarts it if it stops, behind a proxy that serves
HTTPS. The program runs the app's schedules and delivers its events
itself; Run background work covers those.
Read more
A native deployable, Declared deployables, Database and other connection URLs, Values on your machine and in production and Health. To run on Cloudflare instead, see Deploy to Cloudflare Workers.
Not built yet
Counting rate limits in Redis isn't built yet, so a server program counts each route's requests in its own memory. Run one copy of it, or expect each copy to count on its own.
Markdown for agents: /guides/guide-deploy-natively.md.Built from Jadpo main at e00e61f. Jadpo's CI compiles this guide's code and runs its tests.