Skip to content
NebulaCtrldocs

nebula.toml service file

Every table and key of the nebula.toml that configures one service from its repository, with types, defaults, limits and the problems the parser reports.

A nebula.toml in a service's build context configures that service from Git: the build, the processes, mounts, a release command and plain variables. A file that declares a whole project's services and databases is a different format, described in nebula.toml project file. To set one up, follow Configure a service from its repository.

Example

The example configures a Rails service with a web process, a worker, a nightly job, a migration release command, a volume and a production overlay.

nebula.toml
[build]
dockerfile = "Dockerfile"
target = "runtime"

[build.args]
BUNDLE_WITHOUT = "development:test"

[deploy]
release_command = "bin/rails db:migrate"
release_command_timeout = "10m"
depends_on = ["db"]

[env]
RAILS_ENV = "production"
RAILS_LOG_LEVEL = "info"

[processes.web]
cmd = "bin/rails server -b 0.0.0.0 -p 3000"
port = 3000
cpu = "500m"
memory = "512Mi"

[processes.web.checks]
startup = "/up"
readiness = "/up"
liveness = "/up"

[processes.worker]
cmd = "bin/jobs"
kind = "worker"
replicas = 1
cpu = "250m"
memory = "256Mi"

[processes.nightly]
cmd = "bin/rails reports:send"
schedule = "0 3 * * *"

[[mounts]]
source = "storage"
destination = "/rails/storage"
initial_size = "1Gi"
processes = ["web"]

[environments.production.processes.web]
replicas = 3

[environments.production.processes.worker]
replicas = 2

[environments.production.env]
RAILS_LOG_LEVEL = "warn"

Validate a file

nebula config validate checks a file offline with the parser the control plane uses. It needs no database and no running control plane.

nebula config validate nebula.toml

FILE defaults to ./nebula.toml. The command resolves the file for every environment it overlays plus production, development and preview, and prints each problem as <file>:<line>: <path>: <message>. A problem found only after an overlay is applied has no line, and the <line>: part is omitted. The problems of an overlay are listed under a [environments.<slug>] heading. A file without problems prints one summary line:

nebula.toml: ok (3 processes (nightly: cron, web: http :3000, worker: worker), release command, deploys after 1 service, 2 variables, 1 mount)

The command exits with status 1 when it finds a problem in the base file or in any resolved environment. It cannot know the project, so it does not check that a depends_on slug names a real resource.

Tables

TableConfigures
[build]The image build: Dockerfile, stage, build arguments and builder
[deploy]The release command and what the service deploys after
[env]Plain, non-secret variables
[processes]Commands, kinds, ports, replicas, resources and health checks
[[mounts]]Volumes attached to processes
[environments.<slug>]Per-environment overrides of every table above

How the file is read

The file is <build context>/nebula.toml. The build context is the context of the service's Git source and defaults to ., the root of the repository. The file is UTF-8 TOML 1.0 of at most 64 KiB. A key the parser does not know is a problem, not an ignored line: the message is "KEY" is not a nebula.toml setting. A few top-level keys from other deployment files, such as app and vm, get a hint that names the setting to use instead.

Each build reads the file at the exact commit it builds, and each release made from that build applies it. Pushing a new version of the file changes nothing until a build of that commit is deployed.

A file with a top-level [services] or [databases] table is a project file, not a service file. A build that finds one in its build context takes the service's [services.<slug>] block as its file, as the project file reference explains.

The file never names domains, secrets, the cluster, the deployment strategy or approval rules. Those stay in the console.

[build]

Applies to the build of this commit, before the image is compiled.

dockerfile

Type string · Default the service's own Dockerfile setting · Constraints a relative path with no . or .. segment, no empty segment and no leading /

The Dockerfile to build, as a path from the root of the repository. The build context, which is the directory sent to the build, is set separately by the service's Build context.

target

Type string · Default the service's own Build target setting, else the last stage · Constraints a stage name of a multi-stage Dockerfile; refused together with builder = "railpack"

The Dockerfile stage to build. A target in the file wins over the service's setting. A cluster whose agent does not support build options refuses a build that sets one.

[build.args]

Type table of string to string · Default none · Constraints at most 50 entries; each value at most 4 KiB; refused together with builder = "railpack"

Docker --build-arg values. They exist only while the image builds, never reach the running container, and never carry secrets.

builder

Type dockerfile or railpack · Default the service's own Build engine setting, which is Detect automatically unless changed

Overrides the service's builder for this build. railpack builds with Railpack whether or not a Dockerfile exists at dockerfile. dockerfile fails the build if no Dockerfile exists. See Build without a Dockerfile and Build detection.

[deploy]

release_command

Type string or array of strings · Default none · Constraints at most 8 KiB in total

A command that runs once per deployment, before any process of the new release starts. A string is split like a shell would split it: quotes are honored, and nothing is expanded. The command replaces the image's CMD and keeps its ENTRYPOINT. It runs in the environment's namespace with the release's variables and without volumes.

A deployment of a release with a release command passes through the releasing stage. A non-zero exit or a timeout fails the deployment with the release-command blocker, and the previous release keeps serving. A release command set in the console runs when the file sets none; the file's command wins when both exist. A database service takes neither. See Release commands and files.

release_command_timeout

Type duration string · Default 5m · Constraints 10s to 1h

How long the release command may run before it is stopped and the deployment fails.

depends_on

Type array of slugs · Default not set · Constraints unique slugs, at most 20; Since 0.39.0

Services and databases this service's deployments wait for. While a listed resource has a deployment in flight in the same environment that started earlier, this deployment stays pending with the message Waiting for api to finish deploying (r-4), and the current release keeps running. If the awaited deployment fails, this one fails and says so. See Deploy order.

A variable reference (${{ api.KEY }}) and a database connection are dependencies without being listed. List a slug when no variable shows the order, such as a worker that needs the API's migrations to have run. A file that sets depends_on replaces the console's Deploy after list, and depends_on = [] clears it. A file that does not set the key leaves the console's list in force. The console shows a file-owned list read-only, with the file and commit it came from.

The validator checks each slug's syntax, uniqueness and the 20-slug cap. The control plane checks the rest when a release is made: an unknown slug, the service itself, or a list that closes a cycle with the other lists or the references is refused, and the message names the whole cycle. A build refused this way is recorded as built, not deployed.

[env]

Type table of string to string · Default none · Constraints keys match ^[A-Z_][A-Z0-9_]*$; at most 100 keys; each value at most 4 KiB

Plain variables. Every key, with the environment's overlay applied, is written on every release made from the file as a service-scoped variable in that environment. Values are written as they appear in the file. A ${{ slug.KEY }} reference resolves when a release is frozen. ${{ secret(N) }} and ${{ host(slug) }} are not evaluated in a service file, and a project file evaluates them; see Variable expressions.

The file owns a variable it names:

  • Read-only while the file names it. The console shows the variable read-only with a file badge, and the API refuses to change or delete it with 409. The message names the file's path in the repository and the commit, such as KEY is set by api/nebula.toml at 3f9c2ab; change it there, or remove it from the file to edit it here.
  • Editable once the file stops naming it. Remove the key and deploy a build of the new commit. The variable keeps its last value and becomes an ordinary variable. Nothing deletes it.
  • A secret of the same name refuses the release. If an existing secret service or environment variable has the key, the release is refused rather than replace a secret with a value from Git.
  • The service tier wins. Variables merge in ascending order of specificity: external resource, project, environment, then service. The most specific wins, so a file value applies to the service even where a project or environment variable has the same name.

[processes]

Each entry is a process of the service. The name matches ^[a-z][a-z0-9-]{0,29}$ and is the process's name in the console. The table holds at most 20 processes and is not allowed to be empty: leave it out to keep the console's process list.

An entry is either a command string or a table. The string web = "bin/rails server" is shorthand for [processes.web] with cmd set.

cmd

Type string or array of strings · Default the image's CMD

The command. It replaces CMD and keeps ENTRYPOINT. Each string is at most 4 KiB.

entrypoint

Type string or array of strings · Default the image's ENTRYPOINT

Replaces the image's ENTRYPOINT.

kind

Type http, worker, cron or job · Default resolved, see Kind resolution

What the process is. http receives traffic, worker runs continuously without a route, cron runs on a schedule, job runs once.

port

Type integer · Default none · Constraints 1 to 65535; forbidden on cron and job

The listening port. A worker may set one, which is an in-cluster TCP port and never reachable from the internet. A process that the file creates as http must set one.

replicas

Type integer · Default the console's default · Constraints 0 to 100; forbidden on cron and job

The number of instances. The file wins over an environment's own replica override.

schedule

Type string · Default none · Constraints cron only; five fields; evaluated in UTC

A cron expression: minute, hour, day of month, month, day of week. Descriptors such as @daily and TZ= prefixes are refused.

run_as_user

Type integer · Default the console's default, which enforces a non-root user · Constraints 0 to 2147483647

The user ID. 0 opts the process out of the non-root rule.

cpu and memory

Type quantity string, or a table with request and limit · Default the console's default: for a process the console creates, CPU 100m to 500m and memory 128Mi to 512Mi · Constraints each quantity greater than zero; a table sets both keys, and request is at most limit

A scalar such as cpu = "500m" sets request and limit to the same value. A table such as memory = { request = "256Mi", limit = "1Gi" } sets them apart. Quantities use Kubernetes notation, for example 500m, 512Mi or 1Gi.

stabilization_seconds

Type integer · Default the console's default, 10 for a process the console creates · Constraints 0 to 600

How long every instance must stay ready before the deployment leaves the qualifying stage.

drain_seconds

Type integer · Default the console's default, 30 for a process the console creates · Constraints 0 to 600

How long the previous release keeps running, scaled up, after the new one activates. It is also the instance's termination grace period.

[processes.<name>.checks]

Type table of startup, readiness and liveness, each an absolute path · Default none · Constraints http only

The HTTP paths of the probes. A key that is left out sets no probe of that kind. Setting checks on a process with no kind makes it http.

[processes.<name>.metrics]

Type table of enabled, port and path · Default none · Constraints worker only

Opts a worker in to app-exported canvas metrics. Once the app has produced a point, the service card shows Queue and Jobs/min in place of Memory and Restarts, beside CPU. With enabled = true the agent scrapes GET :<port><path> every 15 seconds for two Prometheus gauge or counter names, nebula_queue_depth and nebula_jobs_processed_total.

nebula.toml
[processes.worker]
cmd = "bin/jobs"
kind = "worker"

[processes.worker.metrics]
enabled = true
port = 9090
path = "/metrics"

port is 1 to 65535 and defaults to the process's own port. enabled = true needs a port from one of the two. path defaults to /metrics and must start with /, never start with //, and never contain @, ? or #. Turning enabled off, or removing the table, returns the card to CPU, Memory and Restarts. See Logs and metrics.

Kind resolution

The kind of a process is, in order:

  1. The explicit kind.
  2. cron, when schedule is set.
  3. http, when port or checks is set.
  4. Unspecified.

An unspecified kind does not mean worker. It means the file leaves the kind of an existing process as it is, and still applies the file's other fields, usually only cmd. Only when the file creates a process the service does not have does an unspecified kind become worker. The shorthand web = "bin/rails server" on a service whose web already runs as http changes the command and keeps the kind.

Rules that follow from the kind:

  • A cron process requires schedule, and only cron or an unspecified kind may set one.
  • job and cron set neither port nor checks, and replicas is refused on an explicit cron or job. On an unspecified kind, replicas is refused only when the process turns out to be cron or job in the console.
  • checks is allowed on http only. A process that sets checks and no kind resolves to http.
  • metrics.enabled = true is allowed on worker or an unspecified kind, and needs a port from the process or from metrics.port.
  • Creating a process that resolves to http without a port refuses the release: nebula.toml creates process web as http but sets no port; add port = <n> to [processes.web]. An existing http process keeps the port it has in the console.

The full-list rule

A file that has a [processes] table lists every process. A release made from it runs exactly the processes the file declares:

  • A process the file names and the service lacks is created, with the kind it resolves to.
  • A process the service has and the file no longer names is stopped in that environment. Its volumes keep their data.
  • Giving an existing process a different explicit kind refuses the release: nebula.toml gives process web the kind worker, but it is http here; kinds can't change — rename it in nebula.toml or delete the process in the console. An unspecified kind never triggers this.
  • A domain that still routes to a dropped process refuses the release: domain x.example.com routes to process web, which nebula.toml at 3f9c2ab no longer lists; add it back to the file or move the domain first.

A file with no [processes] table leaves the service's process list as the console has it. Every other table still applies.

[[mounts]]

Each entry attaches a volume to processes. A process that is named gets its own volume at source. A volume the file does not mount is not mounted in that release, and its data is kept.

The file holds at most 10 [[mounts]] entries.

source

Type string · Required · Constraints ^[a-z][a-z0-9-]{0,29}$

The volume's name.

destination

Type string · Required · Constraints an absolute path

Where the volume is mounted.

initial_size

Type quantity string · Required · Constraints greater than zero, at most 1Ti

The size the volume gets when it is first created. An existing volume keeps its current size.

processes

Type array of strings · Default every process, when exactly one non-cron process exists · Constraints names processes the file declares, after overlays; never a cron process

The processes that mount the volume. With no list and more than one non-cron process, the file is refused and the message asks for one.

[environments.<slug>]

An overlay is a second layer with the shape of the base file, minus a nested [environments]. Its key is an environment's slug, or the literal preview, which applies to every preview environment before its own slug's overlay.

nebula.toml
[processes.web]
port = 3000

[environments.production.processes.web]
replicas = 3

[environments.production.env]
RAILS_LOG_LEVEL = "warn"

[environments.preview.processes.web]
replicas = 1

A layer applies in the order base, then preview for preview environments, then the environment's own slug. The merge rules:

  • [build], [deploy] and processes.<name> merge field by field. env and build.args merge key by key.
  • Every other value replaces the base value when the overlay sets it. This includes checks, metrics and [[mounts]], which an overlay replaces as a whole.
  • An overlay may declare a process the base does not have.
  • The limits on processes, [env] keys and build arguments apply to the merged result as well as to each layer.

Precedence over the console

For a release made from a file, a process's settings are, in order: the console's defaults for the process, then the environment's own overrides, then the file. The file comes last and wins for every field it sets. A field the file leaves unset keeps the override.

A field the file sets is read-only in the console for that environment, labeled as coming from the file. It is editable again once the file stops setting it, and the earlier override applies again. The console and the API refuse to stage a replicas change for a process whose file sets replicas, and a size change for the cpu or memory the file sets, with 409 naming the file and commit. Resetting an override is always allowed.

Limits

LimitValue
File size64 KiB
Processes20
Mounts10
[env] keys100
[build.args] entries50
Any string4 KiB, except release_command
release_command8 KiB in total
release_command_timeout10s to 1h, default 5m
replicas0 to 100
stabilization_seconds, drain_seconds0 to 600
initial_sizegreater than zero, at most 1Ti
depends_on20 slugs

Problems

The parser reports every problem in one pass. A problem is {path, line, message}. path locates the field, such as processes.web.port or environments.production.env.KEY. line is the 1-based source line, and 0 when the problem is found after overlays are merged. message says what is wrong and what is allowed.

A file that is not valid TOML reports the syntax error and its line. A file over 64 KiB or not in UTF-8 reports one problem for the whole file.

On this page