Skip to main content

Environment Variables

Environment variables are defined under a variables key. The keys you provide are the names of the environment variables.

A variable is either a plain string or an object, when you need to control build-time availability or mount it as a file.

A top-level variables applies to your whole application, and you can narrow that per resource; see Scoping environment variables.

variables:
  APP_ENV: production
  API_KEY:
    gitlab: NAME_OF_SECRET_IN_GITLAB
  APP_SERVICE_ACCOUNT_JSON:
    value: '{"type": "service_account"}'
    file: google-creds.json

variables replaces env, the original name for this key. env still works today, but it will change in a future release, rename the properties whenever possible. Renaming the key is the whole migration: nothing inside it changes, and your running application sees the same variables. If both keys are present, the value under variables are taken over env.

Options

These are the keys of the object form. Set either value or gitlab, never both.

Property Type Description Default
value string, number or boolean The literal value of the variable. See String values. None
gitlab string, object or null Read the value from a GitLab CI/CD variable instead of writing it here. As a string, the name of that variable. See GitLab CI/CD secrets. The name of the variable itself
gitlab.name string The name of the GitLab CI/CD variable to read, written as an object rather than a bare string. The name of the variable itself
build boolean or 'only' Whether the variable is also available while your image is built. true gives you it at build time and at run time, 'only' at build time alone. See Build-time variables. false
file string A filename. The value is written to a file with this name, and the variable holds the path to it. See Variables as files. None
token object An identity token for your application, mounted as a file. The variable holds the path to that file. Cannot be combined with value, gitlab, file or build. See Identity tokens. None
token.audience string The service the token is intended for. Required when token is set. None
token.ttl integer or string How long each issued token is valid for, as seconds or a duration such as 2h. Values below 600 seconds are rejected. See Identity tokens. 3600

String values

The simplest way to define an environment variable is by providing its name as the key and a string as the value.

variables:
  APP_ENV: production
  LOG_CHANNEL: stack

Alternatively, you can define a basic variable using an object with just the value key. This is functionally identical to the string format:

variables:
  DATABASE_HOST:
    value: mariadb-p-123.db.ondigitalocean.com

Object values

For scenarios requiring more control, such as making variables available only during build-time or mounting their content as files, you must use the object format.

variables:
  MY_VARIABLE:
    value: my-value
    build: false
    file: my-variable.txt

Build-time variables

This variable will be available during the build stage in GitLab CI/CD, but not in the final running container.

variables:
  ASSET_COMPILATION_FLAG:
    value: "true"
    build: only

Variables as files

This is useful for long configuration strings or multi-line values. The APP_SERVICE_ACCOUNT_JSON environment variable will be set to the absolute path of the created file.

variables:
  APP_SERVICE_ACCOUNT_JSON:
    value: '{"type": "service_account", ...}'
    file: google-creds.json

Identity tokens

A token gives your application a short-lived signed token it can present to another service, without you storing or rotating a credential. The token is written to a file and the variable holds the path to it, so your application reads the file when it needs the current token.

The token is replaced automatically before it expires, so read it from the file each time you use it rather than caching it for the lifetime of the process.

variables:
  BILLING_TOKEN_PATH:
    token:
      audience: billing-api

audience names the service you intend to present the token to, and it is required. It is what stops a token issued for one service being accepted by another, so give each service its own audience rather than reusing one everywhere.

Verifying the token

A token is only worth as much as the check the receiving service performs on it. That service must:

  • Verify the signature against the OIDC discovery endpoint for your environment. Contact Cloudbear to get it; it is not something you configure yourself.
  • Check the aud claim matches the audience it expects. Without this check any token from your environment is accepted, including one your application minted for an entirely different service.

A service that skips either step is trusting anything presented to it.

The token identifies your environment, not an individual application within it. Do not use it to tell two of your applications in the same environment apart.

Token lifetime

Each token is valid for an hour by default. Set ttl to change that, either as a number of seconds or as a duration such as 30m or 2h:

variables:
  BILLING_TOKEN_PATH:
    token:
      audience: billing-api
      ttl: 30m

Values below 600 seconds are rejected. A shorter lifetime does not mean your application has to do anything differently: the token is replaced before it expires either way, so read it from the file each time you use it.

Several tokens

Each token gets its own file, so you can declare several for different services:

variables:
  BILLING_TOKEN_PATH:
    token:
      audience: billing-api
  REPORTING_TOKEN_PATH:
    token:
      audience: reporting-api

token cannot be combined with value, gitlab, file or build.

Scoping environment variables

A top-level variables is global: its variables are added to every part of your application: the HTTP server, workers, cronjobs, migrations, and remote access.

In addition, you can define variables on an individual resource. Those variables are added only to that resource, on top of the global ones. This lets you give a specific worker, cronjob, or the HTTP server its own variables without affecting the others.

These are the places variables can be written:

Key What it applies to
variables Every part of your application.
http.variables The web server. See HTTP.
workers.<name>.variables One worker. See Workers.
cronjobs.<name>.variables One scheduled task. See Cronjobs.
migration.variables The migration that runs before a deployment goes live.
access.variables Remote access sessions. See Remote Access.
loadGenerations.<name>.variables One load generation run.
metrics.variables The metrics servers. See The metrics servers.
# Global: available everywhere.
variables:
  APP_ENV: production
  LOG_CHANNEL: stack

http:
  variables:
    # Only on the HTTP server.
    SERVE_STATIC: "true"

workers:
  emails:
    variables:
      # Only on the "emails" worker.
      MAIL_BATCH_SIZE: "50"

When the same variable name is defined both globally and on a resource, the resource's value wins for that resource. Every value format described below (value, file, gitlab, token, and connection templating) works the same whether the variable is global or scoped to a resource.

The metrics servers

The servers metrics are collected from run the same image as your other web servers and usually need the same configuration, so they inherit http.variables rather than starting empty, the same way they inherit http.resources.

Variables of their own are added on top of what they inherit, and a variable named in both takes the value written here:

http:
  variables:
    SERVE_STATIC: "true"
    APP_DEBUG: "false"

metrics:
  enable: true
  variables:
    # SERVE_STATIC is still inherited; APP_DEBUG takes the value below.
    METRICS_TOKEN: "{{ svc.db.conn.default.password }}"
    APP_DEBUG: "true"

Configuring metrics on an http group instead works the same way: the group's own variables are added on top of the ones it inherits from http. See Configure Metrics.

Build-time variables (build: true or build: only) are only read from the top-level variables. A build variable placed inside a resource's variables is applied to that resource at run-time but is not made available to the GitLab CI/CD pipeline. Define build-time variables at the top level.

GitLab CI/CD secrets

You can securely inject secrets defined in your GitLab project's CI/CD settings (Settings > CI/CD > Variables). To do this, use the gitlab key instead of value.

Environment variables should not be set to Hidden in GitLab. We are unable to read these variables at the current time.

Naming the secret

You can specify the name of the secret from GitLab in two ways:

1. As a string:

variables:
  MY_SECRET_KEY:
    gitlab: NAME_OF_SECRET_IN_GITLAB

2. As an object with a name property:

variables:
  MY_SECRET_KEY:
    gitlab:
      name: NAME_OF_SECRET_IN_GITLAB

Implicit names

If you define the gitlab key but leave it null (~) or omit the name property, the name of your environment variable itself will be used to look up the secret in GitLab.

This is useful for maintaining consistency between your cap config file and GitLab CI/CD variables.

variables:
  # This will look for a GitLab secret named "API_KEY"
  API_KEY:
    gitlab: ~

  # This will also look for a GitLab secret named "ANOTHER_SECRET"
  ANOTHER_SECRET:
    gitlab: {}

Secrets as files

You can also mount GitLab secrets as files by providing the file property with a filename.

variables:
  SSH_PRIVATE_KEY:
    # Looks for a GitLab secret named "CI_SSH_PRIVATE_KEY"
    gitlab: CI_SSH_PRIVATE_KEY
    # The SSH_PRIVATE_KEY env var will contain the absolute path to the secret file
    file: id_rsa

Connection string templating

You can build a value out of a service's own hostname, port and credentials instead of hardcoding them:

variables:
  DATABASE_URL:
    value: "mariadb://{{ svc.db.conn.default.username }}:{{ svc.db.conn.default.password }}@{{ svc.db.conn.default.hostname }}:{{ svc.db.conn.default.port }}/{{ svc.db.conn.default.database }}"

See Connections for the syntax and every value available per service.