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

# Deploy Next.js on Upsun

> Complete the last required steps to successfully deploy Next.js on Upsun.


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 GuidesRequirements = ({name}) => {
  const isSymfony = name === "Symfony";
  return <>
      <h2>Before you begin</h2>
      <p>You need:</p>
      <ul>
        <li>
          <a href="https://git-scm.com/downloads">Git</a>.{' '}
          Git is the primary tool to manage everything your app needs to run.
          Push commits to deploy changes and control configuration through YAML files.
          These files describe your infrastructure, making it transparent and version-controlled.
        </li>
        <li>
          An Upsun account.{' '}
          If you don't already have one, <a href="https://auth.upsun.com/register">register for a trial account</a>.{' '}
          You can sign up with an email address or an existing GitHub, Bitbucket, or Google account.
          If you choose one of these accounts, you can set a password for your Upsun account later.
        </li>
        <li>
          The {isSymfony ? <a href="https://symfony.com/download">Symfony CLI</a> : <a href="/cli">Upsun CLI</a>}.{' '}
          This lets you interact with your project from the command line.
          You can also do most things through the <a href="/docs/administration/web">Web Console</a>.
        </li>
      </ul>
    </>;
};

<Info>
  <h4>Note</h4>
  Before you start, check out the [Upsun demo app](https://console.upsun.com/projects/create-project) and the main [Getting started guide](/docs/get-started/here).
  They provide all of the core concepts and common commands you need to know before using the materials below.
</Info>

<GuidesRequirements name="Next.js" />

## 1. Create a Next.js app

To create your Next.js app, follow these steps.

1. Follow the Next.js [installation guide](https://nextjs.org/docs/getting-started/installation).
   To fast track the process, run the following commands:

   ```bash Terminal theme={null}
   npx create-next-app@latest myapp
   ```

2. To initialize the local Git repository and commit local files, run the following commands:

   ```bash Terminal theme={null}
   cd myapp
   git init
   git add .
   git commit -m "Init Next.js application."
   ```

<Info>
  You can view the running app locally by running `npm run dev`.
</Info>

## 2. Create a new project

To create a project on Upsun,
follow these steps.

<Note>
  <h4>Remember</h4>
  After creating your Upsun project, copy your new **project ID** for later use.
</Note>

<Tabs>
  <Tab title="Using the CLI">
    To create a new project with the Upsun CLI, use the following command and follow the prompts:

    ```bash Terminal theme={null}
    upsun project:create
    ```

    <Note>
      When creating a new project using the Upsun CLI command `project:create`,
      you are asked if you want to set the local remote to your new project. Enter **Yes (y)**.

      Your local source code is automatically linked to your newly created Upsun project
      through the creation of a `.upsun/local/project.yaml`.
      This file contains the corresponding `<projectId>` for the Upsun CLI to use,
      and sets a Git remote to `upsun`.
    </Note>
  </Tab>

  <Tab title="Using the Console">
    1. Create an organization or select an existing one.

    2. Click **Create from scratch**.

    3. Fill in details like the project name and [region](/docs/development/regions).

           <Note>
             You can define resources for your project later on, after your first push.
           </Note>

    4. To link your local source code to your new Upsun project,
       run the following command:

       ```bash Terminal theme={null}
       upsun project:set-remote <projectId>
       ```

       This command adds a new remote called `upsun` to your local Git repository,
       which is equivalent to the following commands:

       ```bash Terminal theme={null}
       git remote
       origin
       upsun
       ```

       It also creates a new `.upsun/local/project.yaml` file that contains the `<projectId>`
       for the `upsun` CLI to use.

           <Tip>
             If you forget your `<projectId>`, run the following command and find your project in the list:

             ```bash Terminal theme={null}
             upsun project:list
             ```
           </Tip>
  </Tab>
</Tabs>

## 3. Choose your Git workflow

You can use Upsun projects as a classic Git repository,
where you are able to push your source code in different ways,
using either the Git CLI or the Upsun CLI.
You can choose which way —or Git workflow— you want to use for your project from the following options:

* Your project source code is **hosted on an Upsun Git repository**
* Your project source code is **hosted on your own GitHub repository**

<Tabs>
  <Tab title="Upsun Git repository">
    For the rest of this guide, you will use the normal Git workflow (`git add . && git commit -m "message" && git push upsun`) to commit your source code changes to Git history.
    You will also use the Upsun CLI to deploy your [Upsun environment](/docs/environments) with the latest code updates.
  </Tab>

  <Tab title="GitHub repository">
    Upsun provides a [Github integration](/docs/integrations/source/github) that allows your Upsun project to be fully integrated with your Github repository.
    This enables you, as a developer, to use a normal Git workflow (`git add . && git commit -m "message" && git push`) to deploy your environment—with no need to connect to the Upsun Console.

    <Note>
      Make sure you complete the following steps before adding a [Github integration](/docs/integrations/source/github):

      1. Create a Git repository in your own organization following the relevant
         [Github repository creation guide](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-new-repository).

      2. Create a [Github integration](/docs/integrations/source/github).

      3. Add a Git remote to your local project, from the root of your Next.js directory.<br />
         To do so, run the following commands:

         ```bash Terminal theme={null}
         git remote add origin <urlOfYourOwnGitHubRepo>
         git add . && git commit -m "init next.js"
         git push origin
         ```
    </Note>
  </Tab>
</Tabs>

## 4. Configure your project

To host your Next.js application on Upsun,
you need to have a few YAML configuration files at the root of your project.
These files manage your app's behavior.
They are located in a `.upsun/` folder at the root of your source code
and structured in a similar way to this:

```text Example file structure theme={null}
myapp
├── .upsun
│   └── config.yaml
├── [.environment]
└── <project sources>
```

To generate these files, run the following command at the root of your project:

```{location="Terminal"} theme={null}
upsun project:init
```

Follow the prompts, and you should result in such a config file.

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
      applications:
        myapp:
          source:
            root: "/"
          type: "nodejs:{{version:nodejs:latest}}"
          mounts:
            "/.npm":
              source: "storage"
              source_path: "npm"
          hooks:
            build: |
              set -eux
              npm i
              npm run build
          web:
            commands:
              start: "npx next start -p $PORT"
            upstream:
              socket_family: tcp
            locations:
              "/":
                passthru: true
      routes:
        "https://{default}/": { type: upstream, upstream: "myapp:http" }
        "http://{default}/": { type: redirect, to: "https://{default}/" }`
  }
</DynamicCodeBlock>

As an example, above is the minimum configuration needed to deploy a Next.js application on Upsun without any services.
Depending on your answers to the prompts, you may also have `relationships` and `services` defined.

To commit your new files, run the following commands:

```bash Terminal theme={null}
git add .
git commit -m "Add Upsun config files"
```

## 5. Deploy

And just like that, it’s time to deploy!

Depending on the Git workflow you chose at the beginning of this tutorial,
there are two ways to deploy your source code changes.

<Tabs>
  <Tab title="Using Upsun Git repository">
    You can push your code using the normal Git workflow (`git add . && git commit -m "message" && git push`).
    This pushes your source code changes to your `upsun` remote repository.
    Alternatively, you can use the following Upsun CLI command:

    ```bash Terminal theme={null}
    upsun push
    ```
  </Tab>

  <Tab title="Using third-party Git repository">
    When you choose to use a third-party Git hosting service, the Upsun Git
    repository becomes a read-only mirror of the third-party repository. All your
    changes take place in the third-party repository.

    Add an integration to your existing third-party repository:

    * [BitBucket](/docs/integrations/source/bitbucket)
    * [GitHub](/docs/integrations/source/github)
    * [GitLab](/docs/integrations/source/gitlab)

    If you are using an integration, on each code updates,
    use the normal Git workflow (`git add . && git commit -m "message" && git push`) to push your code to your external repository.
    To do so, run the following command:

    ```bash Terminal theme={null}
    git push origin
    ```

    Your GitHub, GitLab, or Bibucket integration process then automatically deploys changes to your environment.
    If you're pushing a new Git branch, a new environment is created.
  </Tab>
</Tabs>

Upsun then reads your configuration files,
and deploys your project using [default container resources](/docs/manage-resources/resource-init).

<Note>
  <h4>Resource sizing for Next.js</h4>
  Next.js applications typically require more resources than the platform defaults.
  If you encounter a [502 error](/docs/troubleshooting/502-errors) after deploying, your container may not have enough CPU or memory.

  Try increasing your app's resources by running the following command and, when prompted, selecting an application size of <code>0.5</code> or higher:

  ```bash Terminal theme={null}
  upsun resources:set
  ```

  For more details, see [Adjust resources](/docs/manage-resources/adjust-resources).
</Note>

If you don't want to use those default resources,
define your own [resource initialization strategy](/docs/manage-resources/resource-init#specify-a-resource-initialization-strategy),
or [amend those default container resources](/docs/manage-resources/adjust-resources) after your project is deployed.

Et voilà, your Next.js application is live!

<Info>
  <h4>Tip</h4>
  Each environment has its own domain name.
  To open the URL of your new environment, run the following command:

  ```bash Terminal theme={null}
  upsun environment:url --primary
  ```
</Info>

## 6. Make changes to your project

Now that your project is deployed, you can start making changes to it.
For example, you might want to fix a bug or add a new feature.

In your project, the `main` branch always represents the production environment.
Other branches are for developing new features, fixing bugs, or updating the infrastructure.

To make changes to your project, follow these steps:

1. Create a new environment (a Git branch) to make changes without impacting production:

   ```bash Terminal theme={null}
   upsun branch feat-a
   ```

   This command creates a new local `feat-a` Git branch based on the main Git branch,
   and activates a related environment on Upsun.
   The new environment inherits the data (service data and assets) of its parent environment (the production environment here).

2. Make changes to your project.
   For example, edit the `views/index.jade` file and make the following changes:

   ```diff {no-copy="true"} theme={null}
   diff --git a/views/index.jade b/views/index.jade
   index 3d63b9a..77aee43 100644
   --- a/views/index.jade
   +++ b/views/index.jade
   @@ -2,4 +2,4 @@ extends layout

    block content
      h1= title
   -  p Welcome to #{title}
   +  p Welcome to #{title} on Upsun
   ``

   ```

3. Commit your changes:

   ```bash Terminal theme={null}
   git add views/index.jade
   git commit -m "Update index page view."
   ```

4. Deploy your changes to the `feat-a` environment:

   ```bash Terminal theme={null}
   upsun push
   ```

5. Iterate by changing the code, committing, and deploying.
   When satisfied with your changes, merge them to the main branch,
   and remove the feature branch:

   ```bash Terminal theme={null}
   upsun merge
       Are you sure you want to merge feat-a into its parent, main? [Y/n] y
   upsun checkout main
   git pull upsun main
   upsun environment:delete feat-a
   git fetch --prune
   ```

   Note that deploying to production is fast because the image built for the `feat-a` environment is reused.

   For a long running branch, to keep the code up-to-date with the main branch, use `git merge main` or `git rebase main`.
   You can also keep the data in sync with the production environment by using `upsun env:sync`.

## Further resources

### Documentation

* [JavaScript/Node.js documentation](/docs/languages/nodejs)
* [Managing dependencies](/docs/languages/nodejs#dependencies)

### Community content

* [Next.js topics](https://support.platform.sh/hc/en-us/search?utf8=%E2%9C%93\&query=nextjs)
* [Node.js topics](https://support.platform.sh/hc/en-us/search?utf8=%E2%9C%93\&query=node)
* [JavaScript topics](https://support.platform.sh/hc/en-us/search?utf8=%E2%9C%93\&query=js)

### Blogs

* [A quick-start guide on hosting Next.js on Upsun](https://upsun.com/blog/setting-up-next-js-on-upsun/)
