# How Apps works

> How an app turns a commit or an image into healthy replicas in your regions, switches visitors to them and keeps them running.

Source: https://docs.coritan.com/apps/how-apps-work/

An app is a web service we run from its source. Each version you run is a *deployment*: one image, started as replicas in the app's regions. A new deployment goes live only once its replicas pass their health check, and the one before it keeps serving until then.

## Sources

An app has one of two sources, chosen when you create it:

Git
: A repository on a public host, reached over HTTPS, and a branch (`main` unless you choose another). Each deployment builds one commit into an image. 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 deployment runs that image as it is, with no build. The image must be one that anyone can pull, because an app holds no registry credentials.

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

A git deployment 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 then uses one of two methods:

Dockerfile
: Used when you set a Dockerfile path, or when the app's directory holds a file named `Dockerfile`. A path is looked for in the app's directory first, then at the root of the repository. The Dockerfile defines the whole build, so the install, build and start commands are not used.

Railpack
: Used when there is no Dockerfile. [Railpack](https://railpack.com) reads the files in the app's directory and works out how to install, build and start it. The install, build and start commands you set replace the ones it would choose.

Environment variables marked for the build reach it as follows. With Railpack, the build sees each one as an environment variable. With a Dockerfile, a variable that is not secret is a build argument, which the Dockerfile reads after declaring it with `ARG NAME`, and a secret one is a build secret with the variable's name as its id, as in `RUN --mount=type=secret,id=NAME`. A plain variable whose name starts with `BUILDKIT_` is left out of a Dockerfile build. [Set environment variables](/apps/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.

## Deployments

A deployment starts when you create an app (unless you ask it not to), when you deploy, redeploy or roll back, and when the push webhook receives a push to the app's branch. It then moves through these statuses, which the dashboard shows with a capital, as in **Ready**, and spells `canceled` as **Cancelled**:

| Status | Meaning |
| --- | --- |
| `queued` | Waiting for a build to start. Only a git deployment is queued. |
| `building` | The image is being built. |
| `deploying` | Replicas are starting in each region. An image deployment, a redeploy and a rollback start here. |
| `ready` | Every region has its healthy replicas, and the deployment serves the app. |
| `failed` | The build failed, or a region could not bring up its replicas. The deployment's page, and its `error` field, say why. |
| `canceled` | You cancelled it, or it stopped because a newer deployment went live or the app was suspended or deleted. |
| `superseded` | It was ready and served the app until a newer deployment went live. |

A deployment that fails or is cancelled leaves the current one serving. When a deployment goes live, any older deployment that is still on its way is cancelled. [Deploy an app](/apps/deploy-an-app/) shows each way to start one.

## Replicas and regions

Each 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 deployment starts, so a new region joins at the next deployment. `min_replicas` sets how many replicas run in each region, from 1 to 5. `max_replicas` is saved with the app, but the number of replicas does not grow above `min_replicas`.

## Health checks

A replica starts once its image 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 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

A deployment 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 deployment'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 deployment'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

About 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.

## Redeploys and rollbacks

A *redeploy* runs the current deployment's image again, with the app's settings and environment variables as they are now. A redeploy with a rebuild builds the app's branch again, or for an image app deploys its saved image reference again.

A *rollback* is a new deployment of an earlier deployment's image. Only a deployment 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 that image was built. [Roll back to an earlier deployment](/apps/roll-back-a-deployment/) has the details.

## What your app must do

- Listen on the port in the `PORT` environment variable, on every interface (`0.0.0.0`). An app that listens only on `127.0.0.1` fails its health checks. We choose the port for each replica, and the app's `port` setting does not change it.
- Answer `GET` on 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 deployment. Use a database or [Object Storage](/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, which you cannot set yourself:

`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 deployment the replica runs.

## Related

- [Create an app](/apps/create-an-app/)
- [Deploy an app](/apps/deploy-an-app/)
- [App and deployment fields](/apps/fields/)
- [Apps limits](/apps/limits/)
