How to Scale Apps in App Platform

App Platform is a Platform-as-a-Service (PaaS) offering that allows developers to publish code directly to DigitalOcean servers without worrying about the underlying infrastructure.


Scaling your app allows its infrastructure to match its workload. As workloads increase and decrease, you can manually scale or (autoscale) the amount of resources necessary to keep your app running at an acceptable level of performance. For example, if your app receives a large influx of online traffic and the app’s performance becomes slow, you can increase the number of containers the app is operating in to allow more instances of the app to handle the increased workload.

There are two types of scaling:

  • Horizontal scaling adds additional containers to the app and distributes the workload across the additional containers. App Platform offers both fixed and automatic options for horizontal scaling.
  • Vertical scaling increases a container’s memory or CPU power to better handle the app’s workload. App Platform only offers fixed vertical scaling, meaning you must manually change the container’s size in the DigitalOcean Control Panel, API, or CLI, to vertically scale it.
Note
Autoscaling is currently in Early Availability.

Scale an App Using the API or CLI

You can scale an app to a fixed size or configure autoscaling for an app by updating the app’s spec and then submitting the updated spec via the app update command or API endpoint

Scale App to a Fixed Size

To scale an app to a fixed container size and number of containers, update the instance_count or instance_size_slug fields of the component in your app’s spec and then submit the new spec.

    
        
            
- environment_slug: node-js
  git:
    branch: main
    repo_clone_url: https://github.com/digitalocean/sample-nodejs.git
  http_port: 8080
  instance_count: 2
  instance_size_slug: professional-xs
  name: sample-nodejs
  run_command: yarn start
  source_dir: /

        
    

Configure Autoscaling

To configure autoscaling, add the autoscaling object to the app’s spec and then configure the following fields:

  • min_instance_count. This is the minimum number of containers that the app is allowed to automatically scale down to.
  • max_instance_count. This is the maximum number of containers that the app is allowed to automatically scale to.
  • metrics. This object contains the metrics that the app uses to determine whether to scale up or down. Currently, CPU usage is the only supported metric.

For example, the following app’s scales a service component called “web” to a minimum of two containers and a maximum of five containers when the average CPU usage across the component’s containers exceeds or drops below 70%.

    
        
            
name: example-app
services:
- name: web
  github:
    repo: digitalocean/sample-golang-notes
    branch: master
  autoscaling:
    min_instance_count: 2
    max_instance_count: 5
    metrics:
      cpu:
        percent: 70

        
    
How to Scale an App Using the DigitalOcean CLI
  1. Install doctl, the DigitalOcean command-line tool.

  2. Create a personal access token and save it for use with doctl.

  3. Use the token to grant doctl access to your DigitalOcean account.

              doctl auth init
              
  4. Finally, run doctl apps update. Basic usage looks like this, but you can read the usage docs for more details:

                doctl apps update <app id> [flags]
              

    The following example updates an app with the ID f81d4fae-7dec-11d0-a765-00a0c91e6bf6 using an app spec located in a directory called /src/your-app.yaml. Additionally, the command returns the updated app’s ID, ingress information, and creation date:

                  doctl apps update f81d4fae-7dec-11d0-a765-00a0c91e6bf6 --spec src/your-app.yaml --format ID,DefaultIngress,Created
                
How to Scale an App Using the DigitalOcean API
  1. Create a personal access token and save it for use with the API.

  2. Send a PUT request to https://api.digitalocean.com/v2/apps/{id}

    cURL

    Using cURL:

                    curl -X PUT \
      -H "Content-Type: application/json" \
      -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \
      "https://api.digitalocean.com/v2/apps/{id}"
                  

    Python

                    import os
    from pydo import Client
    
    client = Client(token=os.environ.get("DIGITALOCEAN_TOKEN"))
    req = {
    "spec": {
        "name": "web-app-01",
        "region": "nyc",
        "domains": [
            {
                "domain": "app.example.com",
                "type": "DEFAULT",
                "wildcard": True,
                "zone": "example.com",
                "minimum_tls_version": "1.3",
            }
        ],
        "services": [],
        "static_sites": [
            {
                "cors": {
                    "allow_origins": [
                        {"exact": "https://www.example.com"},
                        {"regex": "^.*example.com"},
                    ],
                    "allow_methods": [
                        "GET",
                        "OPTIONS",
                        "POST",
                        "PUT",
                        "PATCH",
                        "DELETE",
                    ],
                    "allow_headers": ["Content-Type", "X-Custom-Header"],
                    "expose_headers": ["Content-Encoding", "X-Custom-Header"],
                    "max_age": "5h30m",
                    "allow_credentials": False,
                },
                "routes": [{"path": "/api", "preserve_path_prefix": True}],
            }
        ],
        "jobs": [
            {
                "name": "api",
                "gitlab": {
                    "branch": "main",
                    "deploy_on_push": True,
                    "repo": "digitalocean/sample-golang",
                },
                "image": {
                    "registry": "registry.hub.docker.com",
                    "registry_type": "DOCR",
                    "repository": "origin/master",
                    "tag": "latest",
                },
                "dockerfile_path": "path/to/Dockerfile",
                "build_command": "npm run build",
                "run_command": "bin/api",
                "source_dir": "path/to/dir",
                "envs": [
                    {
                        "key": "BASE_URL",
                        "scope": "BUILD_TIME",
                        "type": "GENERAL",
                        "value": "http://example.com",
                    }
                ],
                "environment_slug": "node-js",
                "log_destinations": {
                    "name": "my_log_destination",
                    "papertrail": {
                        "endpoint": "https://mypapertrailendpoint.com"
                    },
                    "datadog": {
                        "endpoint": "https://mydatadogendpoint.com",
                        "api_key": "abcdefghijklmnopqrstuvwxyz0123456789",
                    },
                    "logtail": {
                        "token": "abcdefghijklmnopqrstuvwxyz0123456789"
                    },
                   "open_search": {
                        "endpoint": "https://myopensearchendpoint.com:9300"
                        "index_name": "logs"
                        "basic_auth": {
                            "user": "doadmin",
                            "password": "password"
                        }
                    },
                },
                "instance_count": 2,
                "instance_size_slug": "apps-s-1vcpu-0.5gb",
                "kind": "PRE_DEPLOY",
            }
        ],
        "workers": [
            {
                "name": "api",
                "gitlab": {
                    "branch": "main",
                    "deploy_on_push": True,
                    "repo": "digitalocean/sample-golang",
                },
                "image": {
                    "registry": "registry.hub.docker.com",
                    "registry_type": "DOCR",
                    "repository": "origin/master",
                    "tag": "latest",
                },
                "dockerfile_path": "path/to/Dockerfile",
                "build_command": "npm run build",
                "run_command": "bin/api",
                "source_dir": "path/to/dir",
                "envs": [
                    {
                        "key": "BASE_URL",
                        "scope": "BUILD_TIME",
                        "type": "GENERAL",
                        "value": "http://example.com",
                    }
                ],
                "environment_slug": "node-js",
                "log_destinations": {
                    "name": "my_log_destination",
                    "papertrail": {
                        "endpoint": "https://mypapertrailendpoint.com"
                    },
                    "datadog": {
                        "endpoint": "https://mydatadogendpoint.com",
                        "api_key": "abcdefghijklmnopqrstuvwxyz0123456789",
                    },
                    "logtail": {
                        "token": "abcdefghijklmnopqrstuvwxyz0123456789"
                    },
                   "open_search": {
                        "endpoint": "https://myopensearchendpoint.com:9300"
                        "index_name": "logs"
                        "basic_auth": {
                            "user": "doadmin",
                            "password": "password"
                        }
                    },
                },
                "instance_count": 2,
                "instance_size_slug": "apps-s-1vcpu-0.5gb",
            }
        ],
        "functions": [
            {
                "cors": {
                    "allow_origins": [
                        {"exact": "https://www.example.com"},
                        {"regex": "^.*example.com"},
                    ],
                    "allow_methods": [
                        "GET",
                        "OPTIONS",
                        "POST",
                        "PUT",
                        "PATCH",
                        "DELETE",
                    ],
                    "allow_headers": ["Content-Type", "X-Custom-Header"],
                    "expose_headers": ["Content-Encoding", "X-Custom-Header"],
                    "max_age": "5h30m",
                    "allow_credentials": False,
                },
                "routes": [{"path": "/api", "preserve_path_prefix": True}],
                "name": "api",
                "source_dir": "path/to/dir",
                "alerts": [
                    {
                        "rule": "CPU_UTILIZATION",
                        "disabled": False,
                        "operator": "GREATER_THAN",
                        "value": 2.32,
                        "window": "FIVE_MINUTES",
                    }
                ],
                "envs": [
                    {
                        "key": "BASE_URL",
                        "scope": "BUILD_TIME",
                        "type": "GENERAL",
                        "value": "http://example.com",
                    }
                ],
                "gitlab": {
                    "branch": "main",
                    "deploy_on_push": True,
                    "repo": "digitalocean/sample-golang",
                },
                "log_destinations": {
                    "name": "my_log_destination",
                    "papertrail": {
                        "endpoint": "https://mypapertrailendpoint.com"
                    },
                    "datadog": {
                        "endpoint": "https://mydatadogendpoint.com",
                        "api_key": "abcdefghijklmnopqrstuvwxyz0123456789",
                    },
                    "logtail": {
                        "token": "abcdefghijklmnopqrstuvwxyz0123456789"
                    },
                   "open_search": {
                        "endpoint": "https://myopensearchendpoint.com:9300"
                        "index_name": "logs"
                        "basic_auth": {
                            "user": "doadmin",
                            "password": "password"
                        }
                    },
                },
            }
        ],
        "databases": [
            {
                "cluster_name": "cluster_name",
                "db_name": "my_db",
                "db_user": "superuser",
                "engine": "PG",
                "name": "prod-db",
                "production": True,
                "version": "12",
            }
        ],
    }
    }
    update_resp = client.apps.update(id="bb245ba", body=req)
                  

Configure Autoscaling for an App using the Control Panel

Autoscaling allows you to automatically add or remove containers from your app based on its workload. This is useful for managing apps that have high but inconsistent workloads. For example, if you have an app that hosts a website that receives sudden but inconsistent spikes in traffic, you can configure autoscaling to automatically add additional containers to the app to manage the increased workload, and then reduce the number of containers when the traffic drops. This is called horizontal scaling.

Autoscaling ensures that the average CPU usage across all of the app’s containers doesn’t exceed a specified value. If some or all containers use more than the given value, containers might be added. If some or all containers use less than the given value, containers might be removed.

You can configure autoscaling on existing apps or at creation time.

To configure autoscaling for an existing app, navigate to your app’s Overview page, then click the Settings tab and select the component you want to enable autoscaling for. In the Resource Size section, click the Edit button.

The autoscaling feature is only available for apps with a dedicated CPU plan. If your app does not use a dedicated CPU plan, select a dedicated CPU plan from the Instance Size menu, then click Set containers to autoscale. This opens the autoscaling settings:

  • Minimum Containers: The minimum number of containers the app can automatically be scaled down to.
  • Maximum Containers: The maximum number of containers the app can automatically be scaled up to.
  • CPU Threshold: The average CPU usage across the current containers that triggers the app to scale.
A screenshot of the control panel showing the scaling options for an app

Set the parameters of your app’s autoscaling. This generates an updated estimated monthly cost for your app. The price is a range between the app running at its minimum number of containers for an entire month and the app running at its maximum number of containers for an entire month.

Once you have configured the autoscaling parameters, click Save. This automatically triggers a redeployment if necessary.

Scale an App to a Fixed Size using the Control Panel

You can scale your app to a fixed size using the control panel.

To scale an app in the control panel, click on your app, and then click on the Settings tab. From the Settings tab, click on the resource you’d like to scale. The component’s setting open. In the Resource Size section, click the Edit button.

In the Resource Size menu, set the component’s number of containers in the Container field.

After you have selected how you want to scale the component, click Save. The app redeploys with the new scaled configuration.

A screenshot of the control panel showing the scaling options for an app