Skip to main content

Deploy your first app

Inspect 1.36

With a healthy cluster confirmed, deploy a sample application to it and open it in your browser through a Hetzner Load Balancer. Most of the wait is the load balancer coming up.

Prerequisites#

  • A healthy workload cluster with KUBECONFIG still pointed at it. Complete to confirm it is ready.

Deploy the application#

You create two Kubernetes objects. A Deployment keeps a set of identical pods running and replaces any that fail. A Service gives those pods one stable address; with type: LoadBalancer, that address is a public IP on a Hetzner Load Balancer.

Create a file named app.yaml:

yaml
		# app.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello
spec:
  replicas: 2
  selector:
    matchLabels:
      app: hello
  template:
    metadata:
      labels:
        app: hello
    spec:
      containers:
        - name: hello
          image: nginx:stable-alpine
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: hello
spec:
  type: LoadBalancer
  selector:
    app: hello
  ports:
    - port: 80
      targetPort: 80
	

Apply it:

		$ kubectl apply -f app.yaml
deployment.apps/hello created
service/hello created
	

Both pods start within a few seconds. Confirm they are running:

		$ kubectl get pods -l app=hello
NAME                     READY   STATUS    RESTARTS   AGE
hello-6d4b9c8f7d-8kx2p   1/1     Running   0          20s
hello-6d4b9c8f7d-v5qtm   1/1     Running   0          20s
	

Both pods show 1/1 Running. The random suffixes in your pod names differ.

Watch the load balancer appear#

A Service with type: LoadBalancer tells Syself Autopilot to create a Hetzner Load Balancer in the Hetzner Cloud project that holds your cluster. This takes about a minute. Once it exists, every request follows the same path:

flowchart LR
  I["Internet<br/>the external IP"] --> LB["Hetzner Load Balancer"] --> S["Service<br/>hello"] --> P["Pods<br/>your app replicas"]

Watch the Service until an external IP appears:

		$ kubectl get service hello --watch
NAME    TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
hello   LoadBalancer   100.96.45.12   <pending>     80:31820/TCP   15s
hello   LoadBalancer   100.96.45.12   167.235.10.20 80:31820/TCP   70s
	

When the EXTERNAL-IP column changes from <pending> to an IP address, the load balancer is ready. Press Ctrl+ C to stop watching. Your IP and ports differ from the example.

You can also see the new load balancer in the Hetzner Cloud Console under Load Balancers.

Access the application#

Open http://<EXTERNAL-IP> in your browser, or test it with curl:

		$ curl http://<EXTERNAL-IP>
	

You should see the nginx welcome page, an HTML response starting with <title>Welcome to nginx!</title>. Your app is live on the internet. Both pods serve traffic through the same load balancer, so if one fails, the other keeps answering.

Clean up#

Delete the Deployment and Service when you are done:

		$ kubectl delete -f app.yaml
deployment.apps "hello" deleted
service "hello" deleted
	

The Hetzner Load Balancer is deleted automatically when the Service is removed, so it stops billing right away.

Beyond a single load balancer#

This example uses a plain LoadBalancer Service, one load balancer per application. For production workloads you typically want:

  • An ingress controller. This is a component that routes requests for several applications through one load balancer, based on the hostname or URL path. Syself Autopilot does not include a built-in ingress controller; choose one that fits your needs.
  • TLS termination at the ingress level.

See for a complete guide covering ingress and TLS options.

Get the most out of Syself#

You now have a running cluster and a working deploy loop. That is the demo. The platform's depth shows up in what comes next:

  • Run a production-ready workload. Syself Autopilot drains and replaces nodes during upgrades and self-healing. A bare Deployment like the one above takes a traffic hit on every such event. adds the probes, disruption budgets, and resource requests that keep an app serving through all of it.
  • Harden your workloads. The node this app ran on is hardened by default. The pod you deployed is not. covers the security checklist to work through before production.
  • Set up observability. Every component in the cluster exports Prometheus metrics, but nothing collects them yet. turns them into dashboards and alerts.
  • Bring in your team. So far you have used a single kubeconfig file. connects your identity provider so teammates log in with short-lived tokens instead of shared credentials.
Tip

GitOps is a natural next step. You can keep every manifest in a Git repository and use Argo CD or Flux to apply changes automatically. See .

Next step#

You have a running app and a working deploy loop. Continue with to see where to go from here: understanding the platform, upgrades, security, and running real services.