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 cannot edit an app’s default subdomain. However, you can add a custom domain in your app’s settings to map a domain you own to your app. For example, you can add the domain example.com to your app and then update your domain’s DNS configuration to direct traffic to your 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. The app spec must completely define all of your app’s configurations. We recommend downloading your current app spec 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, 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 Add a Domain to 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}" \
    -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

                    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)
                  

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.

Remove a Custom Domain

Before deleting an app, you should remove all custom domains from it. If you do not remove a domain from an app before deleting it, the domain may still point to the deleted app for up to 24 hours after deleting it. This can cause your domain to be unavailable to a new app until the DNS updates.

To remove a custom domain from an app using the control panel, click on your app, and then click on the Settings tab. From there, 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.