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

# Mounts

> Directories that are writable even after the app is built.

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

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

Optional in [single-runtime](/docs/configure-apps/app-reference/single-runtime-image#primary-application-properties) and [composable](/docs/configure-apps/app-reference/composable-image#primary-application-properties) images.

After your app is built, its file system is read-only.
To make changes to your app's code, you need to use Git.

For enhanced flexibility, Upsun allows you to define and use writable directories called "mounts".
Mounts give you write access to files generated by your app (such as cache and log files)
and uploaded files without going through Git.

When you define a mount, you are mounting an external directory to your app container,
much like you would plug a hard drive into your computer to transfer data.

<Note>
  * Mounts aren't available during the build.
  * When you [back up an environment](/docs/environments/backup), the mounts on that environment are also backed up.
</Note>

### Define a mount

To define a mount, use the following configuration:

<Tabs>
  <Tab title="Single-runtime image">
    <DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
      {`
              applications:
                <APP_NAME>:
                  type: nodejs:{{version:nodejs:latest}}
                  source:
                    root: "/"
                  mounts:
                    '<MOUNT_PATH>':
                      source: <MOUNT_TYPE>
                      source_path: <SOURCE_PATH_LOCATION>`
          }
    </DynamicCodeBlock>
  </Tab>

  <Tab title="Composable image">
    <DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
      {`
              applications:
                <APP_NAME>:
                  type: "composable:{{version:composable:latest}}"
                  source:
                    root: "/"
                  stack:
                    runtimes: [ "nodejs@{{version:nodejs:latest}}" ]
                  mounts:
                    '<MOUNT_PATH>':
                      source: <MOUNT_TYPE>
                      source_path: <SOURCE_PATH_LOCATION>`
          }
    </DynamicCodeBlock>
  </Tab>
</Tabs>

`<MOUNT_PATH>` is the path to your mount **within the app container** (relative to the app's root).
If you already have a directory with that name, you get a warning that it isn't accessible after the build.
See how to [troubleshoot the warning](/docs/troubleshooting/mounts#overlapping-folders).

| Name          | Type                                                                 | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| ------------- | -------------------------------------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `source`      | `storage`, `instance`, `tmp` (also called `temporary`), or `service` | Yes      | Specifies the type of the mount:<br /><br />- By design, `storage` mounts can be shared between instances of the same app. You can also configure them so they are [shared between different apps](#share-a-mount-between-several-apps).<br /><br />-`instance` mounts are local mounts; set to 8 GB. Unique to your app, they are useful to store files that remain local to the app instance, such as application logs.<br /><br />- `tmp` (or `temporary`) mounts are local ephemeral mounts, where an external directory is mounted to the `/tmp` directory of your app.<br />The content of a `tmp` mount **may be removed during infrastructure maintenance operations**. Therefore, `tmp` mounts allow you to **store files that you’re not afraid to lose**, such as your application cache that can be seamlessly rebuilt.<br />Note that the `/tmp` directory has **a maximum allocation of 8 GB**.<br /><br />- `service` mounts can be useful if you want to explicitly define and use a [Network Storage](/docs/add-services/network-storage) service to share data between different apps (instead of using a `storage` mount). |
| `source_path` | `string`                                                             | No       | Specifies where the mount points **inside the external directory**.<br /><br /> - If you explicitly set a `source_path`, your mount points to a specific subdirectory in the external directory. <br /><br /> - If the `source_path` is an empty string (`""`), your mount points to the entire external directory.<br /><br /> - If you don't define a `source_path`, Upsun uses the `<MOUNT_PATH>` as default value, without leading or trailing slashes.<br />For example, if your mount lives in the `/web/uploads/` directory in your app container, it will point to a directory named `web/uploads` in the external directory.  <br /><br /> **WARNING:** Changing the name of your mount affects the `source_path` when it's undefined. See [how to ensure continuity](#ensure-continuity-when-changing-the-name-of-your-mount) and maintain access to your files.                                                                                                                                                                                                                                                                    |
| `service`     | `string`                                                             |          | The purpose of the `service` key depends on your use case.<br /><br /> In a multi-app context where a `storage` mount is shared between apps, `service` is required. Its value is the name of the app whose mount you want to share. See how to [share a mount between several apps](#share-a-mount-between-several-apps).<br /><br /> In a multi-app context where a [Network Storage service](/docs/add-services/network-storage) (`service` mount) is shared between apps, `service` is required and specifies the name of that Network Storage.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |

The accessibility to the web of a mounted directory depends on the [`web.locations` configuration](/docs/configure-apps/image-properties/web).
Files can be all public, all private, or with different rules for different paths and file types.

Note that when you remove a `tmp` mount from your `.upsun/config.yaml` file,
the mounted directory isn't deleted.
The files still exist on disk until manually removed,
or until the app container is moved to another host during a maintenance operation.

### Example configuration

<Tabs>
  <Tab title="Single-runtime image">
    <DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
      {`
              applications:
                <APP_NAME>:
                  type: "nodejs@{{version:nodejs:latest}}"
                  source:
                    root: "/"
                  mounts:
                    'web/uploads':
                      source: storage
                      source_path: uploads
                    '/.tmp_platformsh':
                      source: tmp
                      source_path: files/.tmp_platformsh
                    '/build':
                      source: storage
                      source_path: files/build
                    '/.cache':
                      source: tmp
                      source_path: files/.cache
                    '/node_modules/.cache':
                      source: tmp
                      source_path: files/node_modules/.cache`
          }
    </DynamicCodeBlock>
  </Tab>

  <Tab title="Composable image">
    <DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
      {`
              applications:
                <APP_NAME>:
                  type: "composable:{{version:composable:latest}}"
                  source:
                    root: "/"
                  stack:
                    runtimes: [ "nodejs@{{version:nodejs:latest}}" ]
                  mounts:
                    'web/uploads':
                      source: storage
                      source_path: uploads
                    '/.tmp_platformsh':
                      source: tmp
                      source_path: files/.tmp_platformsh
                    '/build':
                      source: storage
                      source_path: files/build
                    '/.cache':
                      source: tmp
                      source_path: files/.cache
                    '/node_modules/.cache':
                      source: tmp
                      source_path: files/node_modules/.cache`
          }
    </DynamicCodeBlock>
  </Tab>
</Tabs>

### Ensure continuity when changing the name of your mount

Changing the name of your mount affects the default `source_path`.

Suppose you have a `/my/cache/` mount with an undefined `source_path`:

<Tabs>
  <Tab title="Single-runtime image">
    <DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
      {`
              applications:
                <APP_NAME>:
                  type: "nodejs@{{version:nodejs:latest}}"
                  mounts:
                    '/my/cache/':
                      source: tmp`
          }
    </DynamicCodeBlock>
  </Tab>

  <Tab title="Composable image">
    <DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
      {`
              applications:
                <APP_NAME>:
                  type: "composable:{{version:composable:latest}}"
                  mounts:
                    '/my/cache/':
                      source: tmp`
          }
    </DynamicCodeBlock>
  </Tab>
</Tabs>

If you rename the mount to `/cache/files/`, it will point to a new, empty `/cache/files/` directory.

To ensure continuity, you must explicitly define the `source_path` as the previous name of the mount, without leading
or trailing slashes:

<Tabs>
  <Tab title="Single-runtime image">
    <DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
      {`
              applications:
                <APP_NAME>:
                  type: "nodejs@{{version:nodejs:latest}}"
                  mounts:
                    '/cache/files/':
                      source: tmp
                      source_path: /my/cache`
          }
    </DynamicCodeBlock>
  </Tab>

  <Tab title="Composable image">
    <DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
      {`
              applications:
                <APP_NAME>:
                  type: "composable:{{version:composable:latest}}"
                  mounts:
                    '/cache/files/':
                      source: tmp
                      source_path: /my/cache`
          }
    </DynamicCodeBlock>
  </Tab>
</Tabs>

The `/cache/files/` mount now points to the original `/my/cache/` directory, maintaining access to all your existing
files in that directory.

### Share a mount between several apps

By design, [`storage` mounts](/docs/configure-apps/image-properties/mounts) are shared **between different instances of the same app**,
which enables [horizontal scaling](/docs/manage-resources).

In a [multi-application context](/docs/configure-apps/multi-app),
you can even share a `storage` mount **between different applications** in the same project.

To do so, you need to define a `storage` mount in each of your app containers,
and point each of those mounts to the same shared external network directory.

Use the following configuration:

<Tabs>
  <Tab title="Single-runtime image">
    <DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
      {`
            applications:
              app1:
                type: "nodejs@{{version:nodejs:latest}}"
                mounts:
                  '<MOUNT_PATH_1>':
                    source: storage
                    source_path: <SOURCE_PATH_LOCATION>

              app2:
                type: "nodejs@{{version:nodejs:latest}}"
                mounts:
                  '<MOUNT_PATH_2>':
                    source: storage
                    service: app1
                    source_path: <SOURCE_PATH_LOCATION>`
          }
    </DynamicCodeBlock>
  </Tab>

  <Tab title="Composable image">
    <DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
      {`
              applications:
                app1:
                  type: "composable:{{version:composable:latest}}"
                  mounts:
                    '<MOUNT_PATH_1>':
                      source: storage
                      source_path: <SOURCE_PATH_LOCATION>

                app2:
                  type: "composable:{{version:composable:latest}}"
                  mounts:
                    '<MOUNT_PATH_2>':
                      source: storage
                      service: app1
                      source_path: <SOURCE_PATH_LOCATION>`
          }
    </DynamicCodeBlock>
  </Tab>
</Tabs>

* `<MOUNT_PATH_1>` and `<MOUNT_PATH_2>` are the paths to each mount **within their
  respective app container** (relative to the app's root).
* When configuring the first `storage` mount, you don't need to include the `service` key.
  The first mount implicitly points to an external network directory.
  The `service` key is required for subsequent mounts, to ensure they use the same external network directory as the
  first mount.
* The `source_path` allows you to point each mount to the same subdirectory **within the shared external network
  directory**.

#### Example

Suppose you have a `backend` app and a `frontend` app, and you want both apps to share data from the same mount.<br />
To achieve this, complete the following steps:

1. In your `backend` app configuration, define a `storage` mount:

<Tabs>
  <Tab title="Single-runtime image">
    <DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
      {`
            applications:
              backend:
              type: "nodejs@{{version:nodejs:latest}}"
                mounts:
                  var/uploads: #The path to your mount within the backend app container.
                    source: storage
                    source_path: backend/uploads #The path to the source of the mount within the external network directory.`
          }
    </DynamicCodeBlock>
  </Tab>

  <Tab title="Composable image">
    <DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
      {`
            applications:
              backend:
                type: "composable:{{version:composable:latest}}"
                mounts:
                  var/uploads: #The path to your mount within the backend app container.
                    source: storage
                    source_path: backend/uploads #The path to the source of the mount within the external network directory.`
          }
    </DynamicCodeBlock>
  </Tab>
</Tabs>

This creates a `storage` mount named `var/uploads` in the `backend` app container.
The mount points to the `backend/uploads` directory within an external network directory.

2. In your `frontend` app configuration, define another `storage` mount:

<Tabs>
  <Tab title="Single-runtime image">
    <DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
      {`
             applications:
                backend:
                  type: "nodejs@{{version:nodejs:latest}}"
                  mounts:
                    var/uploads:
                      source: storage
                      source_path: backend/uploads

                frontend:
                  type: "nodejs@{{version:nodejs:latest}}"
                  mounts:
                    web/uploads: #The path to your mount within the frontend app container.
                      source: storage
                      service: backend #The name of the other app, so the mount can point to the same external network directory as that other app's mount.
                      source_path: backend/uploads #The path to the source of the mount within the shared external network directory.`
          }
    </DynamicCodeBlock>
  </Tab>

  <Tab title="Composable image">
    <DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
      {`
             applications:
               backend:
                 type: "composable:{{version:composable:latest}}"
                 mounts:
                   var/uploads:
                     source: storage
                     source_path: backend/uploads

               frontend:
                 type: "composable:{{version:composable:latest}}"
                 mounts:
                   web/uploads: #The path to your mount within the frontend app container.
                     source: storage
                     service: backend #The name of the other app, so the mount can point to the same external network directory as that other app's mount.
                     source_path: backend/uploads #The path to the source of the mount within the shared external network directory.`
          }
    </DynamicCodeBlock>
  </Tab>
</Tabs>

This creates another `storage` mount named `web/uploads` in the `frontend` app container.

The `service` key allows you to specify that the `web/uploads` mount should use the same external network directory
as the mount previously defined in the `backend` app container.

The `source_path` key specifies which subdirectory within the external network directory both mounts should share (
here, the `backend/uploads` directory).

Note that another way to share data between apps through a mount is by
explicitly [defining a Network Storage service](/docs/add-services/network-storage).

### Composable image only: Local mounts

If you need a local mount (i.e. unique per container),
Upsun allows you to mount a directory within the `/tmp` directory of your app.
However, the following limitations apply:

* Content from `tmp` mounts is removed when your app container is moved to another host during an infrastructure
  maintenance operation
* The `/tmp` directory has a [maximum allocation of 8 GB](/docs/troubleshooting/disks#no-space-left-on-device)

Therefore, `tmp` mounts are ideal to store non-critical data, such as your application cache which can be seamlessly
rebuilt,
but aren't suitable for storing files that are necessary for your app to run smoothly.

Note that Upsun will provide new local mounts in the near future.

### Overlapping mounts

The locations of mounts as they are visible to application containers can overlap somewhat.
For example:

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
      applications:
        <APP_NAME>:
          # ...
          mounts:
            'var/cache_a':
              source: storage
              source_path: cacheA
            'var/cache_b':
              source: tmp
              source_path: cacheB
            'var/cache_c':
              source: instance
              source_path: cacheC
    `}
</DynamicCodeBlock>

In this case, it does not matter that each mount is of a different `source` type.
Each mount is restricted to a subfolder within `var`, and all is well.

The following, however, is not allowed and will result in a failure:

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
      applications:
        <APP_NAME>:
          # ...
          mounts:
            'var/':
              source: storage
              source_path: cacheA
            'var/cache_b':
              source: tmp
              source_path: cacheB
            'var/cache_c':
              source: instance
              source_path: cacheC
    `}
</DynamicCodeBlock>

The `storage` mount type specifically exists to share data between instances of the same application, whereas `tmp` and `instance` are meant to restrict data to build time and runtime of a single application instance, respectively.
These allowances are not compatible, and will result in an error if pushed.
