Reference
deploy.json
The plan the command inferred, and the one file your AI edits when a guess is wrong.
The first deploy writes deploy.json into the app's folder. It records everything the platform decided, with the evidence, and it is what your AI edits instead of passing flags. It contains no secrets and is safe to commit.
An edit takes effect on the next deploy, even when no other file has changed: the app is built again with the new plan.
JSON
{
"version": 1,
"project": { "id": "prj_...", "slug": "family-books" },
"packageManager": "pnpm",
"framework": "nextjs",
"runtime": { "tier": "a", "base": "node22", "arch": "arm64", "memoryMb": 512, "timeoutSec": 30 },
"build": { "planner": "railpack" },
"database": { "engine": "postgres", "orm": "drizzle", "migrate": "npm run db:migrate" },
"storage": { "enabled": true },
"jobs": [{ "name": "monthly-report", "schedule": "0 2 1 * *", "path": "/api/reports/monthly" }],
"secrets": { "required": ["RESEND_API_KEY"], "provided": ["DATABASE_URL", "PORT"] },
"egress": { "allow": ["api.resend.com"] },
"speed": { "enabled": true, "images": true, "otgr": true, "shared": ["/", "/about", "/blog/*"] },
"languages": { "offer": ["fr", "es"] },
"auth": { "library": "none" },
"signals": [{ "for": "framework", "evidence": "next.config.ts present", "file": "next.config.ts" }]
}What survives a re-deploy
Detection runs again on every deploy and follows the code, but it does not undo a person's decision. The file remembers what detection found last time (detected), so a field changed by hand stays changed; set it back to what detection found and the code decides again. Kept from the existing file:
project: assigned by the platform.kind,framework,storageanddatabase.engine, when changed by hand. A changed engine keeps the wholedatabaseblock as written.- The order of the parts in
services(the first is the project's main address),services[].project.id: the ids the platform gave each part, matched by folder, and whatever is set in a part's own entry. build.rootDirectory, when a person picked one folder of a repository that holds several apps.runtime.memoryMb,runtime.timeoutSec,runtime.start: hand-tuned resources.build.command: an explicit override.egress.allow,jobsandsecrets.buildTime: unions. Additions are deliberate and kept.database.migrate: if the engine is unchanged.speed: the whole block, as written. Only a person changes it, so re-detection never turns anything back on.languages: only a person decides which languages the app is read in.
Fields worth knowing
runtime.tier:afor ordinary request/response apps. Detection also recordsbwhen it sees something that needs a process outliving a request — a SQLite file, WebSockets, an in-process timer — butbis not yet a tier an app can run on: everything runs asatoday. Treatbin a plan as a warning that the app needs rewriting (see Databases), not as a promise the platform will handle it.build.command: the exact command that builds the app, for when the planner's own guess is not enough — a Django app that needspython manage.py collectstatic --noinput, a build that has to be run a particular way. It replaces the build step entirely and runs in the app's folder. Leave it out and the planner decides, which is right for most apps.runtime.start: the command that starts the app's server. Same idea: leave it out unless the app is started in a way nothing could work out from its files. The app must listen on the port the platform gives it (PORT); a command that fixes its own port is refused at detection.egress.allow: the outside hosts detection saw in the code, kept for information. It grants nothing. What an app may call depends on whether someone has kept it: an app nobody has kept yet can call a short list of common services over HTTPS (AI model providers, Stripe and PayPal, Supabase, Firebase and Upstash, Google, the main email providers, Twilio, GitHub, Microsoft and Apple sign-in, Sentry, PostHog, Cloudinary and Mapbox), and once the person keeps the app it can call any service with any protocol, including a database outside the platform. Ports 23, 25, 135, 137, 138, 139, 445 and 3389 are closed for every app; send email through a provider's API or port 587. See acceptable use.speed.enabled: on by default. Every page gets a small script (/_uarpie/speed.js, readable in the page source) that starts loading the next page when a link is hovered, touched or scrolled into view, so the click is instant. It only ever fetches same-site links with GET, never anything that looks like an action, and it sends nothing anywhere of its own. Static files with no cache header get sensible ones. Everything the app sends with a body leaves compressed, whatever it answers; the page's own large uploads of data to the app (8 KB or more of JSON, text or a form without files) are sent compressed where they can be, and reach the app exactly as the page wrote them; data sent to the app compressed (Content-Encoding: gzip,br,zstdordeflate) reaches it unpacked, so any app accepts it; and visitors who come back download a fraction of each page; the page they see is exactly what the app sent, and search engines see an ordinary page. Set it tofalseonly if the app needs to be served exactly as written — a strict content-security policy, or a page that must not be prefetched — and know that it also turns off the sign-in headers and translated pages, which run in the same layer. To leave out just one link, mark it withdata-no-prefetch.speed.otgr: on by default. A visitor who comes back sees the page the moment they open it, while the latest version loads. Only pages that are the same for everyone are kept on their device; personal pages show the site's frame instead. The site's own pictures are kept too, never longer than the site's caching headers allow: a changed picture shows at once on a good connection, and on a slow one from the next visit. If the connection drops, a page the visitor has already seen stays on screen with its pictures, and refreshes itself when the connection is back. Set it tofalsefor an app that must only ever show a freshly loaded page. The person who owns the app can also turn this on or off in their dashboard, under Settings, and their choice wins over this file until they hand the decision back to it — so if it is not doing what this file says, check there first.speed.shared: the pages of your app that look the same for every visitor, so they can open instantly for someone who has been before:["/", "/about", "/blog/*"], or["*"]for every page. A path ending in/*covers everything below it. Only needed when your app makes each page as it is asked for — Django, Flask, FastAPI, Laravel, Spring, Express, a Rust server. Next.js, Astro and static sites need nothing here. Never name a page that shows anything personal: a basket, an account, a dashboard, anything behind a sign-in. A page you name is shown again from that person's own device even if your app starts a session on it, as Laravel does on every page, so name only pages that show the same thing to everyone, signed out or signed in. The copy stays on that one device and is never shown to anybody else. A page answeringno-storeis left alone whatever this list says. A page can say it for itself instead, by answering with the headerX-Uarpie-Public: 1.speed.images: on by default. Every<img>pointing at a JPEG, PNG or WebP on the site gets asrcsetof resized copies (384 to 1920 pixels wide, served as WebP where the browser accepts it), so a phone downloads a phone-sized picture instead of the 4 MB original. The original file is untouched and stays the fallback. Images that already have asrcset, SVGs, GIFs, and anything markeddata-no-optimiseare left alone. Set tofalsefor a site that must serve its originals exactly.languages.offer: other languages the site is read in, written the way a browser writes them:["fr", "es", "pt-BR"], up to 30. Each gets its own address,/fr/about. The pages named inspeed.shared(each by its address, not a*pattern) are translated when the app is deployed; everything else on screen is translated as the owner looks around the site in that language. Nobody else sees a translation until the owner publishes it in the dashboard. Needs uarpie 0.3.0 or later, a paid plan and a kept app. See Your app in other languages.auth.library: if the app implements its own login, this names it and the deploy warns that the platform can replace it.services: present only at the top of a repository that holds several apps. Lists each part's name and folder, after the first deploy its id, and anything set for that part alone (its start or build command, memory, migration command, jobs). See Several apps in one project.signals: why each decision was made. Read these before overriding anything.
This page as Markdown: /docs/deploy-json.mdllms.txt