Creating Your First GKE Cluster: Step-by-Step Guide
This walkthrough takes you from a fresh GCP project to a running workload on GKE Autopilot. You will enable the required API, create an Autopilot cluster, configure kubectl, deploy an nginx pod, verify it is running, expose it with a Service, and confirm it is reachable from the internet. If you want background on what GKE is before diving in, read the GKE Overview first.
Prerequisites
Before starting, make sure you have:
- A GCP project with billing enabled. GKE clusters cannot be created without a billing account attached.
- The
gcloudCLI installed and authenticated. Rungcloud auth loginandgcloud config set project YOUR_PROJECT_IDto set your active project. kubectlinstalled. The easiest way is through the gcloud components manager:gcloud components install kubectl.
Verify your setup before proceeding:
gcloud config get-value project
kubectl version --clientThe first command should print your project ID. The second should print the kubectl client version without errors.
Step 1: Enable the Kubernetes Engine API
GKE is not enabled by default in a new GCP project. Enable the Kubernetes Engine API before you can create clusters:
gcloud services enable container.googleapis.comThis command takes about 30 seconds. You only need to run this once per project.
Confirm the API is enabled:
gcloud services list --enabled --filter="name:container.googleapis.com"Enabling the Kubernetes Engine API also automatically enables several dependent APIs, including the Compute Engine API and the Cloud Resource Manager API. You do not need to enable these separately.
Step 2: Create an Autopilot cluster
Create a GKE Autopilot cluster using the create-auto subcommand. Autopilot clusters are always regional — the control plane runs across three zones within the region for high availability.
gcloud container clusters create-auto my-cluster \
--region=us-central1What happens during cluster creation:
- GKE provisions the Kubernetes control plane in Google-managed infrastructure across three zones in
us-central1. You do not see these as VMs in your project. - The cluster’s API server is assigned an external IP address and an SSL certificate is generated.
- GKE configures the default node service account and sets up Workload Identity Federation for GKE if enabled.
- The cluster is enrolled in the Regular release channel by default.
- Cluster networking is configured in your VPC (or the default VPC if you did not specify one).
Cluster creation typically completes in three to five minutes. When control returns to your prompt, the cluster is operational.
Creating cluster my-cluster in us-central1...done.List your clusters at any time:
gcloud container clusters listStep 3: Connect kubectl to the cluster
Creating a cluster does not automatically configure kubectl. You must fetch credentials separately. This command retrieves the cluster’s API endpoint and authentication credentials and writes them into your local kubeconfig file (~/.kube/config):
gcloud container clusters get-credentials my-cluster \
--region=us-central1Expected output:
Fetching cluster endpoint and auth data.
kubeconfig entry generated for my-cluster.Verify the connection:
kubectl cluster-infoThis prints the addresses of the Kubernetes API server and the DNS service. On an Autopilot cluster, kubectl get nodes may return an empty list — this is expected. Autopilot does not provision nodes until workloads are scheduled.
Think of get-credentials as adding a new entry to your contacts app. The cluster already exists and is running, but your phone (kubectl) does not know how to reach it yet. Running get-credentials saves the cluster’s address and the key to authenticate with it.
Step 4: Deploy a sample nginx pod
You can create resources in Kubernetes imperatively (using a single command) or declaratively (by applying a YAML manifest). For learning and production use, the declarative approach is better practice because it produces a file you can version-control and reapply. See Deploying Containers with kubectl for a full explanation of both styles.
Create a file called nginx-pod.yaml with the following content:
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
resources:
requests:
cpu: "250m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"Apply the manifest to the cluster:
kubectl apply -f nginx-pod.yamlExpected output:
pod/nginx createdResource requests and limits are required in GKE Autopilot. Without them, Autopilot cannot determine how much capacity to provision and will reject the pod. This is one of the security and consistency policies Autopilot enforces automatically.
Step 5: Verify the pod is running
Check the status of the pod:
kubectl get podsWhen you first run this, the pod may show a status of Pending. On Autopilot, this is expected — GKE is provisioning a node to run the pod. Within a minute or two, the status transitions to Running:
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 90s1/1 means one container is running out of one defined in the pod spec. If the pod does not reach Running within a few minutes, inspect it for errors:
kubectl describe pod nginxkubectl describe prints detailed information about the pod: its scheduled node, the events that occurred during startup, resource usage, and any error messages. The Events section at the bottom is usually the most useful for diagnosing problems. A pod stuck in CrashLoopBackOff indicates the container is starting but crashing repeatedly — the logs are the next thing to check.
To view the logs from the nginx container:
kubectl logs nginxStep 6: Expose the pod with a Service
The nginx pod has a cluster-internal IP address, but you cannot reach it from the internet directly. You need a Kubernetes Service of type LoadBalancer to expose it. A LoadBalancer Service tells GKE to provision an external GCP load balancer and route traffic to your pod. See Services in Kubernetes for all four service types and when to use each.
Create a file called nginx-service.yaml:
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
type: LoadBalancerThe selector field (app: nginx) matches the label on the pod you created. The Service routes incoming traffic on port 80 to port 80 on any pod with that label.
Apply the Service:
kubectl apply -f nginx-service.yamlWatch for the external IP address to be assigned:
kubectl get services --watchInitially the EXTERNAL-IP column shows <pending>. GKE is provisioning a GCP external load balancer in the background. After about 30 to 60 seconds, a public IP address appears:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-service LoadBalancer 10.96.120.45 34.102.56.78 80:32456/TCP 90sPress Ctrl+C to stop watching.
Step 7: Test the deployment
With the external IP address in hand, test that the nginx server is reachable. Replace 34.102.56.78 with the actual IP from your kubectl get services output:
curl http://34.102.56.78You should receive the default nginx HTML welcome page. You can also open the IP address in a web browser.
This walkthrough deploys a bare pod for simplicity. In production you would deploy a Deployment (not a bare pod) so that Kubernetes can reschedule the workload if the pod or node fails. A bare pod with no owning controller is not rescheduled if its node goes away.
Bonus: Running a command inside a pod
You can execute commands inside a running container with kubectl exec. This is useful for debugging — inspecting configuration files, testing network connectivity, or running diagnostic tools.
Start an interactive shell inside the nginx container:
kubectl exec -it nginx -- /bin/bashFrom inside the container you can run commands such as cat /etc/nginx/nginx.conf, curl localhost, or env to inspect environment variables. Type exit to leave the shell.
To run a single command non-interactively:
kubectl exec nginx -- nginx -vCleaning up
When you have finished, delete the resources you created to avoid ongoing charges.
Delete the Service (this also de-provisions the external load balancer):
kubectl delete service nginx-serviceDelete the pod:
kubectl delete pod nginxDelete the cluster itself:
gcloud container clusters delete my-cluster \
--region=us-central1Deleting the cluster removes all workloads, services, and persistent volumes associated with it. Make sure you do not need anything running in the cluster before deleting it.
Common beginner mistakes
- Not running
get-credentialsbefore using kubectl. Creating a cluster withgcloud container clusters create-autodoes not update your kubeconfig. Until you rungcloud container clusters get-credentials, every kubectl command will fail with a connection error or target a different cluster entirely. - Expecting nodes immediately on Autopilot. After cluster creation, running
kubectl get nodeson an Autopilot cluster returns an empty list. This is not an error — Autopilot provisions nodes only when you schedule a workload. Many beginners assume the cluster is broken and try to recreate it. - Omitting resource requests on Autopilot. GKE Autopilot requires CPU and memory requests on every container. If you apply a pod spec without resource requests, Autopilot rejects the pod with an admission webhook error. Always include
resources.requestsin your container specs. - Forgetting to delete the Service before the cluster. A LoadBalancer Service provisions an external GCP load balancer. Always delete Services of type LoadBalancer before deleting the cluster to ensure the load balancer is properly de-provisioned.
Summary
- Enable the API first:
gcloud services enable container.googleapis.com - Create an Autopilot cluster:
gcloud container clusters create-auto my-cluster —region=us-central1 - Connect kubectl:
gcloud container clusters get-credentials my-cluster —region=us-central1 - Deploy a workload:
kubectl apply -f nginx-pod.yaml - Check status:
kubectl get podsandkubectl describe pod nginx - Expose externally: apply a Service of type LoadBalancer and wait for
EXTERNAL-IP - Debug:
kubectl logs nginxfor container output,kubectl exec -it nginx — /bin/bashfor an interactive shell - Always clean up: delete Services, pods, and the cluster to avoid ongoing charges
Frequently asked questions
How long does it take to create a GKE cluster?
A GKE Autopilot cluster typically takes three to five minutes to provision. The control plane must be initialised and the cluster API endpoint must become reachable before you can connect with kubectl. Standard clusters take a similar amount of time, with additional time if many nodes need to be bootstrapped.
Why does kubectl get nodes show no output on an Autopilot cluster?
In GKE Autopilot, nodes are provisioned on demand when you schedule workloads. An empty Autopilot cluster has no nodes running yet, so kubectl get nodes returns an empty list. Once you deploy a pod, GKE provisions the necessary node capacity and the node appears. This is expected behaviour, not an error.
What is a kubeconfig file and where is it stored?
The kubeconfig file (stored at ~/.kube/config by default) holds the API endpoint, credentials, and context information for every Kubernetes cluster kubectl knows about. The gcloud container clusters get-credentials command adds an entry for your GKE cluster. You can inspect the file with kubectl config view and switch between clusters using kubectl config use-context.
My pod is stuck in Pending status. What does that mean?
On Autopilot, Pending is normal for the first minute or two after creating a pod — GKE is provisioning a node to run it. If a pod stays Pending for more than five minutes, run kubectl describe pod POD_NAME and check the Events section. Common causes include missing resource requests (required on Autopilot), insufficient quota, or node pool constraints.