Cloud Run can serve traffic directly from a custom domain, and it does this by managing TLS certificates for you automatically.
Let’s say you have a Cloud Run service named my-app deployed in us-central1, and you want to access it via app.example.com. You’ve already set up your domain in Google Cloud’s "Domain Mappings" section.
Here’s how it looks when traffic flows:
- User Request: A user types
https://app.example.cominto their browser. - DNS Resolution: The browser queries DNS for
app.example.com. Your DNS provider (e.g., Cloud DNS, GoDaddy) is configured to pointapp.example.comto Google’s global load balancing infrastructure. - Global Load Balancer: Google’s global load balancer receives the request. It identifies that
app.example.comis mapped to your Cloud Run service. - TLS Termination: The load balancer terminates the TLS connection using a Google-managed SSL certificate for
app.example.com. - Internal Routing: The load balancer then routes the request internally to the nearest healthy instance of your
my-appCloud Run service inus-central1. - Cloud Run Service: Your
my-appservice processes the request and sends a response back. - Response Path: The response travels back through the load balancer to the user’s browser, establishing a new TLS connection if necessary.
To set this up, you’ll use the gcloud command-line tool.
First, ensure you have a Cloud Run service deployed. If not, create one:
gcloud run deploy my-app \
--image gcr.io/my-project/my-image:latest \
--platform managed \
--region us-central1 \
--allow-unauthenticated
Next, you need to map your custom domain to this Cloud Run service. You’ll do this using the gcloud run domain-mappings create command. You’ll need to have already verified your domain ownership in your Google Cloud project.
gcloud run domain-mappings create \
--service my-app \
--domain app.example.com \
--region us-central1
After running this command, gcloud will output DNS records that you need to add to your domain’s DNS zone. These typically include A, AAAA, and CNAME records. For example, you might see output like this:
Waiting for domain mapping to become ready.
The domain mapping is not ready. Please update your DNS records.
Record Type | Name | Value
-------------|---------------|-------------------------------------------
A | app.example.com | 35.190.247.89
A | app.example.com | 35.190.247.89
AAAA | app.example.com | 2600:1901:0:4f1f::2
AAAA | app.example.com | 2600:1901:0:581f::2
CNAME | www.app.example.com | app.example.com
Crucially, you must add these records to your DNS provider’s configuration. If you’re using Google Cloud DNS, you’d go to your DNS zone and add these records. For other providers, follow their specific instructions.
Once the DNS records have propagated (this can take anywhere from a few minutes to 48 hours, though usually much faster), Google Cloud will automatically provision and manage an SSL certificate for app.example.com. You can check the status of the domain mapping:
gcloud run domain-mappings describe --domain app.example.com --region us-central1
Look for the status.conditions section. It should eventually show Ready: True.
The most surprising true thing about this setup is that you don’t explicitly configure the load balancer or the SSL certificate. Google Cloud handles the entire lifecycle, from provisioning the certificate with Let’s Encrypt (or its own CA) to renewing it, and routing traffic to your service. The domain mapping resource in Cloud Run is essentially a pointer that tells the global load balancing layer where to send traffic for a given domain, and to what Cloud Run service.
Here’s a snippet of what the domain mapping resource might look like internally (you don’t configure this directly, but it’s what gcloud interacts with):
apiVersion: serving.knative.dev/v1alpha1
kind: DomainMapping
metadata:
name: app.example.com
namespace: my-project-id
spec:
ref:
kind: Service
name: my-app
apiVersion: serving.knative.dev/v1
status:
conditions:
- lastTransitionTime: "2023-10-27T10:00:00Z"
message: The domain mapping is ready.
reason: Ready
status: "True"
type: Ready
address:
type: LoadBalancer
value: app.example.com.a.run.app # This is an internal hostname managed by Google
The spec.ref points to your Cloud Run service, and the status.address.value is an internal hostname that Google’s global infrastructure uses for routing. The magic happens in the Cloud Run control plane and the global load balancing layer which interpret this DomainMapping resource.
When you update your Cloud Run service (e.g., deploy a new revision), the existing domain mapping automatically starts routing traffic to the new revision without any further action required from your side. This seamless transition is a core benefit of Cloud Run’s managed infrastructure.
The next concept you’ll likely encounter is how to secure your Cloud Run service further using Identity-Aware Proxy (IAP) for more fine-grained access control, or how to set up multiple custom domains for the same service.