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 [Explore your cluster](/docs/hetzner/apalla/getting-started/explore-your-cluster) 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:
```console
$ kubectl apply -f app.yaml
deployment.apps/hello created
service/hello created
```
Both pods start within a few seconds. Confirm they are running:
```console
$ 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:
```mermaid
flowchart LR
I["Internet
the external IP"] --> LB["Hetzner Load Balancer"] --> S["Service
hello"] --> P["Pods
your app replicas"]
```
Watch the Service until an external IP appears:
```console
$ kubectl get service hello --watch
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hello LoadBalancer 100.96.45.12 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 `` to an IP address, the load balancer is ready. Press to stop watching. Your IP and ports differ from the example.
You can also see the new load balancer in the [Hetzner Cloud Console](https://console.hetzner.cloud/) under **Load Balancers**.
## Access the application
Open `http://` in your browser, or test it with curl:
```console
$ curl http://
```
You should see the nginx welcome page, an HTML response starting with `Welcome to nginx!`. 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:
```console
$ 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 [Expose an application](/docs/hetzner/apalla/network/expose/choose-how-to-expose) 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. [Run a production-ready workload](/docs/hetzner/apalla/workloads/production/run-a-production-ready-workload) 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. [Next steps](/docs/hetzner/apalla/getting-started/next-steps) 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. [Set up Prometheus and Grafana](/docs/hetzner/apalla/observability/metrics/set-up-prometheus) turns them into dashboards and alerts.
- **Bring in your team.** So far you have used a single kubeconfig file. [Configure OIDC](/docs/hetzner/apalla/clusters/configure/configure-oidc) 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 [GitOps with Argo CD](/docs/hetzner/apalla/clusters/gitops/set-up-argo-cd).
## Next step
You have a running app and a working deploy loop. Continue with [Next steps](/docs/hetzner/apalla/getting-started/next-steps) to see where to go from here: understanding the platform, upgrades, security, and running real services.
## Related
- [Next steps](/docs/hetzner/apalla/getting-started/next-steps)
- [How to: expose an application](/docs/hetzner/apalla/network/expose/choose-how-to-expose)
- [How to: configure a load balancer](/docs/hetzner/apalla/network/load-balancing/configure-a-load-balancer)
- [How to: GitOps with Argo CD](/docs/hetzner/apalla/clusters/gitops/set-up-argo-cd)
- [Explore your cluster](/docs/hetzner/apalla/getting-started/explore-your-cluster)