# What every app needs to deploy

URL: https://getflowing.ai/docs/build/app-requirements

The port, the health check and its path, the start time, memory, build time and settings every app on GetFlowing is held to, however it is built.

GetFlowing runs every app as a container. It builds the container from a Dockerfile: [your own](https://getflowing.ai/docs/build/dockerfile), or [one GetFlowing writes](https://getflowing.ai/docs/build/without-a-dockerfile) for the stack it finds. However it is built, the app has to do the same few things to deploy. [The app contract](https://getflowing.ai/docs/reference/app-contract) has every number in one table.

## The checklist

| What | What your app must do |
| --- | --- |
| Listen | On all interfaces (`0.0.0.0`, not `localhost`), on the port in the `PORT` variable, which is 8080 |
| Answer its health check | Answer `GET /`, or the path set in its **Settings**, with a status from 200 to 399 within 5 seconds |
| Start | Be ready within 60 seconds of starting |
| Fit in memory | Run within 512 MB |
| Build | Build its image within 20 minutes |
| Configuration | Read settings and secrets from environment variables, never from a file in the repository |

## Listen on PORT

GetFlowing sets `PORT` to 8080 and sends your app's traffic and its health checks there. Read the port from `PORT`, and listen on `0.0.0.0` so requests from outside the container reach it. An app that listens on `localhost` or `127.0.0.1` cannot be reached, and its deploy fails.

```js
// Node, with Express
app.listen(Number(process.env.PORT) || 8080, '0.0.0.0');
```

```bash
# Python, with gunicorn
gunicorn app:app --bind 0.0.0.0:$PORT
```

Do not set `PORT` to another number, in your Dockerfile or in your app's variables. Health checks always go to 8080, so an app listening anywhere else never becomes healthy.

## The health check

While your app runs, GetFlowing asks it for `GET /` every 10 seconds, or for the path set in the app's **Settings** (below).

- Any status from 200 to 399 is healthy, so a redirect counts. A `404` never does.
- The answer has to arrive within 5 seconds.
- A new container has 60 seconds to start before failed checks count against it.
- It takes 2 healthy answers in a row to go live, and 3 failed ones in a row to be taken out of service.

A new version only takes traffic once it passes. If it never does, the deploy fails and the version that was running keeps serving your visitors.

### Set the health check path

An app with nothing at `/`, such as an API, names a path it answers on instead.

1. Open the app's **Settings**, under **General**, and find **Health check**.
2. Type the path in **Health check path**, such as `/health`, and choose **Save**. Leave it empty for `/`.
3. Deploy the app. The new path is asked from the app's next deploy.

The path starts with a single `/`, never `//`, and holds letters, digits and `. _ ~ / -` only. The statuses that count as healthy stay the same, so the path must answer with a status from 200 to 399.

### Two things that trip apps up

- **An API with nothing at `/`.** If `GET /` answers `404`, the health check fails. Set a path it answers on, as above, or add a route at `/` that answers `200`.
- **A framework that checks the host name.** The health check asks your app by its own address, not by your domain. If your framework only answers host names it knows, as Django does with `ALLOWED_HOSTS`, make sure it still answers the health check. For a Django app GetFlowing builds without a Dockerfile, see [Django](https://getflowing.ai/docs/build/without-a-dockerfile#django).

## Memory and start time

Your app runs with 512 MB of memory. An app that uses more is stopped and started again. Keep start-up work small too: a new container that is not answering within 60 seconds is replaced.

## Configuration and secrets

Read every setting and secret from an environment variable. Set the values in GetFlowing, in the app's Settings, under Variables. See [Variables and secrets](https://getflowing.ai/docs/configure/variables).

- Commit a `.env.example` with each variable's name and no value, so GetFlowing finds every variable your app reads.
- Never commit `.env` or any file that holds a secret.
- Never copy `.env` into the image in your Dockerfile.

Some frameworks cannot start without a secret only you can give, such as Laravel's `APP_KEY`. When GetFlowing builds an app without a Dockerfile, it asks for these before the deploy. See [What an app cannot start without](https://getflowing.ai/docs/build/without-a-dockerfile#what-an-app-cannot-start-without).

## Run as a user other than root

Every Dockerfile GetFlowing writes runs the app as a user other than root. In a Dockerfile of your own, do the same: add a `USER` line after the install steps. See [What to check](https://getflowing.ai/docs/build/dockerfile#what-to-check).

## What your app can reach

The app can reach anything over HTTPS and HTTP, and its own database, cache and mail servers on their usual ports. Port 25 is closed, so send mail through a mail provider.

## Make it safe before you deploy

GetFlowing scans every app's code, secrets, packages, settings and image. By default what it finds is advice: the app still deploys, and the findings are on the app's **Health** tab for you to fix. An owner or admin can make findings block a deploy instead. See [Security scans](https://getflowing.ai/docs/operate/security-scans).

Before you deploy, it is worth doing the same checks yourself:

- Audit your dependencies with your stack's audit tool, and upgrade any package with a high or critical vulnerability.
- Commit your lockfile, so the versions you audited are the versions that are built.
- Keep secrets out of the code, the repository and the image.
- Build from a maintained base image with a pinned version, and run your app as a user other than root.
- Leave debug modes, stack traces and admin consoles off.

GetFlowing does not build, host or run software meant to harm: malware, phishing or credential harvesting, tools that attack systems their user is not allowed to test, cryptocurrency mining, spam, or an app that hides what it does from the people using it.
