Services
A service is a long-running container, or a one-shot job. You declare services
under the top-level services key in up.yaml. up apply
converges them: it
builds or pulls the image, deploys it, waits for the health check, then shifts
traffic. Unknown keys on a service are rejected, so a typo fails at apply time.
The smallest working service:
services:
- name: api
source:
type: image
image: nginx:latest
port: 8080
Identity
name
The service’s unique name within the environment. It is the handle you use to deploy, log, and reference the service.
name: api
type
A long-running container by default, or a one-shot job. Jobs run to exit and are covered in detail below.
type: job
Source
The source block says where the image comes from. It is required.
[!NOTE]
commandandargsconfigure the process inside the image. They are covered under Command and arguments.
Image source
Deploy an image you already have:
source:
type: image
image: nginx:latest
image
The image reference to deploy, for example nginx:latest or
ghcr.io/myorg/api:1.2.3. Required.
registry
The name of a registry from the registries section, used when the image
needs private credentials. References are by name, not ID.
source:
type: image
image: ghcr.io/myorg/api:1.2.3
registry: ghcr
Git source
Build from a repository. The image is built on a build node and pushed to the output registry before it is deployed:
source:
type: git
url: git@github.com:org/repo.git
url
The repository to clone. Required.
ref
The ref to build: a branch name, a tag, or a full commit SHA. The default, an empty ref, means the repository default branch. Use a commit SHA when a deploy must land on the exact revision it was tested against, e.g. when a CI system pins deployments after a build.
source:
type: git
url: git@github.com:org/repo.git
ref: v1.2.3
token
token
The secret used to connect to private repositories such as ghcr.io or a self-hosted Git host like GitLab or Gitea. It is never written to logs or error messages.
source:
type: git
url: git@github.com:org/repo.git
token: $GIT_TOKEN
Building from source
A git source needs a build block that selects the backend. Both backends
share the options documented under Common build options.
dockerfile backend
Build from a Dockerfile. A minimal config builds Dockerfile from the
repository root:
build:
type: dockerfile
dockerfile
The path to the Dockerfile. Defaults to Dockerfile.
build:
type: dockerfile
dockerfile: Dockerfile.prod
target
The multi-stage build target to stop at.
build:
type: dockerfile
target: production
railpack backend
Railpack auto-detects your language and framework and builds without a Dockerfile. See the Railpack documentation for what it supports and how it configures each language.
build:
type: railpack
Common build options
node
The name of the node that runs the build. It must carry the build role.
build:
type: dockerfile
node: builder-1
root
The subdirectory within the cloned repository that the builder treats as the
app root. It is useful for monorepos, where the source for a service lives in
a subdirectory rather than at the root of the repository. The value is a
relative path, so it must not start with / and must not contain ..
segments. Empty means the repository root, which is the default.
For the dockerfile backend, the context given to the build is the root
directory, so dockerfile and every COPY inside the Dockerfile are
relative to the root. For the railpack backend, the root directory is
scanned for the app, so the start command and language configuration of that
directory are used.
build:
type: railpack
root: apps/api
output_registry
The name of the registry that receives the built image. Defaults to the default registry, so this is only needed when the build output should go somewhere else.
build:
type: dockerfile
output_registry: ghcr
output_prefix
By default the built image is pushed under the name
host/project_name/service_name. Set output_prefix to override the middle
segment. This matters on ghcr.io, where the first part of an image name must
be your GitHub username or organisation name: with output_prefix: myorg the
image becomes host/myorg/project_name/service_name. Defaults to the project
name.
build:
type: dockerfile
output_prefix: myorg
cache
Builds reuse layers from previous builds through a cache image stored in the
output registry, so repeat builds only redo the steps that changed. Set
cache to false to force a fully cold build, for example when you suspect
a stale layer is hiding a problem. Defaults to true.
build:
type: railpack
cache: false
Build environment
The service env is also available during the build. For a railpack build,
the variables enter the Railpack build environment, so RAILPACK_* variables
configure the build. See the Railpack documentation
for the full list. See
Environment variables for how env
works.
Port
The container port your process listens on. Optional: services that read the
PORT environment variable, and jobs, can omit it.
port: 8080
Command and arguments
The command and args fields configure what the container runs: command
replaces the image entrypoint and args supplies the arguments after it. By
default the container runs the image’s own entrypoint and command untouched.
Each field accepts a list of strings, or one plain string that is split on
whitespace. Quote characters are not interpreted, so an argument that must
contain a real space needs the list form.
The common case is an image whose process is configured through arguments
only. NATS, for example, has no environment toggle for JetStream; with args
the official image runs as-is and no wrapper Dockerfile is needed:
services:
- name: nats
source:
type: image
image: nats:2-alpine
args: -js -sd /data
volumes:
- source: nats-data
target: /data
command
The container entrypoint override, applied instead of the image ENTRYPOINT.
Omit it to keep the image entrypoint. Use it when the image entrypoint wraps
the process you actually want, or when the image has none:
command: ["nats-server"]
args
The arguments passed to the entrypoint, replacing the image CMD. Omit it to
keep the image command. Setting args alone keeps the image entrypoint, which
is how argv-configured images like NATS or Memcached are meant to run:
args:
- -js
- -sd
- /data
A change to either field redeploys the service, and
up diff reports it as a command or args change.
Domains
Expose the service on the public internet. Each entry is a hostname the proxy
routes to this service on ports 80 and 443. For a domain to resolve publicly,
point its CNAME (or A record) at the IP address of the node that runs the
deployment. For local-only access, add a domain like api.localhost, which
resolves on the current device with no DNS setup.
domains:
- api.myapp.com
Domains also drive the injected UP_PUBLIC_DOMAIN and UP_PUBLIC_URL
variables, documented under
Environment variables.
Wildcard domains
A domain can be a single-label wildcard, for example *.up.karabo.io, so one
service serves every tenant subdomain without listing each one:
domains:
- "*.up.karabo.io"
The proxy matches the wildcard against the request Host and forwards that
header unchanged, so the application reads the tenant from it. The wildcard
matches exactly one label, so tenant.up.karabo.io matches *.up.karabo.io
but tenant.app.up.karabo.io does not. Point the DNS wildcard record at the
node that runs the deployment, the same way you point a plain domain.
Wildcard domains are HTTP-only. You cannot enable proxy.ssl for a
wildcard, because a certificate authority issues a wildcard certificate only
after a DNS challenge and up does not perform one. The apply fails with a
validation error if you try. Terminate TLS in front of the node if the service
must be served over HTTPS.
UP_PUBLIC_DOMAIN and UP_PUBLIC_URL
skip wildcard domains, because a wildcard is not a usable URL. If the service
needs those variables, declare a concrete domain in addition to the wildcard.
Proxy
The proxy block controls TLS for the service’s domains. It has two options.
ssl
Defaults to false. Set it to true to serve HTTPS with certificates managed
by the proxy (Caddy).
proxy:
ssl: true
ssl_redirect
When ssl is on, HTTP requests are redirected to HTTPS. Enabled by default;
set it to false to pass HTTP through untouched.
proxy:
ssl: true
ssl_redirect: false
Health checks
A health check gates the rollout: the new container is promoted only once the probe passes, and a failing probe fails the deployment. Probes run inside the container network, so they resolve the service by DNS alias without publishing ports.
A minimal HTTP health check:
health:
path: /health
Probe types
The probe kind is set with type: http (default), tcp, or command.
http
Probe an HTTP endpoint. path is required.
health:
type: http
path: /health
port: 8080
port defaults to the service port, then the PORT environment variable.
tcp
Probe a TCP port. port is required.
health:
type: tcp
port: 5432
command
Run an argv list inside the container. command is required.
health:
type: command
command:
- pg_isready
- -U
- postgres
Timing
The timing options apply to every probe type.
interval
Seconds between attempts. Defaults to 3s.
health:
path: /health
interval: 10s
timeout
Seconds per attempt. Defaults to 5s.
health:
path: /health
timeout: 5s
retries
The allowed failures before the check fails. When omitted, up retries until the deployment deadline.
health:
path: /health
retries: 3
start_period
Seconds to wait before probing begins. Lets a slow process boot first.
health:
path: /health
start_period: 5s
Restart
Control when the container restarts. A minimal config:
restart: always
policy
The restart strategy: no, always, on-failure, or unless-stopped.
Defaults to on-failure, which restarts the container when it exits non-zero
and stops retrying after the cap, so a crash loop does not run forever. Choose
unless-stopped when the container should restart on any exit, or always when
it should restart even after a deliberate stop.
The runtime does not restart on-failure containers when the daemon or the
machine restarts. To cover that, the control node redeploys any service it finds
with a stopped container when it starts, and up apply does the same for every
declared service that is not running.
retries
The restart attempt cap, used with on-failure. Defaults to 5. Raise it for a
service that recovers after several crashes, or use policy: always to restart
without a cap.
restart:
policy: on-failure
retries: 3
Environment
The env map holds the variables passed to the container, with ${{ }}
references to other services, $VAR host expansion, and injected UP_*
variables. See Environment variables for
the complete reference.
env:
NODE_ENV: production
Volumes
Persist data with volumes. A minimal config:
volumes:
- source: data
target: /var/lib/postgresql
source
The name of a managed named volume. Host paths are rejected; only names
matching [a-zA-Z0-9][a-zA-Z0-9_.-]* are allowed.
target
The absolute container path to mount the volume at. The container root is not allowed.
Scoping and persistence
Volumes are scoped per environment. The logical name you write in up.yaml is
what up reports, while the physical Docker volume is prefixed with the
environment id, for example up-{environmentId}-data. Two environments of the
same project never share data, and the volume survives redeploys and service
removal. See Environments for details.
Lifecycle scripts
pre_deploy and post_deploy run a shell command in a side container that
shares the service image, environment, volumes, and network. The deployment
waits for the command to exit, and a non-zero exit fails the deployment. Chain
commands with && to run several steps.
pre_deploy
Runs before the service starts. Use it for migrations or setup that must complete first. A failure aborts the deployment before the service is touched.
pre_deploy: "rake db:migrate && rake assets:precompile"
post_deploy
Runs after the new container is promoted. Use it for seed data or cache warmers. The previous deployment is kept running until the new one succeeds; if a post_deploy hook fails, traffic rolls back to the previous deployment.
post_deploy: "./bin/seed"
Jobs
A type: job service is a one-shot task, useful for migrations, backfills, and
periodic work you trigger on demand. Its container runs to completion and the
deployment succeeds only on a zero exit code. Jobs are never promoted, never
made active, and never restarted as long-running services:
services:
- name: db-migrate
type: job
source:
type: image
image: myapp:1.2.3
Job logs stream through the same up service deploy and up apply flow. Jobs cannot be referenced by other services (see Environment variables), but a job may read values from a service.