# How to Manage Domains 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. After a successful deployment, App Platform assigns a starter domain on `ondigitalocean.app`. You can add a custom domain to map a domain you own to the app. For example, you can add `example.com` and update your DNS configuration to route traffic to the app. You can also edit the app spec to [redirect traffic from the starter domain to your custom domain](#use-subdomain-routing). App Platform offers two public ingress IPs that you can target with DNS records to direct traffic to your app, free of charge. For more details, see [How to Add Static IP Addresses to App Platform Components](https://docs.digitalocean.com/products/app-platform/how-to/add-ip-address/index.html.md#use-public-static-ingress-ips). App Platform does not support adding DNSSEC-enabled domains to apps. ## Add a Domain to an App Using Automation You can add a domain to an app using the CLI’s app update command or the API’s app update endpoint. To add a domain, update the [app spec](https://docs.digitalocean.com/products/app-platform/reference/app-spec/index.html.md) with the domain’s specifications and submit the spec using the following command or endpoint. The app spec must completely define all of your app’s configurations. We recommend [downloading your current app spec](https://docs.digitalocean.com/products/app-platform/how-to/update-app-spec/index.html.md) from the control panel, API, or CLI, and modifying it to include the domain. ## How to Add a Domain 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 Domain 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 Domain Using the Control Panel To add a custom domain, go to the [Apps page](https://cloud.digitalocean.com/apps), click your app, then click the **Networking** tab. In the **Domains** section, click **Add domain**. On the **Add domain** screen, enter your custom domain name in the **Domain** field. Under **Management options**, choose one of the following: 1. **We manage your domain**: DigitalOcean manages DNS using its nameservers. 2. **You manage your domain**: your DNS provider manages DNS using a CNAME pointer. ### Option 1: Use DigitalOcean’s Nameservers If you choose **We manage your domain**, copy DigitalOcean’s nameservers and paste them into your domain registrar’s nameserver settings: ``` ns1.digitalocean.com ns2.digitalocean.com ns3.digitalocean.com ``` See [Point to DigitalOcean Name Servers From Common Domain Registrars](https://www.digitalocean.com/community/tutorials/how-to-point-to-digitalocean-nameservers-from-common-domain-registrars) for instructions with popular registrars. Click **Add domain** to complete the setup. ![Add Domain screen with We manage your domain selected](https://docs.digitalocean.com/screenshots/app-platform/add-domain.65eddf9eac032848d31bd05706f991feae410d4eb10218f35bc73b3613bc6806.png) DNS changes can take up to 72 hours to propagate. You can track progress in the App Platform dashboard. ### Option 2: Use a CNAME Alias If you choose **You manage your domain**, copy the displayed CNAME alias ending in `ondigitalocean.app` and [add a CNAME record](https://docs.digitalocean.com/products/networking/dns/how-to/manage-records/index.html.md) with your DNS provider pointing your custom domain to the alias. If your DNS provider is not DigitalOcean, reference your DNS provider’s documentation for instructions. If your DNS provider does not support CNAME flattening at the root domain, [App Platform provides A records](https://docs.digitalocean.com/products/app-platform/how-to/add-ip-address/index.html.md#static-ingress-ips) you can use instead. After you configure the CNAME alias (or A records), click **Add domain** to complete the setup. ![Add Domain screen with You manage your domain selected](https://docs.digitalocean.com/screenshots/app-platform/add-domain-cname.3bcdc661632be141c68696dc2d47fb69a6201b648f781e75e5e8cae4c5daa436.png) DNS changes can take up to 72 hours to propagate. You can track progress in the App Platform dashboard. ## Add a Wildcard Domain A wildcard DNS record (`*.example.com`) catches requests for subdomains that don’t have their own DNS records. For example, `support.example.com` would resolve to the same resource as `example.com`. After adding a root domain in App Platform, you can add a wildcard domain to cover subdomains, or an apex wildcard domain to cover both the apex and all subdomains. **Warning**: App Platform does not support wildcard DNS records for any of the top-level domains (TLDs) listed on this [DigiCert reference page](https://knowledge.digicert.com/solution/Embargoed-Countries-and-Regions.html#:~:text=List%20of%20restricted%20Russia%20and%20Belarus%20TLDs%3A). To add a wildcard record, go to the [Apps page](https://cloud.digitalocean.com/apps), click your app, then click the **Networking** tab. In the **Domains** section, click **Add domain**. On the **Add domain** screen, in the **Domain** field, enter your wildcard domain (for example, `*.example.com`). Under **Management options**, choose one of the following: - **We manage your domain**. If the root domain’s DNS was already configured, no further configuration is required. Click **Add domain** to finish. - **You manage your domain**. Copy the displayed CNAME alias, and then add a wildcard CNAME record in your DNS provider. Set the host to `*` and the target to the CNAME alias you copied (for example, `* → example-1abcd.ondigitalocean.app`). Click **Add domain** to finish. Wildcard domains require additional verification for certificate issuance. App Platform issues certificates using TXT verification tokens. To verify the wildcard domain, go to the **Domains** section of the **Networking** tab and click **Use TXT records to verify** under the wildcard domain you added. Copy the TXT record names and values, then add the TXT records in your DNS provider. App Platform sends notifications in the control panel and by email 30 days before tokens expire. To re-verify the domain, add the updated TXT records in your DNS provider. DNS changes can take up to 72 hours to propagate. You can track progress in the App Platform dashboard. ## Change Domain Management Option You can change your app’s domain management option by [updating the app spec](https://docs.digitalocean.com/products/app-platform/how-to/update-app-spec/index.html.md). If you originally set up your domain with a CNAME pointer and want DigitalOcean to manage your app’s domain instead, add the `zone` field to the spec and use your domain’s name as the value. For example, the following spec adds the domain `example.com` to the app. `example-app-spec.yaml` ```yaml alerts: - rule: DEPLOYMENT_FAILED domains: - domain: example-app.com type: PRIMARY zone: example.com features: - enable-kata-build name: example-app-name region: nyc ... ``` This means that DigitalOcean will manage the domain’s name servers and DNS records going forward. If DigitalOcean already manages your app’s domain and you want to self-manage it instead, remove the `zone` field, and follow the [instructions to add a CNAME record to your domain](#option-2). ## Use Subdomain Routing Components can support multiple domains and multi-tenancy using optional prefix-based routing. You can configure subdomain routing and edit the app’s default subdomain in the control panel or by [updating the app spec](https://docs.digitalocean.com/products/app-platform/how-to/update-app-spec/index.html.md). ## DigitalOcean Control Panel Go to the [Apps page](https://cloud.digitalocean.com/apps), click your app, then click the **Networking** tab. In the **Component routing rules** section, click **Add routing rule**. ![Component routing rule section with the Add routing rule button highlighted.](https://docs.digitalocean.com/screenshots/app-platform/add-routing-rule-net-tab.845c1919cfca5ffce01fffe703fbc12fa9d305d2b7dc7d53eff3f6c8c403597e.png) Use the settings in the **Add a component routing rule** dialog to configure subdomain routing: - **Select domain to route requests from**: Select a domain or select **Match all incoming domains**. - **Route path**: Enter a path to match only certain URLs (for example, `/api/`), or leave it blank (or `/`) to match all requests. - **Path handling**: Select **Preserve Full Path**, so the component receives the original URL. - **Target component**: Select the component to route requests to. - [**Configure CORS**](https://docs.digitalocean.com/products/app-platform/how-to/configure-cors-policies/index.html.md): Optionally customize CORS response headers if the subdomain is accessed from a different origin (for example, multi-tenant frontends, dashboards, or browser-based APIs). The options are: - **Access-Control-Allow-Origins**: Specify which origins can make requests. - **Access-Control-Allow-Methods**: Specify which HTTP methods are allowed. - **Access-Control-Allow-Headers**: Specify which request headers clients can send. - **Access-Control-Expose-Headers**: List which response headers browsers are allowed to expose to JavaScript. - **Access-Control-Max-Age**: Define how long browsers can cache preflight results. - **Access-Control-Allow-Credentials**: Allow cookies, auth headers, and other credentials in cross-origin requests. - Click **Add routing rule** to save. ![Component routing rule section with the Add routing rule button highlighted.](https://docs.digitalocean.com/screenshots/app-platform/add-component-routing-rule.361fda71c2b619faa518b7d3dd15738f1facff6a64d349db94ecaf59419894b7.png) The image above shows a routing rule that directs all requests from the domain `domainexample.xyz` to the `example-nodejs` component. The browser URL remains unchanged. All paths are preserved, so a request such as `/dashboard/settings` reaches the component exactly as `/dashboard/settings`. ## App Spec To add multiple subdomains, create a separate component block for each one. You can’t use wildcard domains in the subdomain routing block. See the following app spec example. `example-app-spec.yaml` ```yaml # Custom domain is *.example.com domains: - domain: example.com type: PRIMARY wildcard: true zone: example.com ingress: rules: # Traffic to https://api.example.com/v1 goes to the legacy API component - component: name: api-legacy match: authority: exact: api.example.com path: prefix: /v1 # Traffic to https://api.example.com/v2 goes to the new API component - component: name: api match: authority: exact: api.example.com path: prefix: /v2 # All other traffic goes to the frontend component - component: name: frontend match: path: prefix: / # Traffic to starter domain is redirected to app's custom domain - redirect: authority: example.com match: authority: # The static placeholder ${STARTER_DOMAIN} matches on the app's starter domain exact: ${STARTER_DOMAIN} ``` ## Add a Domain that Uses CAA Records App Platform supports [LetsEncrypt](https://letsencrypt.org/docs/caa/) and [Google Trust](https://pki.goog/faq) as Certificate Authorities (CAs). If you want to add a domain to App Platform that uses Certification Authority Authorization (CAA), you must add both `letsencrypt.org` and `pki.goog` to your domain’s CAA DNS record. If you do not specify both CAs, the custom domain configuration may fail. Once you have specified both CAs in the domain’s CAA record, add the domain in App Platform following the [custom domain instructions](#custom-domain). App Platform validates the CAA record and issues certificates from one of the CAs. ## Remove a Custom Domain Before deleting an app, remove all custom domains. If a domain is not removed, it may continue pointing to the deleted app for up to 24 hours, making the domain unavailable for use with a new app until DNS updates. To remove a custom domain, go to the [Apps page](https://cloud.digitalocean.com/apps), click your app, then click the **Networking** tab. In the **Domains** section, click **…** beside the domain, then click **Remove domain**.