AWS Bootstrap Installation
Deploy Konstruct on an AWS EKS cluster. Three steps: create an EKS cluster, create a GitHub App, then run Terraform.
Prerequisites
- AWS account with credentials configured (
aws configure) - Terraform 1.5+
kubectlv1.30+eksctl(install guide)- A domain you control (registrar access to update NS records)
Step 0: Create an EKS Cluster
If you already have an EKS cluster, skip to Step 1.
eksctl create cluster \
--name konstruct-mgmt \
--region us-west-2 \
--version 1.35 \
--with-oidc \
--nodegroup-name konstruct-nodes \
--node-type m5.xlarge \
--nodes 3 \
--nodes-min 3 \
--nodes-max 5 \
--managed
This takes ~15-20 minutes.
Cluster requirements:
- Minimum 3x
m5.xlargenodes (the platform deploys ~10 services: ESO, cert-manager, ingress-nginx, external-dns, Dex, and the Konstruct platform with 6 operators) - OIDC provider enabled (required for IRSA — see below)
- No special VPC, subnet, or EBS CSI driver requirements
Once the cluster is ready, verify nodes:
kubectl get nodes
# Expect 3 nodes in Ready state
Enable the OIDC provider (required for IRSA, not enabled by default):
eksctl utils associate-iam-oidc-provider \
--cluster konstruct-mgmt --region us-west-2 --approve
Then record the OIDC endpoint for Terraform:
aws eks describe-cluster --name konstruct-mgmt --region us-west-2 \
--query 'cluster.identity.oidc.issuer' --output text
Step 1: Create a GitHub App
- Go to GitHub > Settings > Developer settings > GitHub Apps > New GitHub App
- Configure:
- Name:
konstruct-<your-domain>(must be unique across GitHub) - Homepage URL:
https://konstruct.<your-domain> - Callback URL:
https://konstruct.<your-domain>/api/v1/github-app/callback - Setup URL:
https://konstruct.<your-domain>/api/v1/github-app/callback - Check "Request user authorization (OAuth) during installation"
- Check "Redirect on update"
- Webhook: Uncheck "Active" (not needed for bootstrap)
- Permissions:
- Repository: Administration (Read & write), Contents (Read & write), Metadata (Read-only)
- Organization: Members (Read-only)
- Where can this GitHub App be installed? Only on this account
- Name:
- After creation, note:
- App ID
- Client ID
- Client Secret (generate one)
- Generate a private key (downloads a
.pemfile)
Do not install the GitHub App on your organization yet. That happens during onboarding in the Konstruct UI.
Step 2: Run Terraform
Terraform handles AWS infrastructure, platform services, and the Konstruct installation.
git clone https://github.com/konstructio/konstruct-aws.git
cd konstruct-aws/konstruct-bootstrap-job-role
Generate a bcrypt password hash
The dex_config.admin_password field requires a bcrypt hash of your desired login password:
htpasswd -nbBC 10 "" 'your-password' | tr -d ':\n' | sed 's/$2y/$2a/'
Copy the output (starts with $2a$10$...) — you'll use it in the tfvars below. Remember the plain text password — you'll need it to login to Konstruct as the kbot user.
Generate a shared OIDC secret
The oidc_client_secret in konstruct_api_config must match the secret in dex_config.static_clients[0]. Generate one:
openssl rand -hex 32
Configure terraform.tfvars
cluster_oidc_endpoint = "https://oidc.eks.us-west-2.amazonaws.com/id/YOUR_OIDC_ID"
cluster_name = "konstruct-mgmt"
region = "us-west-2"
domain = "your-domain.com"
alerts_email = "alerts@your-domain.com"
git_provider = "github"
# DNS: "aws" for Route53 (default), "cloudflare" for Cloudflare
dns_provider = "aws"
# cloudflare_api_token = "your-token" # Required only if dns_provider = "cloudflare"
# Path to the .pem file downloaded in Step 1
github_app_private_key_file = "path/to/your-app.private-key.pem"
konstruct_api_config = {
admin_user = "kbot"
client_email = "admin@your-domain.com"
github_app_client_id = "Iv1.abc123" # From Step 1
github_app_client_secret = "your-client-secret" # From Step 1
github_app_id = "123456" # From Step 1
github_app_name = "konstruct-your-domain-com" # From Step 1
license_validator_url = ""
oidc_client_id = "konstruct"
oidc_client_secret = "YOUR_SHARED_SECRET" # From openssl command above
webhook_url = ""
}
dex_config = {
admin_password = "$2a$10$..." # bcrypt hash from htpasswd command above
# Azure AD (optional — leave empty if not using Azure SSO)
azure_client_id = ""
azure_client_secret = ""
azure_tenant_id = ""
static_clients = [
{
id = "konstruct"
name = "Konstruct"
public = false
redirectURIs = [
"https://konstruct.your-domain.com/api/v1/auth/callback"
]
secret = "YOUR_SHARED_SECRET" # Must match oidc_client_secret above
}
]
}
Apply
terraform init
terraform plan # Review what will be created
terraform apply
Open a second terminal and watch pods come up: kubectl get pods -A -w
This creates:
- IRSA roles for all platform services (ESO, cert-manager, external-dns, konstruct operators)
- SSM parameters for secrets
- Route53 hosted zone (if
dns_provider = "aws") or Cloudflare ExternalSecret - S3 state store bucket
- EKS access entries for Argo CD and Atlantis
Namespaces:dex,konstruct-system,kubefirst- CRDs:
helmtemplates,pipelinetemplates - Helm releases: External Secrets Operator, Ingress NGINX, Cert Manager, External DNS, Dex, Konstruct (chart version
0.4.0-rc.551eae9e) - ClusterSecretStore (
konstruct-aws-ssm), ClusterIssuer (letsencrypt-prod) - ExternalSecrets for Dex config and API secrets
kubefirst-initial-statesecret for the bootstrap operator
Step 3: Configure DNS
After terraform apply completes, you must delegate your domain to the Route53 nameservers so that TLS certificates can be issued and services are reachable.
If using Route53 (dns_provider = "aws")
-
Get the nameservers from the Terraform output:
terraform output route53_nameservers -
Go to your domain registrar and update the NS records for your domain to the four nameservers shown (e.g.,
ns-1234.awsdns-56.org,ns-789.awsdns-01.co.uk, etc.) -
Verify DNS delegation is propagating:
dig NS your-domain.comYou should see the AWS nameservers in the response. Full propagation can take 15 minutes to 48 hours depending on your registrar.
-
Check that cert-manager can issue certificates:
kubectl get certificates -A
kubectl get challenges -ACertificates will stay in a
Pendingstate until DNS propagation completes. This is expected. -
If certificates remain
Pendingafter DNS has propagated (confirmed viadig), the cluster's CoreDNS may have a stale cache. Restart it:kubectl rollout restart deployment coredns -n kube-systemThen watch certificates resolve:
kubectl get certificates -A -w
Confirming the full chain
Use this sequence to verify each layer is working:
# 1. Confirm NS delegation is live (should return awsdns nameservers)
dig NS your-domain.com +short
# 2. Confirm Route53 is answering directly
dig SOA your-domain.com @ns-YOUR-NAMESERVER.awsdns-XX.com +short
# 3. Confirm cert-manager challenges are progressing
kubectl get challenges -A
# 4. If challenges show SERVFAIL, restart CoreDNS (stale cache)
kubectl rollout restart deployment coredns -n kube-system
# 5. Confirm certificates are issued
kubectl get certificates -A
If using Cloudflare (dns_provider = "cloudflare")
Cloudflare manages DNS automatically via the API token you provided. No manual delegation is needed, but verify the zone is active in your Cloudflare dashboard.
TLS certificates and ingress URLs will not work until DNS is fully propagated. If you see certificate errors, run kubectl describe challenges -A to see the specific failure reason.
Step 4: Login and Onboard
Allow time for all services to become ready. Expect:
- NLB provisioning: 3-5 minutes after apply
- DNS propagation: 15 minutes to 48 hours
- TLS certificate issuance: a few minutes after DNS resolves
Check readiness:
# All platform pods running
kubectl get pods -n konstruct-system
# Ingress has an external address
kubectl get svc -n ingress-nginx
# TLS certificate is ready
kubectl get certificates -n konstruct-system
Once the certificate shows Ready = True:
- Open
https://konstruct.<your-domain> - Login with username kbot and the plaintext password you hashed in Step 2
- Click Connect with GitHub and install the GitHub App on your organization
- You're on the dashboard — Konstruct is ready
What happens automatically behind the scenes:
- The API stores the GitHub App connection and creates a bootstrap Project
- The team-management-operator hydrates a GitOps repository with platform templates
- Argo CD is installed and takes over management of all platform components
Verify
# All pods running
kubectl get pods -n konstruct-system
# After onboarding completes, ArgoCD manages everything
kubectl get pods -n argocd
kubectl get applications -n argocd
# Check the gitops repo was created in your GitHub org
Troubleshooting
Pods stuck in CrashLoopBackOff
Check logs for the failing pod:
kubectl logs -n konstruct-system <pod-name> --previous
Common causes: missing SSM parameters (check kubectl get externalsecrets -A), IRSA role misconfiguration.
Certificates stuck in Pending
kubectl describe challenges -A
SERVFAIL errors: If challenges show rcode was expected to be 'NOERROR' or 'NXDOMAIN', but got 'SERVFAIL', but dig NS your-domain.com returns the correct AWS nameservers, the cluster's CoreDNS has a stale cache:
kubectl rollout restart deployment coredns -n kube-system
No SERVFAIL: DNS delegation hasn't propagated yet. Verify with dig NS your-domain.com +short and wait.
"kubefirst-initial-state secret not found"
The secret is created by Terraform in the kubefirst namespace. Verify:
kubectl get secret kubefirst-initial-state -n kubefirst