> ## 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 Flask on Upsun

> Complete the last required steps to successfully deploy Flask 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 the core concepts and common commands you need to know before using the materials below.
</Info>

For Flask to successfully deploy and operate, **after completing the [Getting started guide](/docs/get-started/here)**,
you still need to make a few changes to your Upsun configuration.

<GuidesRequirements name="Django" />

## 1. Leverage environment variables

The CLI generated a `.environment` file during the Getting started guide.
Notice it has already created some environmental variables for you to connect to your database service.

```bash .environment theme={null}
export RELATIONSHIPS_JSON="$(echo "$PLATFORM_RELATIONSHIPS" | base64 --decode)"

# Set database environment variables
export DB_HOST="$(echo $RELATIONSHIPS_JSON | jq -r '.postgresql[0].host')"
export DB_PORT="$(echo $RELATIONSHIPS_JSON | jq -r '.postgresql[0].port')"
export DB_DATABASE="$(echo $RELATIONSHIPS_JSON | jq -r '.postgresql[0].path')"
export DB_USERNAME="$(echo $RELATIONSHIPS_JSON | jq -r '.postgresql[0].username')"
export DB_PASSWORD="$(echo $RELATIONSHIPS_JSON | jq -r '.postgresql[0].password')"
export DB_CONNECTION="$(echo $RELATIONSHIPS_JSON | jq -r '.postgresql[0].scheme')"
export DATABASE_URL="postgresql://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_DATABASE}"
```

To configure all the environment variables Flask needs to run smoothly, follow these steps.

1. Set the `FLASK_APP` environment variable to specify how to load the app.

   ```bash .environment theme={null}
   export FLASK_APP="autoapp.py"
   ```

   The above example uses the file `autoapp.py` as the main entry.
   Adjust according to your situation.

2. Set the `FLASK_ENV` environment variable to enable behaviors based on the environment type.

   Upsun provides information about what type of environment the app is running in via the `PLATFORM_ENVIRONMENT_TYPE` environment variable.
   Its values can be `production`, `development`, or `staging`.
   Use this information to set the value for `FLASK_ENV`.

   ```bash .environment theme={null}
   export FLASK_ENV="${PLATFORM_ENVIRONMENT_TYPE}"
   ```

   Several other environmental variables need to change based on the environment type.
   Leverage the information in `PLATFORM_ENVIRONMENT_TYPE` for these other variables too.

3. Set the `FLASK_DEBUG` environment variable to `1` (enabled) if you're not running in production.

   ```bash .environment theme={null}
   if [ "$PLATFORM_ENVIRONMENT_TYPE" = "production" ]; then
     FLASK_DEBUG=0
   else
     FLASK_DEBUG=1
   fi
   export FLASK_DEBUG
   ```

4. Do the same for `LOG_LEVEL`.

   ```bash .environment theme={null}
   if [ "$PLATFORM_ENVIRONMENT_TYPE" = "production" ]; then
     LOG_LEVEL="info"
   else
     LOG_LEVEL="debug"
   fi
   export LOG_LEVEL
   ```

5. Set the `SEND_FILE_MAX_AGE_DEFAULT` to `0` (disabled) if you're not in production, but a higher value if you are.

   ```bash .environment theme={null}
   if [ "$PLATFORM_ENVIRONMENT_TYPE" = "production" ]; then
     SEND_FILE_MAX_AGE_DEFAULT=31556926
   else
     SEND_FILE_MAX_AGE_DEFAULT=0
   fi
   export SEND_FILE_MAX_AGE_DEFAULT
   ```

6. Optional: You may also need to set a `SECRET_KEY` environment variable.
   It's used for securely signing the session cookie and can be used for any other security-related needs by extensions or your app.
   It usually is a long random string.

   Set the `SECRET_KEY` environment variable to leverage the [`PLATFORM_PROJECT_ENTROPY` variable](/docs/development/variables/use-variables#use-provided-variables) provided by Upsun:

   ```bash .environment theme={null}
   export SECRET_KEY="${PLATFORM_PROJECT_ENTROPY}"
   ```

   Your `.environment` file should now look similar to the following:

   ```bash .environment theme={null}
   # Set database environment variables
   export DB_HOST="$POSTGRESQL_HOST"
   export DB_PORT="$POSTGRESQL_PORT"
   export DB_PATH="$POSTGRESQL_PATH"
   export DB_USERNAME="$POSTGRESQL_USERNAME"
   export DB_PASSWORD="$POSTGRESQL_PASSWORD"
   export DB_SCHEME="postgresql"
   export DATABASE_URL="${DB_SCHEME}://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_PATH}"

   export FLASK_ENV="${PLATFORM_ENVIRONMENT_TYPE}"
   if [ "$PLATFORM_ENVIRONMENT_TYPE" = "production" ]; then
     FLASK_DEBUG=0
   else
     FLASK_DEBUG=1
    fi
   export FLASK_DEBUG
   if [ "$PLATFORM_ENVIRONMENT_TYPE" = "production" ]; then
     LOG_LEVEL="info"
   else
     LOG_LEVEL="debug"
   fi
   export LOG_LEVEL
   if [ "$PLATFORM_ENVIRONMENT_TYPE" = "production" ]; then
     SEND_FILE_MAX_AGE_DEFAULT=31556926
   else
     SEND_FILE_MAX_AGE_DEFAULT=0
   fi
   export SEND_FILE_MAX_AGE_DEFAULT
   export SECRET_KEY="${PLATFORM_PROJECT_ENTROPY}"
   ```

## 2. Configure static assets

You need to add some writable disk space to hold the static assets that `flask-static-digest` generates and `npm` builds.

To do so, define the `./<APP_NAME>:/static` directory as [a mount](/docs/configure-apps/image-properties/mounts).
In your app configuration, locate the section dedicated to mounts and update it as follows:

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
    applications:
      <APP_NAME>:
        #...
        mounts:
          "<APP_NAME>:/static":
            source: "storage"
            source_path: "static_assets"
    `}
</DynamicCodeBlock>

Replace `<APP_NAME>:` with the `app_name` you defined when creating your Flask project.

## 3. Install dependencies and builds

Instruct Upsun to automatically run `npm install` in addition to installing your Python dependencies
when building the application container.

To do so, customize your [build hook](/docs/configure-apps/hooks/hooks-comparison#build-hook).
In your app configuration, locate the section dedicated to it and update it as follows:

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
    applications:
      <APP_NAME>:
        #...
        # Hooks allow you to customize your code/environment as the project moves through the build and deploy stages
        # More information: /docs/configure-apps/app-reference#hooks
        hooks:
          # The build hook is run after any build flavor.
          # More information: /docs/configure-apps/hooks/hooks-comparison#build-hook
          build: |
            set -eux
            pip install -r requirements.txt
    `}
</DynamicCodeBlock>

The Upsun CLI automatically added `pip install -r requirements.txt` to your build hook configuration when you
[configured your Upsun project](/docs/get-started/here/configure).

If you want `pip` to be upgraded first to the latest version, add `pip install --upgrade pip` to the above line.
Then, add `npm install`:

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
      applications:
        <APP_NAME>:
          #...
          # Hooks allow you to customize your code/environment as the project moves through the build and deploy stages
          # More information: /docs/configure-apps/app-reference#hooks
          hooks:
            # The build hook is run after any build flavor.
            # More information: /docs/configure-apps/hooks/hooks-comparison#build-hook
            build: |
              set -eux
              pip install --upgrade pip
              pip install -r requirements.txt
              npm install
    `}
</DynamicCodeBlock>

Note that if your project uses a different package manager, Upsun also supports [several other options](/docs/languages/python/dependencies).

## 4. Configure the deploy phase

Instruct Upsun to automatically run `npm run build` when building the application container.
To do so, customize your [deploy hook](/docs/configure-apps/hooks/hooks-comparison#deploy-hook).
In your app configuration, locate the section dedicated to it:

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
      applications:
        <APP_NAME>:
          #...
          # Hooks allow you to customize your code/environment as the project moves through the build and deploy stages
          # More information: /docs/configure-apps/app-reference#hooks
          hooks:
            #...
            # The deploy hook is run after the app container has been started, but before it has started accepting requests.
            # More information: /docs/configure-apps/hooks/hooks-comparison#deploy-hook
            deploy: |
              set -eux
              # echo 'Put your deploy command here'
    `}
</DynamicCodeBlock>

Add `npm run build`:

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
      applications:
        <APP_NAME>:
          #...
          # Hooks allow you to customize your code/environment as the project moves through the build and deploy stages
          # More information: /docs/configure-apps/app-reference#hooks
          hooks:
            #...
            # The deploy hook is run after the app container has been started, but before it has started accepting requests.
            # More information: /docs/configure-apps/hooks/hooks-comparison#deploy-hook
            deploy: |
              set -eux
              npm run build
    `}
</DynamicCodeBlock>

## 5. Configure the web server

[Configure the web server](/docs/configure-apps#configure-whats-served) running in front of your app to define how your app handles dynamic requests.
To do so, in your app configuration, locate the section dedicated to the web server:

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
      applications:
        <APP_NAME>:
          #...
          # The web key configures the web server running in front of your app.
          # More information: /docs/configure-apps/app-reference#web
          web:
            # Commands are run once after deployment to start the application process.
            # More information: /docs/configure-apps/image-properties/web#web-commands
    `}
</DynamicCodeBlock>

To add a basic Flask server, replace the default information added by the Upsun CLI with `flask run -p $PORT`.
Also, change the `socket_family` value from `unix` to `tcp`:

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
      applications:
        <APP_NAME>:
          #...
          # The web key configures the web server running in front of your app.
          # More information: /docs/anchors/app/reference/web/
          web:
            # Commands are run once after deployment to start the application process.
            # More information: /docs/anchors/app/reference/web/commands/
            commands:
              start: "flask run -p $PORT"
            upstream:
              socket_family: tcp
    `}
</DynamicCodeBlock>

You can now commit all of the above changes and push to Upsun.

```bash Terminal theme={null}
git add .
git commit -m "Add changes to complete my Upsun configuration"
git push
```

Note that if your Flask project requires a different web server,
Upsun also supports [several other options](/docs/languages/python/server), including Gunicorn, Daphne,
Uvicorn, and Hypercorn.

If you use Pip, make sure you add your chosen web server to your `requirements.txt` file.

## 6. Handle database migrations

### Prepare database migrations

If you have a new Flask project that uses [Flask-migrate](https://flask-migrate.readthedocs.io/en/latest/),
or an existing app but need to set up the initial migrations, you can do so using the database service you created earlier.

To do so, follow these steps.

1. Set up a virtual environment where your project can run:

   ```bash Terminal theme={null}
   python3 -m venv env && source venv/bin/activate
   ```

2. Just like in your build hook, update pip and install the requirements:

   ```bash Terminal theme={null}
   pip install --upgrade pip && pip install -r requirements.txt
   ```

3. Set up a way for your local instance of Flask to communicate with your database service:

   ```bash Terminal theme={null}
   upsun tunnel:open -y
   ```

   This opens an SSH tunnel to all the services for the app.
   You can use this connection to allow your local instance to communicate with live services as if they too were local.<br />
   To do so, you need to configure more environment variables.

4. Reopen your `.environment` file.
   Note the use of the `$PLATFORM_RELATIONSHIPS` environment variable to retrieve information about services and their credentials.<br />
   The tunnel you created gives you access to that same data,
   allowing you to generate a local `PLATFORM_RELATIONSHIPS` environment variable containing the same information.

   Set the following environment variable:

   ```bash .environment theme={null}
   export PLATFORM_RELATIONSHIPS="$(upsun tunnel:info --encode)"
   ```

   Since you now have this environmental variable set locally, you can reuse your `.environment` file for Upsun to recreate
   many of the other environmental variables you need to run locally.

5. Set the following environment variables as they aren't set via `PLATFORM_RELATIONSHIPS`:

   ```bash .environment theme={null}
   export PLATFORM_ENVIRONMENT_TYPE="production"
   export PORT=8888
   export PLATFORM_PROJECT_ENTROPY="$(openssl rand -base64 32)"
   ```

6. Source your `.environment` file to finish setting up all the environmental variables in your current bash:

   ```bash Terminal theme={null}
   source ./.environment
   ```

   You now have everything you need for Flask-Migrate to be able to connect to the database and generate your migration files.

### Generate database migrations

1. Initiate the migrations directory and prepare for the `migrate` command:

   ```bash Terminal theme={null}
   flask db init
   ```

2. Generate your migrations:

   ```bash Terminal theme={null}
   flask db migrate
   ```

3. Commit your generated migrations:

   ```bash Terminal theme={null}
   git add migrations/* && git commit -m "Adds migrations"
   ```

4. Instruct Upsun to run the Flask-migrate upgrade command when deploying
   so any migration changes are automatically applied.<br />
   To do so, re-open your `.upsun/config.yaml` file.
   Locate the `deploy` hook where you added `npm run build` previously and update it as follows:

   <DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
     {`
       applications:
       <APP_NAME>:
         #...
         # Hooks allow you to customize your code/environment as the project moves through the build and deploy stages
         # More information: /docs/configure-apps/app-reference#hooks
         hooks:
           #...
           # The deploy hook is run after the app container has been started, but before it has started accepting requests.
           # More information: /docs/configure-apps/hooks/hooks-comparison#deploy-hook
           deploy: |
             set -eux
             npm run build
             flask db upgrade
       `}
   </DynamicCodeBlock>

5. Commit all your changes and push to Upsun.

   ```bash Terminal theme={null}
   git add .
   git commit -m "Add changes to complete my Upsun configuration"
   git push
   ```

## Further resources

### Documentation

* [Python documentation](/docs/languages/python)
* [Managing dependencies](/docs/languages/python/dependencies)
* [Configuring web servers](/docs/languages/python/server)

### Community content

* [Flask topics](https://support.platform.sh/hc/en-us/search?utf8=%E2%9C%93\&query=flask)
* [Python topics](https://support.platform.sh/hc/en-us/search?utf8=%E2%9C%93\&query=python)

### Blogs

* [Up(sun) and running with Django](https://upsun.com/blog/setting-up-django-on-upsun/)
