# Kubernetes Deployment Guide

## **CERTInext OnPremise v2.8 - Kubernetes Deployment Guide**

**Product:** CERTInext Certificate Lifecycle Management\
**Version:** 2.8 (OnPremise)\
**Deployment:** Kubernetes (kubeadm + containerd)\
**Date:** February 2026

***

### **Table of Contents**

* Introduction
* Architecture Overview
* Prerequisites
* Database Setup
* Property Files
* Docker Images
* Kubernetes Secrets
* Application Deployment
* Post-Deployment Configuration
* Verification & Health Checks
* Troubleshooting
* Appendices

***

### **1. Introduction**

This document describes how to deploy CERTInext OnPremise v2.8 on a Kubernetes cluster using pre-built Docker images. It covers infrastructure prerequisites, database setup, application configuration, deployment procedures, and post-deployment validation.

***

### **2. Architecture Overview**

CERTInext consists of four microservices with distinct roles and database requirements:

| Service                     | Image                     | Role                                        | Database             |
| --------------------------- | ------------------------- | ------------------------------------------- | -------------------- |
| **CERTInext Backend API**   | certinextbackendapiv2.8   | Core business logic, certificate operations | certinext            |
| **CERTInext**               | certinextv2.8             | Main portal - UI, OAuth2 login              | certinext\_discovery |
| **CERTInext Backoffice**    | certinextbackofficev2.8   | Admin panel for internal management         | certinext            |
| **CERTInext Discovery API** | certinextdiscoveryapiv2.8 | Certificate discovery, bot management       | certinext\_discovery |

#### **Deployment Order**

The CERTInext Backend API must be deployed and healthy before other services:

1. **CERTInext Backend API (first)** - other services depend on it
2. **CERTInext (second)** - calls Backend API during startup
3. **Backoffice and Discovery API (third)** - can be deployed in parallel

***

### **3. Prerequisites**

#### **3.1 Kubernetes Cluster**

* Kubernetes cluster installed using kubeadm
* containerd configured as the container runtime
* Control plane and worker nodes joined and in Ready state
* CNI plugin installed (Calico recommended)

**Verify cluster readiness:**

```bash
kubectl get nodes -o wide
kubectl get pods -A
```

#### **3.2 MetalLB Load Balancer**

On-premise clusters need MetalLB to provide load balancing capability.

* Layer2 mode with dedicated IP address pool
* Assigns external IPs to LoadBalancer type services

**Verify MetalLB:**

```bash
kubectl -n metallb-system get pods
kubectl get svc -A | grep LoadBalancer
```

#### **3.3 NGINX Ingress Controller**

* NGINX Ingress Controller deployed and exposed via MetalLB
* Provides host-based routing, TLS termination, centralized traffic management

**Verify Ingress Controller:**

```bash
kubectl -n ingress-nginx get pods
kubectl -n ingress-nginx get svc
```

#### **3.4 DNS Entries**

Each CERTInext service requires a DNS record pointing to the Ingress Controller external IP.

| Service                 | Example Hostname                |
| ----------------------- | ------------------------------- |
| CERTInext Backend API   | api.certinext.example.com       |
| CERTInext               | certinext.example.com           |
| CERTInext Backoffice    | bo.certinext.example.com        |
| CERTInext Discovery API | discovery.certinext.example.com |

Replace `example.com` with your organization's domain.

#### **3.5 TLS Certificates**

* Wildcard certificate (\*.certinext.example.com) or per-service certificates
* Certificate and private key files available on Kubernetes nodes

#### **3.6 External MySQL 8.0**

* MySQL 8.0 server accessible from all worker nodes
* Character set: utf8mb4
* Collation: utf8mb4\_0900\_ai\_ci
* ONLY\_FULL\_GROUP\_BY mode disabled

***

### **4. Database Setup (External MySQL)**

#### **4.1 Create MySQL User**

```sql
CREATE USER 'certinext_app'@'%' IDENTIFIED BY '<STRONG_PASSWORD>';
```

Replace `<STRONG_PASSWORD>` with a secure password.

#### **4.2 Create Databases**

```sql
CREATE DATABASE IF NOT EXISTS `certinext`
DEFAULT CHARACTER SET utf8mb4
COLLATE utf8mb4_0900_ai_ci;

CREATE DATABASE IF NOT EXISTS `certinext_discovery`
DEFAULT CHARACTER SET utf8mb4
COLLATE utf8mb4_0900_ai_ci;
```

#### **4.3 Grant Permissions**

```sql
GRANT ALL PRIVILEGES ON `certinext`.* TO 'certinext_app'@'%';
GRANT ALL PRIVILEGES ON `certinext_discovery`.* TO 'certinext_app'@'%';
FLUSH PRIVILEGES;
```

#### **4.4 Execute SQL Scripts**

Three SQL scripts must be executed in strict order:

| Order | Script                                             | Database             | Purpose                            |
| ----- | -------------------------------------------------- | -------------------- | ---------------------------------- |
| 1     | 01\_CERTInext\_Database\_Setup.sql                 | certinext            | Creates tables and seed data       |
| 2     | 02\_CERTInextDiscovery\_Database\_Setup.sql        | certinext\_discovery | Creates discovery database         |
| 3     | 03\_CERTInext\_Post\_Deployment\_Configuration.sql | both                 | Environment-specific configuration |

**Execute in order:**

```bash
mysql -h <MYSQL_HOST> -u certinext_app -p < "01_CERTInext_Database_Setup.sql"
mysql -h <MYSQL_HOST> -u certinext_app -p < "02_CERTInextDiscovery_Database_Setup.sql"
mysql -h <MYSQL_HOST> -u certinext_app -p < "03_CERTInext_Post_Deployment_Configuration.sql"
```

#### **4.5 Disable ONLY\_FULL\_GROUP\_BY**

CERTInext requires ONLY\_FULL\_GROUP\_BY to be disabled. Add to MySQL configuration:

```ini
[mysqld]
sql_mode = "STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION"
```

Then restart MySQL or apply dynamically:

```sql
SET GLOBAL sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';
```

***

### **5. Prepare Property Files**

Each service requires a property file with database connection details and configuration.

#### **5.1 CERTInext Backend API Configuration**

```properties
MYSQL_DB_USERNAME=<ENCRYPTED_USERNAME>
MYSQL_DB_PASSWORD=<ENCRYPTED_PASSWORD>
MYSQL_DB_HOST=<MYSQL_HOST>
MYSQL_DB_PORT=3306
MYSQL_DB_NAME=certinext
LOGFOLDER=/data/certinext-backend-api/Logs/
FILE_DIRECTORY=/data/certinext-backend-api/Temp/
ONPREM=1
```

#### **5.2 CERTInext - certHub.properties**

```properties
WRAPPER_API_URL=http://certinext-backend-api/
WRAPPER_API_APPCODE=emSign@123
WRAPPER_API_AUTHKEY=emSign@123
MYSQL_DB_USERNAME=<ENCRYPTED_USERNAME>
MYSQL_DB_PASSWORD=<ENCRYPTED_PASSWORD>
MYSQL_DB_HOST=<MYSQL_HOST>
MYSQL_DB_PORT=3306
MYSQL_DB_NAME=certinext_discovery
FILE_DIRECTORY=/data/certinext-hub/Temp/
LOGFOLDER=/data/certinext-hub/Logs/
ONPREM=1
```

#### **5.3 CERTInext Backoffice - DB.properties**

```properties
MYSQL_DB_USERNAME=<ENCRYPTED_USERNAME>
MYSQL_DB_PASSWORD=<ENCRYPTED_PASSWORD>
MYSQL_DB_HOST=<MYSQL_HOST>
MYSQL_DB_PORT=3306
MYSQL_DB_NAME=certinext
LOGFOLDER=/data/certinext-backoffice/Logs/
FILE_DIRECTORY=/data/certinext-backoffice/Temp/
ONPREM=1
```

#### **5.4 CERTInext Discovery API - DB.properties**

```properties
MYSQL_DB_USERNAME=<ENCRYPTED_USERNAME>
MYSQL_DB_PASSWORD=<ENCRYPTED_PASSWORD>
MYSQL_DB_HOST=<MYSQL_HOST>
MYSQL_DB_PORT=3306
MYSQL_DB_NAME=certinext_discovery
WRAPPER_API_URL=http://certinext-backend-api/
WRAPPER_API_APPCODE=emSign@123
WRAPPER_API_AUTHKEY=emSign@123
FILE_DIRECTORY=/data/certinext-discovery-api/Temp/
LOGFOLDER=/data/certinext-discovery-api/Logs/
ONPREM=1
```

#### **Key Configuration Notes**

* **ONPREM=1** must be set on all four services
* **Database credentials must be encrypted** using Base64 wrapping
* **File directories must point to container-internal writable paths**
* **Service URLs use internal Kubernetes DNS names**

***

### **6. Import Docker Images**

Docker images must be imported into containerd on every worker node. These are not pulled from a registry.

#### **6.1 Copy Image Files to Worker Nodes**

```bash
scp DockerImages/*.tar <user>@<worker-node>:/tmp/certinext-images/
```

#### **6.2 Import Images on Each Worker Node**

```bash
ctr -n k8s.io images import /tmp/certinext-images/certinextbackendapiv2.8.tar
ctr -n k8s.io images import /tmp/certinext-images/certinextv2.8.tar
ctr -n k8s.io images import /tmp/certinext-images/certinextbackofficev2.8.tar
ctr -n k8s.io images import /tmp/certinext-images/certinextdiscoveryapiv2.8.tar
```

#### **6.3 Verify Imported Images**

```bash
ctr -n k8s.io images ls | grep certinext
```

***

### **7. Namespace Strategy**

#### **Option A - Single Shared Namespace (Recommended)**

A single certinext namespace is recommended for on-premise deployments. It simplifies service communication and secret management.

```bash
kubectl create namespace certinext
```

#### **Option B - Separate Namespaces Per Service**

For stricter isolation, create separate namespaces. Service URLs require fully qualified names:

```bash
kubectl create namespace certinext-backend-api
kubectl create namespace certinext-hub
kubectl create namespace certinext-backoffice
kubectl create namespace certinext-discovery-api
```

***

### **8. Create Kubernetes Secrets**

#### **8.1 TLS Certificate Secret**

```bash
kubectl -n certinext create secret tls certinext-tls \
  --cert=/path/to/certificate.crt \
  --key=/path/to/private.key
```

#### **8.2 Database Property Secrets**

Create a secret for each service from its property file:

```bash
kubectl -n certinext create secret generic certinext-backend-api-db \
  --from-file=db.properties=/path/to/db.properties

kubectl -n certinext create secret generic certinext-hub-db \
  --from-file=certHub.properties=/path/to/certHub.properties

kubectl -n certinext create secret generic certinext-backoffice-db \
  --from-file=DB.properties=/path/to/backoffice-DB.properties

kubectl -n certinext create secret generic certinext-discovery-api-db \
  --from-file=DB.properties=/path/to/discoveryapi-DB.properties
```

***

### **9. Deploy Applications**

YAML manifests are in `Deployment/yaml/`. Each contains a Deployment, Service, and Ingress.

#### **Deployment Order**

* Backend API must be healthy before other services
* CERTInext depends on Backend API
* Backoffice and Discovery API can deploy in parallel

#### **Step 1: Deploy Backend API**

```bash
kubectl apply -f Deployment/yaml/certinext-backend-api.yaml
kubectl -n certinext rollout status deploy/certinext-backend-api
```

#### **Step 2: Deploy CERTInext**

```bash
kubectl apply -f Deployment/yaml/certinext-hub.yaml
```

#### **Step 3: Deploy Backoffice and Discovery API**

```bash
kubectl apply -f Deployment/yaml/certinext-backoffice.yaml
kubectl apply -f Deployment/yaml/certinext-discovery-api.yaml
```

***

### **10. Post-Deployment Configuration**

#### **10.1 Update Database Settings**

```sql
USE certinext;
UPDATE mas_hub_wrapper_creds SET apiBaseURL = 'https://certinext.example.com' WHERE ID = 1;
```

***

### **11. Verification & Health Checks**

#### **11.1 Check Pod Status**

```bash
kubectl -n certinext get pods -o wide
```

All pods should show Running with 1/1 Ready.

#### **11.2 Review Application Logs**

```bash
kubectl -n certinext logs deploy/certinext-backend-api --tail=200
```

Look for successful database connections and Spring Boot startup messages.

#### **11.3 HTTP Health Checks**

```bash
curl -k https://api.certinext.example.com/
curl -k https://certinext.example.com/
curl -k https://bo.certinext.example.com/
curl -k https://discovery.certinext.example.com/
```

***

### **12. Troubleshooting**

#### **Pod in CrashLoopBackOff**

**Symptoms:** Pod continuously restarts.

**Resolution:**

* Check pod logs for error messages
* Verify property file secret is mounted correctly
* Verify database is reachable from pod network

#### **Backend API Not Reachable**

**Symptoms:** Connection errors to WRAPPER\_API\_URL.

**Resolution:**

* Verify Backend API pod is Running
* Test DNS from within cluster
* Verify WRAPPER\_API\_URL in config files

#### **Database Connection Failures**

**Symptoms:** Connection link failures or access denied errors.

**Resolution:**

* Verify MySQL accessibility from worker nodes
* Verify secret contents and encryption
* Check MySQL user grants

#### **Image Pull Errors**

**Symptoms:** Pod stuck in ErrImagePull.

**Resolution:**

* Verify images imported on scheduled worker node
* Confirm imagePullPolicy is IfNotPresent
* Import images on all worker nodes

***

### **Appendix A: YAML Manifest Summary**

| YAML File                    | Resources                      | Service Port | Ingress Host                    |
| ---------------------------- | ------------------------------ | ------------ | ------------------------------- |
| certinext-backend-api.yaml   | Deployment + Service + Ingress | 80 → 8080    | api.certinext.example.com       |
| certinext-hub.yaml           | Deployment + Service + Ingress | 80 → 8080    | certinext.example.com           |
| certinext-backoffice.yaml    | Deployment + Service + Ingress | 80 → 8080    | bo.certinext.example.com        |
| certinext-discovery-api.yaml | Deployment + Service + Ingress | 80 → 8080    | discovery.certinext.example.com |

#### **Common Deployment Patterns**

* Init container creates log and temp directories
* imagePullPolicy set to IfNotPresent for offline environments
* Readiness probe: HTTP GET / with 30s initial delay
* Liveness probe: HTTP GET / with 60s initial delay
* Volumes: Secrets for config, hostPath for logs and temp
* Ingress: NGINX with TLS termination enabled

#### **Quick Reference Checklist**

* Verify Kubernetes cluster meets all prerequisites
* Set up external MySQL database
* Prepare and encrypt property files
* Import Docker images on all worker nodes
* Create Kubernetes namespace and secrets
* Deploy services in correct order
* Run post-deployment configuration
* Execute health checks and verification

***

**Document Version:** 2.8.1\
**Last Updated:** February 2026


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.certinext.io/documentation/deployment-guide-for-on-prem/kubernetes-deployment-guide.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
