materializeinc/agent-skills

materialize-terraform-self-managed

>- Terraform modules for deploying self-managed Materialize on AWS (EKS), Azure (AKS), and GCP (GKE). Covers networking, Kubernetes clusters, managed PostgreSQL, object storage, the Materialize operator, and Materialize instance configuration. Use this skill whenever the user asks about deploying self-managed Materialize with Terraform, setting up Materialize infrastructure on any cloud, configuring EKS/AKS/GKE for Materialize, troubleshooting a self-managed deployment, upgrading Materialize in…

First seen Jul 19, 2026

Installation

$ npx skills add materializeinc/agent-skills --skill materialize-terraform-self-managed

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from materializeinc/agent-skills · top by installs.

npx skills add materializeinc/agent-skills

Browse all from materializeinc/agent-skills

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 3
License LICENSE
Default branch main
Open issues 0
Status Active

Skill metadata

Parsed from SKILL.md frontmatter.

More metadata
source
https://github.com/MaterializeInc/materialize-terraform-self-managed
verified-against
94a87cc5b377

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 19,081 B
  • docs SUMMARY.md 908 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 90 installs

SKILL.md

Materialize Terraform Self-Managed Modules

Production-ready Terraform modules for deploying Materialize on AWS, Azure, and GCP, maintained in the materialize-terraform-self-managed repository. Each cloud provider has its own set of modules plus shared Kubernetes modules for the operator and Materialize instance.

All file paths in this skill refer to that repository. When you need the actual module code, clone the repository or fetch individual files from GitHub.

Repository Layout

aws/
  modules/         # AWS-specific infra modules
  examples/
    simple/        # Minimal production-like deployment
    enterprise/    # Adds Ory (Kratos + Hydra) for auth
azure/
  modules/         # Azure-specific infra modules
  examples/
    simple/
    enterprise/
gcp/
  modules/         # GCP-specific infra modules
  examples/
    simple/
    enterprise/
kubernetes/
  modules/         # Cloud-agnostic K8s modules (operator, instance, cert-manager)
test/              # Rust-based integration test harness
scripts/           # Migration and utility scripts

How to Use This Skill

  1. User wants to deploy Materialize on a specific cloud: Start with the relevant <cloud>/examples/simple/ directory in the repository. Read main.tf and variables.tf there for the full working configuration.
  2. User asks about a specific module: Read <cloud>/modules/<name>/variables.tf and main.tf for that module.
  3. User wants to customize the instance: Read kubernetes/modules/materialize-instance/variables.tf for all instance-level options.
  4. User asks about upgrades or rollouts: See the Upgrades section below and kubernetes/modules/materialize-instance/variables.tf for rollout config.
  5. User wants enterprise/auth setup: Read <cloud>/examples/enterprise/.
  6. User has an existing Terraform project: See "Using the Modules from an Existing Terraform Project" below.

Architecture Layers

Every deployment creates two layers:

Cloud infrastructure (cloud-specific modules):

  • Networking: VPC/VNet, subnets, NAT, security groups
  • Kubernetes: EKS/AKS/GKE cluster with autoscaling
  • Database: Managed PostgreSQL for Materialize metadata
  • Storage: Object storage (S3/Blob/GCS) for persist layer

Kubernetes application (shared modules under kubernetes/modules/):

  • cert-manager for TLS certificates
  • Materialize Operator (Helm chart)
  • Materialize Instance (custom resource)

Cloud Provider Modules

AWS (aws/modules/)

Module Purpose
networking VPC, subnets, NAT gateways, security groups
eks EKS cluster with OIDC provider
eks-node-group Managed node groups for base workloads
karpenter Karpenter autoscaler controller
karpenter-ec2nodeclass EC2NodeClass for Karpenter provisioning
karpenter-nodepool NodePool for Karpenter scheduling
database RDS PostgreSQL for metadata
storage S3 bucket with IRSA
aws-lbc AWS Load Balancer Controller
nlb Network Load Balancer
operator Materialize operator (Helm)
ebs-csi-driver EBS CSI driver
vpc-cni VPC CNI with network policy support

Key AWS patterns:

  • Node autoscaling via Karpenter (not cluster autoscaler)
  • Two Karpenter node classes: generic (t4g.xlarge) and Materialize (r8gd.2xlarge with NVMe swap)
  • Storage auth via IRSA (IAM Roles for Service Accounts)
  • NLB for external access on ports 6875 (SQL), 6876 (HTTP), 8080 (console)

Azure (azure/modules/)

Module Purpose
networking VNet, subnets, NAT gateway
aks AKS cluster with Cilium networking
nodepool Additional AKS node pools
database PostgreSQL Flexible Server
storage Storage Account with workload identity
load_balancers Azure Load Balancers
operator Materialize operator (Helm)

Key Azure patterns:

  • Cilium for networking and network policies
  • Workload Identity Federation for storage auth (passwordless OIDC)
  • StandardE4pdsv6 instances for Materialize nodes with swap
  • Private DNS zone for PostgreSQL resolution
  • Requires registering EnableAPIServerVnetIntegrationPreview feature

GCP (gcp/modules/)

Module Purpose
networking VPC, subnets, Cloud NAT
gke GKE cluster with Workload Identity
nodepool Additional GKE node pools
database Cloud SQL PostgreSQL
storage Cloud Storage bucket with HMAC keys
load_balancers GCP Load Balancers
operator Materialize operator (Helm)

Key GCP patterns:

  • HMAC keys for S3-compatible GCS access (these modules use the S3-compatible API)
  • VPC peering for Cloud SQL private access
  • c4a-highmem-8-lssd instances for Materialize nodes with local SSD and swap
  • Secondary IP ranges for pods and services (VPC-native)
  • Requires enabling multiple GCP APIs (container, compute, sqladmin, servicenetworking, etc.)

Kubernetes Modules (kubernetes/modules/)

Module Purpose
cert-manager Installs cert-manager Helm chart
self-signed-cluster-issuer Creates self-signed ClusterIssuer
materialize-instance Deploys Materialize CR
coredns CoreDNS configuration
grafana Grafana monitoring
prometheus Prometheus monitoring stack
hpa Horizontal Pod Autoscaling
ory-* Ory stack (Kratos, Hydra, etc.) for enterprise auth

Materialize Instance Module

This is the most important Kubernetes module. Key variables in kubernetes/modules/materialize-instance/variables.tf:

Variable Default Notes
crd_version v1alpha1 Use v1 for v26.30+. See CRD section below.
instance_name (required) Name of the Materialize CR
instance_namespace (required) Namespace for the instance
metadatabackendurl (required) PostgreSQL connection string
persistbackendurl (required) Object storage URL
license_key null Community or enterprise key
environmentd_version v26.29.0 Materialize version
rollout_strategy WaitUntilReady See Rollout Strategies below
authenticator_kind None Options: None, Password, Sasl, Oidc
cpu_request 1 CPU request for environmentd
memory_request 4095Mi Memory request for environmentd
memory_limit 4Gi Memory limit for environmentd
system_parameters {} Map of system config parameters
issuer_ref null cert-manager issuer for TLS
internalissuerref null Override for internal mTLS certs

Backend URL Formats

The Materialize instance needs two backend URLs:

Metadata backend (PostgreSQL):

postgres://user:password@host:5432/database?sslmode=require&options=-c%20statement_timeout%3D15min

The statement_timeout=15min parameter is required for metadata operations.

Persist backend (object storage):

AWS S3:

s3://bucket-name/system:serviceaccount:namespace:name

Azure Blob:

https://storageaccount.blob.core.windows.net/container

GCP Cloud Storage (via HMAC/S3-compatible API):

s3://hmac-access-id:hmac-secret@bucket-name/materialize?endpoint=https%3A%2F%2Fstorage.googleapis.com&region=us-central1

Storage authentication support: Materialize currently supports IAM-based authentication for the persist backend on AWS (IRSA) and Azure (Workload Identity Federation) only. Native GCS IAM auth is not supported, which is why the GCP modules use HMAC keys with the S3-compatible API.

CRD Versions: v1alpha1 vs v1

v1alpha1 (default, pre-v26.30): Two-step rollout. Change spec, then set request_rollout to a new UUID to trigger the rollout. Gives explicit control over timing.

v1 (recommended for v26.30+): Spec changes automatically trigger rollouts. The operator computes a hash and handles rollout. The requestrollout field is removed. Use forcerollout with a new UUID for manual triggers.

Set via crd_version variable in the example or the materialize-instance module.

Rollout Strategies

Strategy Behavior Resource impact
WaitUntilReady New pods created, cutover when healthy Temporarily doubles resources
ManuallyPromote New pods created, waits for manual forcePromote Temporarily doubles resources
ImmediatelyPromoteCausingDowntime Old pods torn down first No extra resources, causes downtime

Deploying: Quick Start

Each cloud follows the same pattern:

git clone https://github.com/MaterializeInc/materialize-terraform-self-managed.git
cd materialize-terraform-self-managed/<cloud>/examples/simple

# Create terraform.tfvars with required variables (see below)
terraform init
terraform apply

Variables by Cloud

AWS (aws/examples/simple/):

name_prefix = "my-mz"                # required
aws_profile = "my-profile"            # required
license_key = "your-license-key"      # required
tags        = { environment = "dev" } # required (no default)
aws_region  = "us-east-1"            # optional, defaults to us-east-1

Azure (azure/examples/simple/):

subscription_id     = "12345678-..."   # required
resource_group_name = "materialize-rg" # required
name_prefix         = "my-mz"         # required
tags                = { environment = "dev" } # required (no default)
location            = "westus2"        # optional, defaults to westus2
license_key         = "your-key"       # optional (null default), needed for production

GCP (gcp/examples/simple/):

project_id  = "my-gcp-project"        # required
labels      = { environment = "dev" }  # required (no default)
license_key = "your-license-key"      # optional (null default), needed for production
name_prefix = "my-mz"                # optional, defaults to "materialize"
region      = "us-central1"           # optional, defaults to us-central1

Common Optional Variables (all clouds)

Variable Default Purpose
internalloadbalancer true Set false for internet-facing LB
ingresscidrblocks ["0.0.0.0/0"] Restrict access to Materialize ports
k8sapiserverauthorized_networks varies Restrict K8s API access
crd_version v1alpha1 Use v1 for v26.30+
enable_observability false Install Prometheus + Grafana

Using the Modules from an Existing Terraform Project

You do not need to clone the repository or copy the examples into your project. Reference modules directly with a Git source, pinning to a release tag or commit SHA:

module "materialize_instance" {
  source = "github.com/MaterializeInc/materialize-terraform-self-managed//kubernetes/modules/materialize-instance?ref=<tag-or-commit>"

  # module inputs ...
}
  • The // separates the repository URL from the module subdirectory. Any module in the repository can be referenced this way.
  • Always pin ref to a tag or commit SHA. Never track main, or applies become non-reproducible and can pick up breaking changes.
  • To update, first check the version notes for breaking changes between your current ref and the target, then bump the ref, run terraform init -upgrade, and review the plan before applying. See [Upgrading Materialize](#upgrading-materialize) for the full path.
  • Use the relevant <cloud>/examples/simple/main.tf as the reference for how the modules compose, then reproduce that composition in your own project with pinned Git sources. This is the pattern Materialize uses for its own internal deployments.

Post-Deployment Setup

Configure kubectl

After terraform apply, configure kubectl to talk to the new cluster:

AWS:

aws eks update-kubeconfig --name $(terraform output -raw eks_cluster_name) --region <region>

Azure:

az aks get-credentials --resource-group <rg> --name $(terraform output -raw aks_cluster_name)

GCP:

gcloud container clusters get-credentials $(terraform output -raw gke_cluster_name) --region <region>

Connecting to Materialize

Ports:

  • 6875: PostgreSQL-compatible SQL (pgwire)
  • 6876: HTTP API
  • 8080: Materialize Console (HTTPS)

With a public (internet-facing) load balancer (internalloadbalancer = false):

AWS (uses NLB DNS):

psql "postgres://mz_system@$(terraform output -raw nlb_dns_name):6875/materialize"
open "https://$(terraform output -raw nlb_dns_name):8080/materialize"

Azure / GCP (uses load balancer IPs):

psql "postgres://mz_system@$(terraform output -raw balancerd_load_balancer_ip):6875/materialize"
open "https://$(terraform output -raw console_load_balancer_ip):8080/materialize"

With a private (internal) load balancer (the default):

Use kubectl port-forwarding. The resource ID is in terraform output materializeinstanceresource_id:

kubectl port-forward svc/mz<resource-id>-balancerd 6875:6875 -n materialize-environment
psql "postgres://mz_system@localhost:6875/materialize"
kubectl port-forward svc/mz<resource-id>-console 8080:8080 -n materialize-environment
open "http://localhost:8080"

Use the externalloginpasswordmzsystem output for the password when authentication is enabled. Create dedicated users after initial setup; avoid using mz_system for regular operations.

Observability

When enable_observability = true, Prometheus and Grafana are deployed in the monitoring namespace with pre-configured Materialize dashboards:

kubectl port-forward svc/grafana 3000:80 -n monitoring
# Username: admin, Password: terraform output -raw grafana_admin_password

Upgrading Materialize

  1. Check the version notes for the target version first. Some versions have breaking changes or special upgrade requirements that must be handled before bumping.
  2. Upgrade one minor version at a time for versions before v26. From v26+ you can skip minor versions.
  3. Downgrading is not supported.
  4. Upgrade order: operator first, then instances.

With Terraform: Update environmentdversion (and optionally operatorversion) in your variables, then terraform apply. For v1alpha1, also update request_rollout to a new UUID.

For the full upgrade procedure, see the materialize-docs skill in this repository at skills/materialize-docs/self-managed-deployments/upgrading/index.md (with per-cloud guides in the sibling upgrade-on-* directories), or the online upgrading documentation. Always review the version notes for breaking changes before upgrading.

Instance Sizing

Materialize nodes should use memory-optimized instances with NVMe local storage for swap:

Cloud Instance Type vCPUs Memory Max cluster size
AWS r8gd.2xlarge 8 64 GiB ~300cc
AWS r8gd.4xlarge 16 128 GiB ~600cc
AWS r8gd.8xlarge 32 256 GiB ~1200cc
AWS r8gd.16xlarge 64 512 GiB ~3200cc
Azure StandardE4pdsv6 4 32 GiB varies
GCP c4a-highmem-8-lssd 8 64 GiB varies

ARM-based CPUs with a 1:8 vCPU-to-memory ratio and 8:1 local-storage-to-memory ratio are recommended.

Common Gotchas

  • ECR auth conflicts: If terraform apply fails with 403 pulling public images, run docker logout public.ecr.aws and retry.
  • GCP APIs: You must enable multiple GCP APIs before running Terraform (container, compute, sqladmin, servicenetworking, iamcredentials, iam, storage).
  • Azure preview feature: Register EnableAPIServerVnetIntegrationPreview before deploying on Azure.
  • Self-signed certs: The simple examples use self-signed TLS. For production, use a real CA or ACME issuer. When using a public ACME issuer (like Let's Encrypt), set internalissuerref separately because public CAs cannot sign *.cluster.local names.
  • statementtimeout: The metadata backend URL must include statementtimeout=15min or metadata operations may time out.
  • Console slow loads: If the Console UI is slow, increase mzcatalogserver cluster size from 25cc to 50cc via internal SQL port 6877.

Troubleshooting

Check operator status:

kubectl -n materialize get all
kubectl -n materialize logs -l app.kubernetes.io/name=materialize-operator

Check Materialize instance:

kubectl -n materialize-environment get all
kubectl -n materialize-environment logs <pod-name>
kubectl -n materialize-environment describe pod/<pod-name>

Check Materialize CR status:

kubectl get materialize -n materialize-environment -o jsonpath='{.items[0].status}'

The CR status should show UpToDate when healthy.

Testing

The test harness in test/ uses Rust and runs full lifecycle tests (init, apply, verify, destroy):

cd test
cargo run -- run aws --owner "Name" --license-key-file key.txt \
  --aws-region us-east-1 --aws-profile my-profile

Verification checks: Materialize CR status is UpToDate, all pods (environmentd, console, balancerd, clusterd) are Running, and SELECT 1 succeeds over SQL.

Keeping This Skill Up to Date

The verified-against value in the frontmatter metadata records the upstream commit this skill was last verified against. To refresh the skill:

  1. Diff the upstream repository from that commit to current main, focusing on variables.tf files, example configurations, and READMEs.
  2. Update the affected sections here (variable defaults, module tables, instance types, gotchas).
  3. Bump verified-against to the new commit SHA.