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).
- 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. 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 page.
Create an app from a repository
- Push your code to a GitHub repository, public or private.
- In the panel, go to Apps → Host an app.
- 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=valueper line. Keys and tokens belong here, never in the repository.
- 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.
| 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.
// Node.js (Express)
app.listen(process.env.PORT || 8080, "0.0.0.0")
# 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=valueper 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_TOKENorDATABASE_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.jswithdistin.gitignore) and yourpackage.jsonhas abuildscript, we install yourdevDependenciesand 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.jsis in git starts as before, even if the repository has abuildscript.
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.
Web address
A paid app can answer at https://<name>.fadehost.app with HTTPS, for $2/mo or $20/yr per app. 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.
The full section, including what your app has to do and what is not allowed, is under Web address 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:
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 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 |
| Always on, auto-deploy | ✓ | ✓ | ✓ | ✓ |
| Live console, crash doctor | ✓ | ✓ | ✓ | ✓ |
| 1-click MySQL database | ✓ | ✓ | ✓ | |
Web address (https://name.fadehost.app) | +$2/mo | +$2/mo | +$2/mo | |
| 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.
Example apps
Ready-made apps you can deploy from the panel (Apps, Host an app, One-click template) 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 | A public page of your servers: online or sleeping, who is on, the address to join. | Node.js |
| 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 | A welcome and log bot whose settings live on a web page behind Discord login. | Node.js |
| 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 | Rules, how to join and the mod list as a static site edited in Markdown. | Static site |
| Express API starter, Flask API starter | Bare services wired to the port and the persistent folder, ready to build on. | Node.js, Python |
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. 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.
- Check the app is actually running. The live console shows whether it started.
- Check it listens on
0.0.0.0, not127.0.0.1orlocalhost. - Check the port. Read
PORTfrom 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. - 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.jsonorrequirements.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 and we will get your app running.