# App Hosting

> An app is a program of yours that we run from a GitHub repository and keep running: a web dashboard, an API, a webhook receiver, a status page, a queue...

Source: https://fadehost.com/docs/app-hosting/

An app is a program of yours that we run from a GitHub repository and keep running: a web dashboard, an API, a webhook receiver, a status page, a queue worker, a Discord bot. You point us at the repository, we install it, build it if it needs building, and start it. **Every push to your branch deploys the new version.**

- **Languages:** Node.js and Python, detected from your repository (the full list is [below](#runtimes)).
- **Free tier:** your first app is free, 256 MB of RAM, always on, no card needed.
- **Paid tiers:** $2/mo or $20/yr and up per app, for more memory and more than one app.
- **Regions:** Europe West (France), Canada East (Montreal) and USA West (Oregon).

Apps live under **Apps** in your control panel; Discord bots have their own **Discord bots** page. A Discord bot is an app with a token, so everything on this page applies to bots too. The bot-specific parts (tokens, intents, the premade bots) are on the [Discord Bot Hosting](/docs/discord-bot-hosting/) page.

## Create an app from a repository

1. Push your code to a **GitHub repository**, public or private.
2. In the panel, go to **Apps → Host an app**, say what you want to host (a web app or API, a static site, or something else), and pick a template or your own repository.
3. Fill in:
   - **Name** a label for the app, only you see it.
   - **Region** pick the one closest to you and your players.
   - **Kind** choose **App**. (Choose **Discord bot** if it logs in to Discord with a token, which adds the token field.)
   - **GitHub repository** for example `https://github.com/you/my-app`. Add a **Branch** if it is not your default one.
   - **Start command** and **Build command**, both optional. Leave them blank and we work them out from your repository.
   - **Language**, which is on auto-detect unless you set it.
   - **Environment variables**, one `KEY=value` per line. Keys and tokens belong here, never in the repository.
4. Click **Deploy**. We clone the code, install the dependencies and start the app.

Your first app is free once your email is verified. For more apps, or more memory, add App Hosting to your subscription in the plan builder.

## Runtimes

We read your repository and pick the runtime. You can override it under **Language**, and set your own **Start command**, at any time.

<!--
THE RUNTIME LIST. This table is the only place in the docs that names what we
can run. When a runtime ships, add a row here and nothing else on this page
needs changing.
-->

| Your repository contains | Runtime | Installed with | Started with |
| --- | --- | --- | --- |
| `package.json` | Node.js | npm, pnpm, yarn or bun, whichever your lockfile or `packageManager` pin names | `npm start`, or your entry file if there is no start script |
| `requirements.txt`, `pyproject.toml` or `Pipfile` | Python | pip, in a virtual environment we keep for you | `python bot.py` or `python main.py` |
| `bun.lock`, `bun.lockb` or a `packageManager` pin naming bun | Bun | `bun install` | your `start` script with bun, or the entry file |
| `deno.json` or `deno.jsonc` | Deno | modules cached under `/data` | `deno task start`, or `deno run -A` on the entry file |
| `go.mod` | Go | `go build` of your main package, modules and builds cached under `/data` | the built binary |
| `pom.xml`, `build.gradle` or `build.gradle.kts`, or a `.jar` at the root | Java 21 | `mvn package` or `gradlew build` (tests skipped) | `java -jar` on the built or committed jar |
| `composer.json` or `index.php` | PHP | `composer install` without dev packages | PHP's built-in server on `PORT`, serving `public/` when it exists, else the root |
| `Gemfile` | Ruby 3.4 | `bundle install` into `/data` | your start command, or `rackup` when there is a `config.ru` |
| `index.html`, or a build script producing `dist`, `build`, `public`, `out` or `_site` | Static site | your build script when there is one | a small static server on `PORT` (set `SPA=1` for single-page apps) |

Set a **Start command** when your entry point is somewhere else, for example `node dist/server.js` or `python -m myapp`.

## Listening on a port

Every app gets a **`PORT`** environment variable, whether or not it has a web address. The default is `8080`.

Your web server has to listen on that port **and on `0.0.0.0`**. A server bound to `127.0.0.1` only answers itself, so nothing outside the container, including your web address, can reach it.

```js
// Node.js (Express)
app.listen(process.env.PORT || 8080, "0.0.0.0")
```

```python
# Python (FastAPI with uvicorn)
uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", 8080)))
```

An app with no web server, a Discord bot or a worker, can ignore `PORT` entirely.

## Environment variables

Tokens, API keys, database URLs and any other configuration live in **environment variables**, not in your repository. They are injected into the app at runtime.

- **On create:** fill in the **Environment variables** box, one `KEY=value` per line.
- **Later:** open the app → **Environment**. The variables are a table, values masked. Add rows, remove rows, then **Save** once and the app restarts with the new values.
- Names must start with a letter or an underscore, for example `DISCORD_TOKEN` or `DATABASE_URL`.

Some variables we set for you: `PORT` always, `APP_URL` while a web address is enabled, and the database credentials when you create a database.

## Build step

Every deploy runs three things in order: the **install**, the **build command**, then the **start command**.

If your app has to be compiled before it runs (TypeScript, esbuild, tsup, a bundled `dist/` folder), you do not have to commit the compiled files. Leave the build command blank and we detect the build:

- When the file your start command runs is **not in the repository** (say `node dist/index.js` with `dist` in `.gitignore`) and your `package.json` has a `build` script, we install your `devDependencies` and run that script before starting the app.
- In a **workspace** (pnpm, npm or Yarn workspaces), we build only the package the app starts and the workspace packages it depends on, so a sibling web app in the same repository never has to build.
- A committed entry file is never built over. An app whose `index.js` is in git starts as before, even if the repository has a `build` script.

To build differently, set a **Build command** on the app's page. It runs on every deploy, exactly as written:

```
pnpm --filter @workspace/api run build
```

A failed build prints its error in the **live console**, and every restart repeats one line naming the file the build never produced. Fix it in your repository, or fix the build command, then press **Redeploy**.

### Dockerfile and prebuilt images

A paid app can be built into an image instead of installed at start. Set **Build mode** on the app's page:

- **Dockerfile**: we build the Dockerfile in your repository on a build box, push the image to our private registry and run that image on the node. Anything a Dockerfile can express works: system packages, a different base image, multi-stage builds, a compiled binary.
- **Railpack**: no Dockerfile needed. The build box reads the repository, picks the runtime and produces the image for you. Same result as the default runtime install, but built once per commit and started in seconds on every restart.

Image builds are for paid tiers, because a prebuilt image skips the source scan the free tier relies on. A build that fails prints its log on the app's page, and the previous image keeps running until a build succeeds.

## Web address

Every app can answer at `https://<name>.fadehost.app` with HTTPS. Use it for a dashboard, OAuth2 redirect URLs, incoming webhooks or a status page. Open the app → **Web address**, pick a name, and it is live within seconds.

On the free tier the address **sleeps when nobody is using it** and wakes on the next request, which takes a few seconds; the switch that keeps it awake is locked to paid tiers. On a paid tier the always-on address costs **$2/mo or $20/yr** per app.

The full section, including what your app has to do and what is not allowed, is under [Web address](/docs/discord-bot-hosting/#web-address-a-public-url-for-your-bot-or-app) and applies to every app, bot or not.

A few rules about the name itself:

- 3 to 32 characters, lowercase letters, numbers and dashes.
- One rename a day, with ten minutes after a change to fix a typo for free.
- Switching the address off takes it down at once and holds the name for a day.

## Storage: what survives a deploy

Your code is checked out at **`/data/app`**, which is also the app's working directory. **Every deploy resets it to match your repository**, so a file your app wrote next to its code can be gone after the next push.

Anything under **`/data`** is a volume that survives deploys and restarts. Write runtime data there, with an absolute path:

```js
const file = "/data/storage/state.json"
```

For anything more than a file or two, use a database. Every paid app includes a one-click **MySQL** database (open the app → **Database** → **Create database**) and the credentials are injected into the environment. PostgreSQL and Redis are available as [managed databases](/docs/databases/) on the same private network.

## Plans and limits

| | Free | Starter | Standard | Pro |
| --- | --- | --- | --- | --- |
| Price / app / month | $0 | $2 | $3 | $6 |
| Price / app / year | free | $20 | $30 | $60 |
| RAM | 256 MB | 1 GB | 2 GB | 4 GB |
| vCPU | 0.25 | 1 | 1 | 3 |
| Disk under `/data` | 3 GB | 5 GB | 10 GB | 20 GB |
| Always on, auto-deploy | Yes | Yes | Yes | Yes |
| Live console, crash doctor, deploy history and rollback | Yes | Yes | Yes | Yes |
| Scheduled commands, 24 h usage chart | Yes | Yes | Yes | Yes |
| 1-click MySQL database | | Yes | Yes | Yes |
| Web address (`https://name.fadehost.app`) | Sleeps when idle | +$2/mo always on | +$2/mo always on | +$2/mo always on |
| Daily backups, kept 7 days | +$1/mo | +$1/mo | +$1/mo | +$1/mo |
| Dockerfile and Railpack image builds | | Yes | Yes | Yes |
| Apps per account | 1 (verified email) | Multiple | Multiple | Multiple |

Every app runs in a hardened container: non-root, a read-only filesystem apart from its own storage, and hard memory and CPU limits. It restarts on its own if it falls over, with no sleep timer and nothing to renew.

## Deploy history and rollback

The app's page lists every deploy with its commit and when it went live. **Roll back** on an older one pins the app to that commit and redeploys it; pushes to the branch keep arriving in the history but do not go live until you **Unpin**. Use it when a push breaks the app and you want the last good build back while you fix the code.

## Scheduled commands

**Schedules** on the app's page runs a shell command inside the app's container on a cron schedule, up to ten per app: a nightly cleanup, a cache warm-up, a report script. Each run keeps its exit code and the last lines of output, and a command is stopped after 60 seconds.

## Backups

**Backups** on the app's page turns on a daily backup of everything under `/data` for **$1 a month per app**. Backups are stored off the node, kept for seven days, and restored from the same list in one click: the app stops, the data comes back, and it starts again if it was running. Take one by hand before a risky change with **Back up now**.

## Usage

The **Usage** chart on the app's page shows CPU and memory over the last 24 hours, sampled every five minutes. An app that sits at its memory limit is the first thing to check when the crash doctor reports an out-of-memory restart; the upgrade to the next tier is one click from the plan badge.

## Example apps

Ready-made apps you can deploy from the panel (Apps, Host an app, then a template for what you chose) by filling in a field or two. Each is a public FadeHost repository you can read, fork and change.

| App | What it does | Runtime |
| --- | --- | --- |
| [Server status page](https://github.com/FadeHost/server-status-page) | A public page of your servers: online or sleeping, who is on, the address to join. | Node.js |
| [Whitelist applications](https://github.com/FadeHost/whitelist-applications) | A form players fill in, an admin page for staff, the whitelist command run on approval, an optional Discord notice. | Node.js |
| [Bot with a dashboard](https://github.com/FadeHost/discord-bot-dashboard) | A welcome and log bot whose settings live on a web page behind Discord login. | Node.js |
| [Activity digest](https://github.com/FadeHost/activity-digest) | Who played and for how long, summed up once a day to a Discord webhook, with today's page. | Python |
| [Server guide site](https://github.com/FadeHost/server-guide-site) | Rules, how to join and the mod list as a static site edited in Markdown. | Static site |
| [Express API starter](https://github.com/FadeHost/express-api-starter), [Flask API starter](https://github.com/FadeHost/flask-api-starter) | Bare services wired to the port and the persistent folder, ready to build on. | Node.js, Python |
| [Telegram bot](https://github.com/FadeHost/telegram-starter-bot) | A grammY bot with a few commands, polling by default or webhooks behind the web address. | Node.js |
| [WhatsApp bot](https://github.com/FadeHost/whatsapp-cloud-bot) | An Express webhook receiver for the official WhatsApp Cloud API, with signature checks and a reply helper. | Node.js |

The status page, the whitelist app and the digest talk to the FadeHost API for you: the panel creates their access token when you deploy them, so there is nothing to paste. Each README lists the environment variables.

## What is not allowed

App hosting runs web apps and workers. It is not for:

- phishing or credential-harvesting pages,
- proxies and tunnels of any kind, which get the app stopped,
- adult or illegal content,
- bulk file or media delivery through the web address,
- raw TCP or UDP services, outbound mail, or workloads that need privileged containers or a GPU,
- traffic far beyond fair use.

The full rules are in the [terms of service](https://fadehost.com/terms/). Report an abusive app to support@fadehost.com.

## Troubleshooting

### The address says the app is not listening

The web address is on, but visitors get a page saying the app is not answering on port N, and the **Web address** card says the same.

1. Check the app is actually running. The live console shows whether it started.
2. Check it listens on **`0.0.0.0`**, not `127.0.0.1` or `localhost`.
3. Check the port. Read `PORT` from the environment, or set the card's port field to the one your app hard-codes. The change takes effect at once, no restart needed.
4. Some frameworks need telling: `next start -H 0.0.0.0 -p $PORT`, `flask run --host=0.0.0.0 --port=$PORT`.

### The app restarts over and over

Open the app and read the **crash doctor** card. It reads the logs and names the cause, usually within a minute. The usual reasons:

- a missing environment variable, so the app throws on start,
- a dependency that is installed on your machine but missing from `package.json` or `requirements.txt`,
- a build that never produced the file the start command runs (the console names that file on every restart),
- a script that finishes instead of staying up. An app that exits cleanly looks like a crash to us, because nothing is left running.

Fix it in your repository or the environment, then press **Redeploy**. The live console keeps everything the app printed, so you can see what it said last.

## Need help?

Join our [Discord](https://discord.gg/t4Hy4X5tEq) and we will get your app running.
