This repository is a fork-and-deploy kit for running Plane Community Edition on AWS with GitHub Actions, Terraform, and Helm.
The default path is intentionally simple:
- Fork the repository.
- Create one AWS IAM role trusted by GitHub OIDC for your fork's
mainbranch. - Add that role ARN as
AWS_GITHUB_OIDC_ROLE_ARN. - Run
Terraform Bootstraponce. - Run
Terraform Prodto create infrastructure and install Plane.
No long-lived AWS access keys are required for the default AWS-managed deployment.
Terraform creates:
- VPC, public/private subnets, NAT gateways, route tables, and Kubernetes subnet tags
- EKS cluster, managed node group, EBS CSI addon, and IRSA roles
- Optional AWS-managed Plane dependencies: RDS PostgreSQL, Amazon MQ RabbitMQ, ElastiCache Redis, and S3 docstore
- Secrets Manager entries for generated service credentials and Plane app secrets
- Terraform state backend resources through the bootstrap workflow
Helm installs:
- Plane CE through the wrapper chart in
helm/plane - A
gp3-csiStorageClass - AWS Load Balancer Controller-backed ALB ingress
- metrics-server and Cluster Autoscaler
This dedicated-cluster path is the default. Organizations that already have a platform EKS cluster can set CREATE_EKS_CLUSTER=false and deploy Plane into that cluster instead.
CI: runs on pull requests and pushes tomain; it does not request AWS credentials or GitHub secrets.Terraform Bootstrap: manual only; creates/imports the S3 state bucket and DynamoDB lock table.Terraform Prod: manual only; runsplanorapplyfor AWS infrastructure and the Plane Helm release.
This split keeps public fork PRs reviewable without exposing repository secrets.
Create this GitHub repository secret after creating the AWS OIDC role:
AWS_GITHUB_OIDC_ROLE_ARN: IAM role ARN assumed by GitHub Actions
The defaults work for the standard AWS-managed path.
AWS_REGION: defaulteu-west-1PROJECT_NAME: defaultplaneDEPLOY_ENVIRONMENT: defaultprodTF_STATE_BUCKET: optional explicit Terraform state bucketTF_STATE_BUCKET_PREFIX: defaultplane-cloud-platform-tfstateTF_STATE_KEY: defaultprod/terraform.tfstateTF_LOCK_TABLE_NAME: defaultplane-cloud-platform-tf-locksHELM_RELEASE_NAME: defaultplaneK8S_NAMESPACE: defaultplanePLANE_APP_HOST: optional custom host; when empty, the ALB DNS name is usable after deployCREATE_EKS_CLUSTER: defaulttrue; setfalseto deploy into an existing EKS clusterEKS_CLUSTER_ADMIN_PRINCIPAL_ARNS_JSON: optional JSON array of extra IAM principal ARNs that should receive EKS cluster-admin access for localkubectlINGRESS_CLASS_NAME: defaultalbINGRESS_ENABLED: defaulttrueSTORAGE_CLASS_NAME: defaultgp3-csiwhen this kit creates the StorageClassPLANE_STORAGE_CLASS_NAME: optional existing StorageClass name for in-cluster service PVCs
Use existing-cluster mode when a company wants Plane in a separate namespace on an EKS cluster it already operates, avoiding the extra EKS control-plane cost and keeping cluster autoscaling ownership with the platform team.
Set these repository variables at minimum:
CREATE_EKS_CLUSTER=falseEXISTING_EKS_CLUSTER_NAME=<cluster-name>
If any data service remains AWS-managed, also set:
EXISTING_VPC_ID=<vpc-id>EXISTING_PRIVATE_SUBNET_IDS_JSON=["subnet-a","subnet-b","subnet-c"]EXISTING_VPC_CIDR=<vpc-cidr>
The workflow does not install Cluster Autoscaler, AWS Load Balancer Controller, or the wrapper StorageClass in existing-cluster mode by default. The GitHub OIDC role must already have Kubernetes RBAC in the target cluster and namespace. Set PLANE_STORAGE_CLASS_NAME if in-cluster services should use a specific existing StorageClass. See docs/existing-cluster.md.
Each Plane dependency can be AWS-managed, in-cluster, or external.
POSTGRES_MODE:aws-managed,in-cluster, orexternalREDIS_MODE:aws-managed,in-cluster, orexternalRABBITMQ_MODE:aws-managed,in-cluster, orexternalOBJECT_STORE_MODE:s3-managed,minio-in-cluster, orexternal-s3
See docs/service-modes.md for the required variables and secrets for each mode.
From a machine with AWS CLI admin access to your AWS account:
- Create or reuse the GitHub OIDC provider for
https://token.actions.githubusercontent.com. - Create an IAM role whose trust policy allows
repo:<OWNER>/<REPO>:ref:refs/heads/main. - Attach
.github/iam/terraform-prod-policy.jsonas an inline policy on that role.
Add the printed role ARN as AWS_GITHUB_OIDC_ROLE_ARN.
Optional: set EKS_CLUSTER_ADMIN_PRINCIPAL_ARNS_JSON to a JSON array with your local administrator IAM user or role ARN if you want to use kubectl from your workstation after deploy.
Then run these GitHub Actions manually:
Terraform Bootstrapwithaction=applyTerraform Prodwithaction=planTerraform Prodwithaction=apply
After deploy:
aws eks update-kubeconfig --name plane-prod-eks --region eu-west-1
kubectl get ingress plane-ingress -n planeOpen the ALB hostname unless you configured PLANE_APP_HOST.
If your local IAM principal was not added through EKS_CLUSTER_ADMIN_PRINCIPAL_ARNS_JSON, use the AWS Load Balancer console or CLI to find the ALB DNS name instead:
aws elbv2 describe-load-balancers --region eu-west-1 \
--query "LoadBalancers[?contains(LoadBalancerName, 'k8s-plane')].DNSName" \
--output textThe default instance sizes are suitable for a low-traffic first deploy:
- EKS node group:
t3.medium(3 nodes) - RDS PostgreSQL:
db.t3.micro - ElastiCache Redis:
cache.t3.micro - Amazon MQ RabbitMQ:
mq.m7g.medium
Review and adjust these through the corresponding Terraform variables before deploying for production workloads. See terraform/environments/prod/variables.tf for the full list.
- GitHub uses OIDC to assume a short-lived AWS role.
- Database, RabbitMQ, Redis, MinIO, and Plane app secrets are generated by Terraform and stored in AWS Secrets Manager for workflow injection.
- Terraform state can contain generated secrets; keep the S3 backend private, encrypted, and access controlled.
- Production infrastructure changes require manually dispatched workflows; pushes do not apply infrastructure.
- Do not add AWS access keys to GitHub for the default deployment path.
More detail:
- docs/proposal-to-plane.md
- docs/existing-cluster.md
- docs/iam.md
- docs/runbook.md
- .github/README-OIDC.md
.github/workflows/ci.yml: secret-free validation for PRs and pushes.github/workflows/terraform-bootstrap.yml: manual state backend workflow.github/workflows/terraform-prod.yml: manual plan/apply workflow.github/iam/terraform-prod-policy.json: IAM policy for the GitHub OIDC roleterraform/environments/bootstrap: S3 + DynamoDB backend bootstrapterraform/environments/prod: production Terraform rootterraform/modules: reusable AWS moduleshelm/plane: Plane wrapper chart