Mapping a custom domain to Cloud Functions Gen2 involves a few moving parts, but it’s essentially about telling Google Cloud how to route traffic from your domain to your serverless function.

Here’s a Cloud Function Gen2 deployed to us-central1 that simply returns a greeting:

from cloudevents.http import CloudEvent, from_http
from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/', methods=['POST'])
def hello_world(event: CloudEvent):
    data = event.data
    name = data.get('name', 'World')
    return jsonify({'message': f'Hello {name}!'})

When you deploy this, Cloud Functions Gen2 automatically creates a public URL for it. This URL is not what you want to use for your custom domain. Instead, you’ll be using a load balancer managed by Google Cloud.

The core mechanism is Google Cloud’s Serverless Network Endpoint Group (NEG). This special type of NEG doesn’t point to specific IP addresses; it points to a serverless service like Cloud Functions or Cloud Run. When you create a load balancer and configure it to use a Serverless NEG pointing to your function, the load balancer handles receiving traffic for your custom domain and then intelligently forwards it to the correct Cloud Function instance.

Here’s how you’d typically set this up:

  1. Create a Serverless NEG: This is the bridge between the load balancer and your Cloud Function.

    • Command:
      gcloud compute network-endpoint-groups create my-function-neg \
          --region=us-central1 \
          --network-endpoint-type=SERVERLESS \
          --cloud-run-service=my-function-name \
          --project=your-gcp-project-id
      
    • Explanation: This command creates a Serverless NEG named my-function-neg in the us-central1 region, specifically targeting the Cloud Run service (my-function-name) that underlies your Cloud Function Gen2.
  2. Create a Backend Service: This service defines how the load balancer interacts with the NEG.

    • Command:
      gcloud compute backend-services create my-function-backend-service \
          --global \
          --load-balancing-scheme=EXTERNAL_MANAGED \
          --protocol=HTTP \
          --timeout=30 \
          --enable-cdn \
          --project=your-gcp-project-id
      
    • Explanation: This creates a global backend service. The --enable-cdn flag is useful for caching static responses if your function serves any. The --protocol=HTTP is often sufficient because the traffic between the load balancer and Cloud Functions is typically over HTTP, and SSL is terminated at the load balancer.
  3. Add the Serverless NEG to the Backend Service:

    • Command:
      gcloud compute backend-services add-backend my-function-backend-service \
          --global \
          --network-endpoint-group=my-function-neg \
          --network-endpoint-group-region=us-central1 \
          --project=your-gcp-project-id
      
    • Explanation: This links your my-function-neg to the my-function-backend-service.
  4. Create a URL Map: This directs incoming requests to the correct backend service.

    • Command:
      gcloud compute url-maps create my-function-url-map \
          --default-service=my-function-backend-service \
          --project=your-gcp-project-id
      
    • Explanation: This creates a URL map and sets your my-function-backend-service as the default handler for all incoming requests. You could add more complex path-based routing here if needed.
  5. Create a Global Forwarding Rule: This is the public-facing IP address and port that your custom domain will point to.

    • Command:
      gcloud compute forwarding-rules create my-function-forwarding-rule \
          --global \
          --load-balancing-scheme=EXTERNAL_MANAGED \
          --network-tier=PREMIUM \
          --address=YOUR_STATIC_IP_ADDRESS \
          --ports=443 \
          --url-map=my-function-url-map \
          --project=your-gcp-project-id
      
    • Explanation: This creates the ingress point. You’ll need to provision a static IP address (YOUR_STATIC_IP_ADDRESS) first and then point your domain’s DNS A record to it. The --ports=443 indicates it’s listening for HTTPS traffic.
  6. Configure SSL Certificate: For HTTPS, you need an SSL certificate. Google Cloud offers managed SSL certificates.

    • Command:
      gcloud compute ssl-certificates create my-ssl-cert \
          --domains=your.custom.domain \
          --global \
          --project=your-gcp-project-id
      
    • Explanation: This creates a Google-managed SSL certificate for your.custom.domain. Google will automatically provision and renew this certificate.
  7. Create a Target HTTPS Proxy: This uses the SSL certificate and the URL map.

    • Command:

      gcloud compute target-https-proxies create my-https-proxy \
          --ssl-certificates=my-ssl-cert \
          --url-map=my-function-url-map \
          --global \
          --project=your-gcp-project-id
      
    • Explanation: This proxy terminates SSL and then uses the URL map to route traffic. You’d then update your forwarding rule to use this proxy instead of directly pointing to the URL map if you want HTTPS.

    • Update Forwarding Rule for HTTPS:

      gcloud compute forwarding-rules update my-function-forwarding-rule \
          --global \
          --target-https-proxy=my-https-proxy \
          --project=your-gcp-project-id
      
  8. Update DNS Records: Finally, go to your domain registrar and create an A record pointing your.custom.domain to the static IP address you reserved for the forwarding rule.

The most surprising true thing about this setup is that you’re actually configuring a Global External HTTP(S) Load Balancer, even though you’re not directly managing any VMs or traditional backends. The Serverless NEG abstracts away the complexities of the load balancer’s interaction with the serverless platform.

Once these steps are complete, traffic hitting https://your.custom.domain will be routed through the load balancer, which will then invoke your Cloud Function Gen2.

The next concept you’ll likely encounter is handling more complex routing scenarios, such as mapping different subdomains or URL paths to different serverless services, or implementing authentication and authorization at the load balancer level before traffic even reaches your function.

Want structured learning?

Take the full Cloud-functions course →