> ## 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.

# Single-runtime image

> See all of the options for controlling your apps and how they're built and deployed on Upsun Cloud.

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>;
};

Use the `.upsun/config.yaml` file,
located at the root of your Git repository, to configure the apps in a single-runtime image.

See a [comprehensive example](/docs/configure-apps#comprehensive-example) of a configuration in
a `.upsun/config.yaml` file.

[Multi-app projects](/docs/configure-apps/multi-app) can be set up in various ways.

## Primary application properties

In the `.upsun/config.yaml` file, configure each application as a unique key beneath the top-level `applications` key.

For example, if your deployed site requires a Javascript `frontend` application container and a Python `backend` application container, the `.upsun/config.yaml` file would start to look something like this:

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
      applications:
        frontend:
          type: 'nodejs:{{version:nodejs:latest}}'
          # Additional frontend configuration
        backend:
          type: 'python:{{version:python:latest}}'
          # Additional backend configuration`
  }
</DynamicCodeBlock>

The following table presents all of the properties available to each unique app beneath the `applications` key.

The **Set in instance** column defines whether the given property can be overridden within a `web` or `workers` instance.
To override any part of a property, you have to provide the entire property.

<Info>
  The `build`, `dependencies`, and `runtime` properties are unique to this image type and are described later in this topic. All other properties are available in both single-runtime and composable images — click a property name to view its details in a separate topic.
</Info>

| Name | Type | Required | Set in instance? | Description |
| - | - | - | - | - |
| [`type`](#type) | A type | Yes | No | The base image to use with a specific app language. Format: `runtime:version`. |
| [`container_profile`](/docs/configure-apps/image-properties/container_profile) | A container profile | | Yes | Determines which combinations of CPU and RAM a container can use. |
| [`relationships`](/docs/configure-apps/image-properties/relationships) | A dictionary of relationships | | Yes | Connections to other services and apps. |
| [`mounts`](/docs/configure-apps/image-properties/mounts) | A dictionary of mounts | | Yes | Directories that are writable even after the app is built. Allocated disk for mounts is defined with a separate resource configuration call using `upsun resources:set`. |
| [`web`](/docs/configure-apps/image-properties/web) | A web instance | | N/A | How the web application is served. |
| [`workers`](/docs/configure-apps/image-properties/workers) | A worker instance | | N/A | Alternate copies of the application to run as background processes. |
| [`timezone`](/docs/configure-apps/timezone) | `string` | | No | The timezone for crons to run. Format: a [TZ database name](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). Defaults to `UTC`, which is the timezone used for all logs no matter the value here. See also [app runtime timezones](/docs/projects/change-project-timezone) |
| [`access`](/docs/configure-apps/image-properties/access) | An access dictionary | | Yes | Access control for roles accessing app environments. |
| [`variables`](/docs/configure-apps/image-properties/variables) | A variables dictionary | | Yes | Variables to control the environment. |
| [`build`](#build) | A build dictionary | | No | What happens when the app is built. |
| [`dependencies`](#dependencies) | A dependencies dictionary | | No | What global dependencies to install before the `build` hook runs. |
| [`hooks`](/docs/configure-apps/image-properties/hooks) | A hooks dictionary | | No | What commands run at different stages in the `build`, `deploy`, and `post_deploy` phases. |
| [`crons`](/docs/configure-apps/image-properties/crons) | A cron dictionary | | No | Scheduled tasks for the app. |
| [`source`](/docs/configure-apps/image-properties/source) | A source dictionary | | No | Details about the app’s source code and available operations. |
| [`runtime`](#runtime) | A runtime dictionary | | No | Customizations to your PHP runtime. |
| [`additional_hosts`](/docs/configure-apps/image-properties/additional_hosts) | An additional hosts dictionary | | Yes | Mappings of hostnames to IP addresses. |
| [`authorizations`](/docs/configure-apps/image-properties/authorizations) | | An authorizations dictionary | Yes | An authorizations list that grants applications and tasks narrowly-scoped access to the environment API and task operations. |
| [`operations`](/docs/configure-apps/runtime-operations) | A dictionary of runtime operations | | No | Runtime operations for the application. |

## Root directory

Some of the properties you can define are relative to your app's root directory.
The root defaults to the root of the repository.

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
      applications:
        frontend:
          type: 'nodejs:{{version:nodejs:latest}}'
          # Default behavior of source.root
          source:
            root: "/"`
  }
</DynamicCodeBlock>

That is, if a custom value for `source.root` is not provided in your configuration, the default behavior is equivalent
to the above.

To specify another directory, for example for a [multi-app project](/docs/configure-apps/multi-app),
use the [`source.root` property](/docs/configure-apps/image-properties/source).

## `type`

<Info>
  To install *multiple* runtimes and tools in your application container, use the Upsun Cloud composable image.<br />
  If you arrived here from another page, you may want to review the supported `stack` key where `type` was referenced.<br />
  For details, see the list of [supported Nix runtimes](/docs/configure-apps/app-reference/composable-image#upsun-supported-nix-runtimes) that you can define in a composable image `stack`.
</Info>

The `type` defines the base container image used to run the application.
The version is the major (`X`) and sometimes minor (`X.Y`) version numbers,
depending on the service, as in the following table.
Security and other patches are taken care of for you automatically.

Available languages and their supported versions:

| Language | Type key | Supported versions |
| - | - | - |
| [C#/.Net Core](/docs/languages/dotnet) | `dotnet` | <span class="runtime-version-badge supported" data-tooltip="version: 10.0.401">10.0</span> <span class="runtime-version-badge supported" data-tooltip="version: 8.0.425">8.0</span> |
| [Elixir](/docs/languages/elixir) | `elixir` | <span class="runtime-version-badge supported" data-tooltip="version: 1.20.4">1.20</span> <span class="runtime-version-badge deprecated" data-tooltip="version: 1.19.5">1.19</span> <span class="runtime-version-badge deprecated" data-tooltip="version: 1.18.5">1.18</span> |
| [Go](/docs/languages/go) | `golang` | <span class="runtime-version-badge supported" data-tooltip="version: 1.27.2">1.27</span> <span class="runtime-version-badge supported" data-tooltip="version: 1.26.9">1.26</span> |
| [Java](/docs/languages/java) | `java` | <span class="runtime-version-badge supported" data-tooltip="version: 25.0.4">25</span> <span class="runtime-version-badge supported" data-tooltip="version: 21.0.12">21</span> |
| [JavaScript/Node.js](/docs/languages/nodejs) | `nodejs` | <span class="runtime-version-badge supported" data-tooltip="version: 26.11.1">26</span> <span class="runtime-version-badge supported" data-tooltip="version: 24.21.0">24</span> <span class="runtime-version-badge deprecated" data-tooltip="version: 22.23.3">22</span> |
| [PHP](/docs/languages/php) | `php` | <span class="runtime-version-badge supported" data-tooltip="version: 8.5.11">8.5</span> <span class="runtime-version-badge supported" data-tooltip="version: 8.4.26">8.4</span> <span class="runtime-version-badge deprecated" data-tooltip="version: 8.3.35">8.3</span> <span class="runtime-version-badge deprecated" data-tooltip="version: 8.2.34">8.2</span> |
| [Python](/docs/languages/python) | `python` | <span class="runtime-version-badge supported" data-tooltip="version: 3.14.8">3.14</span> <span class="runtime-version-badge deprecated" data-tooltip="version: 3.13.16">3.13</span> <span class="runtime-version-badge deprecated" data-tooltip="version: 3.12.15">3.12</span> <span class="runtime-version-badge deprecated" data-tooltip="version: 3.11.16">3.11</span> |
| [Ruby](/docs/languages/ruby) | `ruby` | <span class="runtime-version-badge supported" data-tooltip="version: 4.0.7">4.0</span> <span class="runtime-version-badge supported" data-tooltip="version: 3.4.11">3.4</span> <span class="runtime-version-badge supported" data-tooltip="version: 3.3.12">3.3</span> |
| [Rust](/docs/languages/rust) | `rust` | <span class="runtime-version-badge supported" data-tooltip="version: 1.98.1">1</span> |

### Example configuration

These are used in the format `runtime:version`:

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
      applications:
        <APP_NAME>:
          type: 'php:{{version:php:latest}}'`
  }
</DynamicCodeBlock>

## `build`

The only property of the `build` dictionary is `flavor`, which specifies a default set of build tasks to run.
Flavors are language-specific.

See what the build flavor is for your language:

* [Node.js](/docs/languages/nodejs#dependencies)
* [PHP](/docs/languages/php#dependencies)

In all languages, you can also specify a flavor of `none` to take no action at all
(which is the default for any language other than PHP and Node.js).

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
      applications:
        <APP_NAME>:
          source:
            root: "/"
          type: 'nodejs:{{version:nodejs:latest}}'
          build:
            flavor: none`
  }
</DynamicCodeBlock>

## `dependencies`

Installs global dependencies as part of the build process.
They're independent of the code dependencies of your application
and are available in the `PATH` during the build process and in the runtime environment.
They're installed before the `build` hook runs using a package manager for the language.

| Language | Key name | Package manager |
| - | - | - |
| PHP | `php` | [Composer](https://getcomposer.org/) |
| Python 2 | `python` or `python2` | [Pip 2](https://packaging.python.org/tutorials/installing-packages/) |
| Python 3 | `python3` | [Pip 3](https://packaging.python.org/tutorials/installing-packages/) |
| Ruby | `ruby` | [Bundler](https://bundler.io/) |
| Node.js | `nodejs` | [npm](https://www.npmjs.com/) (see [how to use yarn](/docs/languages/nodejs#use-yarn-as-a-package-manager)) |

The format for package names and version constraints are defined by the specific package manager.

An example of dependencies in multiple languages:

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
      applications:
        <APP_NAME>:
          type: 'nodejs:{{version:nodejs:latest}}'
          source:
            root: "/"
          dependencies:
            php: # Specify one Composer package per line.
              drush/drush: '8.0.0'
            python2: # Specify one Python 2 package per line.
              behave: '*'
              requests: '*'
            python3: # Specify one Python 3 package per line.
              numpy: '*'
            ruby: # Specify one Bundler package per line.
              sass: '3.4.7'
            nodejs: # Specify one NPM package per line.
              pm2: '^4.5.0'`
  }
</DynamicCodeBlock>

## `runtime`

This applies to PHP only.

The following table lists the various possible modifications to your PHP runtime:

| Name | Type | Language | Description |
| - | - | - | - |
| `extensions` | List of `string`s OR [extensions definitions](#extensions) | PHP | [PHP extensions](/docs/languages/php/extensions) to enable. |
| `disabled_extensions` | List of `string`s | PHP | [PHP extensions](/docs/languages/php/extensions) to disable. |
| `request_terminate_timeout` | `integer` | PHP | The timeout (in seconds) for serving a single request after which the PHP-FPM worker process is killed. |
| `sizing_hints` | A [sizing hints definition](#sizing-hints) | PHP | The assumptions for setting the number of workers in your PHP-FPM runtime. |
| `xdebug` | An Xdebug definition | PHP | The setting to turn on [Xdebug](/docs/languages/php/xdebug). |

You can also set your [app's runtime timezone](/docs/configure-apps/timezone).

### Extensions

<Info>
  To install multiple runtimes and tools in your application container, use the Upsun Cloud composable image.<br />
  If you arrived here from another page and you are using the composable image, you enable and disable extensions by using the `stack.runtimes.extensions`/`stack.runtimes.disabled_extensions` keys.<br />
  For details and an example, see the [`stack`](/docs/configure-apps/app-reference/composable-image#stack) section in the "Composable image" topic.
</Info>

You can enable [PHP extensions](/docs/languages/php/extensions) just with a list of extensions:

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
      applications:
        <APP_NAME>:
          type: 'php:{{version:php:latest}}'
          source:
            root: "/"
          runtime:
            extensions:
              - geoip
              - tidy`
  }
</DynamicCodeBlock>

Alternatively, if you need to include configuration options, use a dictionary for that extension:

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
      applications:
        <APP_NAME>:
          type: 'php:{{version:php:latest}}'
          source:
            root: "/"
          runtime:
            extensions:
              - geoip
              - name: blackfire
                configuration:
                  server_id: <SERVER_ID>
                  server_token: <SERVER_TOKEN>`
  }
</DynamicCodeBlock>

In this case, the `name` property is required.

### Sizing hints

The following table shows the properties that can be set in `sizing_hints`:

| Name | Type | Default | Minimum | Description |
| - | - | - | - | - |
| `request_memory` | `integer` | 45 | 10 | The average memory consumed per request in MB. |
| `reserved_memory` | `integer` | 70 | 70 | The amount of memory reserved in MB. |

See more about [PHP-FPM workers and sizing](/docs/languages/php/fpm).

## Resources (CPU, memory, and disk space)

By default, Upsun Cloud assigns a container profile and container size to each application and service on the first deployment of a project. <br />

The container *profile* defines and enforces a specific CPU-to-memory ratio. The default container profile for an app or service in a composable image is `HIGH_CPU`.

Use the Upsun Cloud CLI or Console to manually adjust the allocated container *size* (CPU and memory resources)—that is, to perform a **vertical‑scaling** action. When you redeploy, the container runs with the CPU‑to‑memory ratio defined by its profile, so it enforces the size you specified.

Related topics:

* For detailed steps for changing the container size, see the [Vertical scaling](/docs/manage-resources/adjust-resources#vertical-scaling) section of the "Resource configuration topic.
* For details about container sizes for each resource allocation strategy (shared CPU, guaranteed CPU, and initial allocation), see the [Advanced: Container profiles](/docs/manage-resources/adjust-resources#advanced-container-profiles) section of the "Resource configuration" topic.
* To learn more about general resource management in Upsun Cloud, see the topics in the [Manage resources](/docs/manage-resources) section.

### Downsize a disk

You can reduce the target disk size of an app. Keep in mind:

* Backups created before the downsize are incompatible and cannot be used; you must [create new backups](/docs/environments/backup).
* The downsize will fail if the disk contains more data than the target size.

### Combine single-runtime and composable images

In a [multiple application context](/docs/configure-apps/multi-app),
you can use a mix of single-runtime images
and [composable images](/docs/configure-apps/app-reference/composable-image).

The following sample configuration includes two applications:

* `frontend` – uses a single-runtime image
* `backend` – uses a composable image<br />
  In this app, PHP is the primary runtime and is started automatically (PHP-FPM also starts automatically when PHP is the primary runtime). For details, see the [PHP as a primary runtime](/docs/configure-apps/app-reference/composable-image#php-as-a-primary-runtime) section in the Composable image topic.

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
      applications:
        frontend:
          # this app uses the single-runtime image with a specific node.js runtime
          type: 'nodejs:{{version:nodejs:latest}}'
        backend:
          # this app uses the composable image and specifies two runtimes
          type: "composable:{{version:composable:latest}}"
          stack:
            runtimes:
              - "php@8.4":
                  extensions:
                    - apcu
                    - sodium
                    - xsl
                    - pdo_sqlite
              - "python@3.13"
            packages:
              - "python313Packages.yq" # python package specific`
  }
</DynamicCodeBlock>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.