Kubernetes Caddy

Deployment of a WebApp with Kubernetes and Caddy

After describing how to create production-ready Docker images and upload them to Docker Hub in the last article, it is now time to make these images available on a server. The aim is to make the web application accessible to everyone via a dedicated domain. To do this, we use a virtual private server (VPS) from Hetzner and deploy Kubernetes (k3s) with Caddy as a reverse proxy.

You might also be interested in this: Create Docker images and upload them to Dockerhub

Set up a VPS with Hetzner

Hetzner often offers referral links with credit benefits for new customers. Of course, you can also use other providers, but Hetzner is attractively priced and offers solid services.

What is a VPS?

A Virtual Private Server (VPS) is a virtual server that is operated on a physical machine and acts as an independent server. It offers more control than classic shared hosting and is a cost-effective alternative to dedicated servers. Access is usually via SSH (Secure Shell), which allows us to control the server via the command line.

SSH access to the VPS

Once a VPS has been created, it is usually managed via a secure shell (SSH). SSH is a protocol that enables encrypted connections to remote servers. The following command is used to connect to the server:

ssh root@<IP-Server>

If an SSH key has been stored, authentication can be carried out using public key authentication, which is more secure than a password.

Create a server at Hetzner

  1. After logging into the Hetzner Cloud, we navigate to “Projects” and create a new project.
  2. Select “Add server” and can configure an instance.
  3. The cheapest model is often sufficient to start with. However, I recommend activating the option for an IPv4 address, as purely IPv6-based setups often cause compatibility problems.

Setting up a domain

To access the application later under your own domain, you must register a domain and link it to the server.

Apply for a domain at Hetzner

  1. Register a new domain or add an existing domain in the Hetzner ConsoleH.
  2. To manage DNS entries, we need to activate DNS access.

Set name servers

The following name servers should be used:

helium.ns.hetzner.de. 
hydrogen.ns.hetzner.com. 
oxygen.ns.hetzner.com.

These new name servers offer better performance and flexibility compared to the old Hetzner name servers:

ns1.first-ns.de.
robotns2.second-ns.de.
robotns3.second-ns.com.

However, both nameserver variants are possible! The DNS changes take some time. However, we can use tools such as MXToolbox to check whether the changes have already taken place.

Connect the domain to the server

Now the IP address of the server must be linked to the domain:

  1. Switch to DNS zones in the Hetzner Cloud.
  2. Select the registered domain.
  3. Create a new A-Record and enter the IPv4 address of the server.
  4. If available, remove the IPv6 record (AAAA) to avoid compatibility problems.

You can also use MXToolbox to check whether the DNS changes have already been applied.

Set up Kubernetes

Kubernetes is a powerful orchestration tool for containers. I use k3s, a lean Kubernetes variant that is particularly suitable for smaller environments.

Install K3s on the server

Connect to the server via SSH and install k3s with the following command:

curl -sfL https://get.k3s.io | sh - 

The script installs k3s and starts the Kubernetes service. After installation, k3s can be checked with the following command:

kubectl get nodes

k3s comes with its own kubectl version, so that no separate installation is necessary.

Create YAML files for FE, BE, MySQL and Redis

To deploy our application, we need YAML files for:

  • Frontend (Angular)
  • Backend (NestJS)
  • Database (MySQL)
  • Session-Management (Redis)

A deployment file for the backend could look like this:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
        - name: backend
          image: dockerhub-user/backend:latest
          ports:
            - containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
  name: backend-service
spec:
  selector:
    app: backend
  ports:
    - protocol: TCP
      port: 80
      targetPort: 3000
  type: ClusterIP

What are deployments and services?

  • Deployments manage the provision and scaling of containers.
  • Services ensure a stable network connection between containers.
  • ClusterIP means that the service is only accessible within the Kubernetes cluster.

Set up Caddy as a reverse proxy

A reverse proxy is required to ensure that incoming traffic is distributed correctly. K3s comes with Traefik by default, but I opted for a simpler solution: Caddy. I was really surprised how little guidance or documentation there is on Caddy in combination with Kubernetes.

Why Kubernetes with Caddy?

  • Automatic Let’s Encrypt SSL certificates
  • Simple configuration via Caddyfile
  • Built-in load balancer

Remove Traefik

kubectl delete helmrelease traefik -n kube-system

Create Caddy Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: caddy
spec:
  replicas: 1
  selector:
    matchLabels:
      app: caddy
  template:
    metadata:
      labels:
        app: caddy
    spec:
      containers:
        - name: caddy
          image: caddy
          volumeMounts:
            - name: caddy-config
              mountPath: /etc/caddy/Caddyfile
      volumes:
        - name: caddy-config
          configMap:
            name: caddy-config
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: caddy-config
data:
  Caddyfile: |

    example.com {
        reverse_proxy backend-service:3000
    }

---
apiVersion: v1
kind: Service
metadata:
  name: caddy-service
spec:
  type: LoadBalancer
  selector:
    app: caddy
  ports:
    - port: 80
      targetPort: 80
    - port: 443
      targetPort: 443

Important: Since Let’s Encrypt has a rate limit, tests should first be carried out with staging certificates!

Conclusion

After these steps, the application is now running in a Kubernetes cluster on a Hetzner VPS and can be accessed via its own domain. The next step would be to set up an automatic CI/CD pipeline to deploy new versions without manual effort.

Leave a Reply

Your email address will not be published. Required fields are marked *


Cookie Consent with Real Cookie Banner