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

# Upgrade to a maintained version of a runtime or service

> Upgrade a runtime or service to a maintained version to avoid build failures and stay protected against known CVEs.

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 a maintained runtime or service version — outdated images can fail to build or carry known CVEs. See [Image statuses](#image-statuses) to check your image's status and what to do next.

Once you know you need to upgrade, the steps are the same regardless of which runtime or service you use. For version-specific details, see the docs page for your runtime in [Languages](/docs/languages) or your service in [Add services](/docs/add-services).

## Image statuses

"Maintained" means **Active** or **Supported**, depending on the classification below.

<Note>
  Some runtimes and services (for example, [Redis](/docs/add-services/redis)) use the **Active/Sunset/Decommissioned** classification below.

  Others currently use the **Supported/Deprecated/Retired** classification, until they migrate to **Active/Sunset/Decommissioned**.
</Note>

<Tabs>
  <Tab title="Active/Sunset/Decommissioned">
    The Console shows an image's status as a banner on your project's Overview page, or as a decoration on the relevant activity in the **Activity** panel (active images show no decoration, since they're always supported).

    An image's status shows what's supported and what to do:

    | Image status   | What it means                                                                                                                                                                                                                   | What to do                                                                                           |
    | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
    | Active         | Fully maintained, Upsun applies software and security updates received from upstream.                                                                                                                                           | No action needed.                                                                                    |
    | Sunset         | Existing projects (including code pushes) keep working; new projects can't use this image. Upsun applies only critical security updates from upstream, and the image is decommissioned 180 calendar days after entering Sunset. | Upgrade to an active version before the decommission date (180 calendar days after entering Sunset). |
    | Decommissioned | Upsun no longer supports this image. Existing projects with a decommissioned image continue to run as is. All code pushes are blocked.                                                                                          | Upgrade to an active version to continue deploying.                                                  |
  </Tab>

  <Tab title="Supported/Deprecated/Retired">
    "Image status" is the classification shown on each runtime's ([Languages](/docs/languages)) or service's ([Add services](/docs/add-services)) own docs page:

    | Image status     | What it means                                                                                                                                                                                                         | What to do                                                                       |
    | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
    | Supported        | Fully maintained, Upsun applies software and security updates received from upstream.                                                                                                                                 | No action needed.                                                                |
    | Deprecated       | Still available and functional, but at end of life and no longer receiving security updates from upstream.                                                                                                            | Switch to a supported version to keep receiving security updates.                |
    | Retired          | No longer available from upstream and not receiving further updates. This reflects the upstream project's own status only — Upsun doesn't enforce it, so the image keeps working with no scheduled decommission date. | Upgrade to a supported version, since no further security updates are available. |
    | Decommissioned\* | No longer supported by upstream. This reflects the upstream project's own status only — Upsun doesn't enforce it, so builds and deployments aren't blocked.                                                           | Upgrade to a supported version as soon as possible.                              |

    \* At this time, decommissioned images are not listed in the product docs.
  </Tab>
</Tabs>

## Before you upgrade

Test any version change on a non-production branch before merging.

## Upgrade a runtime

Runtimes are defined by the `type` key on your application in `.upsun/config.yaml`. Updating to a new version means changing that value and pushing it.

1. Check your runtime's docs page in [Languages](/docs/languages) for supported versions — new versions may include breaking changes — then update the `type` key with the correct version number:

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

2. Push to a non-production branch. If you don't already have one, create it first:

```bash theme={null}
upsun branch upgrade-runtime
git add .upsun/config.yaml
git commit -m "Update runtime version"
upsun push
```

Pushing triggers Upsun to automatically build and deploy the environment.

3. Verify that your app builds and behaves correctly. Check the deploy log for errors:

```bash theme={null}
upsun activity:log
```

Then open the environment and test your app manually:

```bash theme={null}
upsun environment:url --primary
```

4. Merge to production:

```bash theme={null}
upsun merge
```

If your project uses a source integration (GitHub, GitLab, Bitbucket), `upsun merge` isn't available — merge through a pull/merge request in your Git provider instead.

## Upgrade a service

Services are defined under the `services:` key in `.upsun/config.yaml`. Updating the `type` value triggers a version change on the next deploy. Whether data migrates automatically depends on the service. Check [Add services](/docs/add-services) before you begin.

### In-place upgrade

Some services upgrade automatically when you change the version. PostgreSQL 10 and later, for example, include a built-in upgrade utility that runs at deploy time.

1. Update the `type` key for your service:

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
      services:
        database:
          type: postgresql:{{version:postgresql:latest}}`
  }
</DynamicCodeBlock>

2. Push to a non-production branch. If you don't already have one, create it first:

```bash theme={null}
upsun branch upgrade-service
git add .upsun/config.yaml
git commit -m "Update service version"
upsun push
```

Pushing triggers Upsun to automatically build and deploy the environment.

3. Confirm the service starts and your app connects. Check the deploy log for errors:

```bash theme={null}
upsun activity:log
```

Then open the environment and test your app manually:

```bash theme={null}
upsun environment:url --primary
```

4. Create a production backup, then merge to production:

```bash theme={null}
upsun backup:create --environment main
upsun merge
```

If your project uses a source integration (GitHub, GitLab, Bitbucket), `upsun merge` isn't available — merge through a pull/merge request in your Git provider instead.

Downgrading is not supported after an in-place upgrade. If you need to roll back, restore from a backup.

### Manual data migration

When a service doesn't support in-place upgrades (for example, if you use composable image or Docker images), or when you're moving across several major versions, you need to export your data, provision a new service at the target version, and import.

1. [Export your data](/docs/core-concepts/common-tasks/exporting) from the current service.

2. Rename the service in `.upsun/config.yaml` and set the target version. Renaming forces the platform to create a fresh service container.

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
      services:
        database-target:
          type: postgresql:{{version:postgresql:latest}}`
  }
</DynamicCodeBlock>

3. Update the `relationships` in any application that references the old service name:

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
      applications:
        myapp:
          relationships:
            database:
              service: database-target
              endpoint: postgresql`
  }
</DynamicCodeBlock>

4. Push to a non-production branch and import your data into the new service.

5. Verify your app works correctly, then merge to production.
