Skip to main content

GCP Marketplace — Customer Pre-Deployment Checklist

Purpose: Step-by-step guide for preparing your GKE cluster before deploying OpenDSO from the GCP Marketplace. Complete every step and verify each checkpoint before clicking Deploy in the Marketplace UI.

Time estimate: 30–60 minutes for a fresh cluster (mostly waiting on DNS propagation).

For the full from-scratch Helm path (without the Marketplace), see Deploying OpenDSO on GCP with Helm. For the technical reference behind the Marketplace package, see the GCP Marketplace — Package Reference.


Before You Start

Request the following from Open Energy Solutions before proceeding:

  • OpenDSO License Key (license.key) — required to activate the platform
  • OpenDSO Installation Key (installation.key) — required to activate the platform
  • Confirmation that the Marketplace listing is active and your GCP project is authorized to deploy it (Model A), or that OES has granted your cluster image pull access (Model B — see Step 5)
  • Your base domain — e.g. opendso.example.com. All service endpoints derive from this (e.g. api.opendso.example.com, keycloak.opendso.example.com)

Step 1 — GKE Cluster

  • GKE cluster is running with Kubernetes 1.24 or later
  • kubectl is configured and pointed at the target cluster
# Verify cluster access
kubectl cluster-info
kubectl version --short
  • Cluster has sufficient capacity for the chosen resource profile:
ProfileApproximate node requirement
minimal2× e2-standard-4 (8 vCPU / 32 GB total)
default3× e2-standard-4
production4–6× e2-standard-8 (with autoscaling recommended)
  • Target namespace created:
kubectl create namespace <namespace>

Step 2 — nginx Ingress Controller

OpenDSO requires an nginx ingress controller with an external LoadBalancer IP.

  • nginx ingress controller is installed:
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress-nginx ingress-nginx/ingress-nginx \
-n ingress-nginx --create-namespace \
--set controller.service.type=LoadBalancer
  • LoadBalancer IP is assigned (may take 1–2 minutes):
kubectl get svc -n ingress-nginx ingress-nginx-controller \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}'

Record this IP — you will need it for DNS in Step 3.

LoadBalancer IP: _______________________


Step 3 — DNS Configuration

All OpenDSO endpoints use subdomains of your base domain. A single wildcard DNS record covers all of them.

  • Create a wildcard A record in your DNS provider:
*.opendso.example.com  →  <LoadBalancer IP>
  • Verify DNS propagation (allow 2–30 minutes depending on TTL):
# Replace with your actual domain
nslookup test.opendso.example.com
# or
dig +short test.opendso.example.com

Expected result: the LoadBalancer IP address.

Note: DNS must be propagated before the TLS certificate can be issued in Step 4. For the recommended wildcard certificate path on GKE, use DNS-01, not HTTP-01.

Adding the wildcard A record

In your DNS provider's management console, create an A record:

FieldValue
TypeA
Name / Host*.opendso (or * if your provider scopes records to the base domain automatically)
Value / Points to<LoadBalancer IP from Step 2>
TTL600 seconds (10 minutes) — lower TTL speeds up initial propagation; raise it after confirming everything works

Scope: This wildcard record covers one level of subdomain only — e.g. api.opendso.example.com matches, but foo.api.opendso.example.com does not. OpenDSO only needs one level, so this is sufficient.

cert-manager DNS-01: Many DNS providers do not have a built-in cert-manager solver. For automated wildcard TLS renewal via DNS-01, you have two options:

  • Use a community cert-manager webhook for your DNS provider (if one exists)
  • Delegate the subdomain zone to Cloud DNS (add NS records at your registrar pointing opendso.example.com to Google's nameservers) and use cert-manager's built-in cloudDNS solver as shown in Step 4

Delegation is the more reliable long-term path if you already use GCP.

Delegating a subdomain to Cloud DNS (optional — enables cert-manager DNS-01)

Use this path if you want cert-manager to manage DNS-01 challenges automatically via Cloud DNS, regardless of where your domain is registered.

1. Create a managed zone in Cloud DNS for your subdomain

gcloud dns managed-zones create opendso-zone \
--dns-name="opendso.example.com." \
--description="OpenDSO subdomain zone" \
--project=<gcp-project-id>

2. Retrieve the nameservers Cloud DNS assigned to the zone

gcloud dns managed-zones describe opendso-zone \
--project=<gcp-project-id> \
--format="value(nameServers)"

You will get four nameservers, e.g.:

ns-cloud-a1.googledomains.com.
ns-cloud-a2.googledomains.com.
ns-cloud-a3.googledomains.com.
ns-cloud-a4.googledomains.com.

3. Add NS records at your registrar pointing to those nameservers

In your DNS provider's management console, add four NS records — one per nameserver:

FieldValue
TypeNS
Name / Hostopendso (the subdomain prefix; your provider appends .example.com)
Valueone nameserver per record, e.g. ns-cloud-a1.googledomains.com.
TTL3600

Repeat for all four nameservers.

Some DNS providers require the trailing dot on nameserver values; others do not. Check your provider's documentation if the field rejects the value.

4. Verify delegation has propagated

# Should return the four Cloud DNS nameservers, not your registrar's
dig NS opendso.example.com +short

Allow up to 30 minutes for your registrar's TTL to expire.

5. Move your wildcard A record into Cloud DNS

Once delegation is active, your registrar no longer serves DNS for opendso.example.com. Add the wildcard A record to Cloud DNS instead:

gcloud dns record-sets create "*.opendso.example.com." \
--zone=opendso-zone \
--type=A \
--ttl=600 \
--rrdatas=<LoadBalancer IP> \
--project=<gcp-project-id>

6. Grant cert-manager permission to modify the zone

cert-manager needs IAM access to create and delete TXT records for DNS-01 challenges.

# Create a dedicated service account
gcloud iam service-accounts create cert-manager-dns \
--display-name="cert-manager DNS-01 solver" \
--project=<gcp-project-id>

# Grant it DNS admin on the specific zone
gcloud projects add-iam-policy-binding <gcp-project-id> \
--member="serviceAccount:cert-manager-dns@<gcp-project-id>.iam.gserviceaccount.com" \
--role="roles/dns.admin"

# Create and download a key
gcloud iam service-accounts keys create cert-manager-dns-key.json \
--iam-account=cert-manager-dns@<gcp-project-id>.iam.gserviceaccount.com

# Store the key as a Kubernetes secret
kubectl create secret generic clouddns-dns01-solver-svc-acct \
--from-file=key.json=cert-manager-dns-key.json \
-n cert-manager

If your GKE cluster uses Workload Identity you can bind the cert-manager service account to the GCP service account instead of using a key file. See cert-manager GKE Workload Identity docs for that path.

After completing these steps, continue to Step 4 and use the cloudDNS ClusterIssuer example with hostedZoneName: opendso-zone.


Step 4 — TLS Certificate

OpenDSO requires a Kubernetes TLS secret named <release-name>-tls-secret in the target namespace. The release name is what you will enter as "Application instance name" in the Marketplace UI — decide on it now.

Release name: _______________________ (e.g. opendso)

Production recommendation:

  • use a real certificate before deployment
  • do not rely on the chart's self-signed fallback for customer production installs
  • treat the self-signed path as a test-only safety net for installer resilience

Use DNS-01 if you want a certificate for *.opendso.example.com. HTTP-01 does not work for wildcard certificates.

  • Install cert-manager:
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install cert-manager jetstack/cert-manager \
-n cert-manager --create-namespace \
--set installCRDs=true
  • Verify cert-manager pods are running:
kubectl get pods -n cert-manager
  • Create a ClusterIssuer for Let's Encrypt using a DNS solver

The exact dns01 solver depends on your DNS provider. On GKE, the important point is the challenge type:

  • use dns01 for wildcard certificates
  • request both the base domain and wildcard SANs
  • verify your DNS provider credentials or webhook solver are configured before requesting the certificate

Example shape only:

# letsencrypt-issuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: <your-email>
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- dns01:
# configure the solver for your DNS provider here
# example: Cloud DNS, Route53, Cloudflare, or a cert-manager webhook
<your-dns-solver>: {}

GKE / Google Cloud DNS example:

# letsencrypt-issuer-gcp.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: <your-email>
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- dns01:
cloudDNS:
project: <gcp-project-id>
hostedZoneName: <cloud-dns-zone-name>

If you use Cloud DNS, ensure cert-manager has IAM permission to modify DNS records for the target managed zone.

kubectl apply -f letsencrypt-issuer.yaml
  • Request a certificate for your domain:
# opendso-cert.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: opendso-tls
namespace: <namespace>
spec:
secretName: <release-name>-tls-secret
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- "opendso.example.com"
- "*.opendso.example.com"
kubectl apply -f opendso-cert.yaml
  • Wait for certificate to be issued (2–5 minutes):
kubectl get certificate -n <namespace> -w
# READY column should show: True
  • Confirm the resulting secret name matches the Marketplace release name exactly:
kubectl get secret <release-name>-tls-secret -n <namespace>

Option A1 — HTTP-01 (non-wildcard testing only)

If you are only testing a single hostname and are not using the full wildcard model, HTTP-01 can be used. That is not the recommended production configuration for OpenDSO because the platform expects multiple subdomains such as:

  • api.<domain>
  • keycloak.<domain>
  • grafana.<domain>
  • nats.<domain>
  • UI app subdomains

Option B — Self-signed certificate (testing only)

  • Generate and install a self-signed cert using mkcert:
mkcert "*.opendso.example.com" "opendso.example.com"
kubectl create secret tls <release-name>-tls-secret \
--cert=_wildcard.opendso.example.com.pem \
--key=_wildcard.opendso.example.com-key.pem \
-n <namespace>

Option C — Chart-generated self-signed fallback (last resort, non-production)

If no TLS secret exists, the Marketplace chart can generate a self-signed fallback certificate in some install paths. This behavior is useful for mpdev verify and controlled test deployments, but it should not be your target production configuration.

Use it only when:

  • you are validating installer behavior
  • you are testing in a non-production environment
  • you understand browsers and strict TLS clients will not trust the certificate

Verify TLS secret exists

  • Confirm the secret is present before proceeding:
kubectl get secret <release-name>-tls-secret -n <namespace>

Step 5 — Image Pull Access

All OpenDSO images are hosted by OES in their GCP Artifact Registry. Your GKE cluster needs read access to pull them. How access is granted depends on your deployment model.

Model A — Deploying via GCP Marketplace listing (standard)

No action required on your side for image access. When you deploy through the Marketplace UI, GCP automatically grants your cluster pull access to the OES image registry. The Image Registry field in the Marketplace UI will be pre-populated.

  • Confirm with OES that the Marketplace listing is active and your GCP project is authorized

Model B — Direct / private install

OES must grant your GKE node service account pull access to their registry. Complete these steps and send the output to OES before proceeding.

  • Find your GKE node service account email:
gcloud container clusters describe <cluster-name> \
--zone=<zone> --project=<project-id> \
--format='value(nodeConfig.serviceAccount)'

If the output is default, your full service account email is: <project-number>-compute@developer.gserviceaccount.com

Find your project number with:

gcloud projects describe <project-id> --format='value(projectNumber)'
  • Send the service account email to OES and wait for confirmation that access has been granted
  • Once OES confirms, verify your node can pull a test image:
gcloud auth configure-docker us-central1-docker.pkg.dev
docker pull us-central1-docker.pkg.dev/<PROJECT_ID>/oesinc/nats:<tag>

Step 6 — Application CRD

The GCP Marketplace deployer requires the app.k8s.io/v1beta1 Application CRD.

  • Install the Application CRD:
kubectl apply -f "https://raw.githubusercontent.com/GoogleCloudPlatform/marketplace-k8s-app-tools/master/crd/app-crd.yaml"
  • Verify it is registered:
kubectl get crd applications.app.k8s.io

Step 7 — Final Pre-Deployment Checklist

Run through this summary before clicking Deploy in the Marketplace UI:

ItemCheck
License Key and Installation Key obtained from OES
GKE cluster running (Kubernetes 1.24+)
Target namespace created
nginx ingress controller installed, LoadBalancer IP assigned
Wildcard DNS *.<domain> → LoadBalancer IP, propagated
<release-name>-tls-secret exists in target namespace
Image pull access confirmed with OES (Model B) or Marketplace listing active (Model A)
Application CRD (app.k8s.io/v1beta1) installed

Step 8 — Marketplace UI Parameters

When you click Deploy, you will be prompted for the following. Have these values ready:

ParameterNotes
Application instance nameYour chosen release name — must match the TLS secret prefix (e.g. opendso)
NamespaceYour target namespace
Domain NameYour base domain (e.g. opendso.example.com)
OpenDSO License KeyProvided by OES
OpenDSO Installation KeyProvided by OES
Keycloak Admin PasswordChoose a strong password
MongoDB Root PasswordChoose a strong password
MongoDB App PasswordChoose a strong password
Grafana Admin PasswordChoose a strong password
Resource Profileminimal / default / production
Image RegistryModel A: pre-populated by Marketplace. Model B: us-central1-docker.pkg.dev/<PROJECT_ID>/oesinc

Post-Deployment Verification

After the deployer completes, run the bundled verification script:

bash scripts/verify.sh <release-name> <namespace>

The script checks:

  1. MongoDB StatefulSet readiness
  2. Keycloak and NATS Deployment readiness
  3. GMS API, Historian, and NATS Auth Service readiness
  4. NATS auth keys secret existence
  5. Keycloak OIDC discovery endpoint (via port-forward)
  6. GMS API health endpoint (via port-forward)

On success, all services will be accessible at https://*.<your-domain>.