Step 0: Set workspace variables
These commands re-derive the Part 1 values, so this works in a fresh shell. SetPREFIX, RESOURCE_GROUP, and WORKSPACE_NAME to the same values you used in Part 1.
Step 1: Create the namespace
Step 2: Create the TLS certificate
The workspace endpoint is served over HTTPS by Envoy, which terminates TLS in-cluster using a Kubernetes secret namedenvoy-tls. Unlike AWS - where the NLB can terminate TLS with an ACM cert - the Azure load balancer is L4 and passes through, so the certificate always lives in the cluster. Every option below produces the same envoy-tls secret covering ${CONSOLE_HOSTNAME}. Choose one.
Option A: e6data-provided wildcard certificate
If your workspace endpoint is on an e6data domain (for example*.e6.run), e6data provides a CA-signed wildcard certificate. Load it into the secret:
Option B: Customer-procured certificate
A certificate from your own CA (DigiCert, Namecheap, Sectigo, or a corporate CA). Works with any domain. Make surefullchain.pem includes the full chain (leaf plus intermediates):
tls.crt and tls.key, then create the secret the same way.
Option C: Let’s Encrypt via cert-manager
cert-manager obtains and auto-renews a free Let’s Encrypt certificate using a DNS01 challenge through Azure DNS. This requires theletsencrypt-prod ClusterIssuer (a public domain in an Azure DNS zone you control, plus a cert-manager identity with DNS Zone Contributor):
Option D: Azure Key Vault + Secrets Store CSI
The closest Azure analog to AWS ACM: Key Vault owns and auto-rotates the certificate, and the Secrets Store CSI driver syncs it into the cluster as theenvoy-tls secret. Best for teams already standardized on Key Vault for certificate lifecycle. Enable the azure-keyvault-secrets-provider addon, then create a SecretProviderClass that targets your certificate and sets secretObjects to sync a kubernetes.io/tls secret named envoy-tls. The exact SecretProviderClass is environment-specific - ask your e6data onboarding engineer for a template.
Step 3: Create service accounts and RBAC
Create the core service accounts (engine, monitoring, console, query-router) - each annotated with the Workload Identity client ID from Part 1 - plus their Roles and Bindings. Save asrbac.yaml and apply with envsubst < rbac.yaml | kubectl apply -f -.
This creates the core service accounts. When you enable add-ons (Laminar, Copilot, Metriq), add their service accounts (and Laminar’s Role) here, and add a federated credential for each against the workspace identity (Part 1 Step 8).
Step 4: Apply the NamespaceConfig
The NamespaceConfig is the workspace’s root custom resource. Once applied, the e6-operator creates the console, the compaction job, and (on demand) the MetadataServices:storageBackend uses the wasbs://<container>@<account>.dfs.core.windows.net URI from Part 1 Step 7. The serviceAccounts block lists the core components; add laminar, copilot, and metriq keys when you enable those add-ons.
Step 5: Engine NodeClass and NodePool
Already created in Part 1 Step 6C.5 (Karpenter). If you chose a static pool or NAP instead, make sure its nodes carry the label and taintworkspace-name=${WORKSPACE_NAME} so the NamespaceConfig’s nodeSelector and tolerations match.
Step 6: NVMe init DaemonSet (only for NVMe VM families)
If your engine NodePool includes theL family (Lsv3/Lasv3, local NVMe), this DaemonSet formats and RAIDs the NVMe drives and mounts them at /app/tmp:
Step 7: Deploy the QueryRouter
The QueryRouter deploys Envoy (data path, TLS termination, JWT enforcement) and the xDS control plane. The external load balancer is L4, so JWT auth in the QueryRouter is required - Envoy authenticates every request. Themerged-single-port mode fronts gRPC, HTTP, and PostgreSQL on port 443 behind one load balancer.
Public by default. With no
azure-load-balancer-internal annotation, Azure gives the load balancer a public IP (JWT auth still protects every request). Point your DNS A-record at that public IP.To instead restrict it to a private endpoint reachable only inside your VNet, add service.beta.kubernetes.io/azure-load-balancer-internal: "true" to mergedService.annotations, and reach it through the VNet (jumpbox, VPN, or peering). The DNS record then points at the private IP.Step 8: Create an AccessToken (optional)
Static tokens for HTTP/JDBC query authentication:Step 9: Configure DNS
Point the workspace hostname at the load balancer’s IP address:Use the
EXTERNAL-IP (.status.loadBalancer.ingress[0].ip), not the ClusterIP. With the public default this is a real public address - not a 172.20.x (cluster-internal ClusterIP). A 10.x private VNet IP appears only if you kept the load balancer internal.10.x address only resolves usefully from inside the VNet.
Verify the deployment
console-*, ${WORKSPACE_NAME}-qr-envoy-*, ${WORKSPACE_NAME}-qr-xds-*, and mds-schema-* / mds-storage-* (after the first MetadataServices is created). Metadata pods may take 1–2 minutes to appear.
Next
- Register a catalog - connect your data.
- Create a cluster - provision compute.
- Run your first query - validate the install.