An 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 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.
Quick start#
Most apps need no Dockerfile and no configuration file.
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:
nimbus deployRead the plan. Nimbus Land works out the language and framework and shows a 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.
Open it. Once the build finishes and the new release is healthy, the app answers at
https://<name>.nimbusapps.ca.
If it doesn't answer, check What every app needs.
How your stack is chosen#
Nimbus Land looks for these, highest first:
- A
Dockerfileat the root of the app. It is built as written. See Dockerfile. - A server project at the root:
mix.exswith Phoenix (Elixir),requirements.txt,pyproject.toml,Pipfileorsetup.py(Python),go.mod(Go),pom.xmlor Gradle files (Java),Gemfile(Ruby),composer.json(PHP),build.sbt(Scala) or a.csproj(.NET). Apackage.jsonbeside one that only builds front-end assets, as in Laravel with Vite or Rails withvite_ruby, is that app's asset pipeline, not a separate Node.js app. - Node.js from a
package.json, then the same server manifests found in subdirectories. - A plain HTML site (an
index.htmlat the root) or a Vite project. See Static sites.
To see what Nimbus Land would choose, run this in your project:
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
codewith the sentence, such asmissing_manifest,multiple_appsorno_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>, ordir:underbuild:in.nimbus.yml, - with
app_diron 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 thePORTenvironment 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
webprocess. With aDockerfile, its command starts the app. Otherwise aProcfileweb:line wins:web: gunicorn app:app --bind 0.0.0.0:$PORTWithout 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 aNimbus: no Procfile; …line. A build that still has nowebprocess fails withNimbus: no web process. Add a Procfile with a "web:" line.No
gitduring the build. Builds receive your source without its.gitdirectory, so a build script that runsgit rev-parse HEADfails.Keep lasting files off the app's own disk. Each instance starts from a fresh copy on every release and restart. Keep uploads and databases in a managed database, a volume or object storage.
Release command#
A 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 rootProcfile, for any stack, - otherwise, for Laravel,
php artisan migrate --force(see PHP), - for Rails,
bundle exec rails db:migrate(see Ruby), - for Django,
python manage.py migrate(see Python), - for Phoenix apps with migrations, a command that runs them through the release (see Elixir and Phoenix).
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 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 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.
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 ViteVITE_*variable, must be a plain variable, and a change to it takes effect on the next build. NODE_ENVis never passed to a build, sodevDependenciesare installed while building. See Node.js.
For credentials, use secret variables.
Commands, workers and one-off runs#
Release commands, background workers and one-off commands 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:
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.
- 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 runexits 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 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: version pinning, lockfiles,
devDependencies, Next.js, database TLS. - Python: version, uv, the start command, FastAPI, Django.
- Ruby: Ruby and Rails, master key, storage volume, background jobs, migrations.
- PHP: Laravel, Symfony and plain PHP, document root, writable paths, HTTPS behind the platform's edge.
- Go: Go version, modules with several programs.
- Java: Maven and Gradle, Spring Boot and
PORT, Scala. - Elixir and Phoenix: a stock
mix phx.newapp, 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, and listen on
PORT. - Plain HTML and Vite sites are published as static sites, 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). 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).
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:24builder, 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
Dockerfileis 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.
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), choose the program for a Go module (see Go), 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.
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 covers Laravel and Symfony, and Python covers Django. For other frameworks, set their trusted-proxy option. Then choose Check again on the Overview. See Page check.
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, Python, Elixir and Phoenix.
Also available as Markdown.