This article explains how to use NodePort to expose Kubernetes applications on CMK, how it works, how to enable external access, and what to consider when troubleshooting.
Environment
Crusoe Managed Kubernetes (CMK)
Kubernetes version: v1.30.8-cmk.28
Node pool: c1a.2x instance type
Subnet: default-subnet-eu-iceland1-a
Terraform used to provision cluster (example below)
Application example: Jupyter Notebook (
jupyter/base-notebook)
Procedure
A NodePort is a Kubernetes service type that exposes a specific port (between 30000–32767) on each worker node in the cluster. This allows external clients to reach applications by hitting <NodeIP>:<NodePort> directly.
In this example, we use Jupyter Notebook as the workload to demonstrate the NodePort setup.
-
Deploy Your Application (Jupyter as an Example)
Create a Kubernetes namespace
kubectl create namespace jupyter-labDefine a Deployment manifest:
apiVersion: apps/v1 kind: Deployment metadata: name: jupyter-notebook namespace: jupyter-lab spec: replicas: 2 selector: matchLabels: app: jupyter template: metadata: labels: app: jupyter spec: containers: - name: notebook image: jupyter/base-notebook:latest ports: - containerPort: 8888 #The container running inside the pod will be listening on port 8888. args: - start-notebook.sh env: - name: JUPYTER_ENABLE_LAB value: "yes"Container Port (8888) will be used by the Service’s
targetPort, which maps external traffic to the pod’s actual listening port, Apply the deployment file.kubectl apply -f jupyter-deployment.yamlConfirm pods are running:
kubectl get pods -n jupyter-lab -
Create a NodePort Service
Expose the deployment using a NodePort service. Ensure the selector in the Service exactly matches the labels defined on your Pods. If these do not match, the Service will not forward traffic to your application.
apiVersion: v1 kind: Service metadata: name: jupyter-nodeport-service namespace: jupyter-lab spec: type: NodePort selector: app: jupyter ports: - port: 8888 #The port the service listens on inside the cluster targetPort: 8888 #The port the container exposes nodePort: 30036 #The port exposed on the node externallyApply the service:
kubectl apply -f jupyter-nodeport-service.yamlCheck the service:
kubectl get svc -n jupyter-labOutput should look like:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE jupyter-nodeport-service NodePort 10.233.25.136 <none> 8888:30036/TCP 1m -
You can verify the Service is correctly linked to the Pods by running below command. Look under
Endpoints:it should list IPs and ports. If it says<none>, the label match is broken.kubectl describe svc jupyter-nodeport-service -n jupyter-lab -
By default, NodePort traffic is blocked at the Crusoe Cloud firewall level.
You must create a firewall rule to allow TCP traffic on the NodePort.
Example Firewall Rule:
Name Protocol Source Port Subnet jupyter-nb-ingress TCP 0.0.0.0/0 30036 default-subnet-eu-iceland1-a Note: You must associate this rule with the correct VPC/subnet used by your Kubernetes nodes.
-
Access the Application
Get your node external IP using below command
kubectl get nodes -o wideOutput
NAME STATUS ... INTERNAL-IP EXTERNAL-IP ... np-abc123... Ready 172.27.52.145 216.86.17.xx ...You can now access the service at: http://216.86.17.xx:30036
Note
- Make sure the pod is running and reachable on the targetPort internally.
- Use
curl http://localhost:30036from the node to verify internal reachability. - Check Crusoe firewall if external access isn’t working — firewall rules must match the node port.
Related Articles