# Apps

An [app](key-ideas.md#app) is your code running on Nimbus Land at its own address. You bring the code; Nimbus Land works out what it is, checks it, [builds](key-ideas.md#build) it and keeps it running. This guide covers what happens on the way and what your app needs. For a guided first deploy, start with [Your first app](first-app.md).

## Quick start

Most apps need no `Dockerfile` and no configuration file.

1. **Deploy.** In the console, choose **New** on **Applications**, pick your GitHub repository, and choose **Deploy** (the button carries the app's name). Or run this in your project's folder:

   ```sh
   nimbus deploy
   ```

2. **Read the plan.** Nimbus Land works out the language and framework and shows a [deploy plan](#the-deploy-plan): what will run, and anything worth checking first. For common frameworks it also fills in what a first deploy needs, such as a start command, a secret key or a database migration step. Your own values always win.
3. **Open it.** Once the build finishes and the new [release](key-ideas.md#release) is healthy, the app answers at `https://<name>.nimbusapps.ca`.

If it doesn't answer, check [What every app needs](#what-every-app-needs).

## How your stack is chosen

Nimbus Land looks for these, highest first:

1. **A `Dockerfile` at the root** of the app. It is built as written. See [Dockerfile](dockerfile.md).
2. **A server project at the root**: `mix.exs` with Phoenix (Elixir), `requirements.txt`, `pyproject.toml`, `Pipfile` or `setup.py` (Python), `go.mod` (Go), `pom.xml` or Gradle files (Java), `Gemfile` (Ruby), `composer.json` (PHP), `build.sbt` (Scala) or a `.csproj` (.NET). A `package.json` beside one that only builds front-end assets, as in Laravel with Vite or Rails with `vite_ruby`, is that app's asset pipeline, not a separate Node.js app.
3. **Node.js** from a `package.json`, then the same server manifests found in subdirectories.
4. **A plain HTML site** (an `index.html` at the root) **or a Vite project**. See [Static sites](static-sites.md).

To see what Nimbus Land would choose, run this in your project:

```sh
nimbus detect
```

If the guess is wrong, open **Customize** in the deploy wizard and set **Deploy as** to **Static site** or **Container**.

## The deploy plan

Before anything builds, the deploy wizard, `nimbus detect`, `nimbus deploy` and the build API show a deploy plan. It names what will run (the `Dockerfile`'s command, your `Procfile`'s `web:` line, `npm start`, the Go program, or the standard start command for your framework) and gives one verdict:

- **Ready**: nothing to check.
- **Check this**: notes that never block, such as a program with no web server in it, a hard-coded port that ignores `PORT`, files used from outside the app directory, or a secret read at build time.
- **Blocked**: a failure that is certain, refused before any app, build or service is created. For example: no manifest, an app directory that doesn't exist, a repository holding several apps, a Go module with several programs, a Python app with no start command, or a Go version the build can't supply. In the wizard, **Deploy** stays disabled until it is fixed. The build API returns a `code` with the sentence, such as `missing_manifest`, `multiple_apps` or `no_start_command`.

Once an app has served a healthy release, these findings become notes, so a deploy that works today is never newly refused.

### Repositories with several apps

A repository holds several apps when it has server manifests only in subdirectories, JavaScript workspaces with no server at the root, or a `go.work` using several modules. Tooling folders never count (`docs`, `examples`, `test`, `scripts`, `.github`, `node_modules` and similar). A first deploy of such a repository is blocked until you choose an **App directory**:

- in the deploy wizard, from the candidates under **Choose the app to deploy**, or by typing it in **App directory**,
- with `nimbus deploy --dir <path>`, or `dir:` under `build:` in `.nimbus.yml`,
- with `app_dir` on the build API.

The directory is saved on the app, and later pushes, previews and rebuilds use it. The build sees only that directory, as if it were the repository root, so a sibling folder it depends on (a shared workspace package, for example) isn't included.

## What every app needs

- **Listen on `PORT`.** Nimbus Land sets the `PORT` environment variable and sends traffic to it. Most servers and frameworks already read it. A start command that hard-codes a port never receives traffic.
- **A `web` process.** With a `Dockerfile`, its command starts the app. Otherwise a `Procfile` `web:` line wins:

  ```text
  web: gunicorn app:app --bind 0.0.0.0:$PORT
  ```

  Without one, your language's usual start command runs (`npm start`, for example), and for frameworks that have none Nimbus Land writes a default and says so in the build log with a `Nimbus: no Procfile; …` line. A build that still has no `web` process fails with `Nimbus: no web process. Add a Procfile with a "web:" line`.
- **No `git` during the build.** Builds receive your source without its `.git` directory, so a build script that runs `git rev-parse HEAD` fails.
- **Keep lasting files off the app's own disk.** Each [instance](key-ideas.md#instance) starts from a fresh copy on every release and restart. Keep uploads and databases in a [managed database](dependencies.md), a [volume](volumes.md) or [object storage](object-storage.md).

## Release command

A [release command](key-ideas.md#release-command) runs inside your built app before each release goes live. In practice it runs database migrations. The deploy wizard suggests one:

- a `release:` line in a root `Procfile`, for any stack,
- otherwise, for Laravel, `php artisan migrate --force` (see [PHP](php.md)),
- for Rails, `bundle exec rails db:migrate` (see [Ruby](ruby.md)),
- for Django, `python manage.py migrate` (see [Python](python.md)),
- for Phoenix apps with migrations, a command that runs them through the release (see [Elixir and Phoenix](elixir.md)).

The plan shows it as **Before each release**, and you can edit or clear it under **Customize** in the **Release command** field. The suggestion only applies to an app the wizard creates. An existing app keeps its own release command, and `nimbus deploy`, uploads and push-to-deploy don't change it.

To change it later, open the **Deployments** tab and choose **Edit** in the **On every deploy** row. The command runs through `/bin/sh -c`, and saving redeploys the app so it runs. If the command fails, the release fails, and the **Overview** shows its exit code with a link to its output.

## Environment variables

[Environment variables](key-ideas.md#environment-variable) you set on the app are available to it at run time. Set them on the **Resources** tab under **Environment Variables**, with `nimbus env set`, or under **Customize** in the deploy wizard. Saving a change restarts the app.

[Dependencies](key-ideas.md#dependency) add their own, such as `DATABASE_URL` and `DATABASE_CA_CERT` for Postgres. The default names suit your stack (a Laravel app gets `DB_URL`, `DB_HOST`, `DB_DATABASE` and friends), and you can rename them when you add the dependency. See [App dependencies](dependencies.md).

The build sees the variables passed with the deploy, then the app's stored plain variables, then the platform's suggestions for keys neither sets. Two exceptions:

- **Secrets never reach a build**, unless the deploy itself passes them. A value your build bakes into the page, such as a Next.js `NEXT_PUBLIC_*` or a Vite `VITE_*` variable, must be a plain variable, and a change to it takes effect on the next build.
- **`NODE_ENV` is never passed to a build**, so `devDependencies` are installed while building. See [Node.js](nodejs.md).

For credentials, use [secret variables](secret-env-vars.md).

## Commands, workers and one-off runs

Release commands, [background workers](key-ideas.md#background-worker) and [one-off commands](key-ideas.md#one-off-command) run in your built app with the same environment as the `web` process, so your language's tools (`php`, `bundle`, `node` and friends) are on `PATH`. A command given as one string runs through `/bin/sh -c`; a command given as a list runs as given, without shell expansion:

```sh
nimbus run -- php artisan tinker
```

For apps built without a `Dockerfile`, they run from the app directory (`/workspace/src`). Elixir apps run from the release directory; see [Elixir and Phoenix](elixir.md).

- **Background workers**: on the **Resources** tab, **Background workers** → **Add worker**, with a **Name**, a **Start command**, an **Instance size** and a number of **Instances**. For some frameworks the section offers ready-made starters, such as a Laravel queue worker.
- **One-off commands**: `nimbus run -- <command>`, or **Run a command** on the **Monitoring** tab. `nimbus run` exits with your command's status.

One-off commands and scheduled tasks are billed as compute at your app's instance size, for the time they actually run, and show as **Runs** under the app on the **Billing** page's usage details. Starting a one-off command needs a working billing account, like a deploy. Compute is billed for the instances that are actually running and ready: an app scaled to zero instances isn't billed for its web instances, though its background workers are still billed at their own sizes, and an instance that keeps failing to start isn't billed while it isn't ready. [Static sites](static-sites.md) have no server running, so they aren't billed compute at all.

## Languages

Each language has its own page for versions, framework settings and fixes:

- [Node.js](nodejs.md): version pinning, lockfiles, `devDependencies`, Next.js, database TLS.
- [Python](python.md): version, uv, the start command, FastAPI, Django.
- [Ruby](ruby.md): Ruby and Rails, master key, storage volume, background jobs, migrations.
- [PHP](php.md): Laravel, Symfony and plain PHP, document root, writable paths, HTTPS behind the platform's edge.
- [Go](go.md): Go version, modules with several programs.
- [Java](java.md): Maven and Gradle, Spring Boot and `PORT`, Scala.
- [Elixir and Phoenix](elixir.md): a stock `mix phx.new` app, the build, configuration, migrations, `PHX_HOST`, database TLS, clustering.

Other languages:

- **.NET and Rust** are recognised, but there's no tuned build for them today. Use a [Dockerfile](dockerfile.md), and listen on `PORT`.
- **Plain HTML and Vite sites** are published as [static sites](static-sites.md), with no server to run.

## Builds

Each plan runs a set number of builds at once: **one** on Starter and **two** on Business. A build that has to wait shows **Queued** with the reason, and starts by itself when a slot frees. Your first build of a new app can start even while another build is running, so a first deploy doesn't wait behind a rebuild. Only one such first build runs this way at a time.

Builds are measured in **build minutes**: the time your build spends building, rounded up to the next minute. Waiting in the queue, publishing and rolling out the release don't count. Previews and static sites count too. Agent tasks and CI checks use build minutes as well: the time their sandbox runs, rounded up to the next minute, counted with your builds.

Each billing period includes **300 build minutes** on Starter and **1,000** on Business. Beyond that, build minutes cost **$8 per 1,000** (0.8¢ each) on any plan. A build is never stopped for going over; the extra minutes appear on your bill. See your usage on the **Billing** page, for example **Build minutes: 212 of 300 included**. If builds queue often, you can compare plans there.

## Bandwidth

**Bandwidth** is what your organization sends out to the internet: the responses your apps, static sites and preview environments serve, plus downloads from your buckets through the public storage endpoint (see [Object storage](object-storage.md#downloads-and-bandwidth)). Traffic inside the platform is free: your app reading its own databases and attached buckets doesn't count, and neither do uploads or requests coming in.

Each billing period includes **100 GB** on Starter and **250 GB** on Business (a GB is 1,000,000,000 bytes). Beyond that, bandwidth costs **$0.06 per GB** on any plan. Nothing is slowed or stopped for going over; the extra appears on your bill. Usage is recorded every hour: see it on the **Billing** page, for example **Bandwidth: 12.4 of 100 GB included**, and per app and environment in their cost breakdowns.

## Billing days and cancelling

Your billing period renews on the day of the month you subscribed. In a shorter month it renews on the last day, then goes back to your day: subscribe on January 31 and you're billed on February 28, March 31, April 30. Upgrading starts a new period, and a new billing day, from that moment. Downgrading keeps your day.

When you cancel, your plan runs to the end of the current period. When the plan ends, everything stops: your apps in every environment (staging and previews included), their scheduled tasks and your managed databases. Your buckets become read-only, and nothing more is billed. Your data, configuration and backups are kept. Your last invoice covers usage up to the end of the plan. It can arrive up to a day later, once that usage has been counted. Subscribing again restarts your apps and databases where they left off; an app you had stopped yourself stays stopped. The cancel dialog on the **Billing** page lists what will stop and when.

## Spending budgets

Every paying organization's owners get an email when the period's usage first goes past the plan's included usage, since from then on usage adds to the bill. You can turn this off on the **Billing** page.

On the same page an owner can set a monthly **budget** in CAD. It is measured against the period's estimated bill before tax: the monthly minimum plus usage beyond what the plan includes. AI is prepaid with AI credits, so it isn't part of the bill or the budget, and AI usage doesn't use up the included usage either. Owners are emailed when the estimate reaches 50%, 80% and 100% of the budget, once each per period. If you change the budget during a period, the alerts start over for the new amount: raising a $100 budget to $500 after its 100% alert means you hear again at $250, $400 and $500. To send the alerts to a team address or a webhook as well, add a notification destination subscribed to **Spending budget alert**.

A budget only sends alerts unless you turn on **Pause new spending when the bill reaches the budget**. With that on, once the estimate reaches the budget the following are refused until you raise the budget, turn the pause off, or the next period starts:

- new deploys and builds, including pushes and previews;
- new apps, environments, databases, volumes and buckets;
- scaling up and growing databases or volumes;
- one-off commands;
- agent tasks, which run in a sandbox and use build minutes.

AI requests are not paused: AI is paid from your credits, and stops only when they run out (see [AI gateway](ai-gateway.md)).

Anything already running keeps running: apps, databases and stored data are never stopped, and they keep being billed. The bill can therefore end up somewhat above the budget. Usage is recorded every few minutes (storage daily), so alerts and the pause arrive shortly after the usage that caused them. Scaling down, stopping and deleting always work.

## Organizations and new-organization limits

- **Organizations.** You can own up to **5 organizations**, your personal one included, and create at most **3 new ones an hour**. To own more, contact support.
- **Spending limit for new organizations.** During its first months an organization has a spending limit of **$100 CAD beyond its included usage**. It applies until the organization is 60 days old and one monthly invoice has been paid. It works like a hard budget: when usage beyond the included amount reaches $100, new spending is refused (the same list as above), while running apps keep running. The Billing page shows the limit while it applies, and you can't remove it yourself; contact support to lift it early.
- **Suspended organizations.** Nimbus Land can suspend an organization. Its apps are stopped, and new spending, AI requests and credit purchases are refused. Data is kept, members can still sign in, and a banner on every page says why. Contact support to have it reviewed.

## Under the hood

- **Most languages** (Node.js, Python, Ruby, PHP, Go, Java, Scala) build with Cloud Native Buildpacks, using the `heroku/builder:24` builder, plus the defaults Nimbus Land adds for the frameworks it knows.
- **Elixir and Phoenix apps** are compiled into a self-contained OTP release by a build Nimbus Land generates for them.
- **A root `Dockerfile`** is built as written.

## Troubleshooting

### The app never answers

The build succeeds and the logs look fine, but the address never responds. Either the start command uses a fixed port instead of `PORT`, or there is no `web` process. Add a `Procfile` with a `web:` line that reads `PORT`. See [What every app needs](#what-every-app-needs).

### The wrong program runs

Instances keep restarting on a command-line tool's usage text. The repository holds several programs and the wrong one was picked. Choose the app directory (see [Repositories with several apps](#repositories-with-several-apps)), choose the program for a Go module (see [Go](go.md)), or name the server in a `Procfile` `web:` line.

### The build ran out of memory

The build fails and its failure reason names the memory limit, with a hint for your stack. Trim the build step, or see your language's page for its options, for example [Node.js](nodejs.md).

### App name already in use

App names are unique across Nimbus Land, because the name is the app's `<name>.nimbusapps.ca` address. Pick another: **Rename** in the wizard's **Web address** row, or `--name` (or `app_name` in `.nimbus.yml`) with the CLI. Names use lowercase letters, digits and single hyphens, up to 63 characters.

### Page styles or scripts blocked

The page loads over HTTPS but has no styles or JavaScript, and the **Overview** shows **Page loads styles or scripts over HTTP**. Nimbus Land handles HTTPS at its edge and forwards requests to your app with `X-Forwarded-Proto: https`; a framework that doesn't trust that header builds `http://` links, which the browser blocks. Trust that header in your framework's settings: [PHP](php.md) covers Laravel and Symfony, and [Python](python.md) covers Django. For other frameworks, set their trusted-proxy option. Then choose **Check again** on the **Overview**. See [Page check](page-check.md).

### Database certificate errors

A database driver set to verify the server's certificate fails with an error such as `self-signed certificate in certificate chain` or `certificate verify failed`. The managed database's certificate is signed by Nimbus Land's own authority, which your app receives as `DATABASE_CA_CERT`. Most drivers connect without verifying by default. To verify, give the driver that certificate; your language's page has the snippet: [Node.js](nodejs.md), [Python](python.md), [Elixir and Phoenix](elixir.md).
