<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ana Villar</title>
    <description>The latest articles on DEV Community by Ana Villar (@vilan011).</description>
    <link>https://dev.to/vilan011</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3960129%2F478a841c-62df-4927-9b8e-38ef310de9dd.png</url>
      <title>DEV Community: Ana Villar</title>
      <link>https://dev.to/vilan011</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vilan011"/>
    <language>en</language>
    <item>
      <title>Building an OpenShift 4.18 Cluster from Scratch: Part 1 – The Network Foundation &amp; Utilities Server</title>
      <dc:creator>Ana Villar</dc:creator>
      <pubDate>Tue, 30 Jun 2026 19:21:50 +0000</pubDate>
      <link>https://dev.to/vilan011/building-an-openshift-418-cluster-from-scratch-part-1-the-network-foundation-utilities-server-5g92</link>
      <guid>https://dev.to/vilan011/building-an-openshift-418-cluster-from-scratch-part-1-the-network-foundation-utilities-server-5g92</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This is &lt;strong&gt;Part 1&lt;/strong&gt; of a multi-part series.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;[Part 2: Generating Ignition Configs &amp;amp; VM Prep]&lt;/strong&gt; &lt;em&gt;(Coming Soon)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[Part 3: The Deployment Lifecycle &amp;amp; Troubleshooting]&lt;/strong&gt; &lt;em&gt;(Coming Soon)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;[Part 4: Post-Install Hardening &amp;amp; User Management]&lt;/strong&gt; &lt;em&gt;(Coming Soon)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this series, I walk through a full &lt;strong&gt;OpenShift Container Platform 4.18 deployment from scratch&lt;/strong&gt;. While most tutorials target cloud providers or pre-provisioned infrastructure, this guide replicates a true &lt;strong&gt;platform-agnostic (bare metal)&lt;/strong&gt; installation—executed within a lab of Virtual Machines hosted on &lt;strong&gt;RHEL 10&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This approach allows us to master the critical underlying infrastructure—DNS, DHCP, PXE booting, and load balancing—without the cost of physical hardware. By the end of Part 1, you will have a fully functional &lt;strong&gt;Utilities Server&lt;/strong&gt; that acts as the command center for the entire cluster, handling:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DNS&lt;/strong&gt;: Internal resolution for all cluster nodes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DHCP&lt;/strong&gt;: IP allocation and iPXE boot redirection via Kea.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TFTP &amp;amp; HTTP&lt;/strong&gt;: Serving bootloader images, RHCOS ISOs, and Ignition configs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HAProxy&lt;/strong&gt;: Load balancing for the Kubernetes API and Ingress traffic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We won't run &lt;em&gt;openshift-install&lt;/em&gt; just yet; first, we must build the foundation upon which the cluster relies.&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 References &amp;amp; Background
&lt;/h2&gt;

&lt;p&gt;This lab strictly follows the &lt;strong&gt;Platform-Agnostic&lt;/strong&gt; installation methodology defined by Red Hat. The setup was heavily inspired by the &lt;strong&gt;DO322: Red Hat OpenShift Installation Lab&lt;/strong&gt; training course, ensuring we cover the same critical edge cases and network requirements found in production environments.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://docs.redhat.com/en/documentation/openshift_container_platform/4.18/html/installing_on_any_platform/installing-platform-agnostic" rel="noopener noreferrer"&gt;OpenShift Container Platform 4.18 Installation Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.redhat.com/en/services/training/do322-red-hat-openshift-installation-lab" rel="noopener noreferrer"&gt;DO322 Course Page&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Why use VMs for a "Bare Metal" guide&lt;/strong&gt;? While running on virtual machines provides speed and portability for this lab, the network stack (DNS/DHCP/PXE) and the Ignition configuration flow are &lt;strong&gt;identical&lt;/strong&gt; to what you would encounter on physical hardware. Following the DO322 methodology ensures this tutorial remains relevant for real-world bare-metal deployments.&lt;/p&gt;

&lt;p&gt;Let's get the foundation laid.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛠 Lab Environment Overview
&lt;/h2&gt;

&lt;p&gt;Before diving in, here is the topology I'm working with. All these VMs are hosted on a RHEL 10 hypervisor.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;VM Name&lt;/th&gt;
&lt;th&gt;IP Address&lt;/th&gt;
&lt;th&gt;Specs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Utilities Server&lt;/td&gt;
&lt;td&gt;utilities&lt;/td&gt;
&lt;td&gt;192.168.110.20&lt;/td&gt;
&lt;td&gt;4 vCPU, 8GB RAM, 250GB Disk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bootstrap Node&lt;/td&gt;
&lt;td&gt;bootstos&lt;/td&gt;
&lt;td&gt;192.168.110.100&lt;/td&gt;
&lt;td&gt;4 vCPU, 16GB RAM, 100GB NVMe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Control Plane&lt;/td&gt;
&lt;td&gt;masteros01-03&lt;/td&gt;
&lt;td&gt;.101 to .103&lt;/td&gt;
&lt;td&gt;4 vCPU, 24GB RAM, 100GB NVMe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compute Nodes&lt;/td&gt;
&lt;td&gt;workeros01-03&lt;/td&gt;
&lt;td&gt;.111 to .113&lt;/td&gt;
&lt;td&gt;2 vCPU, 8GB RAM, 100GB SSD&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  1. Setting Up the Utilities Server
&lt;/h2&gt;

&lt;p&gt;We start with a fresh RHEL 10 installation. Once installed, we register the system and ensure all packages are up to date.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;subscription-manager register &lt;span class="nt"&gt;--username&lt;/span&gt; &amp;lt;YOUR_USER&amp;gt; &lt;span class="nt"&gt;--password&lt;/span&gt; &amp;lt;PASSWORD&amp;gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf update redhat-release
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;reboot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After reboot, we verify connectivity and prepare for the services.&lt;/p&gt;

&lt;h4&gt;
  
  
  Network Configuration
&lt;/h4&gt;

&lt;p&gt;The utilities server has two interfaces. I configured them via &lt;em&gt;nmtui&lt;/em&gt; to assign static IPs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;enp1s0: 192.168.1.20 (Management/Internet Gateway)&lt;/li&gt;
&lt;li&gt;enp7s0: 192.168.110.20 (Internal Cluster Network)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and then, add two extra IP addresses to interface enp7s0, 192.168.110.21 and 192.168.110.22, to be used by OpenShift services.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nmcli
&lt;span class="go"&gt;enp1s0: connected to enp1s0
        "Red Hat Virtio 1.0"
        ethernet (virtio_net), 52:54:00:C5:C1:9E, hw, mtu 1500
        ip4 default
        inet4 192.168.1.20/24
        route4 default via 192.168.1.1 metric 100
        route4 192.168.1.0/24 metric 100
        inet6 fe80::5054:ff:fec5:c19e/64
        route6 fe80::/64 metric 1024

enp7s0: connected to enp7s0
        "Red Hat Virtio 1.0"
        ethernet (virtio_net), 52:54:00:E6:D1:A4, hw, mtu 1500
        inet4 192.168.110.22/24
        inet4 192.168.110.21/24
        inet4 192.168.110.20/24
        route4 192.168.110.0/24 metric 101
        route4 192.168.110.0/24 metric 101
        route4 192.168.110.0/24 metric 101
        inet6 fe80::5054:ff:fee6:d1a4/64
        route6 fe80::/64 metric 1024
&lt;/span&gt;&lt;span class="c"&gt;...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Installing &amp;amp; Configuring DNS (BIND)
&lt;/h2&gt;

&lt;p&gt;OpenShift relies heavily on proper DNS. We need records for the API VIPs, ingress VIP, and every node.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install bind &lt;/span&gt;bind-utils &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configuration
&lt;/h3&gt;

&lt;p&gt;I modified the default config (/etc/named.conf) to listen on both interfaces and allow recursion only for our internal subnets. Crucially, we define our zones here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# /etc/named.conf snippet
options {
    listen-on port 53 { 127.0.0.1; 192.168.1.20; 192.168.110.20; };
    allow-query { localhost; 192.168.1.0/24; 192.168.110.0/24; };
    allow-recursion { localhost; 192.168.1.0/24; 192.168.110.0/24; };
    forwarders { 8.8.8.8; 8.8.4.4; }; # Or your ISP DNS
    recursion yes;
};

zone "internal.local" {
    type master;
    file "internal.local.zone";
    allow-query { any; };
};

zone "110.168.192.in-addr.arpa" {
    type master;
    file "110.168.192.in-addr.arpa.zone";
    allow-query { any; };
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Zone Files
&lt;/h3&gt;

&lt;p&gt;We create the forward and reverse lookup zones. Notice the VIPs for the API (192.168.110.21) and Ingress (192.168.110.22). These point to our HAProxy server (which runs on the utilities node).&lt;/p&gt;

&lt;p&gt;/var/named/internal.local.zone:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$TTL 8h
@ IN SOA ns1.internal.local. hostmaster.internal.local. (
    2026042601 ; serial
    1d         ; refresh
    3h         ; retry
    3d         ; expire
    3h )       ; minimum

IN NS ns1.internal.local.

ntp               IN A    192.168.110.20
dns               IN A    192.168.110.20

; Static infrastructure
;hostname IN A 192.168.110.x
utilities.internal.local.  IN A    192.168.110.20

; OpenShift VIPs
api.internal.local.       IN A    192.168.110.21
api-int.internal.local.   IN A    192.168.110.21
*.apps.internal.local.    IN A    192.168.110.22

; Bootstrap Node
bootstos.internal.local.     IN A 192.168.110.100

; Master Nodes
masteros01.internal.local. IN A 192.168.110.101
masteros02.internal.local. IN A 192.168.110.102
masteros03.internal.local. IN A 192.168.110.103

; Worker Nodes
workeros01.internal.local. IN A 192.168.110.111
workeros02.internal.local. IN A 192.168.110.112
workeros03.internal.local. IN A 192.168.110.113
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;/var/named/110.168.192.in-addr.arpa.zone:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$TTL 8h
@ IN SOA ns1.internal.local. hostmaster.internal.local. (
          2026041901 ; serial number
          1d         ; refresh period
          3h         ; retry period
          3d         ; expire time
          3h )       ; minimum TTL

 IN NS   ns1.internal.local.

20                IN PTR  ns1.internal.local.
20                IN PTR  utilities.internal.local.

21    IN PTR api.internal.local.
21    IN PTR api-int.internal.local.

100  IN PTR bootstos.internal.local.
101  IN PTR masteros01.internal.local.
102  IN PTR masteros02.internal.local.
103  IN PTR masteros03.internal.local.

111  IN PTR workeros01.internal.local.
112  IN PTR workeros02.internal.local.
113  IN PTR workeros03.internal.local.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Check configuration and start the service
&lt;/h3&gt;

&lt;p&gt;Change ownership and permissions, and check the configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;named-checkconf
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo chown &lt;/span&gt;root:named /var/named/internal.local.zone
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo chown &lt;/span&gt;root:named /var/named/110.168.192.in-addr.arpa.zone
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;640 /var/named/internal.local.zone
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;640 /var/named/110.168.192.in-addr.arpa.zone
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;named-checkzone internal.local /var/named/internal.local.zone
zone internal.local/IN: loaded serial 2026041901
OK
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;named-checkzone 110.168.192.in-addr.arpa /var/named/110.168.192.in-addr.arpa.zone
zone 110.168.192.in-addr.arpa/IN: loaded serial 2026041901
OK
&lt;span class="nv"&gt;$ &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable and start the service&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; named
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and allow the service through the firewall.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--permanent&lt;/span&gt; &lt;span class="nt"&gt;--add-service&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dns
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--reload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Time Synchronization (Chrony)
&lt;/h2&gt;

&lt;p&gt;Kubernetes clusters are extremely sensitive to time skew. If the clocks drift, certificates and authentication will fail immediately.&lt;/p&gt;

&lt;p&gt;We install chrony (it's already present in RHEL, but we configure it):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# /etc/chrony.conf snippet
pool 2.rhel.pool.ntp.org iburst
allow 192.168.1.0/24
allow 192.168.110.0/24
local stratum 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows the utility server to act as a local NTP source for all nodes while syncing itself with external sources.&lt;/p&gt;

&lt;p&gt;Enable and start the service&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; chronyd
Created symlink &lt;span class="s1"&gt;'/etc/systemd/system/multi-user.target.wants/chronyd.service'&lt;/span&gt; → &lt;span class="s1"&gt;'/usr/lib/systemd/system/chronyd.service'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and allow the service through the firewall.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--add-service&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ntp &lt;span class="nt"&gt;--permanent&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--reload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. DHCP with Kea
&lt;/h2&gt;

&lt;p&gt;Instead of the legacy ISC DHCP server, I used Kea, which is the recommended choice for RHEL 10.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation &amp;amp; Config
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;kea &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The configuration /etc/kea/kea-dhcp4.conf is where the magic happens for PXE booting. We define client classes to distinguish between iPXE clients and standard UEFI/BIOS PXE clients.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "Dhcp4": {
    "interfaces-config": {
      "interfaces": [ "enp7s0/192.168.110.20" ]
    },

    "client-classes": [
        { 
            "name": "iPXE Clients", 
            "test": "option[175].exists", 
            "option-data": [
               { "name": "tftp-server-name", "data": "192.168.110.20" },
               { "name": "boot-file-name", "data": "http://192.168.110.20/boot.ipxe" }
            ]
        },
        {
            "name": "UEFI PXE Clients",
            "test": "option[93].hex == 0x0007 and not option[175].exists",
            "next-server": "192.168.110.20",
            "boot-file-name": "ipxe-snponly-x86_64.efi"
        },
        {
            "name": "BIOS PXE Clients",
            "test": "option[93].hex == 0x0000 and not option[175].exists",
            "next-server": "192.168.110.20",
            "boot-file-name": "undionly.kpxe"
        }
    ],

    "subnet4": [
      {
        "id": 1,
        "subnet": "192.168.110.0/24",
        "pools": [
          {
            "pool": "192.168.110.200 - 192.168.110.250"
          }
        ],

        "reservations": [
          { "hw-address": "AA:BB:CC:DD:EE:01", "ip-address": "192.168.110.100", "hostname": "bootstos.oc41827.internal.local" },

          { "hw-address": "AA:BB:CC:DD:EE:02", "ip-address": "192.168.110.101", "hostname": "masteros01.oc41827.internal.local" },
          { "hw-address": "AA:BB:CC:DD:EE:03", "ip-address": "192.168.110.102", "hostname": "masteros02.oc41827.internal.local" },
          { "hw-address": "AA:BB:CC:DD:EE:04", "ip-address": "192.168.110.103", "hostname": "masteros03.oc41827.internal.local" },

          { "hw-address": "AA:BB:CC:DD:EE:05", "ip-address": "192.168.110.111", "hostname": "workeros01.oc41827.internal.local" },
          { "hw-address": "AA:BB:CC:DD:EE:06", "ip-address": "192.168.110.112", "hostname": "workeros02.oc41827.internal.local" },
          { "hw-address": "AA:BB:CC:DD:EE:07", "ip-address": "192.168.110.113", "hostname": "workeros03.oc41827.internal.local" }
        ],

        "option-data": [
          { "name": "routers", "code": 3, "data": "192.168.110.1" },
          { "name": "domain-name-servers", "code": 6, "space": "dhcp4", "data": "192.168.110.20" },
          { "name": "domain-name", "data": "internal.local" },
          { "name": "domain-search", "data": "oc41827.internal.local internal.local" }
        ]

      },
    ]
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Ensure you replace the MAC addresses with the actual ones assigned to your VMs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check the configuration and enable Kea
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;kea-dhcp4 &lt;span class="nt"&gt;-t&lt;/span&gt; /etc/kea/kea-dhcp4.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might see some warnings, you might ignore them (but do not ignore any error), see this as an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;kea-dhcp4 &lt;span class="nt"&gt;-t&lt;/span&gt; /etc/kea/kea-dhcp4.conf
&lt;span class="go"&gt;2026-04-21 20:52:38.159 WARN  [kea-dhcp4.dhcpsrv/6313.140336065439872] DHCPSRV_MT_DISABLED_QUEUE_CONTROL disabling dhcp queue control when multi-threading is enabled.
2026-04-21 20:52:38.159 WARN  [kea-dhcp4.dhcp4/6313.140336065439872] DHCP4_RESERVATIONS_LOOKUP_FIRST_ENABLED Multi-threading is enabled and host reservations lookup is always performed first.
2026-04-21 20:52:38.160 INFO  [kea-dhcp4.dhcpsrv/6313.140336065439872] DHCPSRV_CFGMGR_NEW_SUBNET4 a new subnet has been added to configuration: 192.168.110.0/24 with params: valid-lifetime=7200
2026-04-21 20:52:38.160 INFO  [kea-dhcp4.dhcpsrv/6313.140336065439872] DHCPSRV_CFGMGR_SOCKET_TYPE_SELECT using socket type raw
2026-04-21 20:52:38.160 INFO  [kea-dhcp4.dhcpsrv/6313.140336065439872] DHCPSRV_CFGMGR_USE_ADDRESS listening on address 192.168.110.20, on interface enp7s0
2026-04-21 20:52:38.160 INFO  [kea-dhcp4.dhcpsrv/6313.140336065439872] DHCPSRV_CFGMGR_SOCKET_TYPE_DEFAULT "dhcp-socket-type" not specified , using default socket type raw
2026-04-21 20:52:38.160 INFO  [kea-dhcp4.dhcpsrv/6313.140336065439872] DHCPSRV_LEASE_MGR_BACKENDS_REGISTERED the following lease backend types are available: memfile
2026-04-21 20:52:38.160 INFO  [kea-dhcp4.hosts/6313.140336065439872] HOSTS_BACKENDS_REGISTERED the following host backend types are available: 
2026-04-21 20:52:38.160 INFO  [kea-dhcp4.dhcpsrv/6313.140336065439872] DHCPSRV_FORENSIC_BACKENDS_REGISTERED the following forensic backend types are available: 
2026-04-21 20:52:38.160 INFO  [kea-dhcp4.database/6313.140336065439872] CONFIG_BACKENDS_REGISTERED the following config backend types are available: 
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable and start the service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; kea-dhcp4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and allow the service through the firewall.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--add-service&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dhcp &lt;span class="nt"&gt;--permanent&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--reload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Web Services &amp;amp; iPXE Boot
&lt;/h2&gt;

&lt;p&gt;OpenShift uses a live-boot mechanism. We need an HTTP server to serve the RHCOS kernel, rootfs, and ignition files, plus TFTP for the initial bootloader.&lt;/p&gt;

&lt;h3&gt;
  
  
  Apache Setup
&lt;/h3&gt;

&lt;p&gt;Install Apache&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;httpd &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To prevent interferences with haproxy, change the listen port to include the IP address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/httpd/conf/httpd.conf 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Listen 80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Listen 192.168.110.20:80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Allow the service through the firewall.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--add-service&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http &lt;span class="nt"&gt;--permanent&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--reload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and enable and start the service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; httpd.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  TFTP Setup
&lt;/h3&gt;

&lt;p&gt;Install TFTP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;tftp-server &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable and start the service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; tftp.socket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and allow the service through the firewall.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--add-service&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;tftp &lt;span class="nt"&gt;--permanent&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--reload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  iPXE Setup
&lt;/h3&gt;

&lt;p&gt;We are going to deploy the OpenShift nodes using ipxe and tftp-server. The key is the boot.ipxe script, which we will discuss in the next blog. For now, we prepare iPXE.&lt;/p&gt;

&lt;p&gt;Install iPXE&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;ipxe-bootimgs

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and prepare the basic structure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo cp&lt;/span&gt; /usr/share/ipxe/undionly.kpxe /var/lib/tftpboot/
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo cp&lt;/span&gt; /usr/share/ipxe/ipxe-snponly-x86_64.efi /var/lib/tftpboot/
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;restorecon &lt;span class="nt"&gt;-Rv&lt;/span&gt; /var/lib/tftpboot

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It can be later used to install other OS, like RHEL or Ubuntu.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Load Balancing with HAProxy
&lt;/h2&gt;

&lt;p&gt;This is critical for High Availability. HAProxy listens on the API VIP (192.168.110.21) and Ingress VIP (192.168.110.22) and forwards traffic to the backend nodes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install HAProxy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;haproxy &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  HAProxy Configuration for Openshift
&lt;/h3&gt;

&lt;p&gt;Use nano or vim to update the configuration file  (/etc/haproxy/haproxy.cfg). See below the changes I made:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# /etc/haproxy/haproxy.cfg snippet
    chroot      /var/lib/haproxy
    #pidfile     /var/run/haproxy.pid

defaults
    mode                    tcp
    log                     global
    option                  tcplog
    retries                 3
    timeout connect         10s
    timeout client          1m
    timeout server          1m
    maxconn                 3000

#---------------------------------------------------------------------
# round robin balancing for RHOCP Kubernetes API Server
#---------------------------------------------------------------------
 listen api-server-6443
   bind 192.168.110.21:6443
   mode tcp
   option httpchk GET /readyz HTTP/1.0
   option log-health-checks
   balance roundrobin
   server bootstos bootstos.oc41827.internal.local:6443 verify none check check-ssl inter 10s fall 2 rise 3 backup
   server masteros01 masteros01.oc41827.internal.local:6443 weight 1 verify none check check-ssl inter 10s fall 2 rise 3
   server masteros02 masteros02.oc41827.internal.local:6443 weight 1 verify none check check-ssl inter 10s fall 2 rise 3
   server masteros03 masteros03.oc41827.internal.local:6443 weight 1 verify none check check-ssl inter 10s fall 2 rise 3

# ---------------------------------------------------------------------
# round robin balancing for RHOCP Machine Config Server
# ---------------------------------------------------------------------
 listen machine-config-server-22623
   bind 192.168.110.21:22623
   mode tcp
   server bootstos bootstos.oc41827.internal.local:22623 check inter 1s backup
   server masteros01 masteros01.oc41827.internal.local:22623 check inter 1s
   server masteros02 masteros02.oc41827.internal.local:22623 check inter 1s
   server masteros03 masteros03.oc41827.internal.local:22623 check inter 1s

# ---------------------------------------------------------------------
# round robin balancing for RHOCP Ingress Insecure Port
# ---------------------------------------------------------------------
 listen ingress-router-80
   bind 192.168.110.22:80
   mode tcp
   balance source
   server workeros01 workeros01.oc41827.internal.local:80 check inter 1s
   server workeros02 workeros02.oc41827.internal.local:80 check inter 1s
   server workeros03 workeros03.oc41827.internal.local:80 check inter 1s

# ---------------------------------------------------------------------
# round robin balancing for RHOCP Ingress Secure Port
# ---------------------------------------------------------------------
 listen ingress-router-443
   bind 192.168.110.22:443
   mode tcp
   balance source
   server workeros01 workeros01.oc41827.internal.local:443 check inter 1s
   server workeros02 workeros02.oc41827.internal.local:443 check inter 1s
   server workeros03 workeros03.oc41827.internal.local:443 check inter 1s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Updating firewalld and SELinux
&lt;/h3&gt;

&lt;p&gt;We need to allow the tcp ports used by OpenShift through firewalld&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--permanent&lt;/span&gt; &lt;span class="nt"&gt;--add-service&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--permanent&lt;/span&gt; &lt;span class="nt"&gt;--add-port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;6443/tcp
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--permanent&lt;/span&gt; &lt;span class="nt"&gt;--add-port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;22623/tcp
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--reload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and also ensure SELinux allows those ports to be used as HTTP ports&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;semanage port &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; http_port_t &lt;span class="nt"&gt;-p&lt;/span&gt; tcp 6443
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;semanage port &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; http_port_t &lt;span class="nt"&gt;-p&lt;/span&gt; tcp 6443
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;semanage port &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; http_port_t &lt;span class="nt"&gt;-p&lt;/span&gt; tcp 22623
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;semanage port &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; http_port_t &lt;span class="nt"&gt;-p&lt;/span&gt; tcp 22623
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configuration check and enabling the HAProxy service
&lt;/h3&gt;

&lt;p&gt;Check the configuration file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;haproxy &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /etc/haproxy/haproxy.cfg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that no output from the previous command is good news!&lt;/p&gt;

&lt;p&gt;and enable and start the service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; haproxy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔜 Coming Up in Part 2
&lt;/h2&gt;

&lt;p&gt;Now that the network is breathing correctly, we need to generate the Ignition files.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Downloading RHCOS and OpenShift CLI tools.&lt;/li&gt;
&lt;li&gt;Creating the install-config.yaml.&lt;/li&gt;
&lt;li&gt;Running openshift-install to generate manifests.&lt;/li&gt;
&lt;li&gt;Critical Step: Disabling Secure Boot on VMs (the "secret" to getting iPXE to work).&lt;/li&gt;
&lt;li&gt;Powering on the nodes and watching the magic happen.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stay tuned!&lt;/p&gt;

&lt;p&gt;Did you find this guide helpful? Let me know in the comments if you'd prefer using Ansible for the setup instead of manual commands! Also, subscribe to follow the rest of the series.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>openshift</category>
      <category>infrastructure</category>
      <category>redhat</category>
    </item>
    <item>
      <title>Building a Kubernetes Cluster on Red Hat Enterprise Linux 10: A kubeadm Guide</title>
      <dc:creator>Ana Villar</dc:creator>
      <pubDate>Thu, 18 Jun 2026 18:22:11 +0000</pubDate>
      <link>https://dev.to/vilan011/building-a-kubernetes-cluster-on-red-hat-enterprise-linux-10-a-kubeadm-guide-1jpe</link>
      <guid>https://dev.to/vilan011/building-a-kubernetes-cluster-on-red-hat-enterprise-linux-10-a-kubeadm-guide-1jpe</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this post, I'll walk you through deploying a production-ready Kubernetes cluster on Red Hat Enterprise Linux 10 using kubeadm. This lab was inspired by Anthony E. Nocentino's excellent &lt;a href="https://app.pluralsight.com/ilx/video-courses/cka-kubeadm-install-basic-cluster-using-cert/course-overview" rel="noopener noreferrer"&gt;Certified Kubernetes Administrator (CKA): Using kubadm to Install a Basic Cluster&lt;/a&gt; training course, which is part of the official &lt;a href="https://app.pluralsight.com/paths/skills/certified-kubernetes-administrator" rel="noopener noreferrer"&gt;Certified Kubernetes Administrator (CKA) path&lt;/a&gt; on &lt;a href="https://www.pluralsight.com/" rel="noopener noreferrer"&gt;Pluralsight&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;⭐ Shout-out: Anthony is a fantastic trainer! His course uses Ubuntu 22.04 as the base OS. I adapted his approach to work on RHEL 10, adding some additional considerations specific to Red Hat's ecosystem.&lt;/p&gt;

&lt;p&gt;One intentional decision in this setup: I deployed Kubernetes v1.35 and CRI-O v1.35, which wasn't the latest version available at installation time. &lt;/p&gt;

&lt;p&gt;This was purposeful. Anthony's course includes a dedicated section on upgrading clusters, and using a slightly older baseline makes that learning path clearer. &lt;/p&gt;

&lt;p&gt;The upgrade procedures (not covered here) are what really solidify your understanding of cluster lifecycle management.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lab Infrastructure Overview
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Nodes Configuration
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Node&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;RAM&lt;/th&gt;
&lt;th&gt;vCPUs&lt;/th&gt;
&lt;th&gt;IP Address&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;rh-cp1&lt;/td&gt;
&lt;td&gt;Control Plane&lt;/td&gt;
&lt;td&gt;12 GiB&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;192.168.110.120&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rh-node1&lt;/td&gt;
&lt;td&gt;Worker&lt;/td&gt;
&lt;td&gt;6 GiB&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;192.168.110.121&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rh-node2&lt;/td&gt;
&lt;td&gt;Worker&lt;/td&gt;
&lt;td&gt;6 GiB&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;192.168.110.122&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rh-node3&lt;/td&gt;
&lt;td&gt;Worker&lt;/td&gt;
&lt;td&gt;6 GiB&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;192.168.110.123&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Note: The IP address schema is just an example and what was more convenient for me.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Supporting Infrastructure
&lt;/h3&gt;

&lt;p&gt;A dedicated &lt;em&gt;utilities&lt;/em&gt; VM (also RHEL 10) provides essential services:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DNS (BIND/named)&lt;/li&gt;
&lt;li&gt;NTP (chrony)&lt;/li&gt;
&lt;li&gt;HTTP (Apache/httpd)&lt;/li&gt;
&lt;li&gt;DHCP (Kea)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This centralized infrastructure simplifies name resolution across all cluster nodes. But this is not essential for this project. You can, instead, ensure the nodes are able to reach each other updating the file &lt;em&gt;/etc/hosts&lt;/em&gt; on all nodes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites &amp;amp; OS Preparation
&lt;/h2&gt;

&lt;p&gt;Before diving into Kubernetes, we need consistent node preparation across &lt;strong&gt;all machines&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. System Registration and Updates
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;subscription-manager register &lt;span class="nt"&gt;--username&lt;/span&gt; &amp;lt;username&amp;gt; &lt;span class="nt"&gt;--password&lt;/span&gt; &amp;lt;password&amp;gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf update redhat-release
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf upgrade
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;reboot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Disable Swap (Required by Kubernetes)
&lt;/h3&gt;

&lt;p&gt;Edit &lt;em&gt;/etc/fstab&lt;/em&gt; to comment out swap entries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;UUID&lt;/span&gt;=&lt;span class="n"&gt;xxxxxxxx&lt;/span&gt;-&lt;span class="n"&gt;xxx&lt;/span&gt;-&lt;span class="n"&gt;xxxx&lt;/span&gt;-&lt;span class="n"&gt;xxxx&lt;/span&gt;-&lt;span class="n"&gt;xxxxxxxxxxxx&lt;/span&gt; &lt;span class="n"&gt;none&lt;/span&gt; &lt;span class="n"&gt;swap&lt;/span&gt; &lt;span class="n"&gt;defaults&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="c"&gt;# ^ Comment this line out
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;swapoff &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;free
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Disable Firewalld
&lt;/h3&gt;

&lt;p&gt;Disable firewalld, as indicated in the &lt;a href="https://docs.tigera.io/calico/latest/getting-started/kubernetes/requirements#node-requirements" rel="noopener noreferrer"&gt;Calico System requirements for Kubernetes&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl stop firewalld
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl disable firewalld
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl mask firewalld
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⚠️ Production Note: Use Calico to maintaining security and enforce network policies later.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Load Kernel Modules and Enable IP Forwarding
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;modprobe overlay
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;modprobe br_netfilter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure sysctl parameters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;sysctl &lt;span class="nt"&gt;--system&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the modules loaded correctly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;lsmod | &lt;span class="nb"&gt;grep &lt;/span&gt;overlay
&lt;span class="nv"&gt;$ &lt;/span&gt;lsmod | &lt;span class="nb"&gt;grep &lt;/span&gt;br_netfilter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Installing Kubernetes Components
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Setting Version Variables
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ KUBERNETES_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;v1.35
&lt;span class="nv"&gt;$ CRIO_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;v1.35
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Adding Repositories
&lt;/h3&gt;

&lt;p&gt;Create the Kubernetes repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/&lt;/span&gt;&lt;span class="nv"&gt;$KUBERNETES_VERSION&lt;/span&gt;&lt;span class="sh"&gt;/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/&lt;/span&gt;&lt;span class="nv"&gt;$KUBERNETES_VERSION&lt;/span&gt;&lt;span class="sh"&gt;/rpm/repodata/repomd.xml.key
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the CRI-O repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; | sudo tee /etc/yum.repos.d/cri-o.repo
[cri-o]
name=CRI-O
baseurl=https://download.opensuse.org/repositories/isv:/cri-o:/stable:/&lt;/span&gt;&lt;span class="nv"&gt;$CRIO_VERSION&lt;/span&gt;&lt;span class="sh"&gt;/rpm/
enabled=1
gpgcheck=1
gpgkey=https://download.opensuse.org/repositories/isv:/cri-o:/stable:/&lt;/span&gt;&lt;span class="nv"&gt;$CRIO_VERSION&lt;/span&gt;&lt;span class="sh"&gt;/rpm/repodata/repomd.xml.key
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Installing Packages
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; kubelet kubeadm kubectl cri-o container-selinux
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configuring CRI-O
&lt;/h3&gt;

&lt;p&gt;Create a cgroup manager configuration file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; | sudo tee /etc/crio/crio.conf.d/02-cgroup-manager.conf
[crio.runtime]
conmon_cgroup = "pod"
cgroup_manager = "cgroupfs"
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable and start services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; crio kubelet
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart crio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Version Locking
&lt;/h3&gt;

&lt;p&gt;To prevent accidental upgrades:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s1"&gt;'dnf-command(versionlock)'&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf versionlock add kubeadm-1.35.4 kubelet-1.35.4 kubectl-1.35.4 cri-o-1.35.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Review the output from the installation of the packages kubeadm, kubelet, kubectl and cri-o, and update the versions to lock in the command above. &lt;/p&gt;

&lt;h2&gt;
  
  
  Initializing the Control Plane
&lt;/h2&gt;

&lt;p&gt;On rh-cp1, download and configure Calico networking. To know the current lastest version, check &lt;a href="https://docs.tigera.io/calico/latest/getting-started/kubernetes/self-managed-onprem/onpremises" rel="noopener noreferrer"&gt;Tigera documentation&lt;/a&gt;, in the Manifest tab:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;wget https://raw.githubusercontent.com/projectcalico/calico/v3.32.0/manifests/calico.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit the CALICO_IPV4POOL_CIDR value to match your pod network plan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CALICO_IPV4POOL_CIDR&lt;/span&gt;
  &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;10.244.0.0/16"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initialize the cluster, to use the same subnet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;kubeadm init &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--kubernetes-version&lt;/span&gt; v1.35.4 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--pod-network-cidr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10.244.0.0/16 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cri-socket&lt;/span&gt; unix:///var/run/crio/crio.sock &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--upload-certs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once successful, save the join commands that appear at the end of the output—you'll need these for worker nodes!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

Alternatively, if you are the root user, you can run:

  export KUBECONFIG=/etc/kubernetes/admin.conf

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 192.168.110.120:6443 --token xxxxxx.xxxxxxxxxxxxxxxx \
 --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx  
$
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your Kubernetes control-plane has initialized successfully!&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring kubectl
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.kube
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo cp&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; /etc/kubernetes/admin.conf &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.kube/config
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;:&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.kube/config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deploy Calico Network
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; calico.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait a few minutes and verify pods are running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get pods &lt;span class="nt"&gt;--all-namespaces&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get nodes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl get pods --all-namespaces
NAMESPACE     NAME                                       READY   STATUS    RESTARTS   AGE
kube-system   calico-kube-controllers-6b4b6457d5-p98c2   1/1     Running   0          2m47s
kube-system   calico-node-5vjrr                          1/1     Running   0          2m47s
kube-system   coredns-7d764666f9-r4vqp                   1/1     Running   0          4m9s
kube-system   coredns-7d764666f9-vh7df                   1/1     Running   0          4m9s
kube-system   etcd-rh-cp1                                1/1     Running   0          4m28s
kube-system   kube-apiserver-rh-cp1                      1/1     Running   0          4m28s
kube-system   kube-controller-manager-rh-cp1             1/1     Running   0          4m27s
kube-system   kube-proxy-4r6h8                           1/1     Running   0          4m10s
kube-system   kube-scheduler-rh-cp1                      1/1     Running   0          4m28s
$ kubectl get nodes
NAME     STATUS   ROLES           AGE     VERSION
rh-cp1   Ready    control-plane   4m40s   v1.35.4
$
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Joining Worker Nodes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;On each worker node&lt;/strong&gt; (rh-node1, rh-node2, rh-node3), run the join command saved during kubeadm init:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;kubeadm &lt;span class="nb"&gt;join &lt;/span&gt;192.168.110.120:6443 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--token&lt;/span&gt; &amp;lt;token&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--discovery-token-ca-cert-hash&lt;/span&gt; sha256:&amp;lt;&lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the cluster health &lt;strong&gt;from the control plane&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get nodes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All nodes should show Ready status.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl get nodes
NAME       STATUS   ROLES           AGE     VERSION
rh-cp1     Ready    control-plane   10m     v1.35.4
rh-node1   Ready    &amp;lt;none&amp;gt;          2m11s   v1.35.4
rh-node2   Ready    &amp;lt;none&amp;gt;          113s    v1.35.4
rh-node3   Ready    &amp;lt;none&amp;gt;          102s    v1.35.4
$
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install bash completion for kubectl:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;bash-completion
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"source &amp;lt;(kubectl completion bash)"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.bashrc
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing the Deployment
&lt;/h2&gt;

&lt;p&gt;Deploy a test application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl create deployment hello-world &lt;span class="nt"&gt;--image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;psk8s.azurecr.io/hello-app:1.0
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get pods &lt;span class="nt"&gt;-o&lt;/span&gt; wide
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expose it via a service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl expose deployment hello-world &lt;span class="nt"&gt;--port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;80 &lt;span class="nt"&gt;--target-port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8080
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get service hello-world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl get service hello-world
NAME          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
hello-world   ClusterIP   10.99.168.218   &amp;lt;none&amp;gt;        80/TCP    8s
$
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, test it. You should see a response from your running container!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl http://10.99.168.218:80
Hello, world!
Version: 1.0.0
hello-world-b5b7f67cc-d26dt
$
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clean up after testing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl delete service hello-world
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl delete deployment hello-world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key Considerations When Moving from Ubuntu to RHEL
&lt;/h2&gt;

&lt;p&gt;Here are the main differences I encountered adapting Anthony's tutorial:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Ubuntu Approach&lt;/th&gt;
&lt;th&gt;RHEL 10 Adaptation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Package Manager&lt;/td&gt;
&lt;td&gt;apt/dpkg&lt;/td&gt;
&lt;td&gt;dnf/rpm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Firewall Management&lt;/td&gt;
&lt;td&gt;ufw/firewalld optional&lt;/td&gt;
&lt;td&gt;firewalld disabled (use Calico policies)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subscription&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;subscription-manager required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SELinux&lt;/td&gt;
&lt;td&gt;Permissive mode default&lt;/td&gt;
&lt;td&gt;Need to handle SELinux context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CRI Runtime&lt;/td&gt;
&lt;td&gt;containerd&lt;/td&gt;
&lt;td&gt;CRI-O&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;This setup provides a solid foundation for learning Kubernetes administration. From here, you could explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cluster upgrades (covered extensively in Anthony's course)&lt;/li&gt;
&lt;li&gt;Network policy enforcement with Calico&lt;/li&gt;
&lt;li&gt;High availability with multiple control plane nodes&lt;/li&gt;
&lt;li&gt;Storage classes and persistent volumes&lt;/li&gt;
&lt;li&gt;Monitoring stack with Prometheus/Grafana&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you found this walkthrough helpful, I'd highly recommend checking out the original Pluralsight course. Anthony's explanations are crystal clear, and adapting them to different distributions is an excellent way to deepen your understanding of what happens under the hood.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://app.pluralsight.com/paths/skills/certified-kubernetes-administrator" rel="noopener noreferrer"&gt;Pluralsight: Certified Kubernetes Administrator Certification Path&lt;/a&gt;&lt;br&gt;
&lt;a href="https://kubernetes.io/docs/" rel="noopener noreferrer"&gt;Official Kubernetes Documentation&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/projectcalico/calico" rel="noopener noreferrer"&gt;Calico Project GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading! Feel free to share your own experiences with Kubernetes on RHEL in the comments below.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>rhel</category>
      <category>kubeadm</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Complete Guide: Setting Up XWiki 17 on Premises</title>
      <dc:creator>Ana Villar</dc:creator>
      <pubDate>Fri, 12 Jun 2026 19:04:27 +0000</pubDate>
      <link>https://dev.to/vilan011/complete-guide-setting-up-xwiki-17-on-premises-4coo</link>
      <guid>https://dev.to/vilan011/complete-guide-setting-up-xwiki-17-on-premises-4coo</guid>
      <description>&lt;p&gt;This guide walks through deploying XWiki 17.10.8 on Red Hat Enterprise Linux 10.2 with Apache Tomcat and PostgreSQL. Total setup time: ~45 minutes. Requires minimum 8GB RAM and 4 vCPUs for acceptable performance.&lt;/p&gt;

&lt;p&gt;If you're looking to deploy a self-hosted wiki solution with enterprise-grade features, XWiki is an excellent choice. Unlike some alternatives, it offers powerful permission management, structured data, and extensibility through extensions. However, the on-premises setup can be intimidating due to multiple moving parts.&lt;/p&gt;

&lt;p&gt;This guide details setting up XWiki 17 on Red Hat Linux Server 10.2 with 16GB RAM and 4 vCPUs. I initially validated these steps in a Mac-hosted VM before deploying them to my production environment on a Dell OptiPlex 7050 Micro.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You'll Need
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Hardware Requirements
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;Minimum&lt;/th&gt;
&lt;th&gt;Recommended&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CPU&lt;/td&gt;
&lt;td&gt;2 cores&lt;/td&gt;
&lt;td&gt;4+ cores&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAM&lt;/td&gt;
&lt;td&gt;4 GB&lt;/td&gt;
&lt;td&gt;16 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage&lt;/td&gt;
&lt;td&gt;20 GB&lt;/td&gt;
&lt;td&gt;100 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; During my initial setup on a 2GB RAM old laptop, XWiki was functional but painfully slow. The jump to 16GB made a night-and-day difference in responsiveness.&lt;/p&gt;

&lt;h3&gt;
  
  
  Software Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Operating System: Red Hat Enterprise Linux 10.2 or Fedora Server &lt;/li&gt;
&lt;li&gt;Servlet Container: Apache Tomcat 10.1.x (Jetty is also supported) &lt;/li&gt;
&lt;li&gt;Database: PostgreSQL 16.x&lt;/li&gt;
&lt;li&gt;Java: OpenJDK 21&lt;/li&gt;
&lt;li&gt;XWiki Platform: 17.10.8 WAR package&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Comments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;As Operating System, you can use other Linux Distributions, including Ubuntu, Windows Server and Docker Engine.&lt;/li&gt;
&lt;li&gt;As Servlet Container, Jetty is also a supported option.&lt;/li&gt;
&lt;li&gt;Other supported databases are HyperSQL, MariaDB, MySQL, or Oracle.&lt;/li&gt;
&lt;li&gt;For more information, review &lt;a href="https://www.xwiki.org/xwiki/bin/view/documentation/xs/admin/installation/prerequisites/" rel="noopener noreferrer"&gt;XWiki prerequisites&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Time Investment
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Installation: ~30 minutes&lt;/li&gt;
&lt;li&gt;Configuration: ~15 minutes&lt;/li&gt;
&lt;li&gt;Testing: ~10 minutes&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Preparation Steps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Install Your Base OS
&lt;/h3&gt;

&lt;p&gt;For this guide, I'm using Red Hat Enterprise Linux, but most steps work equally well on Fedora Server if you don't have RHEL subscriptions.&lt;/p&gt;

&lt;p&gt;Download from &lt;a href="https://developers.redhat.com/" rel="noopener noreferrer"&gt;Red Hat Developer Program&lt;/a&gt; or &lt;a href="https://fedoraproject.org/server/download/" rel="noopener noreferrer"&gt;Fedora Server&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once installed, register (if using RHEL), update, and reboot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;subscription-manager register &lt;span class="nt"&gt;--username&lt;/span&gt; &amp;lt;username&amp;gt; &lt;span class="nt"&gt;--password&lt;/span&gt; &amp;lt;password&amp;gt;
~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;
~&lt;span class="nv"&gt;$ &lt;/span&gt;reboot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Installing Dependencies
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Install and configure Apache Tomcat
&lt;/h4&gt;

&lt;p&gt;Tomcat serves as our servlet container for hosting XWiki:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;tomcat &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command pulls in Java 21 along with necessary dependencies automatically. Review the installation output to confirm java-21-openjdk-headless was included.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Architecture Note&lt;/em&gt;: &lt;br&gt;
Package names vary by architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;x86_64: java-21-openjdk-headless.x86_64&lt;/li&gt;
&lt;li&gt;aarch64: java-21-openjdk-headless.aarch64&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If it seems to be missing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;java-21-openjdk-headless.x86_64 &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;java-21-openjdk-headless.aarch64 &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Configure Firewall
&lt;/h5&gt;

&lt;p&gt;Allow HTTP traffic on port 8080 (Tomcat's default):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--permanent&lt;/span&gt; &lt;span class="nt"&gt;--add-port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8080/tcp
~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--reload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify it's open:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--list-all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Configure Tomcat Memory Settings
&lt;/h5&gt;

&lt;p&gt;By default, Tomcat won't allocate enough memory for XWiki to perform well. Create a custom setenv.sh file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /var/lib/tomcat/bin
~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo chown &lt;/span&gt;tomcat:tomcat &lt;span class="nt"&gt;-R&lt;/span&gt; /var/lib/tomcat
~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /var/lib/tomcat/bin/setenv.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add this configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;JAVA_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;JAVA_OPTS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; -Djava.awt.headless=true"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CATALINA_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-server -Xms1080m -Xmx1600m -Dfile.encoding=utf-8 &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
-Djava.awt.headless=true &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
--add-opens java.base/java.lang=ALL-UNNAMED &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
--add-opens java.base/java.io=ALL-UNNAMED &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
--add-opens java.base/java.util=ALL-UNNAMED &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
--add-opens java.base/java.util.concurrent=ALL-UNNAMED"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Memory Explanation&lt;/em&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;-Xms&lt;/td&gt;
&lt;td&gt;1080MB&lt;/td&gt;
&lt;td&gt;Initial heap size (prevents startup allocation spikes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-Xmx&lt;/td&gt;
&lt;td&gt;1600MB&lt;/td&gt;
&lt;td&gt;Maximum heap size (prevents runaway memory consumption)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;--add-opens&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;Required for Java 17+ compatibility with certain libraries&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Set ownership and test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo chown &lt;/span&gt;tomcat:tomcat /var/lib/tomcat/bin/setenv.sh
~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; tomcat
~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status tomcat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test from another machine by visiting &lt;a href="http://&lt;your-server-name&gt;:8080" rel="noopener noreferrer"&gt;http://your-server-name:8080&lt;/a&gt;. You should see the Tomcat welcome page. Once confirmed, stop the service — we'll restart after XWiki deployment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl stop tomcat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Database Configuration (PostgreSQL)
&lt;/h4&gt;

&lt;h5&gt;
  
  
  Install PostgreSQL
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;postgresql-server postgresql-contrib &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initialize and Start PostgreSQL&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;postgresql-setup &lt;span class="nt"&gt;--initdb&lt;/span&gt;
~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; postgresql
~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status postgresql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Create XWiki User and Database
&lt;/h5&gt;

&lt;p&gt;Switch to the postgres user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;su postgres
bash-5.2&lt;span class="nv"&gt;$ &lt;/span&gt;psql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run these SQL commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="n"&gt;xwiki&lt;/span&gt; &lt;span class="n"&gt;PASSWORD&lt;/span&gt; &lt;span class="s1"&gt;'xwikipassword'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;xwiki&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="k"&gt;OWNER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;xwiki&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;SCHEMA&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;xwiki&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⚠️ Security Warning: Replace 'xwikipassword' with a strong, unique password. Never use default credentials in production. Consider using environment variables or secret management tools like HashiCorp Vault for sensitive data.&lt;/p&gt;

&lt;h5&gt;
  
  
  Configure PostgreSQL Authentication
&lt;/h5&gt;

&lt;p&gt;Find the authentication config file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;su postgres
bash-5.2&lt;span class="nv"&gt;$ &lt;/span&gt;psql &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"SHOW hba_file;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, the file and location is &lt;em&gt;/var/lib/pgsql/data/pg_hba.conf&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Edit the pg_hba.conf file to change authentication method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /var/lib/pgsql/data/pg_hba.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace these lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;# OLD (insecure)
&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;    &lt;span class="n"&gt;all&lt;/span&gt;             &lt;span class="n"&gt;all&lt;/span&gt;             &lt;span class="m"&gt;127&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;1&lt;/span&gt;/&lt;span class="m"&gt;32&lt;/span&gt;            &lt;span class="n"&gt;ident&lt;/span&gt;
&lt;span class="n"&gt;host&lt;/span&gt;    &lt;span class="n"&gt;all&lt;/span&gt;             &lt;span class="n"&gt;all&lt;/span&gt;             ::&lt;span class="m"&gt;1&lt;/span&gt;/&lt;span class="m"&gt;128&lt;/span&gt;                 &lt;span class="n"&gt;ident&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;by&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;# NEW (secure)
&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;    &lt;span class="n"&gt;all&lt;/span&gt;             &lt;span class="n"&gt;all&lt;/span&gt;             &lt;span class="m"&gt;127&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;.&lt;span class="m"&gt;1&lt;/span&gt;/&lt;span class="m"&gt;32&lt;/span&gt;            &lt;span class="n"&gt;scram&lt;/span&gt;-&lt;span class="n"&gt;sha&lt;/span&gt;-&lt;span class="m"&gt;256&lt;/span&gt;
&lt;span class="n"&gt;host&lt;/span&gt;    &lt;span class="n"&gt;all&lt;/span&gt;             &lt;span class="n"&gt;all&lt;/span&gt;             ::&lt;span class="m"&gt;1&lt;/span&gt;/&lt;span class="m"&gt;128&lt;/span&gt;                 &lt;span class="n"&gt;scram&lt;/span&gt;-&lt;span class="n"&gt;sha&lt;/span&gt;-&lt;span class="m"&gt;256&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reload PostgreSQL to apply changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl reload postgresql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify Database Connection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~&lt;span class="nv"&gt;$ &lt;/span&gt;psql postgresql://xwiki:xwikipassword@localhost
&lt;span class="nv"&gt;xwiki&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="se"&gt;\l&lt;/span&gt;  &lt;span class="c"&gt;# Should list databases including 'xwiki'&lt;/span&gt;
&lt;span class="nv"&gt;xwiki&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="se"&gt;\q&lt;/span&gt;  &lt;span class="c"&gt;# Exit psql&lt;/span&gt;
~&lt;span class="err"&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deploying XWiki
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Download Required Files
&lt;/h3&gt;

&lt;p&gt;Get these two files from official sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;XWiki WAR Package: &lt;a href="https://www.xwiki.org/xwiki/bin/view/Download" rel="noopener noreferrer"&gt;Production Installation&lt;/a&gt; → Select "WAR Package for Servlet Container"&lt;/li&gt;
&lt;li&gt;PostgreSQL JDBC Driver: &lt;a href="https://jdbc.postgresql.org/download/" rel="noopener noreferrer"&gt;Official Downloads&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Place them in a download directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; ~/downloads
~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/downloads
&lt;span class="c"&gt;# wget the links here from browser or CLI&lt;/span&gt;
~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls &lt;/span&gt;downloads/
postgresql-42.7.11.jar  xwiki-platform-distribution-war-17.10.8.jar
~&lt;span class="nv"&gt;$ &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deploy the WAR Package
&lt;/h3&gt;

&lt;p&gt;Copy and extract the XWiki WAR:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo cp&lt;/span&gt; ~/downloads/xwiki-platform-distribution-war-17.10.8.jar /var/lib/tomcat/webapps/
~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /var/lib/tomcat/webapps
~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;unzip xwiki-platform-distribution-war-17.10.8.jar
~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo mkdir &lt;/span&gt;xwiki
~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo mv &lt;/span&gt;META-INF redirect resources skins templates WEB-INF xwiki/
~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo rm &lt;/span&gt;xwiki-platform-distribution-war-17.10.8.jar
~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; tomcat:tomcat xwiki/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Common Pitfall: If you get an error about moving files into themselves, ensure you're not inside the extracted directory when creating the target folder.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add PostgreSQL JDBC Driver
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /var/lib/tomcat/webapps/xwiki/WEB-INF/lib
~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo cp&lt;/span&gt; ~/downloads/postgresql-42.7.11.jar &lt;span class="nb"&gt;.&lt;/span&gt;
~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo chown &lt;/span&gt;tomcat:tomcat &lt;span class="k"&gt;*&lt;/span&gt;.jar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configure Hibernate for PostgreSQL
&lt;/h3&gt;

&lt;p&gt;Edit the Hibernate configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /var/lib/tomcat/webapps/xwiki/WEB-INF
~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano hibernate.cfg.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Find and comment out the default database section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;    &lt;span class="c"&gt;&amp;lt;!--
    &amp;lt;property name="hibernate.connection.url"&amp;gt;jdbc:hsqldb:file:${environment.permanentDirectory}/database/xwiki_db;shutdown=true&amp;lt;/property&amp;gt;
    &amp;lt;property name="hibernate.connection.username"&amp;gt;sa&amp;lt;/property&amp;gt;
    &amp;lt;property name="hibernate.connection.password"&amp;gt;&amp;lt;/property&amp;gt;
    &amp;lt;property name="hibernate.connection.driver_class"&amp;gt;org.hsqldb.jdbcDriver&amp;lt;/property&amp;gt;
    --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Find and uncomment the PostgreSQL section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;    &lt;span class="c"&gt;&amp;lt;!-- PostgreSQL configuration.
         Uncomment if you want to use PostgreSQL and comment out other database configurations.
         Notes:
           - "hibernate.jdbc.use_streams_for_binary" needs to be set to "false",
             see https://community.jboss.org/wiki/HibernateCoreMigrationGuide36
           - "xwiki.virtual_mode" can be set to either "schema" or "database". Note that currently the database mode
             doesn't support database creation (see https://jira.xwiki.org/browse/XWIKI-8753)
           - if you want the main wiki database to be different than "xwiki" (or "public" in schema mode)
             you will also have to set the property xwiki.db in xwiki.cfg file
    --&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;property&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"hibernate.connection.url"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;jdbc:postgresql://localhost:5432/xwiki&lt;span class="nt"&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;property&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"hibernate.connection.username"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;xwiki&lt;span class="nt"&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;property&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"hibernate.connection.password"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;**xwikipassword**&lt;span class="nt"&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;property&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"hibernate.connection.driver_class"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;org.postgresql.Driver&lt;span class="nt"&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;property&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"hibernate.jdbc.use_streams_for_binary"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;false&lt;span class="nt"&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;property&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"xwiki.virtual_mode"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;schema&lt;span class="nt"&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;property&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"hibernate.connection.charSet"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;UTF-8&lt;span class="nt"&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;property&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"hibernate.connection.useUnicode"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;property&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"hibernate.connection.characterEncoding"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;utf8&lt;span class="nt"&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;mapping&lt;/span&gt; &lt;span class="na"&gt;resource=&lt;/span&gt;&lt;span class="s"&gt;"xwiki.postgresql.hbm.xml"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;mapping&lt;/span&gt; &lt;span class="na"&gt;resource=&lt;/span&gt;&lt;span class="s"&gt;"feeds.hbm.xml"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;mapping&lt;/span&gt; &lt;span class="na"&gt;resource=&lt;/span&gt;&lt;span class="s"&gt;"instance.hbm.xml"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;mapping&lt;/span&gt; &lt;span class="na"&gt;resource=&lt;/span&gt;&lt;span class="s"&gt;"notification-filter-preferences.hbm.xml"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;mapping&lt;/span&gt; &lt;span class="na"&gt;resource=&lt;/span&gt;&lt;span class="s"&gt;"mailsender.hbm.xml"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;


    &lt;span class="c"&gt;&amp;lt;!-- Oracle configuration.
...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⚠️ Password Reminder: Update the password value to match what you set earlier!&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure Permanent Directory
&lt;/h3&gt;

&lt;p&gt;Create a dedicated directory for XWiki data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /var/lib/xwiki/data
~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; tomcat:tomcat /var/lib/xwiki
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Edit the properties file:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;~$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;nano /var/lib/tomcat/webapps/xwiki/WEB-INF/xwiki.properties
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Uncomment and verify this line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;environment.permanentDirectory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/var/lib/xwiki/data/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Starting the Application
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start tomcat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait 3-5 minutes for Tomcat to fully initialize XWiki (it compiles resources on first boot). Monitor logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/tomcat/catalina.out
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for messages like XWiki context has been initialized successfully.&lt;/p&gt;

&lt;h2&gt;
  
  
  XWiki Configuration Wizard
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Running the Configuration Wizard
&lt;/h3&gt;

&lt;p&gt;Navigate to your server: &lt;a href="http://&lt;your-server-name&gt;:8080/xwiki" rel="noopener noreferrer"&gt;http://your-server-name:8080/xwiki&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You'll see an initialization banner briefly, then the wizard will appear.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmvkp22ygjb44zleqi33e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmvkp22ygjb44zleqi33e.png" alt="XWiki initialization banner" width="751" height="75"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Wizard Steps
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiqlxt96aqqys6y1d8wmr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiqlxt96aqqys6y1d8wmr.png" alt="Distribution wizard" width="629" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Press &lt;strong&gt;Continue&lt;/strong&gt; to begin&lt;/p&gt;

&lt;p&gt;Create Administrator Account: Set a strong admin password (save this securely!).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiawcsqmulnlfo4ubn4on.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiawcsqmulnlfo4ubn4on.png" alt="Create Admin User" width="800" height="730"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Press &lt;strong&gt;Register and Login&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcvbuoc1po2jyga8z0er2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcvbuoc1po2jyga8z0er2.png" alt="User created" width="800" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Press &lt;strong&gt;Continue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Install Standard Flavor: Select "XWiki Standard Flavor" from the dropdown—this includes useful built-in applications&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxtpv1em9qxsii6t2li93.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxtpv1em9qxsii6t2li93.png" alt="Standard Flavor" width="800" height="763"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Press &lt;strong&gt;Install this flavor&lt;/strong&gt;: Wait 2-5 minutes depending on hardware&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fztbtxbuv8616c6wgvqdm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fztbtxbuv8616c6wgvqdm.png" alt="Flavor deployment" width="799" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Press &lt;strong&gt;Install&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fevnguhdox2d17n5aa5y6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fevnguhdox2d17n5aa5y6.png" alt=" " width="800" height="762"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you see it has been Installed, Press &lt;strong&gt;Continue&lt;/strong&gt; to completion.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3szhx03h76tfaxpejqyf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3szhx03h76tfaxpejqyf.png" alt="Deployment completed" width="800" height="759"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After finalization, press Continue and you'll be redirected to your XWiki home page with example documentation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fup5shavbx1toog7rx1z5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fup5shavbx1toog7rx1z5.png" alt="XWiki Home Page" width="800" height="493"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your XWiki server is ready!&lt;/p&gt;

&lt;h2&gt;
  
  
  Post-Installation Checklist
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Security Hardening
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Priority&lt;/th&gt;
&lt;th&gt;Command/File&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Change default PostgreSQL password&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;psql ALTER USER command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Update Tomcat manager passwords&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;tomcat-users.xml&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enable HTTPS via reverse proxy&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;nginx/Apache configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restrict database access&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;pg_hba.conf network rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Regular backups&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;XWiki GUI + cron jobs + pg_dump&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Performance Tuning
&lt;/h3&gt;

&lt;p&gt;With 16GB RAM, you may want to increase XWiki's maximum heap:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Edit setenv.sh&lt;/span&gt;
&lt;span class="nv"&gt;CATALINA_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-Xmx2048m ..."&lt;/span&gt;  &lt;span class="c"&gt;# Increase if needed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Enable query caching in xwiki.properties:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;cache.default.maxSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;5000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Backup Strategy
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Using XWiki GUI
&lt;/h4&gt;

&lt;p&gt;Go to Administer Wiki:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0jwh7usbsqsfjtqpoic7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0jwh7usbsqsfjtqpoic7.png" alt="Administer Wiki" width="705" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select Content:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgrlgjuwv3l5lqq3bvynm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgrlgjuwv3l5lqq3bvynm.png" alt="Administer Options" width="800" height="649"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And select Export:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe4wffbi67ehiepznbf2q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe4wffbi67ehiepznbf2q.png" alt="Option Export" width="800" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can select what pages to export/backup.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fijt941276t7tn0wx75ut.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fijt941276t7tn0wx75ut.PNG" alt="Export" width="800" height="642"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Database:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pg_dump &lt;span class="nt"&gt;-U&lt;/span&gt; xwiki xwiki &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; xwiki_backup_&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y%m%d&lt;span class="si"&gt;)&lt;/span&gt;.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  File Data:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tar &lt;/span&gt;czf xwiki_files_&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y%m%d&lt;span class="si"&gt;)&lt;/span&gt;.tar.gz /var/lib/xwiki/data/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Schedule these as cron jobs for automated backups.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting Common Issues
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Issue: XWiki Won't Load After Startup
&lt;/h3&gt;

&lt;p&gt;Symptoms: Browser shows blank page or timeout at /xwiki&lt;/p&gt;

&lt;p&gt;Solutions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check logs: &lt;code&gt;tail -f /var/log/tomcat/catalina.out&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Verify database connection is active&lt;/li&gt;
&lt;li&gt;Ensure hibernate.cfg.xml has correct credentials&lt;/li&gt;
&lt;li&gt;Restart Tomcat completely&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Issue: Slow Page Loading
&lt;/h3&gt;

&lt;p&gt;Symptoms: Pages take 5+ seconds to load&lt;/p&gt;

&lt;p&gt;Solutions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Increase JVM heap in setenv.sh&lt;/li&gt;
&lt;li&gt;Check available RAM with free -h&lt;/li&gt;
&lt;li&gt;Disable unnecessary extensions in XWiki admin panel&lt;/li&gt;
&lt;li&gt;Consider upgrading to SSD storage&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Issue: Database Connection Error
&lt;/h3&gt;

&lt;p&gt;Symptoms: "Cannot connect to database" during wizard&lt;/p&gt;

&lt;p&gt;Solutions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Verify PostgreSQL is running: &lt;code&gt;sudo systemctl status postgresql&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Test connectivity: &lt;code&gt;psql -U xwiki -d xwiki&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Check pg_hba.conf has scram-sha-256 authentication&lt;/li&gt;
&lt;li&gt;Ensure firewall allows localhost PostgreSQL connections (port 5432)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why On-Premises Over Cloud?
&lt;/h2&gt;

&lt;p&gt;You might wonder why choose self-hosted instead of XWiki Cloud. Here are compelling reasons:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Factor&lt;/th&gt;
&lt;th&gt;On-Premises&lt;/th&gt;
&lt;th&gt;Cloud&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Data Control&lt;/td&gt;
&lt;td&gt;Full&lt;/td&gt;
&lt;td&gt;Limited by provider&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Customization&lt;/td&gt;
&lt;td&gt;Unlimited&lt;/td&gt;
&lt;td&gt;Restricted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost (Long-term)&lt;/td&gt;
&lt;td&gt;Lower&lt;/td&gt;
&lt;td&gt;Subscription-based&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Offline Access&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Requires internet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Integration&lt;/td&gt;
&lt;td&gt;Direct system access&lt;/td&gt;
&lt;td&gt;API-only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For organizations handling sensitive data or requiring deep integration, on-premises deployment provides the flexibility you need.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Setting up XWiki on-premises involves coordinating several components, but once configured, you have a powerful, self-contained knowledge management platform. The setup I've outlined balances security, performance, and maintainability.&lt;/p&gt;

&lt;p&gt;Got questions or encountered different errors? Drop a comment below — I'm happy to help troubleshoot!&lt;/p&gt;

&lt;h2&gt;
  
  
  Further Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.xwiki.org/xwiki/bin/view/Documentation/AdminGuide/Installation/InstallationWAR/" rel="noopener noreferrer"&gt;Installing XWiki .war package manually&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.xwiki.org/xwiki/bin/view/ReleaseNotes/Data/XWiki/17.10.8/" rel="noopener noreferrer"&gt;Release Notes for XWiki 17.10.8&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.xwiki.org/xwiki/bin/view/Documentation/AdminGuide/" rel="noopener noreferrer"&gt;Administrator Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>linux</category>
      <category>xwiki</category>
      <category>tutorial</category>
      <category>devops</category>
    </item>
    <item>
      <title>Hello, DEV Community! 👋</title>
      <dc:creator>Ana Villar</dc:creator>
      <pubDate>Sat, 30 May 2026 15:53:23 +0000</pubDate>
      <link>https://dev.to/vilan011/hello-dev-community-4i30</link>
      <guid>https://dev.to/vilan011/hello-dev-community-4i30</guid>
      <description>&lt;p&gt;I'm Ana, and I'm excited to start sharing my technical journey here. This is a brief post to introduce myself and give you a heads-up on what's coming.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Expect
&lt;/h2&gt;

&lt;p&gt;In upcoming posts, I'll be diving into hands-on, infrastructure-focused tutorials and walkthroughs, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Setting up a XWiki server on-premises&lt;/strong&gt; — from installation to configuration, getting a collaborative wiki platform running in your own environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploying Kubernetes on RHEL 10 virtual machines&lt;/strong&gt; — step-by-step guidance on building a Kubernetes cluster on Red Hat Enterprise Linux 10.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploying OpenShift as virtual machines on a RHEL 10 host&lt;/strong&gt; — exploring how to run OpenShift on top of RHEL 10, combining the power of containers with VM-level control.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My goal is to keep things practical, clear, and rooted in real-world experience — the kind of content I wish I'd had when tackling these setups myself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why These Topics?
&lt;/h2&gt;

&lt;p&gt;Because on-premises infrastructure is far from dead. Whether it's compliance requirements, performance needs, or simply wanting full control over your stack, there's still a strong case for running things yourself. And RHEL was my choice for certification purposes, and it's given me the flexibility to keep using hardware that other vendors have discontinued.&lt;/p&gt;

&lt;p&gt;Stay tuned, and feel free to follow me so you don't miss the upcoming posts. If any of these topics spark your interest, I'd love to hear about it in the comments! 🚀&lt;/p&gt;

</description>
      <category>redhat</category>
      <category>kubernetes</category>
      <category>onprem</category>
    </item>
  </channel>
</rss>
