# How to Manage Services in App Platform App Platform is a fully managed Platform-as-a-Service (PaaS) that deploys applications from Git repositories or container images. It automatically builds, deploys, and scales components while handling all underlying infrastructure. The [usual app creation process](https://docs.digitalocean.com/products/app-platform/getting-started/quickstart/index.html.md#create-an-app) creates a service by default. Use the steps below only if you’re adding a new resource that needs an internet-accessible endpoint. If the resource only needs internal access, create an [internal service](#create-an-internal-service) instead. If it doesn’t need network access, create a [worker](https://docs.digitalocean.com/products/app-platform/how-to/manage-workers/index.html.md). ## Add a Service to an App Using Automation You can add a service to an app using the CLI’s app update command or the API’s app update endpoint. To add a service, update the [app spec](https://docs.digitalocean.com/products/app-platform/reference/app-spec/index.html.md) with the service’s specifications and submit the spec using the following command or endpoint. ## How to Add a Service to an App Using the DigitalOcean CLI 1. [Install `doctl`](https://docs.digitalocean.com/reference/doctl/how-to/install/index.html.md), the official DigitalOcean CLI. 2. [Create a personal access token](https://docs.digitalocean.com/reference/api/create-personal-access-token/index.html.md) and save it for use with `doctl`. 3. Use the token to grant `doctl` access to your DigitalOcean account. ```shell doctl auth init ``` 4. Finally, run `doctl apps update`. Basic usage looks like this, but you can [read the usage docs](https://docs.digitalocean.com/reference/doctl/reference/apps/update/index.html.md) for more details: ```shell doctl apps update [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: ```shell doctl apps update f81d4fae-7dec-11d0-a765-00a0c91e6bf6 --spec src/your-app.yaml --format ID,DefaultIngress,Created ``` ## How to Add a Service to an App Using the DigitalOcean API 1. [Create a personal access token](https://docs.digitalocean.com/reference/api/create-personal-access-token/index.html.md) and save it for use with the API. 2. Send a PUT request to [`https://api.digitalocean.com/v2/apps/{id}`](https://docs.digitalocean.com/reference/api/digitalocean//index.html.md#operation/apps_update). ### cURL Using cURL: ```shell curl -X PUT \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \ "https://api.digitalocean.com/v2/apps/{id}" \ -d '{"alerts":[{"rule":"DEPLOYMENT_FAILED"},{"rule":"DOMAIN_FAILED"}],"domains":[{"domain":"example.com","type":"PRIMARY","zone":"example.com"}],"envs":[{"key":"API_KEY","scope":"RUN_AND_BUILD_TIME","type":"SECRET","value":"EV[1:zqiRIeaaYK/NqctZDYzy6t0pTrtRDez8:wqGpZRrsKN5nPhWQrS479cfBiXT0WQ==]"}],"features":["buildpack-stack=ubuntu-22"],"ingress":{},"name":"example-app","region":"nyc","services":[{"autoscaling":{"max_instance_count":4,"metrics":{"cpu":{"percent":70}},"min_instance_count":2},"git":{"branch":"main","repo_clone_url":"https://github.com/digitalocean/sample-nodejs.git"},"internal_ports":[8080],"log_destinations":[{"name":"your_log_consumer_name","open_search":{"endpoint":"logs.example.com:12345","basic_auth":{"user":"doadmin","password":"1234567890abcdef"},"index_name":"example-index","cluster_name":"example-cluster"}}],"name":"sample-nodejs","run_command":"yarn start","source_dir":"/"}]}' ``` ### Python Using [PyDo](https://github.com/digitalocean/pydo), the official DigitalOcean API client for Python: ```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", } ], “vpc”: { “id”: “c22d8f48-4bc4-49f5-8ca0-58e7164427ac”, } } update_resp = client.apps.update(id="bb245ba", body=req) ``` ## Add a Service to an App Using the Control Panel Go to the [**Apps** page](https://cloud.digitalocean.com/apps) of the DigitalOcean Control Panel and select your app. Click **Add components**, then choose **Create resources from source code** to add a web service. ![An App Platform app with the Add components menu selected, and the Create resources from source code option highlighted.](https://docs.digitalocean.com/screenshots/app-platform/add-components-from-source-code.a959c25eb990da996592eaa08b37a2338cb383e636077540b62de75d78fcdaf6.png) On the **Add resources** screen: - Select your deployment source, either a Git repository or a [container image](https://docs.digitalocean.com/products/app-platform/how-to/deploy-from-container-images/index.html.md). If you have not previously created an app on App Platform, the repository service may prompt you to provide DigitalOcean with read permissions to your account. - If using a Git repository, choose a branch to deploy from. By default, App Platform builds from the repository's root. If your code lives in a subdirectory (for example, in a [monorepo](https://docs.digitalocean.com/products/app-platform/how-to/deploy-from-monorepo/index.html.md)), set the source directory to match. - Clear the **Autodeploy** checkbox if you want to stop automatic redeploys when the repository changes. - Click **Next**. ![The Create an app screen with the Git repository tab selected, and GitHub selected as the Git provider.](https://docs.digitalocean.com/screenshots/app-platform/app-choose-source-repo.fd23836d71b5954a0b709640a8f6984cb718b6f0a3ae624e5ce3482748badccf.png) App Platform retrieves your code, analyzes your repository, and automatically selects the appropriate runtime (such as Node or Ruby). If you need to override this behavior, upload a [Dockerfile](https://docs.docker.com/engine/reference/builder/) to your branch and restart the creation process. ### Configure Resource Settings The **Resource settings** table displays the configuration settings, some of which the detection system auto-fills. ![The Resource settings table showing a web service selected as the Resource type and configuration settings for a web service.](https://docs.digitalocean.com/screenshots/app-platform/create-web-service.aae02f62380c632f4ade0cef9a44915b715d0153d0f96bc45dcf7e725bb646f6.png) You can configure the following settings: - **Name:** A unique name for the component. - **Resource type:** The type of component to deploy (web service, static site, worker, or job). - **Branch:** The branch to deploy from. You can enable automatic redeploys when changes are made to this branch. - **Instance size:** The amount of memory (RAM), CPUs, and bandwidth allocated to the component. You can choose shared or dedicated CPUs. Shared CPUs share their processing power with other DigitalOcean users. Dedicated CPUs are dedicated solely to your app. We recommend dedicated CPUs for more resource-intensive applications that require consistent high performance and autoscaling. - **Containers:** The component's scaling settings. The instance size you select determines the scaling options available. For more details, see [How to Scale Apps in App Platform](https://docs.digitalocean.com/products/app-platform/how-to/scale-app/index.html.md). - **Build command:** A custom build command to run before the app is deployed. This is useful for compiling assets, installing dependencies, or running tests before deployment. - **Run command:** A custom run command for the application to run after deployment. If no run command is specified, the default run command for your app's language is used, such as `npm start` for a Node.js app. For Dockerfile-based builds, entering a run command overrides the Dockerfile's entrypoint. - **Public HTTP port:** For web services only. The port that the app receives HTTP requests on. The default port is `8080`. - **Internal ports:** For web services only. The port that the app receives internal requests on. - **HTTP request routes:** For web services and static sites only. The URL path where the app can be accessed, such as `your-app-v3cl4.ondigitalocean.app/api`. If not specified, the app is accessible from the provided hostname's root. **Note**: The service must bind to and expose the HTTP port on the network interface `0.0.0.0`, rather than on the localhost (`localhost`) or loopback interface (`127.0.0.1`). Full HTTP request path preservation for services is not available in the Control Panel. Instead, set the `preserve_path_prefix` annotation in the [App Spec](https://docs.digitalocean.com/products/app-platform/reference/app-spec/index.html.md) to `true`. - **Environment variables:** Key-value pairs available to your app at runtime that let you keep configuration separate from application code. Use them to provide secrets, API keys, and other external settings, such as connection details for an external database. App Platform uses cloud-native buildpacks to build components and applies the buildpack's default build and run commands. See the [cloud-native buildpack reference](https://docs.digitalocean.com/products/app-platform/reference/buildpacks/index.html.md) for details about supported buildpacks. ### Set App-Level Environment Variables App-level environment variables are available to all components in your app. Use them for shared configuration, secrets, API keys, or other values your app needs at build time or runtime. To set app-level environment variables: - Click **Edit** in the **App-level environment variables** section. - Click **Add environment variable**, type a key and value, then select a scope. - Select **Encrypt** to obscure the value in build, deployment, and application logs. For dynamic, app-specific variables that your app can reference, see [Use Bindable Variables](https://docs.digitalocean.com/products/app-platform/how-to/use-environment-variables/index.html.md#use-bindable-variables). ### Review and Deploy Review the configuration and pricing details in the **Summary** section, then click **Add resources**. App Platform creates the component with the selected settings and deploys it automatically. You can view or update the component's configuration on the **Settings** page. ## Edit a Service Go to the [**Apps** page](https://cloud.digitalocean.com/apps), select your app, then click the **Settings** tab. Select the web service you want to edit. Click **Edit** beside a setting you want to change, then click **Save**. ## Destroy a Service Go to the [**Apps** page](https://cloud.digitalocean.com/apps), select your app, then click the **Settings** tab. Select the web service you want to destroy. At the bottom of the page, click **Destroy Component**. Enter the web service's name to confirm, then click **Destroy**. ## Create an Internal Service Internal services are accessible to other components in the app’s private network. They do not have ports or endpoints exposed to the public internet. You can create an internal service by closing the ports on an existing service or by updating the app spec with the `internal_ports` field. ### Create an Internal Service Using the Control Panel To create an internal service, go to the [**Apps** page](https://cloud.digitalocean.com/apps), click your app, then click the **Settings** tab. On the **Settings** tab, select the service you want to make internal, scroll down to the **Ports** section, and then click **Edit**. In the **Ports** section, clear the **Public HTTP Port** field to close the public port. Click **Add internal ports for private networking**, then enter the port you want to open in the **Internal Port** field. To add more internal ports, click **Add internal port** and repeat this step. When finished, click **Save** to redeploy the app with the new internal ports. ![A screenshot of the Ports section of an app's settings. The internal port field is set to 81.](https://docs.digitalocean.com/screenshots/app-platform/app-internal-ports.5614b3c923c74e3b629b4db1b4a37bd95723b88d88edf206e2fc106118862168.png) ### Create an Internal Service Using the App Spec To create an internal service using the [app spec](https://docs.digitalocean.com/products/app-platform/reference/app-spec/index.html.md), remove any existing `http_port` fields from the target service object and then add the `internal_ports` field to the service: `app.yaml` ```text services: - environment_slug: go github: branch: master deploy_on_push: true repo: digitalocean/sample-golang instance_count: 1 instance_size_slug: apps-s-1vcpu-1gb internal_ports: - 8080 name: internal-service run_command: bin/sample-golang ``` The example service above is an internal service because it has no HTTP port or route, so it is inaccessible from the public internet, but exposes port 8080 internally, so workers and other services can reach it. ## Add a Function To add Functions, adjust your app spec as follows: ```yaml name: go-random region: nyc functions: - name: go-api github: repo: digitalocean/sample-functions-golang-random branch: master deploy_on_push: true ``` This process requires you to authorize to GitHub and specify the region you want to create the Function in. You can also delete the `region` field to automatically create the Function in the datacenter closest to you.