How the Apps API runs an app
How an app turns a commit or an image into healthy replicas in your regions, switches visitors to them and keeps them running.
An app is a web service we run from its source. Each version you run is a release: one image or one build, started as replicas in the app's regions. A new release goes live only once its replicas pass their health check, and the live release keeps serving until then.
This page uses the Apps API's words for an app, a replica and a region. In the dashboard and the rest of App Deployment, an app is a web service, a replica is an instance and a region is a location. They are the same things, so everything here holds for an app you deploy from the dashboard too (Container Apps and Apps in App Deployment). The API names a release deployment, in its objects, its paths and its field names.
Sources
Section titled SourcesAn app has one of two sources, chosen when you create it:
- Git
- A repository on a public host, reached over HTTPS, and a branch (
mainunless you choose another). Each release builds one commit. For a private repository, save an access token with the app. An app in a monorepo can name its directory inside the repository. - Image
- A container image reference, such as
ghcr.io/example/web:1.4.0. Each release runs that image as it is, with no build. The image must be one that anyone can pull, because an app holds no credentials to pull it with. To run a Dockerfile, build the image in your CI and use this source.
The source type cannot change after you create the app. To move an app from an image to git, or back, create a new app.
Builds
Section titled BuildsA release of a git app starts with a build. The build fetches the one commit, without its history, from the app's directory. It does not fetch submodules, and files kept in Git LFS arrive as pointer files.
The build runs in a build container of its own on the platform, made for that build and removed when it ends. It works out the framework from the files in the app's directory, such as package.json and its lockfile, requirements.txt or go.mod, and installs, builds and starts it with that framework's commands (Frameworks we build). The framework, install, build and start commands you set win over what it works out.
The app's directory, with what the build wrote into it, becomes the release's build output, and each replica starts from it on the runtime the build used, such as Node 22, running the start command. A redeploy and a rollback start from the same build output without building again.
Dockerfiles are not built. An app that names a Dockerfile path is refused with 422 and dockerfile_unsupported; build the image in your CI and create an image app instead (Dockerfile? Deploy a container image).
Environment variables marked for the build reach it as environment variables. Set environment variables covers the rest.
The build log hides the repository's access token and every secret build value of six characters or more, which appear as [redacted]. A build that runs for more than an hour is stopped and fails.
Releases
Section titled ReleasesA release starts when you create an app (unless you ask it not to), when you deploy, rebuild, redeploy or roll back, and when the push webhook receives a push to the app's branch. It then moves through these statuses. The Releases tab shows ready as Ready, and as Live while the release serves the app. It shows canceled as Cancelled and superseded as Replaced:
| Status | Meaning |
|---|---|
queued |
Waiting for a build to start. Only a git release is queued. |
building |
The build is running. |
deploying |
Replicas are starting in each region. A release of an image, a redeploy and a rollback start here. |
ready |
Every region has its healthy replicas, and the release serves the app: it is the live release. |
failed |
The build failed, or a region could not bring up its replicas. The release's page, and its error field, say why. |
canceled |
You cancelled it, or it stopped because a newer release went live or the app was suspended or deleted. |
superseded |
It was ready and served the app until a newer release went live. |
A release that fails or is cancelled leaves the live release serving. When a release goes live, any older release that is still on its way is cancelled. Deploy an app shows each way to start one.
Replicas and regions
Section titled Replicas and regionsEach replica runs with the memory, CPU and disk of the app's size:
| Size | Memory | CPU | Disk |
|---|---|---|---|
small |
512 MB | 1 vCPU | 2 GB |
medium |
1 GB | 2 vCPU | 4 GB |
large |
2 GB | 4 vCPU | 8 GB |
An app runs in every region that can run apps, written ["*"], or in up to 20 regions you name by their codes, such as fra. With ["*"], the regions are settled each time a release starts, so a new region joins at the next release. Each region keeps at least min_replicas replicas, from 1 to 5, and can scale with traffic up to max_replicas, at most 10. An app on older terms needs a plan that includes autoscaling (Scale with traffic).
Change where the app runs, and how many replicas each region keeps, on its Instances tab or with the placements request (Choose locations and scaling). Each region can then keep its own numbers, and the app's regions, min_replicas and max_replicas give a summary of them for scripts written for Apps.
Health checks
Section titled Health checksA replica starts once its image, or its build output, is in place. We then send it GET requests for the app's health check path (/ unless you set another) on the port in its PORT variable. An answer counts when it arrives within 5 seconds with a status from 200 to 399. We do not follow redirects, so a 301 or 302 counts as a pass. Two passes in a row make the replica healthy.
A replica fails when its image or build output is not in place within 20 minutes of being planned, or when it is not healthy within 10 minutes of starting. We replace a failed replica. A region fails when it has had two more failed replicas than min_replicas (three for one replica), or when it has no room for a replica for a minute.
Going live
Section titled Going liveA release goes live once every region has min_replicas healthy replicas. Every domain of the app then switches to the new replicas at once. The previous release's replicas stop taking requests, stop about a minute later, and are removed 30 minutes after they stopped taking requests. A rollback within that time starts them again instead of placing new ones.
An app on ["*"] goes live without a region that failed, as long as another region is ready. The release's error then names the region left out, as in Left out sin: No app node in sin has room for another replica of this deployment. An app with named regions goes live only when every one of them comes up, and otherwise fails with Replicas could not start in … and the reason for each region.
While an app runs
Section titled While an app runsAbout once a minute, we check every serving replica's health check path. A replica that fails a check is marked unhealthy and keeps serving. After three failed checks in a row, it stops taking requests and we start a replacement in its region. When every replica of the app is failing, they all keep serving instead of leaving the app with none. We stop replacing replicas in a region once min_replicas plus two of them have failed within an hour, and start again when fewer have.
Routes to an app carry WebSocket connections as well as ordinary requests.
Rebuilds, redeploys and rollbacks
Section titled Rebuilds, redeploys and rollbacksA redeploy runs the live release's image or build again, with the app's settings and environment variables as they are now. Nothing is built or pulled.
A rebuild builds the app's branch again, or for an image app pulls its saved image reference again, and replaces the live release once the new release is healthy. The dashboard calls them Redeploy… and Rebuild…, with Pull again… for an image app. The API serves both with one request, POST /redeploy, which takes {"rebuild": true} or {"rebuild": false} (Rebuild and Redeploy).
A rollback is a new release of an earlier release's image or build. Only a release that went live can be rolled back to. It runs with the environment variables as they are now, in the app's regions as they are now, and build-time values stay as they were when it was built. Roll back to an earlier release has the details.
What your app must do
Section titled What your app must do- Listen on the port in the
PORTenvironment variable, on every interface (0.0.0.0). An app that listens only on127.0.0.1fails its health checks. We choose the port for each replica, and the app'sportsetting does not change it. - Answer
GETon the health check path with a status from 200 to 399 within 5 seconds, once it is ready to take requests. - Keep nothing on its disk that must outlive a release. Use a database or Object Storage.
- Exit when it receives
SIGINT, which is how we stop a replica. A replica that does not exit is stopped by force.
Every replica also gets these variables. The platform sets them, so you cannot:
PORT- The port to listen on.
CORITAN_APP- The app's name on the platform, such as
web-shop. CORITAN_REGION- The code of the replica's region, such as
fra. CORITAN_DEPLOYMENT- The ID of the release the replica runs.
You can set three other names that start with CORITAN_, which the platform never sets and your own app reads: CORITAN_ORG_API_KEY, CORITAN_WEBHOOK_SECRET and CORITAN_MICROSOFT_ADS_FEED_URL. They reach replicas only (Names your app reads).