Builds
How NebulaCtrl turns a Git commit into an image inside your cluster, how it chooses between a Dockerfile and Railpack, and how a build becomes a release.
A build is one attempt to produce an image from a service's Git source, at one commit, for one environment. It runs as a job inside the cluster that will run the service, so source code and images never leave your infrastructure. A successful build creates a release exactly as deploying an image by hand does.
Builds apply to services with the git or public-git source. A service from an image source builds nothing.
What starts a build
| Trigger | Starts when |
|---|---|
| Push | You push to a branch that an environment tracks, and that environment auto-deploys. A service's own per-environment auto-deploy setting wins over the environment's. One push can start a build in every environment that tracks the branch. |
| Manual | You start a build from the console or the API, for example when you create a service from a repository. |
| Retry | You retry a failed build from the Deployments tab. |
Pushes arrive as webhooks from a Git connection, so a public-git service, which has no connection, only builds when you start it. An image service with a push trigger starts a different kind of wait: the control plane polls the registry every 20 seconds for up to 15 minutes until the tag your template renders appears, then creates the release. Nothing is built.
Each environment tracks the branch main until you change it. See Projects and environments.
How a build runs
flowchart LR
push["Push or manual deploy"] --> queued
queued["queued"] --> running["running in the cluster"]
running --> detect["Read nebula.toml, choose an engine"]
detect --> clone["Clone"] --> build["Build"] --> push2["Push to the cluster registry"]
push2 --> release["Release created"] --> deploy["Deployment"]
- The control plane creates the build as queued and numbers it per service and environment (
b-1,b-2). - It reads the repository's
nebula.tomlat the commit being built, if there is one, and decides the engine. See Dockerfile or Railpack. - The agent starts a job in the
nebula-buildnamespace. Its containers clone the repository, build the image with rootless BuildKit, and push it to the cluster's own registry. The build log records the steps:config,detect,schedule,pull-image,clone,buildandpush. - When the image is pushed, the control plane creates a release from its digest, freezing the service's configuration at that moment, and starts a deployment. In a protected environment the deployment first waits for an approval.
A build ends as succeeded, failed, cancelled or superseded. A cluster runs at most 2 builds at once, and the rest wait as queued. A build that runs longer than 30 minutes is failed and cancelled. A failed build changes nothing: the release that's serving keeps serving.
A newer intent ends a build that's still queued or running for the same service and environment. A newer push, a rollback or a promotion supersedes it. A build that finished before a rollback doesn't create its release afterwards, so a rollback is never undone by an older build.
The control plane keeps build logs for 7 days.
Dockerfile or Railpack
A service's builder decides how the repository becomes an image. The setting is Detect automatically (auto) by default. You can pin it to Dockerfile or Railpack in the service's source settings, or in nebula.toml with [build] builder. The file wins over the console.
With Detect automatically, each build decides for itself:
- If a Dockerfile exists at the service's configured path, within its build context, NebulaCtrl builds it. The default path is
Dockerfileand the default context is the repository root. - Otherwise project detection reads the repository's files at that commit: package manager, framework, start command and port. If it recognizes a project, NebulaCtrl builds with Railpack, which needs no Dockerfile.
- Otherwise the build fails and says what's missing. The message names the Dockerfile path and the files that would make a project detectable:
package.json,go.mod,requirements.txt,Gemfile,composer.jsonorCargo.toml.
A pinned Dockerfile builder fails when the file is missing. A pinned Railpack builder skips the Dockerfile check. See Build without a Dockerfile for the how-to and Build detection for what Railpack recognizes.
A Railpack-built image can't declare its own listen port, so for a service with exactly one http process the release sets PORT to that process's port, unless you set PORT yourself.
Boundaries
- A build option such as a Dockerfile build target or build args needs an agent that supports it. If a cluster's agent is too old, the build fails with a message that tells you to update the agent. See Update an agent.
- NebulaCtrl never runs a Docker Compose file. A Compose import turns it into services, and each service builds the usual way.
- A build only reads the repository. It doesn't write back to it, except commit statuses and deployments reported to your Git provider.
Related
- Deploy from Git: connect a provider and set the tracked branch.
- Configure a service from its repository: build settings in
nebula.toml. - Troubleshooting builds: the messages a failed build shows.
Change sets and approvals
How edits to an environment are staged, reviewed and applied together, and how production changes wait for an approval, a deploy freeze or a concurrency limit.
Networking
How traffic moves in NebulaCtrl. Internal service hostnames, environment isolation, domains and certificates, Tailscale exposure, edge clusters and the WireGuard mesh.