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.