Environment Variables
Every service carries an env map. Variables are applied at build time and at
runtime, so a variable set here is visible to both the build and the container.
services:
- name: api
source:
type: image
image: nginx:latest
env:
NODE_ENV: production
DATABASE_URL: ${{ db.DATABASE_URL }}
Plain values
A value with no ${{ }} token is passed through as written. Secrets in env
are never written to logs or error messages.
Additive changes and removal
up apply merges the declared env into the stored map. A declared key is
added or updated. A stored key that the file does not declare stays. The diff
reports only the keys that the file adds or changes, so a stored-only key never
causes a redeploy.
up service update with -e KEY=VALUE also merges. Remove a key with
up service update --unset KEY. A key cannot appear in both -e and --unset
in one command. Removal is imperative only; the file cannot remove a variable.
See the known limitation.
When up creates an environment as part of a deploy, it starts as a copy of the
environment it derives from, so its services inherit that env. See
Environments for the seed rules.
Host environment expansion
A bare $VAR or ${VAR} is expanded from the shell environment where you run
up apply. Use it for values you do not want to commit:
export GHCR_TOKEN=ghp_...
registries:
- name: ghcr
host: ghcr.io
password: $GHCR_TOKEN
An unresolvable host variable is an error, so a typo fails loudly at apply time instead of shipping an empty secret.
References between services
Use a ${{ }} template to read a variable from another service. This is the
Railway-style form and the only supported reference syntax:
services:
- name: db
source:
type: image
image: postgres:16
env:
POSTGRES_DB: app
- name: api
source:
type: image
image: myapp:1.2.3
env:
DATABASE_URL: postgres://postgres@${{ db.HOST }}/app
Reference yourself with ${{ self.NAME }} or a bare ${{ NAME }}. Tokens may
be embedded in a larger value, as in the DATABASE_URL example above.
self always means the service that declares the value, not the service that
references it. A ${{ other.VAR }} value may itself contain self. tokens,
and those resolve against other. This lets a service own a fully-formed
connection string built from its own variables and then share it with a
consumer in one reference:
services:
- name: postgres
source:
type: image
image: postgres:16
env:
POSTGRES_USER: karabo
POSTGRES_PASSWORD: $POSTGRES_PASSWORD
POSTGRES_URL: postgres://${{ self.POSTGRES_USER }}:${{ self.POSTGRES_PASSWORD }}@${{ self.UP_PRIVATE_DOMAIN }}:5432/karabo
- name: api
source:
type: image
image: myapp:1.2.3
env:
DATABASE_URL: ${{ postgres.POSTGRES_URL }}
The legacy $db.HOST / ${db.HOST} form is rejected with a hint to use
${{ }}, and a ${{ }} that names an unknown service or variable is an error.
Injected system variables
up injects a set of reserved UP_* variables into every service. You can read
them in config. A user key of the same name overrides the injected value.
UP_SERVICE_NAME
The name of the service.
api
UP_PROJECT_NAME
The name of the project.
myapp
UP_ENVIRONMENT_NAME
The name of the environment.
production
UP_PRIVATE_DOMAIN
The private DNS name of the service on the environment network.
api.up.internal
UP_PUBLIC_DOMAIN
The first configured domain, if any. Empty when the service has no domains.
api.myapp.com
UP_PRIVATE_URL
The private URL, including the effective container port.
http://api.up.internal:8080
UP_PUBLIC_URL
The public URL. The port is omitted because the proxy serves 80/443.
https://api.myapp.com
Injected variables are excluded from diffing, so they never cause a perpetual redeploy.
Dependencies and ordering
A cross-service reference creates a dependency edge. up deploys dependencies first (topological order), so the database is running and healthy before the API that references it is deployed.
Circular references are rejected with the cycle path. References to a job
service are also rejected: only long-running services may be linked, and a job
may reference a service but nothing may reference a job.
Build-time vs runtime
The same env map feeds the build and the container, so a variable set here
is available to both.
For a railpack build, the variables enter the Railpack build environment,
where RAILPACK_* variables configure the build. See the
Railpack documentation for the full list.
References are resolved once at apply time and again at deploy time, so the container receives resolved values even for services created imperatively with up service create.