> ## Documentation Index
> Fetch the complete documentation index at: https://developer.upsun.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Variables overview

> Variables give you control over your project's build process and runtime environment. You can set them in your code to make changes across your project or independent of the code for environment-specific settings.


export const DynamicCodeBlock = ({language = 'yaml', filename, icon, lines, wrap, expandable, highlight, focus, children}) => {
  const STORAGE_KEY = 'upsun_versions_cache';
  const COMPOSABLE_STORAGE_KEY = 'upsun_composable_cache';
  const CACHE_TTL = 5 * 60 * 1000;
  const API_URL = 'https://meta.upsun.com/images';
  const COMPOSABLE_API_URL = 'https://meta.upsun.com/composable';
  const DEBUG_PREFIX = '[DynamicCodeBlock cache]';
  const [versionData, setVersionData] = useState(null);
  const [versionError, setVersionError] = useState(false);
  const [composableData, setComposableData] = useState(null);
  const [composableError, setComposableError] = useState(false);
  useEffect(() => {
    const fetchData = async () => {
      let cachedData = null;
      let cachedEtag = null;
      if (typeof localStorage !== 'undefined') {
        try {
          const cached = localStorage.getItem(STORAGE_KEY);
          if (cached) {
            const parsed = JSON.parse(cached);
            cachedData = parsed?.data || null;
            cachedEtag = parsed?.etag || null;
            if (cachedData && Date.now() - parsed.timestamp < CACHE_TTL) {
              return cachedData;
            }
          }
        } catch (err) {
          console.error('Failed to load from cache:', err);
        }
      }
      const requestHeaders = cachedEtag ? {
        'If-None-Match': cachedEtag
      } : {};
      console.debug(`${DEBUG_PREFIX} revalidating`, {
        storageKey: STORAGE_KEY,
        hasCachedData: Boolean(cachedData),
        hasCachedEtag: Boolean(cachedEtag)
      });
      const response = await fetch(API_URL, {
        headers: requestHeaders
      });
      if (response.status === 304 && cachedData) {
        console.debug(`${DEBUG_PREFIX} revalidated (304)`, {
          storageKey: STORAGE_KEY
        });
        if (typeof localStorage !== 'undefined') {
          try {
            const etag = response.headers.get('etag') || cachedEtag;
            localStorage.setItem(STORAGE_KEY, JSON.stringify({
              data: cachedData,
              etag,
              timestamp: Date.now()
            }));
          } catch (err) {
            console.error('Failed to refresh cache metadata:', err);
          }
        }
        return cachedData;
      }
      if (!response.ok) throw new Error(`API request failed: ${response.statusText}`);
      const data = await response.json();
      const etag = response.headers.get('etag');
      console.debug(`${DEBUG_PREFIX} refreshed (200)`, {
        storageKey: STORAGE_KEY,
        etag
      });
      if (typeof localStorage !== 'undefined') {
        try {
          localStorage.setItem(STORAGE_KEY, JSON.stringify({
            data,
            etag,
            timestamp: Date.now()
          }));
        } catch (err) {
          console.error('Failed to cache data:', err);
        }
      }
      return data;
    };
    fetchData().then(data => setVersionData(data)).catch(err => console.error('Failed to fetch version data:', err));
  }, []);
  const findHighestVersion = versionsMap => {
    if (!versionsMap || Object.keys(versionsMap).length === 0) return null;
    const entries = Object.entries(versionsMap);
    const active = entries.filter(([, v]) => v.upsun && v.upsun.status === 'supported' || v.upsun && v.upsun.status === 'deprecated');
    const candidates = active.length > 0 ? active : entries;
    let [highestName] = candidates[0];
    for (let i = 1; i < candidates.length; i++) {
      const [currentName] = candidates[i];
      const cp = currentName.split('.').map(Number);
      const hp = highestName.split('.').map(Number);
      for (let j = 0; j < Math.max(cp.length, hp.length); j++) {
        if ((cp[j] || 0) > (hp[j] || 0)) {
          highestName = currentName;
          break;
        } else if ((cp[j] || 0) < (hp[j] || 0)) {
          break;
        }
      }
    }
    return highestName;
  };
  const getVersion = (lang, requestedVersion = 'latest') => {
    if (lang === 'composable') {
      if (!composableData || !composableData.versions || Object.keys(composableData.versions).length === 0) return null;
      if (requestedVersion && requestedVersion !== 'latest') {
        return (requestedVersion in composableData.versions) ? requestedVersion : null;
      }
      return findHighestVersion(composableData.versions);
    }
    if (!versionData) return null;
    const imageData = versionData[lang];
    if (!imageData || !imageData.versions || Object.keys(imageData.versions).length === 0) {
      return null;
    }
    if (requestedVersion && requestedVersion !== 'latest') {
      return (requestedVersion in imageData.versions) ? requestedVersion : null;
    }
    return findHighestVersion(imageData.versions);
  };
  let code = typeof children === 'string' ? children : String(children || '');
  const codeLines = code.split('\n');
  while (codeLines.length > 0 && codeLines[0].trim() === '') codeLines.shift();
  while (codeLines.length > 0 && codeLines[codeLines.length - 1].trim() === '') codeLines.pop();
  if (codeLines.length > 0) {
    const indents = codeLines.filter(line => line.trim().length > 0).map(line => line.match(/^[ \t]*/)[0].length);
    const minIndent = Math.min(...indents);
    code = codeLines.map(line => line.slice(minIndent)).join('\n');
  }
  code = code.replace(/\{\{version:(.*?)\}\}/g, (match, params) => {
    const parts = params.split(':');
    const lang = parts[0];
    const ver = parts[1] || 'latest';
    const isComposable = lang === 'composable';
    const hasError = isComposable ? composableError : versionError;
    const dataReady = isComposable ? composableData !== null : versionData !== null;
    if (hasError) return '(unavailable)';
    if (dataReady) {
      const resolvedVersion = getVersion(lang, ver);
      return resolvedVersion || match;
    }
    return '...';
  });
  const codeBlockProps = {
    language,
    ...filename && ({
      filename
    }),
    ...icon && ({
      icon
    }),
    ...lines !== undefined && ({
      lines
    }),
    ...wrap !== undefined && ({
      wrap
    }),
    ...expandable !== undefined && ({
      expandable
    }),
    ...highlight && ({
      highlight
    }),
    ...focus && ({
      focus
    })
  };
  return <CodeBlock {...codeBlockProps}>{code}</CodeBlock>;
};

export const VariableBlock = ({name}) => {
  return <var spellCheck={false} title={`Replace '${name}' with your own data`}>{name}</var>;
};

export const MetaImageVersion = ({language, version}) => {
  const [selectedVersion, setSelectedVersion] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  const isComposable = language === 'composable';
  const STORAGE_KEY = isComposable ? 'upsun_composable_cache' : 'upsun_versions_cache';
  const CACHE_TTL = 5 * 60 * 1000;
  const API_URL = isComposable ? 'https://meta.upsun.com/composable' : 'https://meta.upsun.com/images';
  const findHighestVersion = versionsMap => {
    if (!versionsMap || Object.keys(versionsMap).length === 0) return null;
    const entries = Object.entries(versionsMap);
    const active = entries.filter(([, v]) => v.upsun && v.upsun.status === 'supported' || v.upsun && v.upsun.status === 'deprecated');
    const candidates = active.length > 0 ? active : entries;
    let [highestName] = candidates[0];
    for (let i = 1; i < candidates.length; i++) {
      const [currentName] = candidates[i];
      const cp = currentName.split('.').map(Number);
      const hp = highestName.split('.').map(Number);
      for (let j = 0; j < Math.max(cp.length, hp.length); j++) {
        if ((cp[j] || 0) > (hp[j] || 0)) {
          highestName = currentName;
          break;
        } else if ((cp[j] || 0) < (hp[j] || 0)) {
          break;
        }
      }
    }
    return highestName;
  };
  useEffect(() => {
    if (!language) {
      setLoading(false);
      return;
    }
    setLoading(true);
    setError(null);
    const fetchData = async () => {
      let cachedData = null;
      let cachedEtag = null;
      if (typeof localStorage !== 'undefined') {
        try {
          const cached = localStorage.getItem(STORAGE_KEY);
          if (cached) {
            const parsed = JSON.parse(cached);
            cachedData = parsed?.data || null;
            cachedEtag = parsed?.etag || null;
            if (cachedData && Date.now() - parsed.timestamp < CACHE_TTL) return cachedData;
          }
        } catch (error_) {
          console.error('Failed to load from cache:', error_);
        }
      }
      const requestHeaders = cachedEtag ? {
        'If-None-Match': cachedEtag
      } : {};
      const response = await fetch(API_URL, {
        headers: requestHeaders
      });
      if (response.status === 304 && cachedData) {
        if (typeof localStorage !== 'undefined') {
          try {
            const etag = response.headers.get('etag') || cachedEtag;
            localStorage.setItem(STORAGE_KEY, JSON.stringify({
              data: cachedData,
              etag,
              timestamp: Date.now()
            }));
          } catch (error_) {
            console.error('Failed to refresh cache metadata:', error_);
          }
        }
        return cachedData;
      }
      if (!response.ok) throw new Error(`API request failed: ${response.statusText}`);
      const data = await response.json();
      const etag = response.headers.get('etag');
      if (typeof localStorage !== 'undefined') {
        try {
          localStorage.setItem(STORAGE_KEY, JSON.stringify({
            data,
            etag,
            timestamp: Date.now()
          }));
        } catch (error_) {
          console.error('Failed to cache data:', error_);
        }
      }
      return data;
    };
    fetchData().then(data => {
      if (!data) {
        setSelectedVersion(null);
        setLoading(false);
        return;
      }
      const imageData = isComposable ? data : data[language];
      if (!imageData || !imageData.versions || Object.keys(imageData.versions).length === 0) {
        setSelectedVersion(null);
        setLoading(false);
        return;
      }
      let versionName = null;
      if (version && version !== 'latest') {
        versionName = (version in imageData.versions) ? version : null;
      } else {
        versionName = findHighestVersion(imageData.versions);
      }
      setSelectedVersion(versionName);
      setLoading(false);
    }).catch(error_ => {
      console.error('MetaImageVersion error:', error_);
      setError(error_.message);
      setLoading(false);
    });
  }, [language, version]);
  if (loading) return <span>…</span>;
  if (error) return <span title={error}>⚠ unavailable</span>;
  if (!selectedVersion) return <span>No version found</span>;
  return <span>{selectedVersion}</span>;
};

In this way, your app has additional information, such as database credentials, the host or port it can use, and which server to connect to.

## Variable types

You can set variables at different levels.
All variables can be strings or base64-encoded JSON-serialized values.

The following table defines what types of variables are available to you:

| Type                                                                                           | Definer     | Scope       | Precedence | Build | Runtime | Uses                                                                                                   |
| ---------------------------------------------------------------------------------------------- | ----------- | ----------- | ---------- | ----- | ------- | ------------------------------------------------------------------------------------------------------ |
| [Application](/docs/development/variables/set-variables#set-variables-in-your-app)             | Application | Application | 4          | Yes   | Yes     | Non-secret values that are the same across all environments                                            |
| [Project](/docs/development/variables/set-variables#create-project-variables)                  | User        | Project     | 3          | Yes   | Yes     | Secret values that are the same across all environments, such as database credentials                  |
| [Environment](/docs/development/variables/set-variables#create-environment-specific-variables) | User        | Environment | 2          | Some  | Yes     | Values that vary by environment, such as which database to connect to or which payment API keys to use |
| [Upsun](/docs/development/variables/use-variables#use-provided-variables)                      | Pre-defined | Environment | 1          | Some  | Yes     | For information about your Upsun project                                                               |

If there are conflicts between variables with the same name, variables [take precedence](#overrides) from 1 down.
So Upsun-provided values (1) override environment variables (2), which override project variables (3),
which override application-provided variables (4).

All of the variables can also be [overridden via a script](/docs/development/variables/set-variables#set-variables-via-script).

### Choosing a variable type

Choose how to set the variable based on what you are trying to do.

Some environment variables should be the same for all environments.
For example:

* Build tool versions.
  If you have scripts that use specific versions of build tools (such as a [specific Node.js version](/docs/languages/nodejs/node-version)),
  You want the tools to be versioned along with your code so you can track the impact of changes.
  Set those variables [in the application](/docs/development/variables/set-variables#set-variables-in-your-app).
* Credentials for common services.
  If you have credentials for services shared across your environments,
  you don't want to commit these secrets to code.
  Set them as sensitive [project variables](/docs/development/variables/set-variables#create-project-variables).

Other configurations should vary between environment types.
For example:

* Service configuration for databases and such.
  This information be read from the [service environment variables](/docs/development/variables#service-environment-variables),
  or the Upsun-provided [`PLATFORM_RELATIONSHIPS` variable](/docs/development/variables/use-variables#use-provided-variables).
  It varies by environment automatically.
* Mode toggles such as enabling `debug` mode, disabling certain caches, and displaying more verbose errors.
  This information might vary by environment type and should be set on the [environment level](/docs/development/variables/set-variables#create-environment-specific-variables).
* API keys for remote services, especially payment gateways.
  If you have a different payment gateway for production and for testing,
  set its keys on the [environment level](/docs/development/variables/set-variables#create-environment-specific-variables).

## Overrides

If the names of variables at different levels match,
an environment variable overrides a variable with the same name in a parent environment
and both override a project variable.
All variables can also be [overridden via script](/docs/development/variables/set-variables#set-variables-via-script).

For an example of how the different levels work,
suppose you have the following inheritable variables defined for the `main` environment:

```sh theme={null}
upsun var -e main
Variables on the project Example (abcdef123456), environment main:
+----------------+-------------+--------+---------+
| Name           | Level       | Value  | Enabled |
+----------------+-------------+--------+---------+
| system_name    | project     | Spiffy |         |
| system_version | project     | 1.5    |         |
| api_key        | environment | abc123 | true    |
| debug_mode     | environment | 1      | true    |
+----------------+-------------+--------+---------+
```

And the following variables defined for the `feature-x` environment, a child environment of `main`:

```sh theme={null}
upsun var -e feature-x
Variables on the project Example (abcdef123456), environment feature-x:
+----------------+-------------+--------+---------+
| Name           | Level       | Value  | Enabled |
+----------------+-------------+--------+---------+
| system_name    | project     | Spiffy |         |
| system_version | project     | 1.5    |         |
| api_key        | environment | def456 | true    |
| system_version | environment | 1.7    | true    |
+----------------+-------------+--------+---------+
```

In the `main` environment, you can access `$PLATFORM_VARIABLES`:

```bash theme={null}
echo $PLATFORM_VARIABLES | base64 --decode | jq
```

The output looks like this:

```json theme={null}
{
    "system_name": "Spiffy",
    "system_version": "1.5",
    "api_key": "abc123",
    "debug_mode": "1"
}
```

While in the `feature-x` environment, it looks like this:

```json theme={null}
{
    "system_name": "Spiffy",
    "system_version": "1.7",
    "api_key": "def456",
    "debug_mode": "1"
}
```

To get a visual overview of which variables are overridden in an environment,
navigate in the Console to that environment's variables settings.
This example shows how it looks within the `feature-x` environment:

{/* spelling turned off because of the "api_key" */}

<img src="https://mintcdn.com/upsun-c9761871/tdb1nLmHEPAmx6I9/images/management-console/variables-overridden.png?fit=max&auto=format&n=tdb1nLmHEPAmx6I9&q=85&s=0188074c26874bff10f7d5d8618228d5" alt="The Console showing the variables split into environment and project ones, with the environment variables api_key and system_version labeled as overridden and debug_mode as inherited the project variable system_version labeled as inactive." width="2844" height="1978" data-path="images/management-console/variables-overridden.png" />

Project variables that conflict with environment variables are labeled as **Inactive**.
Environment variables are labeled as **Inherited** when they get their value from a parent environment
and as **Overridden** when there is a conflict and the parent environment's value doesn't apply.

## Variable prefixes

Certain variable name prefixes have special meanings.
Some are defined by Upsun and apply automatically.
Others are just available as a convention for your application code to follow.

### Top-level environment variables

By default, project and environment variables are only added to the `PLATFORM_VARIABLES` environment variable.
You can also expose a variable as its own environment variable by giving it the prefix `env:`.

For example, the variable `env:foo` creates an environment variable called `FOO`.
(Note the automatic upper-casing.)

```bash theme={null}
upsun variable:create --name env:foo --value bar
```

You can then access that variable directly in your app container:

```bash theme={null}
echo $FOO
bar
```

Note that environment variables with the `env:` prefix aren't added to the `PLATFORM_VARIABLES` environment variable.

### PHP-specific variables

Any variable with the prefix `php` is added to the PHP configuration for all PHP-based application containers in the project.

Using variables, you can use the same files for all your environments and override values on any given environment if needed.

You can set the PHP memory limit to 256 MB on a specific environment by running the following [CLI command](/cli):

```bash theme={null}
upsun variable:create --level environment --prefix php --name memory_limit --value 256M --environment <ENVIRONMENT_NAME>
```

To use variables across environments, set them in your [app configuration](/docs/configure-apps).
For example, to change the PHP memory limit for all environments, use the following configuration:

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
      applications:
        <APP_NAME>:
          variables:
            php:
              memory_limit: "256M"`
  }
</DynamicCodeBlock>

## Framework-specific variables

For specific frameworks, you can implement logic to override global configurations with [environment-specific variables](/docs/development/variables/set-variables#create-environment-specific-variables).
So you can use the same codebase and settings for all your environments,
but still adapt the behavior to each environment.

## Service environment variables

For each service defined via a relationship to your application,
Upsun automatically generates corresponding environment variables within your application container,
in the `$<RELATIONSHIP-NAME>_<SERVICE-PROPERTY>` format.
All the non-alphanumerical or `_` characters (`[^0-9A-Z_]`) are transformed into an `_` , such as `MY_DATABASE_PASSWORD` for a `my-database` relationship name.

**Example:**

For a relationship named `database` to a service named `postgresql`,
the following environment variables are automatically generated in your `myapp` container:

<DynamicCodeBlock language="bash">
  {`
      DATABASE_URL=pgsql://main:main@postgresql.internal:5432/main
      DATABASE_INSTANCE_IPS=['123.456.78.901']
      DATABASE_SERVICE=database
      DATABASE_QUERY={'is_master': True}
      DATABASE_CLUSTER=oiaenewa6sfiq-test-services-afdwftq
      DATABASE_HOST_MAPPED=false
      DATABASE_NAME=main
      DATABASE_FRAGMENT=
      DATABASE_PATH=main
      DATABASE_SCHEME=pgsql
      DATABASE_EPOCH=0
      DATABASE_PORT=5432
      DATABASE_HOSTNAME=azertyuiop1234567890.database.service._.eu-3.platformsh.site
      DATABASE_TYPE=postgresql:{{version:postgresql:latest}}
      DATABASE_PUBLIC=false
      DATABASE_PASSWORD=main
      DATABASE_IP=123.456.78.901
      DATABASE_USERNAME=main
      DATABASE_HOST=postgresql.internal
      DATABASE_REL=postgresql
    `
  }
</DynamicCodeBlock>
