Skip to documentation content
Cloud documentationCloud statusPilot in preparation
On this page
Deploy applications

Deploy your first application

Adapt a Deployment and Service, validate the manifest and check a rollout without public exposure.

GuideTechnical reference · not required for Cloud UIReading time · approx.12 minutesUpdated
Availability of this guideEnterprise reference

For Enterprise Self-Hosted or a separately agreed technical evaluation. Not a Cloud customer procedure.

Before you start

You have a provided pilot cluster, a trusted kubeconfig, kubectl and access to an agreed namespace. Set KUBECONFIG, CP_CONTEXT and CP_NAMESPACE using the access guide before running the commands. The examples use a POSIX shell and a single access file. Grouped blocks stop at missing settings without closing your terminal; they do not provision infrastructure or grant permissions. Connect to your cluster →

You need rights to manage Deployments and Services, an image approved for this environment and an HTTP readiness endpoint. Private images require the agreed registry access. Run the exercise in an assigned test namespace with no existing cloud-demo resources.

1. Adapt the application template

Save the template as cloud-demo.yaml. Replace REPLACE_WITH_APPROVED_IMAGE with your reviewed image, preferably pinned to its approved digest. Confirm the image's non-root user, listen port and health endpoint; change 8080 and /readyz if needed. The read-only filesystem needs the image to write only to agreed writable paths such as /tmp. Resource values below are illustrative and must fit your workload and quota.

The startup probe allows initialization before readiness checks. Liveness is intentionally not added here: define a meaningful recovery check for your application before enabling restart decisions. This sample is a workload template, not a tested ClusterPilot Cloud demo image or a high-availability configuration.

Download the application template (.yaml) ↓
Show Deployment and Service YAML
cloud-demo.yaml · YAML
apiVersion: apps/v1
kind: Deployment
metadata:
  name: cloud-demo
  labels:
    app: cloud-demo
spec:
  replicas: 1
  revisionHistoryLimit: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: cloud-demo
  template:
    metadata:
      labels:
        app: cloud-demo
    spec:
      automountServiceAccountToken: false
      securityContext:
        runAsNonRoot: true
        seccompProfile:
          type: RuntimeDefault
      containers:
        - name: app
          image: REPLACE_WITH_APPROVED_IMAGE
          ports:
            - name: http
              containerPort: 8080
          resources:
            requests:
              cpu: 250m
              memory: 128Mi
            limits:
              cpu: 500m
              memory: 256Mi
          startupProbe:
            httpGet:
              path: /readyz
              port: http
            periodSeconds: 5
            failureThreshold: 30
          readinessProbe:
            httpGet:
              path: /readyz
              port: http
            periodSeconds: 5
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities:
              drop: [ALL]
          volumeMounts:
            - name: tmp
              mountPath: /tmp
      volumes:
        - name: tmp
          emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
  name: cloud-demo
spec:
  type: ClusterIP
  selector:
    app: cloud-demo
  ports:
    - name: http
      port: 80
      targetPort: http

2. Validate and review the change

Validate before applying · POSIX
(
: "${KUBECONFIG:?Set KUBECONFIG from your handover}"
: "${CP_CONTEXT:?Set CP_CONTEXT from your handover}"
: "${CP_NAMESPACE:?Set CP_NAMESPACE from your handover}"
case "$CP_CONTEXT:$CP_NAMESPACE" in
  YOUR_*|*:YOUR_*) printf '%s\n' 'Replace the YOUR_ context and namespace placeholders first.' >&2; exit 1 ;;
esac
kubectl --kubeconfig="$KUBECONFIG" --context="$CP_CONTEXT" --namespace="$CP_NAMESPACE" get deployment/cloud-demo service/cloud-demo
kubectl --kubeconfig="$KUBECONFIG" --context="$CP_CONTEXT" --namespace="$CP_NAMESPACE" apply --dry-run=server -f cloud-demo.yaml
kubectl --kubeconfig="$KUBECONFIG" --context="$CP_CONTEXT" --namespace="$CP_NAMESPACE" diff -f cloud-demo.yaml
)

In the first check, NotFound is expected for a new exercise. If cloud-demo exists, stop and choose another test environment or review ownership. Server dry-run performs API validation/admission without persisting objects. diff shows what would change; exit code 1 means differences, while a higher code is an error. It may require additional API permissions. Neither check proves that an image can be pulled or that the application starts.

3. Apply and check readiness

The next commands create or update the reviewed workload and consume your booked resources. Run them only after the dry-run and diff are accepted.

Apply the reviewed workload
(
: "${KUBECONFIG:?Set KUBECONFIG from your handover}"
: "${CP_CONTEXT:?Set CP_CONTEXT from your handover}"
: "${CP_NAMESPACE:?Set CP_NAMESPACE from your handover}"
case "$CP_CONTEXT:$CP_NAMESPACE" in
  YOUR_*|*:YOUR_*) printf '%s\n' 'Replace the YOUR_ context and namespace placeholders first.' >&2; exit 1 ;;
esac
kubectl --kubeconfig="$KUBECONFIG" --context="$CP_CONTEXT" --namespace="$CP_NAMESPACE" apply -f cloud-demo.yaml
kubectl --kubeconfig="$KUBECONFIG" --context="$CP_CONTEXT" --namespace="$CP_NAMESPACE" rollout status deployment/cloud-demo --timeout=300s
kubectl --kubeconfig="$KUBECONFIG" --context="$CP_CONTEXT" --namespace="$CP_NAMESPACE" get pods -l app=cloud-demo
kubectl --kubeconfig="$KUBECONFIG" --context="$CP_CONTEXT" --namespace="$CP_NAMESPACE" get endpointslices -l kubernetes.io/service-name=cloud-demo
)
Troubleshoot common failures →

4. Test locally and record the result

Temporary local access
(
: "${KUBECONFIG:?Set KUBECONFIG from your handover}"
: "${CP_CONTEXT:?Set CP_CONTEXT from your handover}"
: "${CP_NAMESPACE:?Set CP_NAMESPACE from your handover}"
case "$CP_CONTEXT:$CP_NAMESPACE" in
  YOUR_*|*:YOUR_*) printf '%s\n' 'Replace the YOUR_ context and namespace placeholders first.' >&2; exit 1 ;;
esac
kubectl --kubeconfig="$KUBECONFIG" --context="$CP_CONTEXT" --namespace="$CP_NAMESPACE" port-forward service/cloud-demo 8080:80 --address=127.0.0.1
)

Keep the port-forward running and open http://127.0.0.1:8080 in your browser, or check /readyz in a second terminal. The listener is bound to localhost. Stop it with Ctrl+C when finished. This checks the application path through the Service; it does not validate public DNS, ingress or TLS.

Record the image digest, manifest revision, rollout result and application response. For a disposable exercise, remove only the named sample Deployment and Service after confirming they belong to this exercise. The command below does not target PVCs or the namespace.

Optional sample cleanup · removes these two resources
(
: "${KUBECONFIG:?Set KUBECONFIG from your handover}"
: "${CP_CONTEXT:?Set CP_CONTEXT from your handover}"
: "${CP_NAMESPACE:?Set CP_NAMESPACE from your handover}"
case "$CP_CONTEXT:$CP_NAMESPACE" in
  YOUR_*|*:YOUR_*) printf '%s\n' 'Replace the YOUR_ context and namespace placeholders first.' >&2; exit 1 ;;
esac
kubectl --kubeconfig="$KUBECONFIG" --context="$CP_CONTEXT" --namespace="$CP_NAMESPACE" delete deployment/cloud-demo service/cloud-demo
)
Domains, ingress & TLS →

Technical references

These Kubernetes references describe technical behavior. Your pilot agreement defines the capabilities confirmed for your cluster.

Use this guide within your agreed pilot scope. Kubernetes examples are not evidence of a generally available Cloud capability.