Tutorial

Deploy Your First Application

In this tutorial, you’ll deploy a simple web application to your KUP6S cluster using ArgoCD. You’ll learn the GitOps workflow and how to expose your app via Traefik ingress.

What you’ll deploy

A simple “Hello KUP6S” web application that demonstrates:

  • ArgoCD Application manifest

  • Kubernetes Deployment and Service

  • Traefik Ingress with Let’s Encrypt SSL

  • Health checks and readiness probes

Step 1: Verify cluster access

Ensure you can access your cluster:

export KUBECONFIG=~/kup6s/kube-hetzner/kup6s_kubeconfig.yaml
kubectl get nodes

All nodes should show Ready status.

Step 2: Access ArgoCD UI

Get ArgoCD admin password

kubectl get secret -n argocd argocd-initial-admin-secret \
  -o jsonpath='{.data.password}' | base64 -d
echo

Access ArgoCD

Open your browser to https://argocd.ops.kup6s.net

Note

If you haven’t configured DNS yet, add to your /etc/hosts:

YOUR_LOAD_BALANCER_IP  argocd.ops.kup6s.net

Get your LB IP: kubectl get svc -n kube-system traefik -o jsonpath='{.status.loadBalancer.ingress[0].ip}'

Login with:

  • Username: admin

  • Password: (from command above)

Step 3: Create application namespace

kubectl create namespace hello-kup6s

Verify:

kubectl get namespace hello-kup6s

Step 4: Create application manifests

Create deployment manifest

Save as hello-deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-kup6s
  namespace: hello-kup6s
  labels:
    app: hello-kup6s
spec:
  replicas: 2
  selector:
    matchLabels:
      app: hello-kup6s
  template:
    metadata:
      labels:
        app: hello-kup6s
    spec:
      containers:
      - name: hello
        image: nginx:1.27-alpine
        ports:
        - containerPort: 80
          name: http
        resources:
          requests:
            cpu: 50m
            memory: 64Mi
          limits:
            cpu: 100m
            memory: 128Mi
        livenessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 10
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 5
        volumeMounts:
        - name: html
          mountPath: /usr/share/nginx/html
      volumes:
      - name: html
        configMap:
          name: hello-html
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: hello-html
  namespace: hello-kup6s
data:
  index.html: |
    <!DOCTYPE html>
    <html>
    <head>
        <title>Hello KUP6S</title>
        <style>
            body {
                font-family: Arial, sans-serif;
                display: flex;
                justify-content: center;
                align-items: center;
                height: 100vh;
                margin: 0;
                background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
                color: white;
            }
            .container {
                text-align: center;
            }
            h1 {
                font-size: 4em;
                margin: 0;
            }
            p {
                font-size: 1.5em;
            }
        </style>
    </head>
    <body>
        <div class="container">
            <h1>🚀 Hello KUP6S!</h1>
            <p>Your first app is running on Kubernetes</p>
            <p>Pod: <strong id="hostname"></strong></p>
        </div>
        <script>
            fetch('/hostname')
                .then(r => r.text())
                .then(h => document.getElementById('hostname').textContent = h)
                .catch(() => document.getElementById('hostname').textContent = window.location.hostname);
        </script>
    </body>
    </html>

Create service manifest

Save as hello-service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: hello-kup6s
  namespace: hello-kup6s
  labels:
    app: hello-kup6s
spec:
  type: ClusterIP
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP
    name: http
  selector:
    app: hello-kup6s

Create ingress manifest

Save as hello-ingress.yaml:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hello-kup6s
  namespace: hello-kup6s
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    traefik.ingress.kubernetes.io/router.entrypoints: websecure
    traefik.ingress.kubernetes.io/router.tls: "true"
spec:
  ingressClassName: traefik
  tls:
  - hosts:
    - hello.sites.kup6s.com
    secretName: hello-kup6s-tls
  rules:
  - host: hello.sites.kup6s.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: hello-kup6s
            port:
              number: 80

Step 5: Deploy the application

Apply manifests

kubectl apply -f hello-deployment.yaml
kubectl apply -f hello-service.yaml
kubectl apply -f hello-ingress.yaml

Watch the deployment

kubectl get pods -n hello-kup6s -w

Wait until both pods show Running and 2/2 ready.

Press Ctrl+C to stop watching.

Step 6: Verify the deployment

Check pods

kubectl get pods -n hello-kup6s

Expected output:

NAME                            READY   STATUS    RESTARTS   AGE
hello-kup6s-xxxxxxxxxx-xxxxx    1/1     Running   0          1m
hello-kup6s-xxxxxxxxxx-xxxxx    1/1     Running   0          1m

Check service

kubectl get svc -n hello-kup6s

Check ingress

kubectl get ingress -n hello-kup6s

You should see an IP address assigned.

Check certificate

kubectl get certificate -n hello-kup6s

Wait until READY shows True (may take 1-2 minutes for Let’s Encrypt).

Step 7: Access your application

Configure DNS

Point hello.sites.kup6s.com to your load balancer IP.

Or for testing, add to /etc/hosts:

YOUR_LOAD_BALANCER_IP  hello.sites.kup6s.com

Open in browser

Visit: https://hello.sites.kup6s.com

You should see your “Hello KUP6S!” page with a valid SSL certificate! 🎉

Step 8: Create ArgoCD Application (GitOps way)

Now let’s manage this app the GitOps way with ArgoCD.

Create Git repository

mkdir hello-kup6s-gitops
cd hello-kup6s-gitops
git init

Commit manifests

cp ../hello-*.yaml .
git add .
git commit -m "Initial hello-kup6s application"

Push to Git server

# Push to your Git server (GitHub, GitLab, etc.)
git remote add origin git@your-git-server:your-org/hello-kup6s.git
git push -u origin main

Create ArgoCD Application

Save as argocd-app.yaml:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: hello-kup6s
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://your-git-server/your-org/hello-kup6s.git
    targetRevision: main
    path: .
  destination:
    server: https://kubernetes.default.svc
    namespace: hello-kup6s
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
    - CreateNamespace=true

Apply it:

kubectl apply -f argocd-app.yaml

Watch ArgoCD sync

Go to ArgoCD UI and you’ll see your application syncing automatically!

Step 9: Test GitOps workflow

Make a change

Edit hello-deployment.yaml and change replicas:

spec:
  replicas: 3  # Changed from 2

Commit and push

git add hello-deployment.yaml
git commit -m "Scale to 3 replicas"
git push

Watch ArgoCD

Within 3 minutes, ArgoCD will detect the change and automatically sync!

Check pods:

kubectl get pods -n hello-kup6s

You should now see 3 pods running! 🚀

Congratulations! 🎉

You’ve successfully deployed your first application using GitOps!

What you’ve learned

  • How to create Kubernetes Deployments, Services, and Ingress

  • How to configure Traefik ingress with Let’s Encrypt SSL

  • How to use ArgoCD for GitOps deployments

  • How the automated sync and self-heal features work

What’s next?

Troubleshooting

Pods stuck in Pending

kubectl describe pod -n hello-kup6s <pod-name>

Check for resource constraints or storage issues.

Certificate not ready

kubectl describe certificate -n hello-kup6s hello-kup6s-tls
kubectl logs -n cert-manager -l app=cert-manager

Ingress not accessible

# Check Traefik logs
kubectl logs -n kube-system -l app.kubernetes.io/name=traefik

# Verify ingress
kubectl get ingress -n hello-kup6s -o yaml

ArgoCD not syncing

  • Check the repository URL is correct

  • Verify ArgoCD has access to your Git repository

  • Check ArgoCD application status: kubectl get application -n argocd hello-kup6s -o yaml