Regional External Load Balancer with Private Cloud Run and SSL¶
This tutorial guides you through setting up a Regional External Application Load Balancer to serve traffic to a Private Cloud Run service using a custom domain and a Google-managed SSL certificate.
Prerequisites¶
- GCP Project: A project with billing enabled.
- Domain Name: You own a domain (e.g.,
test.gcp.devopspilot.com) and can configure DNS records. - gcloud CLI: Installed and authorized.
- Permissions: Permissions to create Cloud Run services, Compute Engine resources (addresses, NEGs, backend services, URL maps, SSL certs), and DNS records.
Architecture¶
We will configure the Cloud Run service to accept traffic only from the Load Balancer (Internal Ingress). The Load Balancer will handle SSL termination and route requests to the private service.
Traffic flows as follows:
- User (HTTPS): The user sends an HTTPS request to your custom domain (e.g.,
https://test.gcp.devopspilot.com). - Regional External LB: The request hits the Load Balancer, which handles SSL termination and decrypts the traffic.
- Serverless NEG: The LB forwards the request to the Serverless Network Endpoint Group.
- Cloud Run Service: The NEG routes the request to the Cloud Run service. The service accepts the request because it originates from a valid Load Balancer, satisfying the "Internal and Cloud Load Balancing" ingress restriction.
Step 1: Create Private Cloud Run Service¶
Deploy the service with ingress restricted to "Internal and Cloud Load Balancing". This checks that requests come from a Google Cloud Load Balancer or VPC, preventing direct access from the public internet url.
export REGION=us-central1
export SERVICE_NAME=private-service
gcloud run deploy $SERVICE_NAME \
--image=us-docker.pkg.dev/cloudrun/container/hello \
--region=$REGION \
--allow-unauthenticated \
--ingress=internal-and-cloud-load-balancing
--ingress=internal-and-cloud-load-balancing: Ensures the service URL is not reachable directly from the internet.--allow-unauthenticated: We allow unauthenticated invocations at the service level because the Load Balancer will forward traffic without authentication headers (unless using IAP). The security comes from the ingress restriction.
Step 2: Reserve a Regional External IP Address¶
Reserve a static IP for your load balancer.
gcloud compute addresses create my-ssl-lb-ip \
--region=$REGION \
--network-tier=STANDARD
Retrieve the IP:
export LB_IP=$(gcloud compute addresses describe my-ssl-lb-ip \
--region=$REGION \
--format="get(address)")
echo "Load Balancer IP: $LB_IP"
Step 3: Create Regional Managed SSL Certificate¶
Create a Google-managed SSL certificate for your domain.
export DOMAIN=test.gcp.devopspilot.com
gcloud compute ssl-certificates create my-ssl-cert \
--domains=$DOMAIN \
--region=$REGION
Note: The certificate will remain in PROVISIONING state until you update your DNS records (Step 7).
Step 4: Create Serverless NEG¶
Create the Serverless Network Endpoint Group (NEG) for the private Cloud Run service.
gcloud compute network-endpoint-groups create my-private-neg \
--region=$REGION \
--network-endpoint-type=serverless \
--cloud-run-service=$SERVICE_NAME
Step 5: Configure Backend Service¶
Create a backend service and add the NEG.
gcloud compute backend-services create my-ssl-backend \
--load-balancing-scheme=EXTERNAL_MANAGED \
--protocol=HTTP \
--region=$REGION
gcloud compute backend-services add-backend my-ssl-backend \
--region=$REGION \
--network-endpoint-group=my-private-neg \
--network-endpoint-group-region=$REGION
Step 6: Create IP Map and Forwarding Rule¶
-
URL Map:
gcloud compute url-maps create my-ssl-url-map \ --default-service=my-ssl-backend \ --region=$REGION -
Target HTTPS Proxy: Link the URL map and the SSL certificate.
gcloud compute target-https-proxies create my-https-proxy \ --url-map=my-ssl-url-map \ --ssl-certificates=my-ssl-cert \ --region=$REGION -
Forwarding Rule: Create the rule to listen on port 443 (HTTPS).
gcloud compute forwarding-rules create my-ssl-forwarding-rule \ --load-balancing-scheme=EXTERNAL_MANAGED \ --network-tier=STANDARD \ --address=my-ssl-lb-ip \ --target-https-proxy=my-https-proxy \ --ports=443 \ --region=$REGION
Step 7: Configure DNS¶
Go to your DNS provider and create an A Record pointing your domain (test.gcp.devopspilot.com) to the Load Balancer IP ($LB_IP).
Once the DNS propagates, Google will verify domain ownership and provision the SSL certificate. This can take anywhere from 15 minutes to a few hours.
Check certificate status:
gcloud compute ssl-certificates describe my-ssl-cert \
--region=$REGION \
--format="get(managed.status, managed.domainStatus)"
Step 8: Verify¶
Once the certificate is ACTIVE:
- Public URL: Visit
https://test.gcp.devopspilot.com. You should see the "Hello World" app. - Direct Cloud Run URL: Try visiting the Cloud Run service URL directly (e.g.,
https://private-service-xxxx.a.run.app). You should receive a 403 Forbidden error, confirming the private ingress restriction works.
Quiz¶
Which flag restrictions traffic to the Cloud Run service so it only accepts requests from the Load Balancer?
Cleanup¶
gcloud compute forwarding-rules delete my-ssl-forwarding-rule --region=$REGION --quiet
gcloud compute target-https-proxies delete my-https-proxy --region=$REGION --quiet
gcloud compute url-maps delete my-ssl-url-map --region=$REGION --quiet
gcloud compute backend-services delete my-ssl-backend --region=$REGION --quiet
gcloud compute network-endpoint-groups delete my-private-neg --region=$REGION --quiet
gcloud compute ssl-certificates delete my-ssl-cert --region=$REGION --quiet
gcloud compute addresses delete my-ssl-lb-ip --region=$REGION --quiet
gcloud run services delete $SERVICE_NAME --region=$REGION --quiet
📬 DevopsPilot Weekly — Learn DevOps, Cloud & Gen AI the simple way.
👉 Subscribe here