Deploy workspace and e6data
Deploy an e6data workspace on AKS - namespace, TLS, service accounts and RBAC, NamespaceConfig, QueryRouter, and DNS, using explicit kubectl and YAML.
With the infrastructure and platform components in place, deploy a workspace. Run this part once per workspace, in order: namespace → TLS → service accounts and RBAC → NamespaceConfig → NVMe init (optional) → QueryRouter → AccessToken (optional) → DNS.
Step 0: Set workspace variables
These commands re-derive the Part 1 values, so this works in a fresh shell. Set PREFIX, RESOURCE_GROUP, and WORKSPACE_NAME to the same values you used in Part 1.
export PREFIX="e6data"
export RESOURCE_GROUP="${PREFIX}-rg"
export LOCATION="eastus"
export WORKSPACE_NAME="my-workspace"
export NAMESPACE="${WORKSPACE_NAME}"
export TENANT_NAME="<tenant-name-from-e6data>"
export CONSOLE_HOSTNAME="e6data.yourdomain.com" # the workspace endpoint
export IMAGE_REPOSITORY="e6labs.azurecr.io"
# Metadata storage account (from Part 1 Step 7)
export STORAGE_ACCOUNT=$(echo "${PREFIX}workspaces" | tr -d '-' | tr '[:upper:]' '[:lower:]' | cut -c1-24)
# Workspace Managed Identity (from Part 1 Step 8). PREFIX, WORKSPACE_NAME, and RESOURCE_GROUP
# must match Part 1 exactly, or this returns "ResourceNotFound".
export WORKSPACE_IDENTITY_CLIENT_ID=$(az identity show \
--resource-group $RESOURCE_GROUP \
--name ${PREFIX}-${WORKSPACE_NAME}-engine-identity \
--query clientId -o tsv)
# e6data control plane - the console polls this for cluster create/suspend/resume commands
export AGENT_API_BASE_URL="https://app.e6.run"
export AGENT_POLL_INTERVAL="30s"
# Console super-admin(s) - REQUIRED, comma-separated emails. Without this, logins land with
# no admin access. Use your own login email.
export AUTHZ_SUPER_ADMINS="[email protected]"
# JWT auth - REQUIRED. The load balancer is L4, so Envoy enforces auth itself.
# Values provided by e6data during onboarding.
export JWT_ISSUER="https://app.e6.run"
export JWT_JWKS_URI="https://app.e6.run/.well-known/jwks.json"
export JWT_AUDIENCE="e6-controlplane"
# Image tags - use the versions from your e6data release notes
export CONSOLE_IMAGE_TAG="1.0.4603"
export ENVOY_IMAGE_TAG="1.37.1-pgrouter-e6.36"
export XDS_IMAGE_TAG="1.0.350"
export ENVOY_INSTANCE_TYPE="Standard_D4ps_v5"NAMESPACE must stay equal to WORKSPACE_NAME - the federated identity credentials from Part 1 Step 8 are bound to service accounts in exactly that namespace. If you change it, Workload Identity authentication fails.
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 named envoy-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 sure fullchain.pem includes the full chain (leaf plus intermediates):
If your certificate is stored in Azure Key Vault, download it first, split it into 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 the letsencrypt-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 the envoy-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 as rbac.yaml and apply with envsubst < rbac.yaml | kubectl apply -f -.
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 taint workspace-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 the L 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. The merged-single-port mode fronts gRPC, HTTP, and PostgreSQL on port 443 behind one load balancer.
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:
For a private/internal load balancer, put the A-record in a Private DNS zone (or an internal resolver), since a public record pointing at a 10.x address only resolves usefully from inside the VNet.
Verify the deployment
Expected pods once active: 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.
If something doesn't come up cleanly, see Troubleshooting.
Last updated
Was this helpful?

