How to Deploy UniHTML on Kubernetes

Kubernetes has become the go-to platform for deploying scalable applications, and if you’re dealing with HTML-to-PDF conversion through UniHTML, you’re in for a smooth ride. In this guide, we’ll walk through the process of deploying UniHTML on a Kubernetes Talos cluster. By the end, you’ll have UniHTML up and running, converting HTML pages to PDF via an API.

What is UniHTML?

UniHTML is part of the UniDoc toolkit, a powerful tool for manipulating PDF files with Golang. While UniPDF handles PDF files, it doesn’t natively support HTML. That’s where UniHTML comes in, acting as a bridge to convert HTML into a format that UniDoc can process.

By deploying UniHTML on Kubernetes, you gain the ability to manage and scale the service in both public and private cloud environments. This approach ensures fast, reliable, and scalable document conversions with low latency across your network.

Prerequisites

Before we dive in, here’s what you need:

  1. A Kubernetes Talos cluster with multiple control planes and worker nodes.
  2. Flux to manage manifests.
  3. Cloudflared with Zero Trust for the ingress controller. This is already set up and working in our case, so we won’t cover it here.

We’ll use a hypothetical domain unihtml.command.is to illustrate how this deployment works. Let’s get started!

Deployment Manifests for UniHTML

The deployment process for UniHTML in Kubernetes involves several configuration files. These files, or “manifests,” will help Kubernetes understand how to deploy, scale, and manage the service.

Let’s break down the key files you need.

1. kustomization.yaml

This is the main file that coordinates all the resources we’re about to configure. It tells Kustomize what to include in the deployment.

apiVersion:
    kustomize.config.k8s.io/v1beta1kind:
        Kustomizationresources:
            - namespace.yaml
            - deployment.yaml
            - service.yaml
            - ingress.yaml
            - serviceaccount.yaml
            - secret.sops.yaml

2. namespace.yaml

Namespaces in Kubernetes allow you to group and isolate resources. This file creates a dedicated namespace for UniHTML to keep it self-contained and easier to manage.

apiVersion:
    v1kind:
        Namespacemetadata:
            name: unihtml

3. serviceaccount.yaml

A service account restricts the permissions that UniHTML has in your cluster. This ensures that UniHTML only gets access to the resources it needs.

apiVersion:
    v1kind:
        ServiceAccountmetadata:
            name:
                unihtmlnamespace: unihtml

4. deployment.yaml

This is the heart of the deployment, defining how many replicas of UniHTML to run, the container image to use, and how to handle the API that listens on port 8080.

The secret to running this? Licensing. You’ll need to mount a license file in the container to activate UniHTML. We store this license in a Kubernetes secret and mount it as read-only.

apiVersion: apps/v1
kind: Deployment

metadata:
    name: unihtml-server
    namespace: unihtml

spec:
    replicas: 1

    selector:
        matchLabels:
            app: unihtml

    template:
        metadata:
            labels:
                app: unihtml

        spec:
            volumes:
                - name: secret-volume
                  secret:
                      secretName: unihtml

            serviceAccountName: unihtml
            containers:
                - name: unihtml-server
                  image: unidoccloud/unihtml:202408

                  volumeMounts:
                      - name: secret-volume
                        mountPath: /etc/secret-volume
                        readOnly: true

                  env:
                      - name: UNIHTML_LICENSE_PATH
                        value: /etc/secret-volume/license_file

                      - name: UNIHTML_CUSTOMER_NAME
                        valueFrom:
                            secretKeyRef:
                                name: unihtml
                                key: customer_name

                  ports:
                      - containerPort: 8080

Security Note: Chromium, which UniHTML uses for rendering, requires the container to run as root. It’s not ideal, but this is a Chromium limitation for now.

5. secret.sops.yaml

Secrets should never be stored as plain text in your repository. We’re using SOPS to encrypt this file, which contains the customer_name and the license content.

apiVersion: v1
kind: Secret

metadata:
    name: unihtml
    namespace: unihtml

data:
    customer_name: <base64-encoded-customer-name>
    license_file: <base64-encoded-license-file>

6. service.yaml

This file exposes the UniHTML service on port 8080 inside the cluster. We’ll pair this with an Ingress to make it accessible to the public.

apiVersion: v1
kind: Service

metadata:
    name: unihtml-server-service
    namespace: unihtml

spec:
    selector:
        app: unihtml

    ports:
        - protocol: TCP
          port: 8080
          targetPort: 8080

7. ingress.yaml

Ingress allows you to expose your service to the internet.

apiVersion: networking.k8s.io/v1
kind: Ingress

metadata:
    name: command-ingress-unihtml
    namespace: unihtml
    annotations:
        nginx.org/mergeable-ingress-type: 'minion'

spec:
    ingressClassName: nginx
    rules:
        - host: unihtml.command.is
          http:
              paths:
                  - path: /
                    pathType: Prefix
                    backend:
                        service:
                            name: unihtml-server-service
                            port:
                                number: 8080

If you’re looking for additional configurations or insights, this guide offers another helpful perspective on deploying UniHTML.

Converting HTML to PDF

With the service deployed, we can now convert HTML to PDF using UniHTML’s API. Below is a simple client program in Go that takes a webpage (in this case, https://command.is) and converts it to PDF.

package main

import (
    "context"
    "fmt"
    "os"
    "time"

    "github.com/unidoc/unihtml"
    "github.com/unidoc/unihtml/sizes"
    "github.com/unidoc/unipdf/v3/common/license"
    "github.com/unidoc/unipdf/v3/creator"
)

const offlineLicenseKey = `
-----BEGIN UNIDOC LICENSE KEY-----
YOUR-LICENSE-KEY-HERE
-----END UNIDOC LICENSE KEY-----
`
func init() {
    customerName := `Command`
    err := license.SetLicenseKey(offlineLicenseKey, customerName)
    if err != nil {
        panic(err)
    }
}

func main() {
    if len(os.Args) != 2 {
        fmt.Println("Err: No UniHTML server path provided")
        os.Exit(1)
    }

    err := unihtml.Connect(os.Args[1])
    if err != nil {
        fmt.Printf("Err: Connect failed: %v\n", err)
        os.Exit(1)
    }

    c := creator.New()

    webDocument, err := unihtml.NewDocument("https://command.is")
    if err != nil {
        fmt.Printf("Err: NewDocument failed: %v\n", err)
        os.Exit(1)
    }

    webDocument.SetPageSize(sizes.A3)
    webDocument.SetMargins(30, 30, 30, 30)
    webDocument.SetLandscapeOrientation()

    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()

    pages, err := webDocument.GetPdfPages(ctx)
    for _, p := range pages {
        if err := c.AddPage(p); err != nil {
            fmt.Printf("Err: Adding page failed: %v\n", err)
            os.Exit(1)
        }
    }

    err = c.WriteToFile("weburl.pdf")
    if err != nil {
        fmt.Printf("Err: %v\n", err)
        os.Exit(1)
    }
}

Compile and Run

To convert your HTML page, compile and run the program:

go build -o unihtml-convert main.go

./unihtml-convert https://unihtml.command.is:443

This generates weburl.pdf, a PDF version of https://command.is. Voilà!

Conclusion

Deploying UniHTML on a Kubernetes Talos cluster is a straightforward process with the right tools. Once you’ve set up your manifests and deployed using Flux, you can scale, update, and manage your deployment with ease.

The ability to convert HTML to PDF with an API gives you flexibility and control over document generation. Whether you’re handling complex web content or simple HTML pages, UniHTML on Kubernetes gets the job done efficiently.

So, what are you waiting for? Start converting, and watch your PDFs roll in like magic!