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.
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, but a user key of the same name never overrides them.
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.