How to Manage Domains 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.


App Platform provides a subdomain for apps on ondigitalocean.app upon successful deployment. You can also add a custom domain in your app’s settings to enable DNS mapping to the app. For example, you can add the domain example.com to your app and then map your domain’s DNS to point at the app.

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’s spec with the domain’s specifications and submit the spec using the following command or endpoint.

How to add a domain to an app using the DigitalOcean CLI

To add a domain to an app via the command-line, follow these steps:

  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, add a domain to an app with doctl apps update. The basic usage looks like this, but you'll want to 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 add a domain to an app using the DigitalOcean API

To add a domain to an app using the DigitalOcean API, follow these steps:

  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

    To add a domain to an app with cURL, call:

    
                    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": "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": "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)

Add a Custom Domain Using the Control Panel

Go to https://cloud.digitalocean.com/apps, click on your app, and click on the Settings tab. Then, click the Edit link to the right of Domains, and the Add Domain button.

On the Add Domain form, enter your custom domain name at the top of the form in the text area under Domain or Subdomain Name, then click to proceed.

You are given a choice to delegate your DNS management to DigitalOcean or to point to your app by adding a CNAME record to your domain configuration.

Option 1: Using DigitalOcean’s Name Servers

Domain add screen with Delegate to DigitalOcean selected

If you decide to use DigitalOcean’s name servers, copy and paste DigitalOcean’s name servers (ns1.digitalocean.com, ns2.digitalocean.com, ns3.digitalocean.com) to your domain’s registrar name server records. See our Community tutorial on how to delegate your domain to DigitalOcean’s name servers from popular registrars.

DNS changes can take up to 72 hours to propagate across the internet. You can check the progress of the transfer in App Platform’s dashboard.

Option 2: Using a CNAME Pointer

Domain add screen with Point to DigitalOcean selected

You can also add a CNAME record to your domain. If your DNS provider is not DigitalOcean, reference your DNS provider’s documentation to see how to do this.

Use the “copy” button to copy the ondigitalocean.app alias, and paste it into the CNAME record on your DNS provider so that it points your custom domain to your App Platform app. When this is complete on your DNS provider, click the Add Domain button.

You can also add apex wildcard domains. App Platform validates the wildcard domain and issues certificates using TXT records.

TXT validation instructions

To validate, click Instructions and copy the TXT Name and TXT Value.

TXT validation instructions

Add the values to your DNS provider to validate the wildcard domain.

You will receive a control panel and an email notification 30 days prior to the token expiration. To re-validate the domain, copy the new TXT Name and TXT Value and add them to your DNS provider.

DNS changes can take up to 72 hours to propagate across the internet. You can check the progress of the transfer in App Platform’s dashboard.

Change Domain Management Option

You can change your app’s domain management option by updating the app’s spec.

If you originally set up your domain with a CNAME pointer and want to 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.

    
        
            
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 is already managing 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.

Create a Wildcard Record for Domain

Wildcard DNS records direct requests for non-existent subdomains to a specified resource or IP address. For example, a wildcard record for *.example.com would mean that a DNS query for a non-existent domain like support.example.com would redirect to the domain’s homepage.

Warning
App Platform does not support wildcard DNS records for any of the top-level domains (TLDs) listed on this DigiCert reference page.

In App Platform, you can add a wildcard record to your app’s domain to redirect queries for non-existent subdomains to the domain’s root.

Before you can add a wildcard domain, you must add the root domain to your app first.

To add a wildcard record to your app, go to your app’s Overview page and then select the Settings tab. In the Domains section, click Edit and then click Add Domain.

In the Domain or Subdomain Name field, enter an asterisk followed by a dot and then your domain. For example, *.example.com. The asterisk denotes that this is a wildcard record. Once you’ve entered the wildcard domain, click Add Domain.

Wildcard domain entered into field

DNS queries for non-existent subdomains now redirect to the root domain of your app.

View DNS Provider Instructions

Go to https://cloud.digitalocean.com/apps, click on your app, and click on the Settings tab. Then click the Edit link to the right of Domains.

Click the triple-dot () menu item next to the custom domain name, and click View Instructions to view the instructions to register your domain with your DNS provider.

Remove domain

Add a Domain that Uses CAA Records

App Platform supports LetsEncrypt and Google Trust 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. App Platform validates the CAA record and issues certificates from one of the CAs.

Delete a Custom Domain

Go to https://cloud.digitalocean.com/apps, click on your app, and click on the Settings tab. Then click the Edit link to the right of Domains.

Click the triple-dot () menu item, and click Remove Domain to remove a domain that is associated with your app.