<?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: Janak Shrestha</title>
    <description>The latest articles on DEV Community by Janak Shrestha (@janak0ff).</description>
    <link>https://dev.to/janak0ff</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%2F669804%2F8baf407d-13b3-4a51-888b-808a72d4ef8f.jpg</url>
      <title>DEV Community: Janak Shrestha</title>
      <link>https://dev.to/janak0ff</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/janak0ff"/>
    <language>en</language>
    <item>
      <title>Install and Configure HaProxy LBR</title>
      <dc:creator>Janak Shrestha</dc:creator>
      <pubDate>Fri, 18 Sep 2026 17:16:35 +0000</pubDate>
      <link>https://dev.to/janak0ff/install-and-configure-haproxy-lbr-4h0a</link>
      <guid>https://dev.to/janak0ff/install-and-configure-haproxy-lbr-4h0a</guid>
      <description>&lt;p&gt;There is a static website running in &lt;code&gt;Stratos Datacenter&lt;/code&gt;. They have already configured the app servers and code is already deployed there. To make it work properly, they need to configure &lt;code&gt;LBR&lt;/code&gt; server. There are number of options for that, but team has decided to go with &lt;code&gt;HAproxy&lt;/code&gt;. FYI, apache is running on port &lt;code&gt;8087&lt;/code&gt; on all app servers. Complete this task as per below details.  &lt;/p&gt;

&lt;p&gt;a. Install and configure &lt;code&gt;HAproxy&lt;/code&gt; on &lt;code&gt;LBR&lt;/code&gt; server using &lt;code&gt;yum&lt;/code&gt; only and make sure all app servers are added to &lt;code&gt;HAproxy&lt;/code&gt; load balancer. &lt;code&gt;HAproxy&lt;/code&gt; must serve on default &lt;code&gt;http&lt;/code&gt; port (&lt;code&gt;Note&lt;/code&gt;: Please do not remove &lt;code&gt;stats socket /var/lib/haproxy/stats&lt;/code&gt; entry from haproxy default config.).&lt;br&gt;&lt;br&gt;
b. Once done, you can access the website using &lt;code&gt;curl http://localhost:80&lt;/code&gt; on the &lt;code&gt;LBR&lt;/code&gt; server.&lt;/p&gt;


&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Load balancing is a critical component of modern infrastructure, ensuring high availability, scalability, and reliability for web applications. HAProxy (High Availability Proxy) is one of the most popular open-source load balancers, known for its performance, reliability, and rich feature set. It is widely used in production environments to distribute traffic across multiple backend servers.&lt;/p&gt;

&lt;p&gt;This guide covers the complete process of installing and configuring HAProxy on a load balancer server to distribute traffic to multiple application servers running Apache. The tutorial is based on a real-world scenario where a static website needed to be load balanced across three app servers in a datacenter environment.&lt;/p&gt;


&lt;h2&gt;
  
  
  Understanding HAProxy
&lt;/h2&gt;
&lt;h3&gt;
  
  
  What is HAProxy
&lt;/h3&gt;

&lt;p&gt;HAProxy is a free, open-source software that provides high availability load balancing and proxying for TCP and HTTP-based applications. It is written in C and is known for its high performance, reliability, and low resource consumption.&lt;/p&gt;
&lt;h3&gt;
  
  
  Key Features
&lt;/h3&gt;

&lt;p&gt;HAProxy offers several features that make it ideal for production environments. It supports layer 4 (TCP) and layer 7 (HTTP) load balancing. Various load balancing algorithms are available, including round-robin, least connections, and source IP hash. Health checks ensure traffic is only sent to healthy backend servers. SSL/TLS termination and session persistence are supported. A statistics interface provides real-time monitoring.&lt;/p&gt;
&lt;h3&gt;
  
  
  HAProxy Architecture
&lt;/h3&gt;

&lt;p&gt;HAProxy acts as an intermediary between clients and backend servers. Clients connect to HAProxy, which then forwards requests to one of the available backend servers based on the configured load balancing algorithm.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Frontend&lt;/td&gt;
&lt;td&gt;Defines how requests are received&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backend&lt;/td&gt;
&lt;td&gt;Defines the servers that will handle requests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Listen&lt;/td&gt;
&lt;td&gt;Combines frontend and backend in one section&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Global&lt;/td&gt;
&lt;td&gt;Process-wide settings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Defaults&lt;/td&gt;
&lt;td&gt;Default parameters for all sections&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before beginning, ensure the following are available:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Access to the load balancer server (stlb01) with root or sudo privileges&lt;/li&gt;
&lt;li&gt;App servers configured and running Apache on port 8087&lt;/li&gt;
&lt;li&gt;Network connectivity between the LBR server and app servers&lt;/li&gt;
&lt;li&gt;Basic understanding of Linux networking and web servers&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Server Details for This Tutorial
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;LBR Server&lt;/td&gt;
&lt;td&gt;stlb01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User&lt;/td&gt;
&lt;td&gt;loki&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Password&lt;/td&gt;
&lt;td&gt;Mischi3f&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App Server 1&lt;/td&gt;
&lt;td&gt;stapp01 (Apache on port 8087)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App Server 2&lt;/td&gt;
&lt;td&gt;stapp02 (Apache on port 8087)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App Server 3&lt;/td&gt;
&lt;td&gt;stapp03 (Apache on port 8087)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HAProxy Port&lt;/td&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  Installing HAProxy
&lt;/h2&gt;

&lt;p&gt;Connect to the load balancer server and switch to root.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh loki@stlb01
&lt;span class="nb"&gt;sudo &lt;/span&gt;su -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install HAProxy using the system package manager.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The installation includes HAProxy and its dependencies.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding the Default Configuration
&lt;/h2&gt;

&lt;p&gt;The default HAProxy configuration file is located at &lt;code&gt;/etc/haproxy/haproxy.cfg&lt;/code&gt;. It contains global settings, defaults, a frontend, and a backend.&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="nb"&gt;cat&lt;/span&gt; /etc/haproxy/haproxy.cfg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default configuration includes a stats socket at &lt;code&gt;/var/lib/haproxy/stats&lt;/code&gt; which must be preserved. It also includes default frontend and backend sections that need to be modified.&lt;/p&gt;




&lt;h2&gt;
  
  
  Configuring HAProxy
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Backup the Original Configuration
&lt;/h3&gt;

&lt;p&gt;Always backup the original configuration before making 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="nb"&gt;cp&lt;/span&gt; /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.bak
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Edit the Configuration File
&lt;/h3&gt;

&lt;p&gt;Edit the configuration file to add the correct frontend and backend sections.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vi /etc/haproxy/haproxy.cfg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Complete Configuration
&lt;/h3&gt;

&lt;p&gt;Replace the configuration with the following content.&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;#---------------------------------------------------------------------
# Global settings
#---------------------------------------------------------------------
&lt;/span&gt;&lt;span class="n"&gt;global&lt;/span&gt;
    &lt;span class="n"&gt;log&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="n"&gt;local2&lt;/span&gt;
    &lt;span class="n"&gt;chroot&lt;/span&gt;      /&lt;span class="n"&gt;var&lt;/span&gt;/&lt;span class="n"&gt;lib&lt;/span&gt;/&lt;span class="n"&gt;haproxy&lt;/span&gt;
    &lt;span class="n"&gt;pidfile&lt;/span&gt;     /&lt;span class="n"&gt;var&lt;/span&gt;/&lt;span class="n"&gt;run&lt;/span&gt;/&lt;span class="n"&gt;haproxy&lt;/span&gt;.&lt;span class="n"&gt;pid&lt;/span&gt;
    &lt;span class="n"&gt;maxconn&lt;/span&gt;     &lt;span class="m"&gt;4000&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;        &lt;span class="n"&gt;haproxy&lt;/span&gt;
    &lt;span class="n"&gt;group&lt;/span&gt;       &lt;span class="n"&gt;haproxy&lt;/span&gt;
    &lt;span class="n"&gt;daemon&lt;/span&gt;
    &lt;span class="n"&gt;stats&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt; /&lt;span class="n"&gt;var&lt;/span&gt;/&lt;span class="n"&gt;lib&lt;/span&gt;/&lt;span class="n"&gt;haproxy&lt;/span&gt;/&lt;span class="n"&gt;stats&lt;/span&gt;
    &lt;span class="n"&gt;ssl&lt;/span&gt;-&lt;span class="n"&gt;default&lt;/span&gt;-&lt;span class="n"&gt;bind&lt;/span&gt;-&lt;span class="n"&gt;ciphers&lt;/span&gt; &lt;span class="n"&gt;PROFILE&lt;/span&gt;=&lt;span class="n"&gt;SYSTEM&lt;/span&gt;
    &lt;span class="n"&gt;ssl&lt;/span&gt;-&lt;span class="n"&gt;default&lt;/span&gt;-&lt;span class="n"&gt;server&lt;/span&gt;-&lt;span class="n"&gt;ciphers&lt;/span&gt; &lt;span class="n"&gt;PROFILE&lt;/span&gt;=&lt;span class="n"&gt;SYSTEM&lt;/span&gt;

&lt;span class="c"&gt;#---------------------------------------------------------------------
# common defaults
#---------------------------------------------------------------------
&lt;/span&gt;&lt;span class="n"&gt;defaults&lt;/span&gt;
    &lt;span class="n"&gt;mode&lt;/span&gt;                    &lt;span class="n"&gt;http&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;                     &lt;span class="n"&gt;global&lt;/span&gt;
    &lt;span class="n"&gt;option&lt;/span&gt;                  &lt;span class="n"&gt;httplog&lt;/span&gt;
    &lt;span class="n"&gt;option&lt;/span&gt;                  &lt;span class="n"&gt;dontlognull&lt;/span&gt;
    &lt;span class="n"&gt;option&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;-&lt;span class="n"&gt;server&lt;/span&gt;-&lt;span class="n"&gt;close&lt;/span&gt;
    &lt;span class="n"&gt;option&lt;/span&gt; &lt;span class="n"&gt;forwardfor&lt;/span&gt;       &lt;span class="n"&gt;except&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;0&lt;/span&gt;/&lt;span class="m"&gt;8&lt;/span&gt;
    &lt;span class="n"&gt;option&lt;/span&gt;                  &lt;span class="n"&gt;redispatch&lt;/span&gt;
    &lt;span class="n"&gt;retries&lt;/span&gt;                 &lt;span class="m"&gt;3&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;-&lt;span class="n"&gt;request&lt;/span&gt;    &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;           &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="n"&gt;connect&lt;/span&gt;         &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;          &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;          &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;-&lt;span class="n"&gt;keep&lt;/span&gt;-&lt;span class="n"&gt;alive&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;           &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
    &lt;span class="n"&gt;maxconn&lt;/span&gt;                 &lt;span class="m"&gt;3000&lt;/span&gt;

&lt;span class="c"&gt;#---------------------------------------------------------------------
# Main frontend
#---------------------------------------------------------------------
&lt;/span&gt;&lt;span class="n"&gt;frontend&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;
    &lt;span class="n"&gt;bind&lt;/span&gt; *:&lt;span class="m"&gt;80&lt;/span&gt;
    &lt;span class="n"&gt;default_backend&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;

&lt;span class="c"&gt;#---------------------------------------------------------------------
# Backend for app servers
#---------------------------------------------------------------------
&lt;/span&gt;&lt;span class="n"&gt;backend&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;
    &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="n"&gt;roundrobin&lt;/span&gt;
    &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="n"&gt;stapp01&lt;/span&gt; &lt;span class="n"&gt;stapp01&lt;/span&gt;:&lt;span class="m"&gt;8087&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;
    &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="n"&gt;stapp02&lt;/span&gt; &lt;span class="n"&gt;stapp02&lt;/span&gt;:&lt;span class="m"&gt;8087&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;
    &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="n"&gt;stapp03&lt;/span&gt; &lt;span class="n"&gt;stapp03&lt;/span&gt;:&lt;span class="m"&gt;8087&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Configuration Explanation
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Section&lt;/th&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;global&lt;/td&gt;
&lt;td&gt;stats socket&lt;/td&gt;
&lt;td&gt;Unix socket for stats (must be preserved)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;defaults&lt;/td&gt;
&lt;td&gt;mode http&lt;/td&gt;
&lt;td&gt;Operate in HTTP mode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;defaults&lt;/td&gt;
&lt;td&gt;timeout&lt;/td&gt;
&lt;td&gt;Various timeout settings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;frontend&lt;/td&gt;
&lt;td&gt;bind *:80&lt;/td&gt;
&lt;td&gt;Listen on all interfaces on port 80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;frontend&lt;/td&gt;
&lt;td&gt;default_backend&lt;/td&gt;
&lt;td&gt;Send traffic to backend named app&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;backend&lt;/td&gt;
&lt;td&gt;balance roundrobin&lt;/td&gt;
&lt;td&gt;Use round-robin load balancing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;backend&lt;/td&gt;
&lt;td&gt;server&lt;/td&gt;
&lt;td&gt;Define backend servers with health checks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Verifying the Configuration
&lt;/h2&gt;

&lt;p&gt;Before restarting HAProxy, verify the configuration is valid.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&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;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;Configuration file is valid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Starting and Enabling HAProxy
&lt;/h2&gt;

&lt;p&gt;Start the HAProxy service and enable it to start on boot.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl start haproxy
systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;haproxy
systemctl status haproxy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The status output should show the service is active and running.&lt;/p&gt;




&lt;h2&gt;
  
  
  Verifying the Setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Check Listening Ports
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ss &lt;span class="nt"&gt;-tlnp&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; :80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output shows HAProxy listening on port 80.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LISTEN 0 3000 0.0.0.0:80 0.0.0.0:* users:(("haproxy",pid=59036,fd=7))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test with Curl
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:80
&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;Welcome to xFusionCorp Industries!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test Round-Robin Load Balancing
&lt;/h3&gt;

&lt;p&gt;Run multiple curl commands to verify traffic is distributed across all backend servers.&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="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;1..6&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://localhost:80&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Check Backend Health
&lt;/h3&gt;

&lt;p&gt;Test each backend server directly to ensure they are reachable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-I&lt;/span&gt; http://stapp01:8087
curl &lt;span class="nt"&gt;-I&lt;/span&gt; http://stapp02:8087
curl &lt;span class="nt"&gt;-I&lt;/span&gt; http://stapp03:8087
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output shows HTTP 200 OK for each server.&lt;/p&gt;




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

&lt;h3&gt;
  
  
  503 Service Unavailable
&lt;/h3&gt;

&lt;p&gt;When HAProxy returns a 503 error, it means no backend server is available. This typically indicates that HAProxy cannot reach the backend servers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cause 1: Incorrect IP Addresses or Hostnames&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the backend servers are configured with wrong IP addresses or hostnames that don't resolve, HAProxy cannot connect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Use hostnames that resolve 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="c"&gt;# Check if hostnames resolve&lt;/span&gt;
getent hosts stapp01
getent hosts stapp02
getent hosts stapp03

&lt;span class="c"&gt;# Update configuration to use hostnames&lt;/span&gt;
backend app
    balance roundrobin
    server stapp01 stapp01:8087 check
    server stapp02 stapp02:8087 check
    server stapp03 stapp03:8087 check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cause 2: Backend Servers Not Running&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If Apache is not running on the app servers, HAProxy cannot connect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Start Apache on each app server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh tony@stapp01
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start httpd
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;httpd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cause 3: Firewall Blocking Connections&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If a firewall is blocking port 8087 on the app servers, HAProxy cannot connect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Allow port 8087 on each app server.&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="nb"&gt;sudo &lt;/span&gt;firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--add-port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8087/tcp &lt;span class="nt"&gt;--permanent&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;
  
  
  Configuration File Errors
&lt;/h3&gt;

&lt;p&gt;When HAProxy fails to start, check the configuration syntax.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&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;Common errors include duplicate section names, invalid syntax, or missing parameters.&lt;/p&gt;

&lt;h3&gt;
  
  
  Duplicate Section Names
&lt;/h3&gt;

&lt;p&gt;HAProxy does not allow duplicate frontend or backend names. Ensure each section has a unique name.&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;# Check for duplicates&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"^frontend&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;^backend"&lt;/span&gt; /etc/haproxy/haproxy.cfg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Port Already in Use
&lt;/h3&gt;

&lt;p&gt;If port 80 is already in use by another service, HAProxy fails to start.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ss &lt;span class="nt"&gt;-tlnp&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; :80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Identify and stop the conflicting service.&lt;/p&gt;

&lt;h3&gt;
  
  
  Checking Logs
&lt;/h3&gt;

&lt;p&gt;HAProxy logs can be checked using journalctl.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; haproxy &lt;span class="nt"&gt;-n&lt;/span&gt; 50
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Best Practices
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Always Backup Configuration
&lt;/h3&gt;

&lt;p&gt;Before making changes, back up 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="nb"&gt;cp&lt;/span&gt; /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.bak
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Preserve the Stats Socket
&lt;/h3&gt;

&lt;p&gt;The stats socket at &lt;code&gt;/var/lib/haproxy/stats&lt;/code&gt; is used for monitoring and management. Do not remove this entry.&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;stats&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt; /&lt;span class="n"&gt;var&lt;/span&gt;/&lt;span class="n"&gt;lib&lt;/span&gt;/&lt;span class="n"&gt;haproxy&lt;/span&gt;/&lt;span class="n"&gt;stats&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use Health Checks
&lt;/h3&gt;

&lt;p&gt;Always configure health checks to ensure traffic is only sent to healthy servers.&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;server&lt;/span&gt; &lt;span class="n"&gt;stapp01&lt;/span&gt; &lt;span class="n"&gt;stapp01&lt;/span&gt;:&lt;span class="m"&gt;8087&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test Configuration Before Restart
&lt;/h3&gt;

&lt;p&gt;Always verify the configuration before restarting 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;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;h3&gt;
  
  
  Monitor Backend Health
&lt;/h3&gt;

&lt;p&gt;Use the HAProxy stats interface to monitor backend health.&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="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"show stat"&lt;/span&gt; | socat stdio /var/lib/haproxy/stats
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Document Configuration Changes
&lt;/h3&gt;

&lt;p&gt;Maintain documentation of configuration changes for auditing and troubleshooting.&lt;/p&gt;




&lt;h2&gt;
  
  
  Automation Script
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Installing and Configuring HAProxy"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"==================================="&lt;/span&gt;

&lt;span class="c"&gt;# Install HAProxy&lt;/span&gt;
yum &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; haproxy

&lt;span class="c"&gt;# Backup original configuration&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.bak

&lt;span class="c"&gt;# Write new configuration&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /etc/haproxy/haproxy.cfg &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
global
    log         127.0.0.1 local2
    chroot      /var/lib/haproxy
    pidfile     /var/run/haproxy.pid
    maxconn     4000
    user        haproxy
    group       haproxy
    daemon
    stats socket /var/lib/haproxy/stats
    ssl-default-bind-ciphers PROFILE=SYSTEM
    ssl-default-server-ciphers PROFILE=SYSTEM

defaults
    mode                    http
    log                     global
    option                  httplog
    option                  dontlognull
    option http-server-close
    option forwardfor       except 127.0.0.0/8
    option                  redispatch
    retries                 3
    timeout http-request    10s
    timeout queue           1m
    timeout connect         10s
    timeout client          1m
    timeout server          1m
    timeout http-keep-alive 10s
    timeout check           10s
    maxconn                 3000

frontend main
    bind *:80
    default_backend app

backend app
    balance roundrobin
    server stapp01 stapp01:8087 check
    server stapp02 stapp02:8087 check
    server stapp03 stapp03:8087 check
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# Verify configuration&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;span class="c"&gt;# Start and enable HAProxy&lt;/span&gt;
systemctl start haproxy
systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;haproxy

&lt;span class="c"&gt;# Verify&lt;/span&gt;
systemctl status haproxy
ss &lt;span class="nt"&gt;-tlnp&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; :80
curl http://localhost:80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Summary of the Setup
&lt;/h3&gt;

&lt;p&gt;HAProxy has been successfully installed and configured on the load balancer server. The frontend listens on port 80 and distributes traffic to three backend app servers running Apache on port 8087. The round-robin algorithm ensures even distribution of requests. Health checks ensure traffic is only sent to healthy servers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Takeaways
&lt;/h3&gt;

&lt;p&gt;HAProxy is a powerful and efficient load balancer for HTTP and TCP applications. The stats socket at &lt;code&gt;/var/lib/haproxy/stats&lt;/code&gt; must be preserved. Backend servers should be referenced by hostnames or reachable IP addresses. Health checks are essential for ensuring high availability. Configuration should always be verified before restarting the service.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verification Results
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Configuration valid&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service active&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Listening on port 80&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backend servers reachable&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Website accessible&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;frontend&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;
    &lt;span class="n"&gt;bind&lt;/span&gt; *:&lt;span class="m"&gt;80&lt;/span&gt;
    &lt;span class="n"&gt;default_backend&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;

&lt;span class="n"&gt;backend&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;
    &lt;span class="n"&gt;balance&lt;/span&gt; &lt;span class="n"&gt;roundrobin&lt;/span&gt;
    &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="n"&gt;stapp01&lt;/span&gt; &lt;span class="n"&gt;stapp01&lt;/span&gt;:&lt;span class="m"&gt;8087&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;
    &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="n"&gt;stapp02&lt;/span&gt; &lt;span class="n"&gt;stapp02&lt;/span&gt;:&lt;span class="m"&gt;8087&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;
    &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="n"&gt;stapp03&lt;/span&gt; &lt;span class="n"&gt;stapp03&lt;/span&gt;:&lt;span class="m"&gt;8087&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;h3&gt;
  
  
  Manual Pages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;man haproxy&lt;/code&gt; - HAProxy documentation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;man haproxy.cfg&lt;/code&gt; - Configuration file reference&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;man socat&lt;/code&gt; - Socket cat for stats&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Related Topics
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;HAProxy ACLs and content switching&lt;/li&gt;
&lt;li&gt;SSL/TLS termination with HAProxy&lt;/li&gt;
&lt;li&gt;Session persistence and stick tables&lt;/li&gt;
&lt;li&gt;HAProxy statistics and monitoring&lt;/li&gt;
&lt;li&gt;High availability with keepalived&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Useful Commands
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&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;&lt;code&gt;haproxy -c -f /etc/haproxy/haproxy.cfg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Verify configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;systemctl status haproxy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check service status&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;`ss -tlnp \&lt;/td&gt;
&lt;td&gt;grep :80`&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;curl http://localhost:80&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Test the load balancer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;`echo "show stat" \&lt;/td&gt;
&lt;td&gt;socat stdio /var/lib/haproxy/stats`&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>linux</category>
      <category>haproxy</category>
    </item>
    <item>
      <title>Day 78: Jenkins Conditional Pipeline</title>
      <dc:creator>Janak Shrestha</dc:creator>
      <pubDate>Fri, 18 Sep 2026 16:55:22 +0000</pubDate>
      <link>https://dev.to/janak0ff/day-78-jenkins-conditional-pipeline-451n</link>
      <guid>https://dev.to/janak0ff/day-78-jenkins-conditional-pipeline-451n</guid>
      <description>&lt;p&gt;The development team of xFusionCorp Industries is working on to develop a new static website and they are planning to deploy the same on Nautilus App Server using Jenkins pipeline. They have shared their requirements with the DevOps team and accordingly we need to create a Jenkins pipeline job. Please find below more details about the task:  &lt;/p&gt;

&lt;p&gt;Click on the &lt;code&gt;Jenkins&lt;/code&gt; button on the top bar to access the Jenkins UI. Login using username &lt;code&gt;admin&lt;/code&gt; and password &lt;code&gt;Adm!n321&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;Similarly, click on the &lt;code&gt;Gitea&lt;/code&gt; button on the top bar to access the Gitea UI. Login using username &lt;code&gt;sarah&lt;/code&gt; and password &lt;code&gt;Sarah_pass123&lt;/code&gt;. There under user &lt;code&gt;sarah&lt;/code&gt; you will find a repository named &lt;code&gt;web_app&lt;/code&gt; that is already cloned on &lt;strong&gt;App Server 1&lt;/strong&gt; under &lt;code&gt;/var/www/html&lt;/code&gt;. sarah is a developer who is working on this repository.  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Add a slave node named &lt;code&gt;App Server 1&lt;/code&gt;. It should be labeled as &lt;code&gt;stapp01&lt;/code&gt; and its remote root directory should be &lt;code&gt;/home/sarah/jenkins_agent&lt;/code&gt; (the repository is cloned under &lt;code&gt;/var/www/html&lt;/code&gt;).  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We have already cloned repository on &lt;strong&gt;App Server 1&lt;/strong&gt; under &lt;code&gt;/var/www/html&lt;/code&gt;.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Apache is already installed on the app server and is running on port &lt;code&gt;8080&lt;/code&gt;.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a Jenkins pipeline job named &lt;code&gt;xfusion-webapp-job&lt;/code&gt; (it must not be a &lt;code&gt;Multibranch pipeline&lt;/code&gt;) and configure it to:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a string parameter named &lt;code&gt;BRANCH&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;It should conditionally deploy the code from &lt;code&gt;web_app&lt;/code&gt; repository under &lt;code&gt;/var/www/html&lt;/code&gt; on &lt;strong&gt;App Server 1&lt;/strong&gt;, as this is the document root of the app server. The pipeline should have a single stage named &lt;code&gt;Deploy&lt;/code&gt; ( which is case sensitive ) to accomplish the deployment.
&lt;/li&gt;
&lt;li&gt;The pipeline should be conditional, if the value &lt;code&gt;master&lt;/code&gt; is passed to the &lt;code&gt;BRANCH&lt;/code&gt; parameter then it must deploy the &lt;code&gt;master&lt;/code&gt; branch, on the other hand if the value &lt;code&gt;feature&lt;/code&gt; is passed to the &lt;code&gt;BRANCH&lt;/code&gt; parameter then it must deploy the &lt;code&gt;feature&lt;/code&gt; branch.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;LB server is already configured. You should be able to see the latest changes you made by clicking on the &lt;code&gt;App&lt;/code&gt; button. Please make sure the required content is loading on the main URL &lt;code&gt;https://&amp;lt;LBR-URL&amp;gt;&lt;/code&gt; i.e there should not be a sub-directory like &lt;code&gt;https://&amp;lt;LBR-URL&amp;gt;/web_app&lt;/code&gt; etc.&lt;br&gt;&lt;br&gt;
&lt;code&gt;Note:&lt;/code&gt;  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You might need to install some plugins and restart Jenkins service. So, we recommend clicking on &lt;code&gt;Restart Jenkins when installation is complete and no jobs are running&lt;/code&gt; on plugin installation/update page i.e &lt;code&gt;update centre&lt;/code&gt;. Also, Jenkins UI sometimes gets stuck when Jenkins service restarts in the back end. In this case, please make sure to refresh the UI page.
&lt;/li&gt;
&lt;li&gt;For these kind of scenarios requiring changes to be done in a web UI, please take screenshots so that you can share it with us for review in case your task is marked incomplete. You may also consider using a screen recording software such as loom.com to record and share your work.&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  Understanding the Architecture
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌───────────────────────────────────────────────────────────────┐
│           Jenkins Controller (jenkins server)                 │
│                                                               │
│  Job: xfusion-webapp-job                                      │
│  Parameters:                                                  │
│    BRANCH (string, default: master)                           │
│                                                               │
│  Pipeline:                                                    │
│    agent: label 'stapp01'                                     │
│    stage: Deploy                                              │
│      if BRANCH == master  → git checkout master               │
│      if BRANCH == feature → git checkout feature              │
└──────────────────┬────────────────────────────────────────────┘
                   │
                   │ SSH (sarah user)
                   ▼
┌───────────────────────────────────────────────────────────────┐
│           Jenkins Agent (App Server 1 / stapp01)              │
│                                                               │
│  Workspace: /home/sarah/jenkins_agent/workspace/...           │
│  Repository: /var/www/html (sarah/web_app)                    │
│  Apache: port 8080                                            │
└──────────────────┬────────────────────────────────────────────┘
                   │
                   │ HTTP
                   ▼
┌───────────────────────────────────────────────────────────────┐
│                 Load Balancer (LBR)                           │
│              Routes to App Server 1:8080                      │
│                https://&amp;lt;LBR-URL&amp;gt;                              │
└───────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Task Requirements
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Details&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Job Name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;xfusion-webapp-job&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job Type&lt;/td&gt;
&lt;td&gt;Pipeline (NOT Multibranch)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent Node&lt;/td&gt;
&lt;td&gt;App Server 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent Label&lt;/td&gt;
&lt;td&gt;&lt;code&gt;stapp01&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remote Root Directory&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/home/sarah/jenkins_agent&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;String Parameter&lt;/td&gt;
&lt;td&gt;&lt;code&gt;BRANCH&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stage Name&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Deploy&lt;/code&gt; (case sensitive)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Repository&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;sarah/web_app&lt;/code&gt; (Gitea)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deploy Path&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/var/www/html&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conditional Logic&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;BRANCH=master&lt;/code&gt; → deploy master; &lt;code&gt;BRANCH=feature&lt;/code&gt; → deploy feature&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  Part 1: Prepare the Infrastructure
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Step 1: SSH to Jenkins Server
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh jenkins@jenkins
&lt;span class="c"&gt;# Password: j@rv!s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Step 2: Generate SSH Key (if missing)
&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;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; ~/.ssh/id_rsa

&lt;span class="c"&gt;# If missing:&lt;/span&gt;
ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; rsa &lt;span class="nt"&gt;-b&lt;/span&gt; 4096 &lt;span class="nt"&gt;-N&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; ~/.ssh/id_rsa
&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.ssh/id_rsa   &lt;span class="c"&gt;# Copy this for Jenkins credentials&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Step 3: Copy SSH Key to &lt;code&gt;sarah&lt;/code&gt; on App Server 1
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-copy-id &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;StrictHostKeyChecking&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no sarah@stapp01
&lt;span class="c"&gt;# Password: Sarah_pass123&lt;/span&gt;

&lt;span class="c"&gt;# Verify passwordless access&lt;/span&gt;
ssh &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;StrictHostKeyChecking&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no sarah@stapp01 &lt;span class="nb"&gt;hostname&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


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

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Install Java 21 on App Server 1
&lt;/h3&gt;

&lt;p&gt;Jenkins agents require Java 17 or higher. Java 11 causes &lt;code&gt;UnsupportedClassVersionError&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-t&lt;/span&gt; sarah@stapp01 &lt;span class="s2"&gt;"sudo yum install -y java-21-openjdk"&lt;/span&gt;
&lt;span class="c"&gt;# Password: Sarah_pass123&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Register Java 21 as an alternative and set it as default:&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;ssh sarah@stapp01 &lt;span class="s2"&gt;"echo 'Sarah_pass123' | sudo -S alternatives --install /usr/bin/java java /usr/lib/jvm/java-21-openjdk-21.0.12.1.1-2.1.el9.x86_64/bin/java 2000"&lt;/span&gt;
ssh sarah@stapp01 &lt;span class="s2"&gt;"echo 'Sarah_pass123' | sudo -S alternatives --set java /usr/lib/jvm/java-21-openjdk-21.0.12.1.1-2.1.el9.x86_64/bin/java"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Verify:&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;ssh sarah@stapp01 &lt;span class="s2"&gt;"java -version"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;openjdk version "21.0.12.1" 2026-08-18 LTS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Create the Agent Directory
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh sarah@stapp01 &lt;span class="s2"&gt;"mkdir -p /home/sarah/jenkins_agent"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Part 2: Install Required Jenkins Plugins
&lt;/h2&gt;

&lt;p&gt;Jenkins requires several plugins for SSH-based agents and declarative pipelines.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Log in to Jenkins as &lt;code&gt;admin&lt;/code&gt; / &lt;code&gt;Adm!n321&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Manage Jenkins&lt;/strong&gt; → &lt;strong&gt;Plugins&lt;/strong&gt; → &lt;strong&gt;Available plugins&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Install the following:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Plugin&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;&lt;strong&gt;Credentials&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Core credential management&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Credentials Binding&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Bind credentials to build environment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SSH Credentials&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Adds "SSH Username with private key" option&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SSH Build Agents&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Launch agents via SSH&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pipeline&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Core Pipeline functionality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pipeline: Declarative&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Enables the &lt;code&gt;pipeline { }&lt;/code&gt; syntax&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Restart Jenkins when prompted (select &lt;strong&gt;Restart Jenkins when installation is complete and no jobs are running&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;Refresh the UI page after the restart&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Part 3: Add SSH Credentials
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Manage Jenkins&lt;/strong&gt; → &lt;strong&gt;Credentials&lt;/strong&gt; → &lt;strong&gt;System&lt;/strong&gt; → &lt;strong&gt;Global credentials (unrestricted)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Add Credentials&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;SSH Username with private key&lt;/strong&gt; → &lt;strong&gt;Next&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Configure:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ID:&lt;/strong&gt; &lt;code&gt;sarah-stapp01-credentials&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description:&lt;/strong&gt; Sarah SSH credentials for App Server 1&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Username:&lt;/strong&gt; &lt;code&gt;sarah&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Private Key:&lt;/strong&gt; Select &lt;strong&gt;Enter directly&lt;/strong&gt; → paste the private key from &lt;code&gt;~/.ssh/id_rsa&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Part 4: Add the Jenkins Agent Node
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Manage Jenkins&lt;/strong&gt; → &lt;strong&gt;Nodes&lt;/strong&gt; → &lt;strong&gt;New Node&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;App Server 1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Permanent Agent&lt;/strong&gt; → &lt;strong&gt;OK&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Configure:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Remote root directory&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;/home/sarah/jenkins_agent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Labels&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;stapp01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Usage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Use this node as much as possible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Launch method&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Launch agents via SSH&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Host&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;stapp01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Credentials&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;sarah-stapp01-credentials&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Host Key Verification Strategy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Non-verifying Verification Strategy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;Save&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Verify the node shows &lt;strong&gt;Online&lt;/strong&gt; with a green checkmark&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Agent log should show:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Agent successfully connected and online
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The &lt;code&gt;JavaPath&lt;/code&gt; advanced option is &lt;strong&gt;not required&lt;/strong&gt; here because the default &lt;code&gt;java&lt;/code&gt; in PATH already points to Java 21.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 5: Create the Conditional Pipeline Job
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Create a New Pipeline Job
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;From the Jenkins Dashboard, click &lt;strong&gt;New Item&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Enter name: &lt;code&gt;xfusion-webapp-job&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Pipeline&lt;/strong&gt; (NOT Multibranch Pipeline)&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;OK&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 2: Add the &lt;code&gt;BRANCH&lt;/code&gt; Parameter
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;In the &lt;strong&gt;General&lt;/strong&gt; section, check &lt;strong&gt;This project is parameterized&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Add Parameter&lt;/strong&gt; → &lt;strong&gt;String Parameter&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Configure:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;BRANCH&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Default Value:&lt;/strong&gt; &lt;code&gt;master&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description:&lt;/strong&gt; Enter the branch to deploy (master or feature)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 3: Configure the Pipeline Script
&lt;/h3&gt;

&lt;p&gt;Scroll down to the &lt;strong&gt;Pipeline&lt;/strong&gt; section and enter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="s1"&gt;'stapp01'&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;parameters&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;name:&lt;/span&gt; &lt;span class="s1"&gt;'BRANCH'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;defaultValue:&lt;/span&gt; &lt;span class="s1"&gt;'master'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;description:&lt;/span&gt; &lt;span class="s1"&gt;'Enter the branch to deploy (master or feature)'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;stages&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Deploy'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;script&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;BRANCH&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'master'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                        &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'''
                            cd /var/www/html
                            git config --global --add safe.directory /var/www/html
                            git fetch origin master
                            git checkout master
                            git reset --hard origin/master
                        '''&lt;/span&gt;
                    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;BRANCH&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'feature'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                        &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'''
                            cd /var/www/html
                            git config --global --add safe.directory /var/www/html
                            git fetch origin feature
                            git checkout feature
                            git reset --hard origin/feature
                        '''&lt;/span&gt;
                    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                        &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Invalid BRANCH value: ${params.BRANCH}. Allowed values: master, feature"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;}&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation of the Script
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Line&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;&lt;code&gt;agent { label 'stapp01' }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Runs the pipeline on the agent node&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;parameters { string(...) }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Declares the &lt;code&gt;BRANCH&lt;/code&gt; parameter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;stage('Deploy')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Single stage named &lt;strong&gt;Deploy&lt;/strong&gt; (case sensitive)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;script { ... }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enables Groovy conditional logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;if (params.BRANCH == 'master')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Checks if BRANCH is &lt;code&gt;master&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git fetch origin master&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fetches the latest from the master branch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git checkout master&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Switches to the master branch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git reset --hard origin/master&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ensures the working tree matches the remote exactly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;else if (params.BRANCH == 'feature')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Checks if BRANCH is &lt;code&gt;feature&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;error(...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fails the pipeline for invalid values&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Step 4: Save the Job
&lt;/h3&gt;

&lt;p&gt;Click &lt;strong&gt;Save&lt;/strong&gt; at the bottom of the page.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 6: Verify the Feature Branch Exists
&lt;/h2&gt;

&lt;p&gt;Before testing the feature deployment, ensure the &lt;code&gt;feature&lt;/code&gt; branch exists in the Gitea repository.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click the &lt;strong&gt;Gitea&lt;/strong&gt; button on the top bar&lt;/li&gt;
&lt;li&gt;Login as &lt;code&gt;sarah&lt;/code&gt; / &lt;code&gt;Sarah_pass123&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Open the &lt;code&gt;web_app&lt;/code&gt; repository&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;feature&lt;/code&gt; branch doesn't exist:

&lt;ul&gt;
&lt;li&gt;Click the branch selector → &lt;strong&gt;New Branch&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Name: &lt;code&gt;feature&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Base: &lt;code&gt;master&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create Branch&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Commit a small change to the feature branch to differentiate it from master&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Part 7: Test Both Deployments
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Test 1: Deploy &lt;code&gt;master&lt;/code&gt; Branch
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;In Jenkins, click &lt;strong&gt;Build with Parameters&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Set &lt;strong&gt;BRANCH&lt;/strong&gt; = &lt;code&gt;master&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Build&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Check the &lt;strong&gt;Console Output&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Expected Output:&lt;/strong&gt;&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="go"&gt;Started by user admin
[Pipeline] Start of Pipeline
[Pipeline] node
Running on App Server 1 in /home/sarah/jenkins_agent/workspace/xfusion-webapp-job
[Pipeline] stage
[Pipeline] { (Deploy)
[Pipeline] sh
+ cd /var/www/html
+ git config --global --add safe.directory /var/www/html
+ git fetch origin master
+ git checkout master
+ git reset --hard origin/master
&lt;/span&gt;&lt;span class="gp"&gt;HEAD is now at &amp;lt;hash&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;master-commit-message&amp;gt;
&lt;span class="go"&gt;[Pipeline] }
[Pipeline] // stage
[Pipeline] End of Pipeline
Finished: SUCCESS
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test 2: Deploy &lt;code&gt;feature&lt;/code&gt; Branch
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;Build with Parameters&lt;/strong&gt; again&lt;/li&gt;
&lt;li&gt;Set &lt;strong&gt;BRANCH&lt;/strong&gt; = &lt;code&gt;feature&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Build&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Check the &lt;strong&gt;Console Output&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Expected Output:&lt;/strong&gt;&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="go"&gt;+ git fetch origin feature
+ git checkout feature
+ git reset --hard origin/feature
HEAD is now at 4a8b390 Added feature.html file
Finished: SUCCESS
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Verify on App Server 1
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh sarah@stapp01 &lt;span class="s2"&gt;"cd /var/www/html &amp;amp;&amp;amp; git branch --show-current &amp;amp;&amp;amp; git log -1 --oneline"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected:&lt;/strong&gt; Branch matches the last deployed branch (master or feature).&lt;/p&gt;

&lt;h3&gt;
  
  
  Verify via Load Balancer
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Click the &lt;strong&gt;App&lt;/strong&gt; button on the top bar&lt;/li&gt;
&lt;li&gt;Confirm the website loads at the root URL (&lt;code&gt;https://&amp;lt;LBR-URL&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Verify the URL does not have a subdirectory like &lt;code&gt;/web_app&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Complete Configuration Summary
&lt;/h2&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;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Job Name&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;xfusion-webapp-job&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Job Type&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Parameter Name&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;BRANCH&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Parameter Type&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;String Parameter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Default Value&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;master&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agent Label&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;stapp01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Stage Name&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Deploy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deploy Path&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;/var/www/html&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Issue&lt;/th&gt;
&lt;th&gt;Cause&lt;/th&gt;
&lt;th&gt;Solution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;git fetch origin feature&lt;/code&gt; fails&lt;/td&gt;
&lt;td&gt;Branch doesn't exist on remote&lt;/td&gt;
&lt;td&gt;Create the feature branch in Gitea&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"dubious ownership" Git error&lt;/td&gt;
&lt;td&gt;Modern Git requires safe.directory&lt;/td&gt;
&lt;td&gt;Add &lt;code&gt;git config --global --add safe.directory&lt;/code&gt; in the pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Permission denied on /var/www/html&lt;/td&gt;
&lt;td&gt;Directory not owned by sarah&lt;/td&gt;
&lt;td&gt;Run &lt;code&gt;sudo chown -R sarah:sarah /var/www/html&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Invalid BRANCH value error&lt;/td&gt;
&lt;td&gt;Wrong input&lt;/td&gt;
&lt;td&gt;Use exactly &lt;code&gt;master&lt;/code&gt; or &lt;code&gt;feature&lt;/code&gt; (case sensitive)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;params.BRANCH&lt;/code&gt; is null&lt;/td&gt;
&lt;td&gt;Parameter not declared&lt;/td&gt;
&lt;td&gt;Add the &lt;code&gt;parameters { string(...) }&lt;/code&gt; block&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Credentials/Pipeline options missing&lt;/td&gt;
&lt;td&gt;Plugins not installed&lt;/td&gt;
&lt;td&gt;Install Credentials, SSH Credentials, and Pipeline plugins&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Task Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Agent node &lt;code&gt;App Server 1&lt;/code&gt; online&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Label &lt;code&gt;stapp01&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remote root &lt;code&gt;/home/sarah/jenkins_agent&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;String parameter &lt;code&gt;BRANCH&lt;/code&gt; added&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pipeline job &lt;code&gt;xfusion-webapp-job&lt;/code&gt; created&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Single stage named &lt;code&gt;Deploy&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conditional deploy (master vs feature)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Website accessible at root URL&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Key Learnings
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Parameterized Pipelines&lt;/strong&gt;: The &lt;code&gt;parameters&lt;/code&gt; block lets jobs accept user inputs at build time, making them more flexible and reusable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Conditional Logic in Declarative Pipelines&lt;/strong&gt;: The &lt;code&gt;script { }&lt;/code&gt; block enables Groovy-based conditionals such as &lt;code&gt;if/else&lt;/code&gt; inside declarative syntax.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Exact Branch Matching&lt;/strong&gt;: Using &lt;code&gt;git fetch&lt;/code&gt; + &lt;code&gt;git checkout&lt;/code&gt; + &lt;code&gt;git reset --hard&lt;/code&gt; guarantees the local branch matches the remote exactly, avoiding stale files or merge conflicts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Failing Fast&lt;/strong&gt;: Using &lt;code&gt;error(...)&lt;/code&gt; for invalid inputs prevents unexpected behavior and makes debugging easy.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reusability&lt;/strong&gt;: A single pipeline job handles multiple branches, reducing the number of jobs needed for different deployment scenarios.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Load Balancer Routing&lt;/strong&gt;: Regardless of which branch is deployed, content must be served from &lt;code&gt;/var/www/html&lt;/code&gt; root for the LB to route correctly.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this challenge, we extended our Jenkins deployment pipeline with a &lt;strong&gt;conditional mechanism&lt;/strong&gt; driven by a &lt;code&gt;BRANCH&lt;/code&gt; string parameter. The same job now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deploys the &lt;code&gt;master&lt;/code&gt; branch when &lt;code&gt;BRANCH=master&lt;/code&gt; is passed&lt;/li&gt;
&lt;li&gt;Deploys the &lt;code&gt;feature&lt;/code&gt; branch when &lt;code&gt;BRANCH=feature&lt;/code&gt; is passed&lt;/li&gt;
&lt;li&gt;Fails with a clear error message for any other value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This pattern is common in real-world CI/CD pipelines where a single job handles multiple environments or branches, using parameters and conditionals to route deployment logic.&lt;/p&gt;

&lt;p&gt;The pipeline now provides a flexible, reusable deployment mechanism through the Load Balancer at the root URL, and developers can trigger deployments for either branch directly from the Jenkins UI.&lt;/p&gt;




&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;To further enhance this pipeline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Add more branches&lt;/strong&gt;: Use a choice parameter to present available branches&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment-specific deployments&lt;/strong&gt;: Deploy to different servers based on the branch&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Post-Build Notifications&lt;/strong&gt;: Send Slack or email notifications on success/failure&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Webhook Integration&lt;/strong&gt;: Automatically trigger builds on Gitea push events&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rollback&lt;/strong&gt;: Add a stage to roll back to the previous commit on failure&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>jenkins</category>
      <category>cicd</category>
    </item>
    <item>
      <title>Linux Postfix Troubleshooting</title>
      <dc:creator>Janak Shrestha</dc:creator>
      <pubDate>Thu, 17 Sep 2026 17:00:02 +0000</pubDate>
      <link>https://dev.to/janak0ff/linux-postfix-troubleshooting-2eep</link>
      <guid>https://dev.to/janak0ff/linux-postfix-troubleshooting-2eep</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Postfix is one of the most widely used Mail Transfer Agents (MTAs) in Linux environments. It is known for its security, performance, and reliability. However, like any complex service, Postfix can encounter configuration issues that prevent it from functioning correctly.&lt;/p&gt;

&lt;p&gt;This guide covers a real-world troubleshooting scenario where the Postfix service on a mail server was failing due to duplicate configuration entries in the main configuration file. The issue caused mail delivery problems and prevented remote servers from connecting to the mail server.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding the Problem
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Scenario
&lt;/h3&gt;

&lt;p&gt;Users of a monitoring application reported issues with the company mail server. The Postfix service appeared to fail intermittently. The mail server was located in the Stork DC and used Postfix as its Mail Transfer Agent.&lt;/p&gt;

&lt;h3&gt;
  
  
  Symptoms Observed
&lt;/h3&gt;

&lt;p&gt;When checking the Postfix service status, the following symptoms were present. The service showed as inactive when first checked. After starting, the service ran but generated numerous warnings in the logs. The configuration check command produced warnings about overriding entries. Remote connections to the mail server on port 25 failed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Initial Diagnosis
&lt;/h3&gt;

&lt;p&gt;The first step was to check the service status and configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl status postfix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output showed the service was inactive. After enabling and starting the service, warnings appeared in the logs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;postfix check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command revealed the root cause: warnings about overriding earlier entries in the configuration file.&lt;/p&gt;




&lt;h2&gt;
  
  
  Root Cause Analysis
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Identifying the Duplicate Entries
&lt;/h3&gt;

&lt;p&gt;The warnings pointed to a specific line in the Postfix configuration file. To identify all occurrences of the problematic parameter, the following command was used.&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="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"inet_interfaces"&lt;/span&gt; /etc/postfix/main.cf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output revealed the issue clearly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;122:# The inet_interfaces parameter specifies the network interface
132:inet_interfaces = all
133:#inet_interfaces = $myhostname
134:#inet_interfaces = $myhostname, localhost
135:inet_interfaces = localhost
143:# the address list specified with the inet_interfaces parameter.
173:# receives mail on (see the inet_interfaces parameter).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Understanding the Problem
&lt;/h3&gt;

&lt;p&gt;There were two active &lt;code&gt;inet_interfaces&lt;/code&gt; entries:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Line&lt;/th&gt;
&lt;th&gt;Entry&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;132&lt;/td&gt;
&lt;td&gt;&lt;code&gt;inet_interfaces = all&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Active&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;135&lt;/td&gt;
&lt;td&gt;&lt;code&gt;inet_interfaces = localhost&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Active&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Because line 135 appeared later in the file, it overrode line 132. This meant Postfix was configured to listen only on the loopback interface (127.0.0.1) instead of all network interfaces.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why This Causes Problems
&lt;/h3&gt;

&lt;p&gt;When Postfix listens only on localhost, it can only receive connections from the local machine. Remote servers, monitoring applications, and other clients cannot connect to the mail server. This explains why the monitoring app users experienced issues.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Backup the Configuration
&lt;/h3&gt;

&lt;p&gt;Before making any changes, always create a backup of 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="nb"&gt;cp&lt;/span&gt; /etc/postfix/main.cf /etc/postfix/main.cf.bak
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Fix the Duplicate Entry
&lt;/h3&gt;

&lt;p&gt;Comment out or remove the duplicate &lt;code&gt;inet_interfaces&lt;/code&gt; line. In this case, line 135 was commented out.&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="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'135s/^inet_interfaces/#inet_interfaces/'&lt;/span&gt; /etc/postfix/main.cf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, the line could be removed entirely.&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="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'/^inet_interfaces = localhost/d'&lt;/span&gt; /etc/postfix/main.cf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Verify the Fix
&lt;/h3&gt;

&lt;p&gt;Confirm that only one active &lt;code&gt;inet_interfaces&lt;/code&gt; entry remains.&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="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"^inet_interfaces"&lt;/span&gt; /etc/postfix/main.cf
&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;132:inet_interfaces = all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Check Configuration
&lt;/h3&gt;

&lt;p&gt;Run the Postfix configuration check to ensure there are no errors.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;postfix check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A successful check produces no output.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Restart Postfix
&lt;/h3&gt;

&lt;p&gt;Restart the service to apply the changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl restart postfix
systemctl status postfix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 6: Verify Listening Ports
&lt;/h3&gt;

&lt;p&gt;Confirm that Postfix is now listening on all interfaces.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ss &lt;span class="nt"&gt;-tlnp&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; :25
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output shows &lt;code&gt;0.0.0.0:25&lt;/code&gt; and &lt;code&gt;[::]:25&lt;/code&gt;, indicating all interfaces.&lt;/p&gt;




&lt;h2&gt;
  
  
  Verification and Results
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Service Status After Fix
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight systemd"&gt;&lt;code&gt;&lt;span class="err"&gt;●&lt;/span&gt; &lt;span class="err"&gt;postfix.service&lt;/span&gt; &lt;span class="err"&gt;-&lt;/span&gt; &lt;span class="err"&gt;Postfix&lt;/span&gt; &lt;span class="err"&gt;Mail&lt;/span&gt; &lt;span class="err"&gt;Transport&lt;/span&gt; &lt;span class="err"&gt;Agent&lt;/span&gt;
     &lt;span class="err"&gt;Loaded:&lt;/span&gt; &lt;span class="err"&gt;loaded&lt;/span&gt; &lt;span class="err"&gt;(/usr/lib/systemd/system/postfix.service&lt;/span&gt;&lt;span class="c"&gt;; enabled; preset: disabled)&lt;/span&gt;
     &lt;span class="err"&gt;Active:&lt;/span&gt; &lt;span class="err"&gt;active&lt;/span&gt; &lt;span class="err"&gt;(running)&lt;/span&gt; &lt;span class="err"&gt;since&lt;/span&gt; &lt;span class="err"&gt;Thu&lt;/span&gt; &lt;span class="err"&gt;2026-09-17&lt;/span&gt; &lt;span class="err"&gt;16:53:47&lt;/span&gt; &lt;span class="err"&gt;UTC&lt;/span&gt;&lt;span class="c"&gt;; 24ms ago&lt;/span&gt;
   &lt;span class="err"&gt;Main&lt;/span&gt; &lt;span class="err"&gt;PID:&lt;/span&gt; &lt;span class="err"&gt;9390&lt;/span&gt; &lt;span class="err"&gt;(master)&lt;/span&gt;
      &lt;span class="err"&gt;Tasks:&lt;/span&gt; &lt;span class="err"&gt;3&lt;/span&gt; &lt;span class="err"&gt;(limit:&lt;/span&gt; &lt;span class="err"&gt;404516)&lt;/span&gt;
     &lt;span class="err"&gt;Memory:&lt;/span&gt; &lt;span class="err"&gt;3.5M&lt;/span&gt;
     &lt;span class="err"&gt;CGroup:&lt;/span&gt; &lt;span class="err"&gt;/system.slice/postfix.service&lt;/span&gt;
             &lt;span class="err"&gt;├─9390&lt;/span&gt; &lt;span class="err"&gt;/usr/libexec/postfix/master&lt;/span&gt; &lt;span class="err"&gt;-w&lt;/span&gt;
             &lt;span class="err"&gt;├─9391&lt;/span&gt; &lt;span class="err"&gt;pickup&lt;/span&gt; &lt;span class="err"&gt;-l&lt;/span&gt; &lt;span class="err"&gt;-t&lt;/span&gt; &lt;span class="err"&gt;unix&lt;/span&gt; &lt;span class="err"&gt;-u&lt;/span&gt;
             &lt;span class="err"&gt;└─9392&lt;/span&gt; &lt;span class="err"&gt;qmgr&lt;/span&gt; &lt;span class="err"&gt;-l&lt;/span&gt; &lt;span class="err"&gt;-t&lt;/span&gt; &lt;span class="err"&gt;unix&lt;/span&gt; &lt;span class="err"&gt;-u&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Listening Ports After Fix
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LISTEN 0 100 0.0.0.0:25 0.0.0.0:* users:(("master",pid=9390,fd=13))
LISTEN 0 100   [::]:25   [::]:* users:(("master",pid=9390,fd=14))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configuration Check After Fix
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;postfix check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No output, indicating a clean configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comparison Table
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Active &lt;code&gt;inet_interfaces&lt;/code&gt; entries&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;postfix check&lt;/code&gt; warnings&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Listening address&lt;/td&gt;
&lt;td&gt;127.0.0.1:25&lt;/td&gt;
&lt;td&gt;0.0.0.0:25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remote connectivity&lt;/td&gt;
&lt;td&gt;Failed&lt;/td&gt;
&lt;td&gt;Working&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service status&lt;/td&gt;
&lt;td&gt;Warnings&lt;/td&gt;
&lt;td&gt;Clean&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Understanding Postfix Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The main.cf File
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;/etc/postfix/main.cf&lt;/code&gt; file is the primary configuration file for Postfix. It contains parameters that control how Postfix operates. Each parameter is specified as &lt;code&gt;parameter = value&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Duplicate Parameters
&lt;/h3&gt;

&lt;p&gt;When a parameter appears multiple times, Postfix uses the last occurrence. However, it generates a warning about the earlier entry being overridden. This behavior is important to understand because it can lead to unexpected configurations.&lt;/p&gt;

&lt;h3&gt;
  
  
  The inet_interfaces Parameter
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;inet_interfaces&lt;/code&gt; parameter specifies which network interfaces Postfix listens on. Common values include:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;all&lt;/td&gt;
&lt;td&gt;Listen on all interfaces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;localhost&lt;/td&gt;
&lt;td&gt;Listen only on loopback (127.0.0.1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;127.0.0.1&lt;/td&gt;
&lt;td&gt;Listen only on loopback&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;$myhostname&lt;/td&gt;
&lt;td&gt;Listen on the host's IP address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;eth0&lt;/td&gt;
&lt;td&gt;Listen on a specific interface&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For a mail server that needs to receive mail from other servers, &lt;code&gt;inet_interfaces = all&lt;/code&gt; is the correct setting.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Postfix Configuration Issues
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Issue 1: Duplicate Parameters
&lt;/h3&gt;

&lt;p&gt;Duplicate parameters cause warnings and unpredictable behavior. Use &lt;code&gt;grep&lt;/code&gt; to find duplicates.&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="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"^parameter_name"&lt;/span&gt; /etc/postfix/main.cf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remove duplicates by editing the file or using &lt;code&gt;sed&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Issue 2: Incorrect Hostname
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;myhostname&lt;/code&gt; parameter must be a fully qualified domain name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;myhostname = stmail01.stratos.xfusioncorp.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify with &lt;code&gt;hostname -f&lt;/code&gt; and check &lt;code&gt;/etc/hosts&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Issue 3: Missing Mail Directory
&lt;/h3&gt;

&lt;p&gt;Postfix requires the mail directory to exist and have correct permissions.&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="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /home/user/Maildir/&lt;span class="o"&gt;{&lt;/span&gt;cur,new,tmp&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; user:user /home/user/Maildir
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Issue 4: Port Conflicts
&lt;/h3&gt;

&lt;p&gt;If port 25 is already in use, Postfix fails to start.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ss &lt;span class="nt"&gt;-tlnp&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; :25
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Identify and stop the conflicting service.&lt;/p&gt;

&lt;h3&gt;
  
  
  Issue 5: Permission Problems
&lt;/h3&gt;

&lt;p&gt;Postfix requires specific permissions on its directories.&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="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /var/spool/postfix/
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /etc/postfix/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fix permissions if needed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Best Practices for Postfix Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Always Backup Before Changes
&lt;/h3&gt;

&lt;p&gt;Create a backup of the configuration file before making 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="nb"&gt;cp&lt;/span&gt; /etc/postfix/main.cf /etc/postfix/main.cf.bak
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use postfix check
&lt;/h3&gt;

&lt;p&gt;Always run the configuration check after making changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;postfix check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Review Configuration Regularly
&lt;/h3&gt;

&lt;p&gt;Periodically review the configuration for duplicate or outdated entries.&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="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"^parameter_name"&lt;/span&gt; /etc/postfix/main.cf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test After Changes
&lt;/h3&gt;

&lt;p&gt;Restart the service and verify it is running correctly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl restart postfix
systemctl status postfix
ss &lt;span class="nt"&gt;-tlnp&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; :25
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Monitor Logs
&lt;/h3&gt;

&lt;p&gt;Regularly check Postfix logs for errors and warnings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; postfix &lt;span class="nt"&gt;-n&lt;/span&gt; 50
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/maillog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Document Changes
&lt;/h3&gt;

&lt;p&gt;Maintain a record of configuration changes for troubleshooting and audit purposes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Troubleshooting Workflow
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Check Service Status
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl status postfix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Check Configuration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;postfix check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Check Logs
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; postfix &lt;span class="nt"&gt;-n&lt;/span&gt; 50
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Identify Duplicate Parameters
&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;grep&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"^parameter_name"&lt;/span&gt; /etc/postfix/main.cf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Fix the Configuration
&lt;/h3&gt;

&lt;p&gt;Edit the file to remove duplicates or correct errors.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Verify the Fix
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;postfix check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 7: Restart the Service
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl restart postfix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 8: Verify Listening Ports
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ss &lt;span class="nt"&gt;-tlnp&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; :25
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Summary of the Fix
&lt;/h3&gt;

&lt;p&gt;The Postfix service on the mail server was failing due to duplicate &lt;code&gt;inet_interfaces&lt;/code&gt; entries in the configuration file. The last entry, &lt;code&gt;inet_interfaces = localhost&lt;/code&gt;, was overriding the intended setting of &lt;code&gt;inet_interfaces = all&lt;/code&gt;. This caused Postfix to listen only on the loopback interface, preventing remote connections.&lt;/p&gt;

&lt;p&gt;The fix involved commenting out the duplicate entry, verifying the configuration, and restarting the service. After the fix, Postfix was listening on all interfaces, and the monitoring application could connect properly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Takeaways
&lt;/h3&gt;

&lt;p&gt;Duplicate configuration parameters in Postfix can cause unexpected behavior. The &lt;code&gt;grep&lt;/code&gt; command is useful for identifying duplicates. The &lt;code&gt;postfix check&lt;/code&gt; command validates the configuration. Always backup configuration files before making changes. Verify listening ports after restarting the service. Understanding the &lt;code&gt;inet_interfaces&lt;/code&gt; parameter is essential for mail server configuration.&lt;/p&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;inet_interfaces = all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Final Service Status
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Active: active (running)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Final Listening Ports
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LISTEN 0 100 0.0.0.0:25 0.0.0.0:*
LISTEN 0 100   [::]:25   [::]:*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;h3&gt;
  
  
  Manual Pages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;man postfix&lt;/code&gt; - Postfix documentation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;man main.cf&lt;/code&gt; - Postfix configuration parameters&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;man postconf&lt;/code&gt; - Postfix configuration tool&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;man postfix-check&lt;/code&gt; - Configuration check&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Related Topics
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Postfix TLS/SSL configuration&lt;/li&gt;
&lt;li&gt;Virtual domain configuration&lt;/li&gt;
&lt;li&gt;Spam filtering with SpamAssassin&lt;/li&gt;
&lt;li&gt;Mail queue management&lt;/li&gt;
&lt;li&gt;Postfix logging and monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Useful Commands
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&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;&lt;code&gt;postfix check&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Validate configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;postconf -n&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show non-default parameters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;postconf -d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show default parameters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mailq&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show mail queue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;postsuper -d ALL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete all queued mail&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>linux</category>
      <category>postfix</category>
    </item>
    <item>
      <title>Day 77: Jenkins Deploy Pipeline</title>
      <dc:creator>Janak Shrestha</dc:creator>
      <pubDate>Thu, 17 Sep 2026 16:42:56 +0000</pubDate>
      <link>https://dev.to/janak0ff/day-77-jenkins-deploy-pipeline-51k2</link>
      <guid>https://dev.to/janak0ff/day-77-jenkins-deploy-pipeline-51k2</guid>
      <description>&lt;p&gt;The development team of xFusionCorp Industries is working on to develop a new static website and they are planning to deploy the same on Nautilus App Server using Jenkins pipeline. They have shared their requirements with the DevOps team and accordingly we need to create a Jenkins pipeline job. Please find below more details about the task:  &lt;/p&gt;

&lt;p&gt;Click on the &lt;code&gt;Jenkins&lt;/code&gt; button on the top bar to access the Jenkins UI. Login using username &lt;code&gt;admin&lt;/code&gt; and password &lt;code&gt;Adm!n321&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;Similarly, click on the &lt;code&gt;Gitea&lt;/code&gt; button on the top bar to access the Gitea UI. Login using username &lt;code&gt;sarah&lt;/code&gt; and password &lt;code&gt;Sarah_pass123&lt;/code&gt;. There under user &lt;code&gt;sarah&lt;/code&gt; you will find a repository named &lt;code&gt;web_app&lt;/code&gt; that is already cloned on &lt;strong&gt;App Server 1&lt;/strong&gt; under &lt;code&gt;/var/www/html&lt;/code&gt;. sarah is a developer who is working on this repository.  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Add a slave node named &lt;code&gt;App Server 1&lt;/code&gt;. It should be labeled as &lt;code&gt;stapp01&lt;/code&gt; and its remote root directory should be &lt;code&gt;/home/sarah/jenkins_agent&lt;/code&gt; (the repository is cloned under &lt;code&gt;/var/www/html&lt;/code&gt;; the agent uses a separate directory so it does not pollute the repo).  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We have already cloned repository on &lt;strong&gt;App Server 1&lt;/strong&gt; under &lt;code&gt;/var/www/html&lt;/code&gt;.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Apache is already installed on the app server and is running on port &lt;code&gt;8080&lt;/code&gt;.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a Jenkins pipeline job named &lt;code&gt;xfusion-webapp-job&lt;/code&gt; (it must not be a &lt;code&gt;Multibranch pipeline&lt;/code&gt;) and configure it to:  &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Deploy the code from `web_app` repository under `/var/www/html` on **App Server 1**, as this is the document root of the app server. The pipeline should have a single stage named `Deploy` ( which is case sensitive ) to accomplish the deployment.  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;LB server is already configured. You should be able to see the latest changes you made by clicking on the &lt;code&gt;App&lt;/code&gt; button. Please make sure the required content is loading on the main URL &lt;code&gt;https://&amp;lt;LBR-URL&amp;gt;&lt;/code&gt; i.e there should not be a sub-directory like &lt;code&gt;https://&amp;lt;LBR-URL&amp;gt;/web_app&lt;/code&gt; etc.  &lt;/p&gt;

&lt;p&gt;&lt;code&gt;Note:&lt;/code&gt;  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You might need to install some plugins and restart Jenkins service. So, we recommend clicking on &lt;code&gt;Restart Jenkins when installation is complete and no jobs are running&lt;/code&gt; on plugin installation/update page i.e &lt;code&gt;update centre&lt;/code&gt;. Also, Jenkins UI sometimes gets stuck when Jenkins service restarts in the back end. In this case, please make sure to refresh the UI page.
&lt;/li&gt;
&lt;li&gt;For these kind of scenarios requiring changes to be done in a web UI, please take screenshots so that you can share it with us for review in case your task is marked incomplete. You may also consider using a screen recording software such as loom.com to record and share your work.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Understanding the Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌───────────────────────────────────────────────────────────────┐
│           Jenkins Controller (jenkins server)                 │
│                                                               │
│  Job: xfusion-webapp-job                                      │
│  ├── agent: label 'stapp01'                                   │
│  └── stages:                                                  │
│      └── Deploy: git pull in /var/www/html                    │
└──────────────────┬────────────────────────────────────────────┘
                   │
                   │ SSH (sarah user)
                   ▼
┌───────────────────────────────────────────────────────────────┐
│           Jenkins Agent (App Server 1 / stapp01)              │
│                                                               │
│  Workspace: /home/sarah/jenkins_agent/workspace/...           │
│  Repository: /var/www/html (sarah/web_app)                    │
│  Apache: port 8080                                            │
└──────────────────┬────────────────────────────────────────────┘
                   │
                   │ HTTP
                   ▼
┌───────────────────────────────────────────────────────────────┐
│                 Load Balancer (LBR)                           │
│              Routes to App Server 1:8080                      │
│                https://&amp;lt;LBR-URL&amp;gt;                              │
└───────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Task Requirements
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Details&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Job Name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;xfusion-webapp-job&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job Type&lt;/td&gt;
&lt;td&gt;Pipeline (not Multibranch)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent Node&lt;/td&gt;
&lt;td&gt;App Server 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent Label&lt;/td&gt;
&lt;td&gt;&lt;code&gt;stapp01&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remote Root Directory&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/home/sarah/jenkins_agent&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stage Name&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Deploy&lt;/code&gt; (case sensitive)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Repository&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;sarah/web_app&lt;/code&gt; (Gitea)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deploy Path&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/var/www/html&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Part 1: Configure the Jenkins Agent Node
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Access Jenkins Server via SSH
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh jenkins@jenkins
&lt;span class="c"&gt;# Password: j@rv!s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Generate SSH Key on Jenkins Server
&lt;/h3&gt;

&lt;p&gt;The Jenkins agent runs as the &lt;code&gt;jenkins&lt;/code&gt; user, so SSH keys must be generated for that user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; rsa &lt;span class="nt"&gt;-b&lt;/span&gt; 4096 &lt;span class="nt"&gt;-N&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; ~/.ssh/id_rsa
&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.ssh/id_rsa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the private key output — you'll need it for Jenkins credentials.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Copy SSH Key to &lt;code&gt;sarah&lt;/code&gt; on App Server 1
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-copy-id &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;StrictHostKeyChecking&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no sarah@stapp01
&lt;span class="c"&gt;# Password: Sarah_pass123&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;StrictHostKeyChecking&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no sarah@stapp01 &lt;span class="nb"&gt;hostname&lt;/span&gt;
&lt;span class="c"&gt;# Output: stapp01&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Install Java 21 on App Server 1
&lt;/h3&gt;

&lt;p&gt;Jenkins agents require Java 17 or higher. Java 11 will cause an &lt;code&gt;UnsupportedClassVersionError&lt;/code&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="c"&gt;# Install with TTY allocation for sudo&lt;/span&gt;
ssh &lt;span class="nt"&gt;-t&lt;/span&gt; sarah@stapp01 &lt;span class="s2"&gt;"sudo yum install -y java-21-openjdk"&lt;/span&gt;
&lt;span class="c"&gt;# Password: Sarah_pass123&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Register Java 21 as an alternative and set it as default:&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;ssh sarah@stapp01 &lt;span class="s2"&gt;"echo 'Sarah_pass123' | sudo -S alternatives --install /usr/bin/java java /usr/lib/jvm/java-21-openjdk-21.0.12.1.1-2.1.el9.x86_64/bin/java 2000"&lt;/span&gt;
ssh sarah@stapp01 &lt;span class="s2"&gt;"echo 'Sarah_pass123' | sudo -S alternatives --set java /usr/lib/jvm/java-21-openjdk-21.0.12.1.1-2.1.el9.x86_64/bin/java"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Verify:&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;ssh sarah@stapp01 &lt;span class="s2"&gt;"java -version"&lt;/span&gt;
&lt;span class="c"&gt;# Expected: openjdk version "21.0.12.1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Create the Agent Directory
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh sarah@stapp01 &lt;span class="s2"&gt;"mkdir -p /home/sarah/jenkins_agent"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Part 2: Install Required Jenkins Plugins
&lt;/h2&gt;

&lt;p&gt;Jenkins does not ship with the plugins needed for SSH agents and pipelines by default. Navigate to &lt;strong&gt;Manage Jenkins&lt;/strong&gt; → &lt;strong&gt;Plugins&lt;/strong&gt; → &lt;strong&gt;Available plugins&lt;/strong&gt; and install:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Plugin&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;&lt;strong&gt;Credentials&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Core credential management&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Credentials Binding&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Bind credentials to build environment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SSH Credentials&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Adds "SSH Username with private key" option&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SSH Build Agents&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Launch agents via SSH&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pipeline&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Core Pipeline functionality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pipeline: Declarative&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Enables the &lt;code&gt;pipeline { }&lt;/code&gt; syntax&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;After installing, select &lt;strong&gt;Restart Jenkins when installation is complete and no jobs are running&lt;/strong&gt;. Refresh the UI page after Jenkins restarts.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 3: Add SSH Credentials
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Manage Jenkins&lt;/strong&gt; → &lt;strong&gt;Credentials&lt;/strong&gt; → &lt;strong&gt;System&lt;/strong&gt; → &lt;strong&gt;Global credentials (unrestricted)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Add Credentials&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;SSH Username with private key&lt;/strong&gt; and click &lt;strong&gt;Next&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Configure:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ID:&lt;/strong&gt; &lt;code&gt;sarah-stapp01-credentials&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description:&lt;/strong&gt; Sarah SSH credentials for App Server 1&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Username:&lt;/strong&gt; &lt;code&gt;sarah&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Private Key:&lt;/strong&gt; Select &lt;strong&gt;Enter directly&lt;/strong&gt; and paste the private key from Step 2&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Part 4: Add the Jenkins Agent Node
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Manage Jenkins&lt;/strong&gt; → &lt;strong&gt;Nodes&lt;/strong&gt; → &lt;strong&gt;New Node&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;App Server 1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Permanent Agent&lt;/strong&gt; → &lt;strong&gt;OK&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Configure:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Remote root directory&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;/home/sarah/jenkins_agent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Labels&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;stapp01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Usage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Use this node as much as possible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Launch method&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Launch agents via SSH&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Host&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;stapp01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Credentials&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;sarah-stapp01-credentials&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Host Key Verification Strategy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Non-verifying Verification Strategy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;Save&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Verify the Node is Online
&lt;/h3&gt;

&lt;p&gt;Go to &lt;strong&gt;Manage Jenkins&lt;/strong&gt; → &lt;strong&gt;Nodes&lt;/strong&gt;. The node should show &lt;strong&gt;Online&lt;/strong&gt; with a green checkmark. The agent log will show:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Agent successfully connected and online
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note on JavaPath:&lt;/strong&gt; In this setup, the &lt;code&gt;JavaPath&lt;/code&gt; advanced option is &lt;strong&gt;not required&lt;/strong&gt; because the default &lt;code&gt;java&lt;/code&gt; command in the agent's PATH already points to Java 21.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 5: Create the Pipeline Job
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Create a New Pipeline Job
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;From the Jenkins Dashboard, click &lt;strong&gt;New Item&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Enter name: &lt;code&gt;xfusion-webapp-job&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Pipeline&lt;/strong&gt; (NOT Multibranch Pipeline)&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;OK&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 2: Configure the Pipeline Script
&lt;/h3&gt;

&lt;p&gt;Scroll to the &lt;strong&gt;Pipeline&lt;/strong&gt; section and enter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="s1"&gt;'stapp01'&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;stages&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Deploy'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'''
                    cd /var/www/html
                    git config --global --add safe.directory /var/www/html
                    git pull origin master
                '''&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation of the Script
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Line&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;&lt;code&gt;agent { label 'stapp01' }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Runs the pipeline on the node with label &lt;code&gt;stapp01&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;stage('Deploy')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Defines a single stage named &lt;strong&gt;Deploy&lt;/strong&gt; (case sensitive)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sh '''...'''&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Executes shell commands on the agent (as &lt;code&gt;sarah&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd /var/www/html&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Navigates to the Apache document root&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git config --global --add safe.directory /var/www/html&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prevents "dubious ownership" errors (Git 2.35.2+)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git pull origin master&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pulls the latest code from Gitea&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Step 3: Save and Build
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;Save&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Build Now&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click on the build number → &lt;strong&gt;Console Output&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Expected Console Output
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Started by user admin
[Pipeline] Start of Pipeline
[Pipeline] node
Running on App Server 1 in /home/sarah/jenkins_agent/workspace/xfusion-webapp-job
[Pipeline] {
[Pipeline] stage
[Pipeline] { (Deploy)
[Pipeline] sh
+ cd /var/www/html
+ git config --global --add safe.directory /var/www/html
+ git pull origin master
From http://gitea:3000/sarah/web_app
&lt;/span&gt;&lt;span class="gp"&gt; * branch            master     -&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;FETCH_HEAD
&lt;span class="go"&gt;Already up to date.
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Verify on App Server 1
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh sarah@stapp01 &lt;span class="s2"&gt;"cd /var/www/html &amp;amp;&amp;amp; git log -1 --oneline"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify via Load Balancer
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Click the &lt;strong&gt;App&lt;/strong&gt; button on the top bar&lt;/li&gt;
&lt;li&gt;Confirm the website loads at the root URL (&lt;code&gt;https://&amp;lt;LBR-URL&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Ensure no subdirectory appears in the URL (e.g., NOT &lt;code&gt;https://&amp;lt;LBR-URL&amp;gt;/web_app&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Test End-to-End
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Log in to &lt;strong&gt;Gitea&lt;/strong&gt; as &lt;code&gt;sarah&lt;/code&gt; (password: &lt;code&gt;Sarah_pass123&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Make a change to a file in the &lt;code&gt;web_app&lt;/code&gt; repository&lt;/li&gt;
&lt;li&gt;Commit and push&lt;/li&gt;
&lt;li&gt;In Jenkins, click &lt;strong&gt;Build Now&lt;/strong&gt; for &lt;code&gt;xfusion-webapp-job&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Refresh the App page — the new change should be visible&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Task Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Agent node &lt;code&gt;App Server 1&lt;/code&gt; added&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Label &lt;code&gt;stapp01&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remote root &lt;code&gt;/home/sarah/jenkins_agent&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent online&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pipeline job &lt;code&gt;xfusion-webapp-job&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Single stage named &lt;code&gt;Deploy&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deploys &lt;code&gt;web_app&lt;/code&gt; to &lt;code&gt;/var/www/html&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Website accessible at root URL&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Troubleshooting Reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Issue&lt;/th&gt;
&lt;th&gt;Cause&lt;/th&gt;
&lt;th&gt;Solution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Credentials option missing&lt;/td&gt;
&lt;td&gt;Credentials plugin not installed&lt;/td&gt;
&lt;td&gt;Install &lt;strong&gt;Credentials&lt;/strong&gt; plugin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSH key option missing&lt;/td&gt;
&lt;td&gt;SSH Credentials plugin not installed&lt;/td&gt;
&lt;td&gt;Install &lt;strong&gt;SSH Credentials&lt;/strong&gt; plugin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pipeline option missing&lt;/td&gt;
&lt;td&gt;Pipeline plugin not installed&lt;/td&gt;
&lt;td&gt;Install &lt;strong&gt;Pipeline&lt;/strong&gt; plugin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UnsupportedClassVersionError&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Java 11 instead of 21&lt;/td&gt;
&lt;td&gt;Install Java 21 and set as default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo: a terminal is required&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No TTY for SSH&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;ssh -t&lt;/code&gt; or `echo 'pass' \&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"dubious ownership" Git error&lt;/td&gt;
&lt;td&gt;Modern Git safe.directory&lt;/td&gt;
&lt;td&gt;Add {% raw %}&lt;code&gt;git config --global --add safe.directory&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent offline&lt;/td&gt;
&lt;td&gt;Java/SSH misconfiguration&lt;/td&gt;
&lt;td&gt;Check agent log in Jenkins UI&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Key Learnings
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Jenkins Agent Architecture&lt;/strong&gt;: The controller schedules jobs, and agents execute them. Labels are used to target specific nodes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pipeline as Code&lt;/strong&gt;: Declarative pipelines use &lt;code&gt;pipeline { agent { ... } stages { ... } }&lt;/code&gt; syntax, providing a clean, version-controllable way to define deployments.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Java Version Compatibility&lt;/strong&gt;: Jenkins agents require Java 17+. The &lt;code&gt;JavaPath&lt;/code&gt; advanced option is only necessary when the default &lt;code&gt;java&lt;/code&gt; doesn't point to the correct version.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Plugin Dependencies&lt;/strong&gt;: Missing plugins hide UI options. The Credentials, SSH Credentials, and Pipeline plugins are essential for this type of workflow.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Git Safe Directory&lt;/strong&gt;: Modern Git requires explicit &lt;code&gt;safe.directory&lt;/code&gt; configuration for non-root users.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Load Balancer Routing&lt;/strong&gt;: The website must be served from &lt;code&gt;/var/www/html&lt;/code&gt; root so the LB routes correctly without a subdirectory.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this challenge, we successfully:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generated SSH keys on the Jenkins controller&lt;/li&gt;
&lt;li&gt;Copied the public key to &lt;code&gt;sarah&lt;/code&gt; on App Server 1&lt;/li&gt;
&lt;li&gt;Installed Java 21 and set it as default&lt;/li&gt;
&lt;li&gt;Created the agent directory &lt;code&gt;/home/sarah/jenkins_agent&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Installed the required Jenkins plugins&lt;/li&gt;
&lt;li&gt;Added SSH credentials for &lt;code&gt;sarah&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Added the &lt;code&gt;App Server 1&lt;/code&gt; slave node with label &lt;code&gt;stapp01&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Created the pipeline job &lt;code&gt;xfusion-webapp-job&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Configured a single &lt;code&gt;Deploy&lt;/code&gt; stage that pulls the latest code&lt;/li&gt;
&lt;li&gt;Verified the deployment through the Load Balancer&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The pipeline is now ready to automatically deploy the latest code from the Gitea repository to the Apache document root on App Server 1. The website is accessible through the Load Balancer at the root URL, and developers can trigger deployments by pushing changes to the repository and clicking &lt;strong&gt;Build Now&lt;/strong&gt; (or by adding a webhook for full automation).&lt;/p&gt;




&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;To further enhance this pipeline, consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Adding a Webhook&lt;/strong&gt;: Configure a Gitea webhook so that pushes automatically trigger the Jenkins build&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment Variables&lt;/strong&gt;: Use parameterized builds for more flexibility&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Post-Build Actions&lt;/strong&gt;: Add notifications (Slack, email) on success/failure&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-Stage Pipelines&lt;/strong&gt;: Extend to build, test, and deploy stages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rollback Strategy&lt;/strong&gt;: Implement a rollback mechanism on failure&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>jenkins</category>
    </item>
    <item>
      <title>Day 76: Jenkins Project Security</title>
      <dc:creator>Janak Shrestha</dc:creator>
      <pubDate>Wed, 16 Sep 2026 11:19:06 +0000</pubDate>
      <link>https://dev.to/janak0ff/day-76-jenkins-project-security-3i7e</link>
      <guid>https://dev.to/janak0ff/day-76-jenkins-project-security-3i7e</guid>
      <description>&lt;p&gt;The xFusionCorp Industries has recruited some new developers. There are already some existing jobs on Jenkins and two of these new developers need permissions to access those jobs. The development team has already shared those requirements with the DevOps team, so as per details mentioned below grant required permissions to the developers.  &lt;/p&gt;

&lt;p&gt;Click on the &lt;code&gt;Jenkins&lt;/code&gt; button on the top bar to access the Jenkins UI. Login using username &lt;code&gt;admin&lt;/code&gt; and password &lt;code&gt;Adm!n321&lt;/code&gt;.  m&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;There is an existing Jenkins job named &lt;code&gt;Packages&lt;/code&gt;, there are also two existing Jenkins users named &lt;code&gt;sam&lt;/code&gt; with password &lt;code&gt;sam@pass12345&lt;/code&gt; and &lt;code&gt;rohan&lt;/code&gt; with password &lt;code&gt;rohan@pass12345&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;Grant permissions to these users to access &lt;code&gt;Packages&lt;/code&gt; job as per details mentioned below:
a.) Make sure to select &lt;code&gt;Inherit permissions from parent ACL&lt;/code&gt; under &lt;code&gt;inheritance strategy&lt;/code&gt; for granting permissions to these users.
b.) Grant mentioned permissions to &lt;code&gt;sam&lt;/code&gt; user : &lt;code&gt;build&lt;/code&gt;, &lt;code&gt;configure&lt;/code&gt; and &lt;code&gt;read&lt;/code&gt;.
c.) Grant mentioned permissions to &lt;code&gt;rohan&lt;/code&gt; user : &lt;code&gt;build&lt;/code&gt;, &lt;code&gt;cancel&lt;/code&gt;, &lt;code&gt;configure&lt;/code&gt;, &lt;code&gt;read&lt;/code&gt;, &lt;code&gt;update&lt;/code&gt; and &lt;code&gt;tag&lt;/code&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;Note:&lt;/code&gt;  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Please do not modify/alter any other existing job configuration.
&lt;/li&gt;
&lt;li&gt;You might need to install some plugins and restart Jenkins service. So, we recommend clicking on &lt;code&gt;Restart Jenkins when installation is complete and no jobs are running&lt;/code&gt; on plugin installation/update page i.e &lt;code&gt;update centre&lt;/code&gt;. Also Jenkins UI sometimes gets stuck when Jenkins service restarts in the back end. In this case, please make sure to refresh the UI page.
&lt;/li&gt;
&lt;li&gt;For these kind of scenarios requiring changes to be done in a web UI, please take screenshots so that you can share it with us for review in case your task is marked incomplete. You may also consider using a screen recording software such as loom.com to record and share your work.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Understanding the Requirements
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Task Overview
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Details&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Job Name&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Packages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Users&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;sam, rohan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Inheritance Strategy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Inherit permissions from parent ACL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;sam's Permissions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;build, configure, read&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;rohan's Permissions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;build, cancel, configure, read, update, tag&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  User Credentials
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;User&lt;/th&gt;
&lt;th&gt;Password&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;admin&lt;/td&gt;
&lt;td&gt;Adm!n321&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sam&lt;/td&gt;
&lt;td&gt;sam@pass12345&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rohan&lt;/td&gt;
&lt;td&gt;rohan@pass12345&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Understanding Jenkins Security Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is Project-Based Security?
&lt;/h3&gt;

&lt;p&gt;Project-based security allows you to configure permissions at the &lt;strong&gt;individual job level&lt;/strong&gt;, rather than only at the global level. This provides granular control over who can do what on specific jobs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inheritance Strategies
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Strategy&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Inherit permissions from parent ACL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Combines global permissions with job-specific permissions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Inherit globally defined permissions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Only inherits global permissions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Do not inherit permission grants from other ACLs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Only uses permissions explicitly set on the job&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Permission Definitions
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Permission&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Job/Build&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Trigger builds for the job&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Job/Cancel&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cancel running builds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Job/Configure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Edit the job's configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Job/Read&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;View the job on the dashboard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Job/Update&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Update parameterized builds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SCM/Tag&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Create tags in the source code repository&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Step-by-Step Implementation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Access Jenkins UI
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Click the &lt;strong&gt;Jenkins&lt;/strong&gt; button on the top bar&lt;/li&gt;
&lt;li&gt;Login with:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Username:&lt;/strong&gt; &lt;code&gt;admin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Password:&lt;/strong&gt; &lt;code&gt;Adm!n321&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 2: Install the Matrix Authorization Strategy Plugin
&lt;/h3&gt;

&lt;p&gt;Before configuring project-based security, we need to install the &lt;strong&gt;Matrix Authorization Strategy&lt;/strong&gt; plugin.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Manage Jenkins&lt;/strong&gt; → &lt;strong&gt;Plugins&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click on &lt;strong&gt;Available plugins&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Search for &lt;strong&gt;Matrix Authorization Strategy&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Check the box and click &lt;strong&gt;Install&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;If prompted, select &lt;strong&gt;Restart Jenkins when installation is complete and no jobs are running&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Refresh the UI page if needed after Jenkins restarts&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Why this plugin?&lt;/strong&gt; It provides the ability to configure fine-grained permissions at both global and job levels.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Configure Global Security (Parent ACL)
&lt;/h3&gt;

&lt;p&gt;This step is critical because users need at least &lt;code&gt;Overall/Read&lt;/code&gt; permission to log in and see the Jenkins dashboard.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Manage Jenkins&lt;/strong&gt; → &lt;strong&gt;Configure Global Security&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;Authorization&lt;/strong&gt;, select &lt;strong&gt;Project-based Matrix Authorization Strategy&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;In the global permission matrix:

&lt;ul&gt;
&lt;li&gt;Click &lt;strong&gt;Add user&lt;/strong&gt; → Enter &lt;code&gt;sam&lt;/code&gt; → Click &lt;strong&gt;OK&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Add user&lt;/strong&gt; → Enter &lt;code&gt;rohan&lt;/code&gt; → Click &lt;strong&gt;OK&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Grant permissions:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;sam:&lt;/strong&gt; Check &lt;code&gt;Overall/Read&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;rohan:&lt;/strong&gt; Check &lt;code&gt;Overall/Read&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;admin:&lt;/strong&gt; Ensure &lt;code&gt;Overall/Administer&lt;/code&gt; is checked (should already be)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Save&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 4: Enable Project-Based Security on the Packages Job
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to the &lt;strong&gt;Packages&lt;/strong&gt; job on the Jenkins dashboard&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Configure&lt;/strong&gt; in the left sidebar&lt;/li&gt;
&lt;li&gt;In the &lt;strong&gt;General&lt;/strong&gt; section, check the box &lt;strong&gt;Enable project-based security&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Important Note:&lt;/strong&gt; The &lt;strong&gt;Authorization&lt;/strong&gt; section (with inheritance strategy and permission matrix) will only appear &lt;strong&gt;AFTER&lt;/strong&gt; you check this box. This is by design — Jenkins shows/hides sections based on the options you select.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Configure the Inheritance Strategy
&lt;/h3&gt;

&lt;p&gt;Once you check &lt;strong&gt;Enable project-based security&lt;/strong&gt;, an &lt;strong&gt;Authorization&lt;/strong&gt; section will appear below it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Under &lt;strong&gt;Inheritance Strategy&lt;/strong&gt;, select:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   Inherit permissions from parent ACL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;You will see a description confirming:
&amp;gt; "This item will inherit its parent item's permissions (in addition to any permissions granted here). If this item is at the top level in Jenkins, it will inherit the global security settings."&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 6: Add User &lt;code&gt;sam&lt;/code&gt; and Grant Permissions
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Click the &lt;strong&gt;Add user&lt;/strong&gt; button&lt;/li&gt;
&lt;li&gt;Enter the username: &lt;code&gt;sam&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;OK&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;In the row for &lt;code&gt;sam&lt;/code&gt;, check the following permissions:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Permission Category&lt;/th&gt;
&lt;th&gt;Permission&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Job&lt;/td&gt;
&lt;td&gt;Job/Build&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job&lt;/td&gt;
&lt;td&gt;Job/Configure&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job&lt;/td&gt;
&lt;td&gt;Job/Read&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Summary for sam:&lt;/strong&gt; &lt;code&gt;Job: Build, Configure, Read&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Leave all other permissions &lt;strong&gt;unchecked&lt;/strong&gt; for &lt;code&gt;sam&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7: Add User &lt;code&gt;rohan&lt;/code&gt; and Grant Permissions
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Click the &lt;strong&gt;Add user&lt;/strong&gt; button&lt;/li&gt;
&lt;li&gt;Enter the username: &lt;code&gt;rohan&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;OK&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;In the row for &lt;code&gt;rohan&lt;/code&gt;, check the following permissions:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Permission Category&lt;/th&gt;
&lt;th&gt;Permission&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Job&lt;/td&gt;
&lt;td&gt;Job/Build&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job&lt;/td&gt;
&lt;td&gt;Job/Cancel&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job&lt;/td&gt;
&lt;td&gt;Job/Configure&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job&lt;/td&gt;
&lt;td&gt;Job/Read&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run&lt;/td&gt;
&lt;td&gt;Run/Update&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SCM&lt;/td&gt;
&lt;td&gt;SCM/Tag&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Summary for rohan:&lt;/strong&gt; &lt;code&gt;Job: Build, Cancel, Configure, Read · Run: Update · SCM: Tag&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Leave all other permissions &lt;strong&gt;unchecked&lt;/strong&gt; for &lt;code&gt;rohan&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 8: Save the Configuration
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Click the &lt;strong&gt;Save&lt;/strong&gt; button at the bottom left of the page&lt;/li&gt;
&lt;li&gt;The configuration will be applied immediately&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Permission Matrix Visualization
&lt;/h2&gt;

&lt;p&gt;After completing the configuration, the permission matrix should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────┬─────────┬─────────┬───────────┐
│ Permission          │ sam     │ rohan   │ Anonymous │
├─────────────────────┼─────────┼─────────┼───────────┤
│ Job/Build           │   ✓     │   ✓     │           │
│ Job/Cancel          │         │   ✓     │           │
│ Job/Configure       │   ✓     │   ✓     │           │
│ Job/Read            │   ✓     │   ✓     │           │
│ Job/Update          │         │   ✓     │           │
│ SCM/Tag             │         │   ✓     │           │
│ (other permissions) │         │         │           │
└─────────────────────┴─────────┴─────────┴───────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Log Out as Admin
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Click your profile icon (top right)&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Sign out&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 2: Log in as sam
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Username: &lt;code&gt;sam&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Password: &lt;code&gt;sam@pass12345&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Navigate to the &lt;strong&gt;Packages&lt;/strong&gt; job&lt;/li&gt;
&lt;li&gt;Verify you can:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;See the job&lt;/strong&gt; (Job/Read)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Click Configure&lt;/strong&gt; (Job/Configure)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Click Build Now&lt;/strong&gt; (Job/Build)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 3: Log in as rohan
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Log out from sam's session&lt;/li&gt;
&lt;li&gt;Username: &lt;code&gt;rohan&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Password: &lt;code&gt;rohan@pass12345&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Navigate to the &lt;strong&gt;Packages&lt;/strong&gt; job&lt;/li&gt;
&lt;li&gt;Verify you can:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;See the job&lt;/strong&gt; (Job/Read)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Click Configure&lt;/strong&gt; (Job/Configure)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Click Build Now&lt;/strong&gt; (Job/Build)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cancel a running build&lt;/strong&gt; (Job/Cancel)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Update parameters&lt;/strong&gt; (Run/Update)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tag in SCM&lt;/strong&gt; (SCM/Tag)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




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

&lt;h3&gt;
  
  
  Issue 1: Authorization Section Not Visible
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; The Authorization section doesn't appear on the job configuration page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; The "Enable project-based security" checkbox is not checked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Check the &lt;strong&gt;Enable project-based security&lt;/strong&gt; checkbox in the General section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Issue 2: Users Cannot Log In
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; Users get "Access Denied" when logging in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; Users don't have &lt;code&gt;Overall/Read&lt;/code&gt; permission at the global level.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Add users to the global security matrix and grant &lt;code&gt;Overall/Read&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Issue 3: Job Not Visible to Users
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; Users can log in but cannot see the Packages job.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; Users lack &lt;code&gt;Job/Read&lt;/code&gt; permission on the specific job.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Grant &lt;code&gt;Job/Read&lt;/code&gt; permission in the project-level matrix.&lt;/p&gt;

&lt;h3&gt;
  
  
  Issue 4: Matrix Option Not Available
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; "Project-based Matrix Authorization Strategy" is not listed in the Authorization options.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; The Matrix Authorization Strategy plugin is not installed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Install the plugin from Available Plugins and restart Jenkins.&lt;/p&gt;

&lt;h3&gt;
  
  
  Issue 5: Wrong Inheritance Strategy
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; Users have more or fewer permissions than intended.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; The wrong inheritance strategy was selected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Change the inheritance strategy to &lt;strong&gt;Inherit permissions from parent ACL&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Best Practices for Jenkins Project Security
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Principle of Least Privilege
&lt;/h3&gt;

&lt;p&gt;Grant only the permissions that users absolutely need. Avoid giving broad permissions like &lt;code&gt;Job/Delete&lt;/code&gt; or &lt;code&gt;Overall/Administer&lt;/code&gt; unless necessary.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Use Groups Instead of Individual Users
&lt;/h3&gt;

&lt;p&gt;For larger teams, use Jenkins groups (backed by LDAP or Active Directory) instead of managing individual users.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Regular Audits
&lt;/h3&gt;

&lt;p&gt;Periodically review the permission matrix to ensure users have appropriate access. Remove permissions for users who no longer need them.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Document Permission Changes
&lt;/h3&gt;

&lt;p&gt;Keep a record of who has what permissions and why. This helps during audits and troubleshooting.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Test with Non-Admin Accounts
&lt;/h3&gt;

&lt;p&gt;Always verify permissions by logging in as the actual user, not just as an administrator.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Learnings
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Project-Based Security&lt;/strong&gt;: Jenkins allows granular permission management at the job level using the Matrix Authorization Strategy plugin.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Inheritance Strategy&lt;/strong&gt;: The "Inherit permissions from parent ACL" strategy combines global and job-level permissions, ensuring users have at least the global &lt;code&gt;Overall/Read&lt;/code&gt; permission.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Conditional UI&lt;/strong&gt;: The Authorization section in job configuration only appears after checking the "Enable project-based security" checkbox.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Permission Verification&lt;/strong&gt;: Always verify permissions by logging in as the actual user.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Principle of Least Privilege&lt;/strong&gt;: Grant only the permissions required for each user's role.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Task Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Matrix Authorization Strategy plugin installed&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Global security configured with &lt;code&gt;Overall/Read&lt;/code&gt; for sam and rohan&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Project-based security enabled on Packages job&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inheritance Strategy: &lt;strong&gt;Inherit permissions from parent ACL&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sam granted: build, configure, read&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rohan granted: build, cancel, configure, read, update, tag&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No other job configuration modified&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Configuration Summary
&lt;/h2&gt;

&lt;h3&gt;
  
  
  sam's Permissions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Job: Build, Configure, Read
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  rohan's Permissions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Job: Build, Cancel, Configure, Read
Run: Update
SCM: Tag
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Inheritance Strategy
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Inherit permissions from parent ACL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Screenshots for Documentation
&lt;/h2&gt;

&lt;p&gt;Make sure to capture the following screenshots for review:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Global Security Configuration&lt;/strong&gt; – Showing &lt;code&gt;sam&lt;/code&gt; and &lt;code&gt;rohan&lt;/code&gt; with &lt;code&gt;Overall/Read&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Project-Based Security Enabled&lt;/strong&gt; – The checkbox checked in the Packages job&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inheritance Strategy&lt;/strong&gt; – "Inherit permissions from parent ACL" selected&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Permission Matrix&lt;/strong&gt; – Showing the permissions for sam and rohan&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verification&lt;/strong&gt; – Logged in as sam and rohan accessing the Packages job&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this challenge, we successfully:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Accessed the Jenkins UI and logged in as admin&lt;/li&gt;
&lt;li&gt;Installed the Matrix Authorization Strategy plugin&lt;/li&gt;
&lt;li&gt;Configured global security with &lt;code&gt;Overall/Read&lt;/code&gt; for both users&lt;/li&gt;
&lt;li&gt;Enabled project-based security on the Packages job&lt;/li&gt;
&lt;li&gt;Selected the correct inheritance strategy&lt;/li&gt;
&lt;li&gt;Granted the appropriate permissions to sam and rohan&lt;/li&gt;
&lt;li&gt;Verified the configuration by logging in as each user&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This setup demonstrates how Jenkins project-based security provides fine-grained control over user permissions, following the Principle of Least Privilege. The developers now have exactly the permissions they need to perform their tasks without unnecessary access.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>jenkins</category>
    </item>
    <item>
      <title>Linux Postfix Mail Server</title>
      <dc:creator>Janak Shrestha</dc:creator>
      <pubDate>Wed, 16 Sep 2026 09:42:09 +0000</pubDate>
      <link>https://dev.to/janak0ff/linux-postfix-mail-server-abk</link>
      <guid>https://dev.to/janak0ff/linux-postfix-mail-server-abk</guid>
      <description>&lt;p&gt;&lt;code&gt;xFusionCorp Industries&lt;/code&gt; has planned to set up a common email server in &lt;code&gt;Stork DC&lt;/code&gt;. After several meetings and recommendations they have decided to use &lt;code&gt;postfix&lt;/code&gt; as their &lt;code&gt;mail transfer agent&lt;/code&gt; and &lt;code&gt;dovecot&lt;/code&gt; as an &lt;code&gt;IMAP/POP3&lt;/code&gt; server. We would like you to perform the following steps:  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install and configure &lt;code&gt;postfix&lt;/code&gt; on &lt;code&gt;Stork DC&lt;/code&gt; mail server.
&lt;/li&gt;
&lt;li&gt;Create an email account &lt;code&gt;anita@stratos.xfusioncorp.com&lt;/code&gt; identified by &lt;code&gt;8FmzjvFU6S&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;Set its mail directory to &lt;code&gt;/home/anita/Maildir&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;Install and configure &lt;code&gt;dovecot&lt;/code&gt; on the same server.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Understanding the Components
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Postfix
&lt;/h3&gt;

&lt;p&gt;Postfix is a Mail Transfer Agent responsible for routing and delivering email. It handles SMTP connections, accepts incoming mail, and delivers it to local mailboxes or relays it to other servers. Postfix is known for its security, performance, and ease of administration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dovecot
&lt;/h3&gt;

&lt;p&gt;Dovecot is an IMAP and POP3 server that allows users to access their email. It handles authentication and provides access to mailboxes stored on the server. Dovecot supports various mailbox formats, including Maildir and mbox.&lt;/p&gt;

&lt;h3&gt;
  
  
  Maildir Format
&lt;/h3&gt;

&lt;p&gt;Maildir stores each email as a separate file in a directory structure. This format is more reliable than mbox because it avoids file locking issues and supports concurrent access. The structure consists of three subdirectories: cur for current messages, new for newly delivered messages, and tmp for temporary files during delivery.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before beginning, ensure the following are available:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Access to the mail server with root or sudo privileges&lt;/li&gt;
&lt;li&gt;The mail server hostname and domain information&lt;/li&gt;
&lt;li&gt;Network connectivity between the mail server and other infrastructure components&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Server Details for This Tutorial
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Server&lt;/td&gt;
&lt;td&gt;stmail01.stratos.xfusioncorp.com&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User&lt;/td&gt;
&lt;td&gt;groot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Password&lt;/td&gt;
&lt;td&gt;Gr00T123&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Email Address&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:anita@stratos.xfusioncorp.com"&gt;anita@stratos.xfusioncorp.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Email Password&lt;/td&gt;
&lt;td&gt;8FmzjvFU6S&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mail Directory&lt;/td&gt;
&lt;td&gt;/home/anita/Maildir&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Installing Postfix
&lt;/h2&gt;

&lt;p&gt;Connect to the mail server and switch to root.&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;# Password: Gr00T123&lt;/span&gt;
ssh groot@stmail01
&lt;span class="nb"&gt;sudo &lt;/span&gt;su -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install Postfix using the system package manager.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The installation includes Postfix and its dependencies. Postfix version 3.5.25 is installed on CentOS Stream 9.&lt;/p&gt;




&lt;h2&gt;
  
  
  Configuring Postfix
&lt;/h2&gt;

&lt;p&gt;Edit the main Postfix configuration file at &lt;code&gt;/etc/postfix/main.cf&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vi /etc/postfix/main.cf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add or modify the following parameters:&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;myhostname&lt;/span&gt; = &lt;span class="n"&gt;stmail01&lt;/span&gt;.&lt;span class="n"&gt;stratos&lt;/span&gt;.&lt;span class="n"&gt;xfusioncorp&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;
&lt;span class="n"&gt;mydomain&lt;/span&gt; = &lt;span class="n"&gt;stratos&lt;/span&gt;.&lt;span class="n"&gt;xfusioncorp&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;
&lt;span class="n"&gt;myorigin&lt;/span&gt; = $&lt;span class="n"&gt;mydomain&lt;/span&gt;
&lt;span class="n"&gt;inet_interfaces&lt;/span&gt; = &lt;span class="n"&gt;all&lt;/span&gt;
&lt;span class="n"&gt;mydestination&lt;/span&gt; = $&lt;span class="n"&gt;myhostname&lt;/span&gt;, &lt;span class="n"&gt;localhost&lt;/span&gt;.$&lt;span class="n"&gt;mydomain&lt;/span&gt;, &lt;span class="n"&gt;localhost&lt;/span&gt;, $&lt;span class="n"&gt;mydomain&lt;/span&gt;
&lt;span class="n"&gt;mynetworks&lt;/span&gt; = &lt;span class="m"&gt;172&lt;/span&gt;.&lt;span class="m"&gt;16&lt;/span&gt;.&lt;span class="m"&gt;238&lt;/span&gt;.&lt;span class="m"&gt;0&lt;/span&gt;/&lt;span class="m"&gt;24&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;0&lt;/span&gt;/&lt;span class="m"&gt;8&lt;/span&gt;
&lt;span class="n"&gt;home_mailbox&lt;/span&gt; = &lt;span class="n"&gt;Maildir&lt;/span&gt;/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Parameter Explanation
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&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;myhostname&lt;/td&gt;
&lt;td&gt;Fully qualified domain name of the mail server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mydomain&lt;/td&gt;
&lt;td&gt;Domain name used for email addresses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;myorigin&lt;/td&gt;
&lt;td&gt;Domain used for outgoing mail&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;inet_interfaces&lt;/td&gt;
&lt;td&gt;Network interfaces Postfix listens on&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mydestination&lt;/td&gt;
&lt;td&gt;Domains for which Postfix accepts mail&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mynetworks&lt;/td&gt;
&lt;td&gt;Trusted networks for relaying&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;home_mailbox&lt;/td&gt;
&lt;td&gt;Mailbox format and location&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;home_mailbox = Maildir/&lt;/code&gt; setting is critical. It tells Postfix to deliver mail in Maildir format within the user's home directory.&lt;/p&gt;




&lt;h2&gt;
  
  
  Starting and Enabling Postfix
&lt;/h2&gt;

&lt;p&gt;Start the Postfix service and enable it to start on boot.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl start postfix
systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;postfix
systemctl status postfix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify that Postfix is running and listening on port 25.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ss &lt;span class="nt"&gt;-tlnp&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; :25
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Creating the Email Account
&lt;/h2&gt;

&lt;p&gt;Create a system user for the email account. The username becomes the local part of the email address.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;useradd anita
passwd anita
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When prompted, enter the password &lt;code&gt;8FmzjvFU6S&lt;/code&gt; twice.&lt;/p&gt;

&lt;p&gt;Verify the user was created.&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="nb"&gt;id &lt;/span&gt;anita
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Creating the Maildir Directory
&lt;/h2&gt;

&lt;p&gt;Create the Maildir structure in the user's home 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="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /home/anita/Maildir/&lt;span class="o"&gt;{&lt;/span&gt;cur,new,tmp&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set the correct ownership so the user can access the mailbox.&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="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; anita:anita /home/anita/Maildir
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the directory 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="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /home/anita/Maildir/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output should show three subdirectories: cur, new, and tmp, all owned by anita.&lt;/p&gt;




&lt;h2&gt;
  
  
  Installing Dovecot
&lt;/h2&gt;

&lt;p&gt;Install Dovecot using the system package manager.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Dovecot version 2.3.16 is installed along with its dependencies, including clucene-core and libexttextcat.&lt;/p&gt;




&lt;h2&gt;
  
  
  Configuring Dovecot
&lt;/h2&gt;

&lt;p&gt;Dovecot configuration is split across several files in the &lt;code&gt;/etc/dovecot/&lt;/code&gt; directory. Each file controls a specific aspect of the server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enable IMAP and POP3 Protocols
&lt;/h3&gt;

&lt;p&gt;Edit &lt;code&gt;/etc/dovecot/dovecot.conf&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vi /etc/dovecot/dovecot.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set the protocols line to enable both IMAP and POP3.&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;protocols&lt;/span&gt; = &lt;span class="n"&gt;imap&lt;/span&gt; &lt;span class="n"&gt;pop3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Set the Mail Location
&lt;/h3&gt;

&lt;p&gt;Edit &lt;code&gt;/etc/dovecot/conf.d/10-mail.conf&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vi /etc/dovecot/conf.d/10-mail.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set the mail location to match the Postfix Maildir setting.&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;mail_location&lt;/span&gt; = &lt;span class="n"&gt;maildir&lt;/span&gt;:~/&lt;span class="n"&gt;Maildir&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This must match the &lt;code&gt;home_mailbox&lt;/code&gt; setting in Postfix for mail delivery to work correctly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure Authentication
&lt;/h3&gt;

&lt;p&gt;Edit &lt;code&gt;/etc/dovecot/conf.d/10-auth.conf&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vi /etc/dovecot/conf.d/10-auth.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set the authentication parameters.&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;disable_plaintext_auth&lt;/span&gt; = &lt;span class="n"&gt;yes&lt;/span&gt;
&lt;span class="n"&gt;auth_mechanisms&lt;/span&gt; = &lt;span class="n"&gt;plain&lt;/span&gt; &lt;span class="n"&gt;login&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configure the Auth Service Socket
&lt;/h3&gt;

&lt;p&gt;Edit &lt;code&gt;/etc/dovecot/conf.d/10-master.conf&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vi /etc/dovecot/conf.d/10-master.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure the auth service socket for Postfix integration.&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;service&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt; {
  &lt;span class="n"&gt;unix_listener&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;-&lt;span class="n"&gt;userdb&lt;/span&gt; {
    &lt;span class="n"&gt;mode&lt;/span&gt; = &lt;span class="m"&gt;0660&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; = &lt;span class="n"&gt;postfix&lt;/span&gt;
    &lt;span class="n"&gt;group&lt;/span&gt; = &lt;span class="n"&gt;postfix&lt;/span&gt;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows Postfix to authenticate users through Dovecot.&lt;/p&gt;




&lt;h2&gt;
  
  
  Starting and Enabling Dovecot
&lt;/h2&gt;

&lt;p&gt;Start the Dovecot service and enable it to start on boot.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl start dovecot
systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;dovecot
systemctl status dovecot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify that Dovecot is running and listening on ports 110 and 143.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ss &lt;span class="nt"&gt;-tlnp&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;":110|:143"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Check Service Status
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl is-active postfix
systemctl is-enabled postfix
systemctl is-active dovecot
systemctl is-enabled dovecot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All services should return active and enabled.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check Listening Ports
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ss &lt;span class="nt"&gt;-tlnp&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;":25|:110|:143"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output shows Postfix on port 25 and Dovecot on ports 110 and 143.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check User and Maildir
&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;id &lt;/span&gt;anita
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /home/anita/Maildir/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The user should exist with the correct UID, and the Maildir should contain cur, new, and tmp directories.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test SMTP
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;telnet localhost 25
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The connection should succeed and show the Postfix banner.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test POP3
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;telnet localhost 110
user anita
pass 8FmzjvFU6S
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The authentication should succeed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Postfix Warnings About Overriding
&lt;/h3&gt;

&lt;p&gt;When configuration lines are duplicated, Postfix issues warnings. Remove duplicate entries from &lt;code&gt;/etc/postfix/main.cf&lt;/code&gt; and restart Postfix.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dovecot Fails to Start
&lt;/h3&gt;

&lt;p&gt;Check the Dovecot logs for errors.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; dovecot &lt;span class="nt"&gt;-n&lt;/span&gt; 50
doveadm log find
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Mail Not Delivered
&lt;/h3&gt;

&lt;p&gt;Verify that the &lt;code&gt;home_mailbox&lt;/code&gt; setting in Postfix matches the &lt;code&gt;mail_location&lt;/code&gt; setting in Dovecot. Both should point to Maildir.&lt;/p&gt;

&lt;h3&gt;
  
  
  Authentication Failures
&lt;/h3&gt;

&lt;p&gt;Ensure the auth service socket is configured correctly in &lt;code&gt;10-master.conf&lt;/code&gt; with the postfix user and group.&lt;/p&gt;

&lt;h3&gt;
  
  
  Port Conflicts
&lt;/h3&gt;

&lt;p&gt;Ensure no other service is using ports 25, 110, or 143.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ss &lt;span class="nt"&gt;-tlnp&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;":25|:110|:143"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Configuration Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Configuration&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mail Server&lt;/td&gt;
&lt;td&gt;stmail01.stratos.xfusioncorp.com&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Email Address&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:anita@stratos.xfusioncorp.com"&gt;anita@stratos.xfusioncorp.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Password&lt;/td&gt;
&lt;td&gt;8FmzjvFU6S&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mail Directory&lt;/td&gt;
&lt;td&gt;/home/anita/Maildir&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Postfix Port&lt;/td&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dovecot IMAP Port&lt;/td&gt;
&lt;td&gt;143&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dovecot POP3 Port&lt;/td&gt;
&lt;td&gt;110&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mailbox Format&lt;/td&gt;
&lt;td&gt;Maildir&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Best Practices
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Use Maildir Format
&lt;/h3&gt;

&lt;p&gt;Maildir is more reliable than mbox for concurrent access and avoids file locking issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep Postfix and Dovecot Settings Consistent
&lt;/h3&gt;

&lt;p&gt;The mail location in Dovecot must match the home_mailbox setting in Postfix.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enable Only Required Protocols
&lt;/h3&gt;

&lt;p&gt;Enable only the protocols that are needed. If POP3 is not required, disable it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Secure Authentication
&lt;/h3&gt;

&lt;p&gt;Use TLS for authentication when possible. Configure Dovecot to use SSL/TLS certificates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Monitor Logs
&lt;/h3&gt;

&lt;p&gt;Regularly check mail logs for errors and delivery issues.&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="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/maillog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Regular Backups
&lt;/h3&gt;

&lt;p&gt;Back up mail directories and configuration files regularly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Setting up a mail server with Postfix and Dovecot provides a reliable and secure email solution for enterprise environments. Postfix handles mail transfer and delivery, while Dovecot provides IMAP and POP3 access to mailboxes. The Maildir format ensures reliable mail storage.&lt;/p&gt;

&lt;p&gt;The key steps are installing both packages, configuring Postfix to deliver mail in Maildir format, creating user accounts with the correct mail directory structure, and configuring Dovecot to access those mailboxes. Verification of services, ports, and user accounts ensures the setup is complete and functional.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>postfix</category>
      <category>mailserver</category>
    </item>
    <item>
      <title>Linux Firewalld Setup</title>
      <dc:creator>Janak Shrestha</dc:creator>
      <pubDate>Tue, 15 Sep 2026 10:55:49 +0000</pubDate>
      <link>https://dev.to/janak0ff/linux-firewalld-setup-5e6j</link>
      <guid>https://dev.to/janak0ff/linux-firewalld-setup-5e6j</guid>
      <description>&lt;p&gt;To secure our &lt;code&gt;Nautilus&lt;/code&gt; infrastructure in &lt;code&gt;Stratos Datacenter&lt;/code&gt;, we have decided to install and configure &lt;code&gt;firewalld&lt;/code&gt; on one of the app servers named App Server &lt;code&gt;1&lt;/code&gt;. We have Apache and Nginx services running on these apps. Nginx is running as a reverse proxy server for Apache. We might have more robust firewall settings in the future, but for now we have decided to go with the given requirements listed below:  &lt;/p&gt;

&lt;p&gt;a. Allow all incoming connections on Nginx port, i.e &lt;code&gt;80&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
b. Block all incoming connections on Apache port, i.e &lt;code&gt;8085&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
c. All rules must be permanent.&lt;br&gt;&lt;br&gt;
d. Zone should be public.&lt;br&gt;&lt;br&gt;
e. If Apache or Nginx services aren't running already, please make sure to start them.&lt;/p&gt;


&lt;h2&gt;
  
  
  Understanding Firewalld
&lt;/h2&gt;
&lt;h3&gt;
  
  
  What is Firewalld
&lt;/h3&gt;

&lt;p&gt;Firewalld is a firewall management tool that provides a dynamically managed firewall with support for network zones. It acts as a frontend for nftables or iptables and offers a simpler way to manage firewall rules compared to directly manipulating iptables.&lt;/p&gt;
&lt;h3&gt;
  
  
  Key Concepts
&lt;/h3&gt;

&lt;p&gt;Firewalld uses zones to define trust levels for network connections. Each zone has its own set of rules. The public zone is the default zone and is used for public-facing services. Rich rules allow for more granular control over traffic, including dropping specific ports.&lt;/p&gt;
&lt;h3&gt;
  
  
  Zones Overview
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Zone&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;public&lt;/td&gt;
&lt;td&gt;Public-facing services, default zone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;trusted&lt;/td&gt;
&lt;td&gt;All network connections accepted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;home&lt;/td&gt;
&lt;td&gt;Home network, more permissive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;work&lt;/td&gt;
&lt;td&gt;Work network, moderate restrictions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;drop&lt;/td&gt;
&lt;td&gt;All incoming connections dropped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;block&lt;/td&gt;
&lt;td&gt;Incoming connections rejected&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before beginning, ensure the following prerequisites are met. A Linux server with root or sudo access is required. The server should have Apache and Nginx installed. Basic command-line knowledge is necessary. Understanding of network ports and protocols is helpful.&lt;/p&gt;
&lt;h3&gt;
  
  
  Server Details for This Tutorial
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Server&lt;/td&gt;
&lt;td&gt;App Server 1 (stapp01)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User&lt;/td&gt;
&lt;td&gt;tony&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Password&lt;/td&gt;
&lt;td&gt;Ir0nM@n&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nginx Port&lt;/td&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apache Port&lt;/td&gt;
&lt;td&gt;8085&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zone&lt;/td&gt;
&lt;td&gt;public&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  Step 1: Connect to the Server
&lt;/h2&gt;

&lt;p&gt;Access the target server using SSH with the appropriate credentials.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh tony@stapp01
Password: Ir0nM@n
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Switch to root to perform administrative tasks.&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="nb"&gt;sudo &lt;/span&gt;su -
Password: Ir0nM@n
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 2: Install Firewalld
&lt;/h2&gt;

&lt;p&gt;Install the firewalld package using the system package manager.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The installation includes firewalld and its dependencies, including ipset, nftables, and python3-firewall.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Start and Enable Firewalld
&lt;/h2&gt;

&lt;p&gt;Start the firewalld service and enable it to start automatically on boot.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl start firewalld
systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;firewalld
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The enable command creates a symlink for the service to start on boot.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Set the Default Zone
&lt;/h2&gt;

&lt;p&gt;Configure the default zone to public as required.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;firewall-cmd &lt;span class="nt"&gt;--set-default-zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the default zone has been set correctly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;firewall-cmd &lt;span class="nt"&gt;--get-default-zone&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 5: Allow Nginx Port (80)
&lt;/h2&gt;

&lt;p&gt;Add a permanent rule to allow incoming traffic on port 80 for Nginx.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--add-port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;80/tcp &lt;span class="nt"&gt;--permanent&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The permanent flag ensures the rule persists across reboots.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6: Block Apache Port (8085)
&lt;/h2&gt;

&lt;p&gt;Remove any existing rule for port 8085 and add a rich rule to block it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--remove-port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8085/tcp &lt;span class="nt"&gt;--permanent&lt;/span&gt;
firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--add-rich-rule&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'rule family="ipv4" port port="8085" protocol="tcp" drop'&lt;/span&gt; &lt;span class="nt"&gt;--permanent&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rich rule uses the drop action to silently discard packets on port 8085.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7: Reload Firewall
&lt;/h2&gt;

&lt;p&gt;Apply all the permanent rules by reloading 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;firewall-cmd &lt;span class="nt"&gt;--reload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 8: Configure and Start Services
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Port Conflict Resolution
&lt;/h3&gt;

&lt;p&gt;By default, both Apache and Nginx listen on port 80, causing a conflict. Since Nginx acts as a reverse proxy, Apache should listen on port 8085 while Nginx listens on port 80.&lt;/p&gt;

&lt;p&gt;Configure Apache to listen on port 8085.&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="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/^Listen 80/Listen 8085/'&lt;/span&gt; /etc/httpd/conf/httpd.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Start Apache
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl start httpd
systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;httpd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Start Nginx
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl start nginx
systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify Both Services
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl status httpd
systemctl status nginx
systemctl is-active httpd
systemctl is-active nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 9: Complete Verification
&lt;/h2&gt;

&lt;p&gt;Verify the firewall configuration and service status.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--list-all&lt;/span&gt;
firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--list-ports&lt;/span&gt;
firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--list-rich-rules&lt;/span&gt;
ss &lt;span class="nt"&gt;-tlnp&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;":80|:8085"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Understanding the Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Firewall Rules Summary
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;th&gt;Port&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Permanent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Nginx&lt;/td&gt;
&lt;td&gt;80/tcp&lt;/td&gt;
&lt;td&gt;Allow&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apache&lt;/td&gt;
&lt;td&gt;8085/tcp&lt;/td&gt;
&lt;td&gt;Block&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zone&lt;/td&gt;
&lt;td&gt;public&lt;/td&gt;
&lt;td&gt;Default&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Rich Rule Breakdown
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rule &lt;span class="nv"&gt;family&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ipv4"&lt;/span&gt; port &lt;span class="nv"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"8085"&lt;/span&gt; &lt;span class="nv"&gt;protocol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"tcp"&lt;/span&gt; drop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rich rule specifies IPv4 traffic on port 8085 using TCP protocol. The drop action silently discards packets without notifying the sender.&lt;/p&gt;

&lt;h3&gt;
  
  
  Service Ports
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Default Port&lt;/th&gt;
&lt;th&gt;Configured Port&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Nginx&lt;/td&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apache&lt;/td&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;td&gt;8085&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Alternative Methods
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Using firewall-cmd with Different Syntax
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;firewall-cmd &lt;span class="nt"&gt;--permanent&lt;/span&gt; &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--add-port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;80/tcp
firewall-cmd &lt;span class="nt"&gt;--permanent&lt;/span&gt; &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--add-rich-rule&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'rule family="ipv4" port port="8085" protocol="tcp" drop'&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;
  
  
  Using Direct Rules
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;firewall-cmd &lt;span class="nt"&gt;--permanent&lt;/span&gt; &lt;span class="nt"&gt;--direct&lt;/span&gt; &lt;span class="nt"&gt;--add-rule&lt;/span&gt; ipv4 filter INPUT 0 &lt;span class="nt"&gt;-p&lt;/span&gt; tcp &lt;span class="nt"&gt;--dport&lt;/span&gt; 8085 &lt;span class="nt"&gt;-j&lt;/span&gt; DROP
firewall-cmd &lt;span class="nt"&gt;--reload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using Services Instead of Ports
&lt;/h3&gt;



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

&lt;/div&gt;






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

&lt;h3&gt;
  
  
  Firewalld Not Found
&lt;/h3&gt;

&lt;p&gt;When the firewalld command is not found, install the package using the system package manager.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Failed to Start Firewalld
&lt;/h3&gt;

&lt;p&gt;Check the service logs for errors.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; firewalld &lt;span class="nt"&gt;-n&lt;/span&gt; 50
systemctl restart firewalld
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Port Not Blocked
&lt;/h3&gt;

&lt;p&gt;Verify the rich rule has been applied and reload 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;firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--list-rich-rules&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;
  
  
  Nginx Fails to Start
&lt;/h3&gt;

&lt;p&gt;The most common cause is a port conflict with Apache. Check which service is using port 80.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ss &lt;span class="nt"&gt;-tlnp&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; :80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If Apache is using port 80, stop Apache, reconfigure it to use port 8085, and start Nginx.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;systemctl stop httpd
&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/^Listen 80/Listen 8085/'&lt;/span&gt; /etc/httpd/conf/httpd.conf
systemctl start httpd
systemctl start nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Rules Not Permanent
&lt;/h3&gt;

&lt;p&gt;Ensure the permanent flag is used with every firewall-cmd command that modifies rules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--add-port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;80/tcp &lt;span class="nt"&gt;--permanent&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;
  
  
  Verification Commands
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check firewall status&lt;/span&gt;
systemctl status firewalld

&lt;span class="c"&gt;# Check default zone&lt;/span&gt;
firewall-cmd &lt;span class="nt"&gt;--get-default-zone&lt;/span&gt;

&lt;span class="c"&gt;# Check open ports&lt;/span&gt;
firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--list-ports&lt;/span&gt;

&lt;span class="c"&gt;# Check rich rules&lt;/span&gt;
firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--list-rich-rules&lt;/span&gt;

&lt;span class="c"&gt;# Check complete configuration&lt;/span&gt;
firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--list-all&lt;/span&gt;

&lt;span class="c"&gt;# Check services status&lt;/span&gt;
systemctl is-active httpd
systemctl is-active nginx

&lt;span class="c"&gt;# Check listening ports&lt;/span&gt;
ss &lt;span class="nt"&gt;-tlnp&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;":80|:8085"&lt;/span&gt;

&lt;span class="c"&gt;# Test connectivity&lt;/span&gt;
curl &lt;span class="nt"&gt;-I&lt;/span&gt; http://localhost:80
curl &lt;span class="nt"&gt;-I&lt;/span&gt; http://localhost:8085
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Expected Output
&lt;/h2&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;public
  target: default
  icmp-block-inversion: no
  interfaces: eth0
  sources:
  services: cockpit dhcpv6-client ssh
  ports: 80/tcp
  protocols:
  forward: &lt;span class="nb"&gt;yes
  &lt;/span&gt;masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:
        rule &lt;span class="nv"&gt;family&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ipv4"&lt;/span&gt; port &lt;span class="nv"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"8085"&lt;/span&gt; &lt;span class="nv"&gt;protocol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"tcp"&lt;/span&gt; drop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Service Status
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;httpd: active
nginx: active
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Listening Ports
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;LISTEN 0 511 0.0.0.0:80 0.0.0.0:&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;users&lt;/span&gt;:&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="s2"&gt;"nginx"&lt;/span&gt;,pid&lt;span class="o"&gt;=&lt;/span&gt;34219,fd&lt;span class="o"&gt;=&lt;/span&gt;6&lt;span class="o"&gt;))&lt;/span&gt;
LISTEN 0 511 0.0.0.0:8085 0.0.0.0:&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;users&lt;/span&gt;:&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="s2"&gt;"httpd"&lt;/span&gt;,pid&lt;span class="o"&gt;=&lt;/span&gt;8699,fd&lt;span class="o"&gt;=&lt;/span&gt;4&lt;span class="o"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Best Practices
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Use Permanent Rules
&lt;/h3&gt;

&lt;p&gt;Always use the permanent flag when adding rules to ensure they survive reboots.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--add-port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;80/tcp &lt;span class="nt"&gt;--permanent&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Reload After Changes
&lt;/h3&gt;

&lt;p&gt;Always reload the firewall after making permanent changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;firewall-cmd &lt;span class="nt"&gt;--reload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Document Rules
&lt;/h3&gt;

&lt;p&gt;Maintain documentation of all firewall rules for audit purposes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test Before Production
&lt;/h3&gt;

&lt;p&gt;Test firewall rules in a staging environment before applying to production.&lt;/p&gt;

&lt;h3&gt;
  
  
  Monitor Logs
&lt;/h3&gt;

&lt;p&gt;Regularly check firewall logs for blocked traffic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; firewalld
&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/firewalld
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Automation Script
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Firewall Configuration Script"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=============================="&lt;/span&gt;

&lt;span class="c"&gt;# Install firewalld&lt;/span&gt;
yum &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; firewalld

&lt;span class="c"&gt;# Start and enable&lt;/span&gt;
systemctl start firewalld
systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;firewalld

&lt;span class="c"&gt;# Set zone&lt;/span&gt;
firewall-cmd &lt;span class="nt"&gt;--set-default-zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public

&lt;span class="c"&gt;# Allow port 80&lt;/span&gt;
firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--add-port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;80/tcp &lt;span class="nt"&gt;--permanent&lt;/span&gt;

&lt;span class="c"&gt;# Block port 8085&lt;/span&gt;
firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--remove-port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8085/tcp &lt;span class="nt"&gt;--permanent&lt;/span&gt;
firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--add-rich-rule&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'rule family="ipv4" port port="8085" protocol="tcp" drop'&lt;/span&gt; &lt;span class="nt"&gt;--permanent&lt;/span&gt;

&lt;span class="c"&gt;# Reload&lt;/span&gt;
firewall-cmd &lt;span class="nt"&gt;--reload&lt;/span&gt;

&lt;span class="c"&gt;# Configure Apache port&lt;/span&gt;
&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/^Listen 80/Listen 8085/'&lt;/span&gt; /etc/httpd/conf/httpd.conf

&lt;span class="c"&gt;# Start services&lt;/span&gt;
systemctl start httpd
systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;httpd
systemctl start nginx
systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;nginx

&lt;span class="c"&gt;# Verify&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Verification:"&lt;/span&gt;
firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--list-all&lt;/span&gt;
systemctl is-active httpd
systemctl is-active nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What Has Been Accomplished
&lt;/h3&gt;

&lt;p&gt;Firewalld has been successfully installed and configured on App Server 1. Port 80 has been allowed for Nginx traffic, and port 8085 has been blocked for Apache using a rich rule. The zone has been set to public, and all rules have been made permanent. Both Apache and Nginx services are running with Apache on port 8085 and Nginx on port 80.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Takeaways
&lt;/h3&gt;

&lt;p&gt;Firewalld provides a dynamic and flexible way to manage firewall rules. The public zone is the default and is suitable for public-facing services. Rich rules allow granular control over traffic. The permanent flag ensures rules persist across reboots. Port conflicts between services must be resolved by configuring services to use different ports.&lt;/p&gt;

&lt;h3&gt;
  
  
  Complete Solution Summary
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Firewall Rules&lt;/span&gt;
firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--add-port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;80/tcp &lt;span class="nt"&gt;--permanent&lt;/span&gt;
firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--remove-port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8085/tcp &lt;span class="nt"&gt;--permanent&lt;/span&gt;
firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--add-rich-rule&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'rule family="ipv4" port port="8085" protocol="tcp" drop'&lt;/span&gt; &lt;span class="nt"&gt;--permanent&lt;/span&gt;
firewall-cmd &lt;span class="nt"&gt;--reload&lt;/span&gt;

&lt;span class="c"&gt;# Service Configuration&lt;/span&gt;
&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/^Listen 80/Listen 8085/'&lt;/span&gt; /etc/httpd/conf/httpd.conf
systemctl start httpd
systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;httpd
systemctl start nginx
systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;nginx

&lt;span class="c"&gt;# Verification&lt;/span&gt;
firewall-cmd &lt;span class="nt"&gt;--zone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;public &lt;span class="nt"&gt;--list-all&lt;/span&gt;
systemctl is-active httpd
systemctl is-active nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>linux</category>
      <category>firewalld</category>
    </item>
    <item>
      <title>Day 75: Jenkins Slave Nodes</title>
      <dc:creator>Janak Shrestha</dc:creator>
      <pubDate>Tue, 15 Sep 2026 10:40:32 +0000</pubDate>
      <link>https://dev.to/janak0ff/day-75-jenkins-slave-nodes-2jja</link>
      <guid>https://dev.to/janak0ff/day-75-jenkins-slave-nodes-2jja</guid>
      <description>&lt;p&gt;The Nautilus DevOps team has installed and configured new Jenkins server in Stratos DC which they will use for CI/CD and for some automation tasks. There is a requirement to add all app servers as slave nodes in Jenkins so that they can perform tasks on these servers using Jenkins. Find below more details and accomplish the task accordingly.  &lt;/p&gt;

&lt;p&gt;Click on the &lt;code&gt;Jenkins&lt;/code&gt; button on the top bar to access the Jenkins UI. Login using username &lt;code&gt;admin&lt;/code&gt; and password &lt;code&gt;Adm!n321&lt;/code&gt;.  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Add all app servers as SSH build agent/slave nodes in Jenkins. Slave node name for &lt;code&gt;app server 1&lt;/code&gt;, &lt;code&gt;app server 2&lt;/code&gt; and &lt;code&gt;app server 3&lt;/code&gt; must be &lt;code&gt;App_server_1&lt;/code&gt;, &lt;code&gt;App_server_2&lt;/code&gt;, &lt;code&gt;App_server_3&lt;/code&gt; respectively.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add labels as below:  &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;App_server_1 : stapp01&lt;/code&gt;&lt;br&gt;&lt;br&gt;
&lt;code&gt;App_server_2 : stapp02&lt;/code&gt;&lt;br&gt;&lt;br&gt;
&lt;code&gt;App_server_3 : stapp03&lt;/code&gt;  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Remote root directory for &lt;code&gt;App_server_1&lt;/code&gt; must be &lt;code&gt;/home/tony/jenkins&lt;/code&gt;, for &lt;code&gt;App_server_2&lt;/code&gt; must be &lt;code&gt;/home/steve/jenkins&lt;/code&gt; and for &lt;code&gt;App_server_3&lt;/code&gt; must be &lt;code&gt;/home/banner/jenkins&lt;/code&gt;.  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Make sure slave nodes are online and working properly.  &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;Note:&lt;/code&gt;  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You might need to install some plugins and restart Jenkins service. So, we recommend clicking on &lt;code&gt;Restart Jenkins when installation is complete and no jobs are running&lt;/code&gt; on plugin installation/update page i.e &lt;code&gt;update centre&lt;/code&gt;. Also, Jenkins UI sometimes gets stuck when Jenkins service restarts in the back end. In this case, please make sure to refresh the UI page.
&lt;/li&gt;
&lt;li&gt;For these kind of scenarios requiring changes to be done in a web UI, please take screenshots so that you can share it with us for review in case your task is marked incomplete. You may also consider using a screen recording software such as loom.com to record and share your work.&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  Understanding Jenkins Master-Agent Architecture
&lt;/h2&gt;

&lt;p&gt;Jenkins uses a &lt;strong&gt;master-agent&lt;/strong&gt; architecture:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Master (Controller)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Schedules jobs, manages configuration, serves the UI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agent (Slave/Node)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Executes build tasks assigned by the master&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Why Use Slave Nodes?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Distributed Builds&lt;/strong&gt;: Run multiple jobs in parallel&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server-Specific Tasks&lt;/strong&gt;: Run jobs on specific servers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Load Distribution&lt;/strong&gt;: Reduce load on the master&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment Isolation&lt;/strong&gt;: Different environments for different builds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability&lt;/strong&gt;: Add more agents as needed&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Task Requirements
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Server&lt;/th&gt;
&lt;th&gt;Node Name&lt;/th&gt;
&lt;th&gt;Label&lt;/th&gt;
&lt;th&gt;Remote Root Directory&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;App Server 1&lt;/td&gt;
&lt;td&gt;App_server_1&lt;/td&gt;
&lt;td&gt;stapp01&lt;/td&gt;
&lt;td&gt;/home/tony/jenkins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App Server 2&lt;/td&gt;
&lt;td&gt;App_server_2&lt;/td&gt;
&lt;td&gt;stapp02&lt;/td&gt;
&lt;td&gt;/home/steve/jenkins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App Server 3&lt;/td&gt;
&lt;td&gt;App_server_3&lt;/td&gt;
&lt;td&gt;stapp03&lt;/td&gt;
&lt;td&gt;/home/banner/jenkins&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  Step-by-Step Implementation
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Step 1: Access Jenkins UI
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Click the &lt;strong&gt;Jenkins&lt;/strong&gt; button on the top bar&lt;/li&gt;
&lt;li&gt;Login with:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Username:&lt;/strong&gt; &lt;code&gt;admin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Password:&lt;/strong&gt; &lt;code&gt;Adm!n321&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Step 2: Install SSH Build Agents Plugin
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Manage Jenkins&lt;/strong&gt; → &lt;strong&gt;Plugins&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Available plugins&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Search for &lt;strong&gt;SSH Build Agents&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Install the plugin and restart Jenkins&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Select &lt;strong&gt;Restart Jenkins when installation is complete and no jobs are running&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 3: Generate SSH Keys on Jenkins Server
&lt;/h3&gt;

&lt;p&gt;The Jenkins job runs as the &lt;code&gt;jenkins&lt;/code&gt; user, so SSH keys must be generated for this 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="c"&gt;# SSH to Jenkins server&lt;/span&gt;
ssh jenkins@jenkins
&lt;span class="c"&gt;# Password: j@rv!s&lt;/span&gt;

&lt;span class="c"&gt;# Generate SSH key&lt;/span&gt;
ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; rsa &lt;span class="nt"&gt;-b&lt;/span&gt; 4096 &lt;span class="nt"&gt;-N&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; ~/.ssh/id_rsa

&lt;span class="c"&gt;# View the private key (needed for Jenkins credentials)&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.ssh/id_rsa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Generating public/private rsa key pair.
Your identification has been saved in /var/lib/jenkins/.ssh/id_rsa
Your public key has been saved in /var/lib/jenkins/.ssh/id_rsa.pub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Copy SSH Keys to All App Servers
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Copy public key to App Server 1&lt;/span&gt;
ssh-copy-id &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;StrictHostKeyChecking&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no tony@stapp01
&lt;span class="c"&gt;# Password: Ir0nM@n&lt;/span&gt;

&lt;span class="c"&gt;# Copy public key to App Server 2&lt;/span&gt;
ssh-copy-id &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;StrictHostKeyChecking&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no steve@stapp02
&lt;span class="c"&gt;# Password: Am3ric@&lt;/span&gt;

&lt;span class="c"&gt;# Copy public key to App Server 3&lt;/span&gt;
ssh-copy-id &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;StrictHostKeyChecking&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no banner@stapp03
&lt;span class="c"&gt;# Password: BigGr33n&lt;/span&gt;

&lt;span class="c"&gt;# Verify passwordless access&lt;/span&gt;
ssh &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;StrictHostKeyChecking&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no tony@stapp01 &lt;span class="nb"&gt;hostname
&lt;/span&gt;ssh &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;StrictHostKeyChecking&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no steve@stapp02 &lt;span class="nb"&gt;hostname
&lt;/span&gt;ssh &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;StrictHostKeyChecking&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no banner@stapp03 &lt;span class="nb"&gt;hostname&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;stapp01
stapp02
stapp03
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Install Java 21 on All App Servers
&lt;/h3&gt;

&lt;p&gt;Jenkins agents require Java 17 or higher. Java 11 will cause an &lt;code&gt;UnsupportedClassVersionError&lt;/code&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="c"&gt;# Install Java 21 on all servers&lt;/span&gt;
ssh tony@stapp01 &lt;span class="s2"&gt;"sudo yum install -y java-21-openjdk"&lt;/span&gt;
ssh steve@stapp02 &lt;span class="s2"&gt;"sudo yum install -y java-21-openjdk"&lt;/span&gt;
ssh banner@stapp03 &lt;span class="s2"&gt;"sudo yum install -y java-21-openjdk"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 6: Register and Set Java 21 as Default
&lt;/h3&gt;

&lt;p&gt;After installing Java 21, you need to register it as an alternative and set it as 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="c"&gt;# Register Java 21 as an alternative&lt;/span&gt;
ssh tony@stapp01 &lt;span class="s2"&gt;"echo 'Ir0nM@n' | sudo -S alternatives --install /usr/bin/java java /usr/lib/jvm/java-21-openjdk-21.0.12.1.1-2.1.el9.x86_64/bin/java 2000"&lt;/span&gt;
ssh steve@stapp02 &lt;span class="s2"&gt;"echo 'Am3ric@' | sudo -S alternatives --install /usr/bin/java java /usr/lib/jvm/java-21-openjdk-21.0.12.1.1-2.1.el9.x86_64/bin/java 2000"&lt;/span&gt;
ssh banner@stapp03 &lt;span class="s2"&gt;"echo 'BigGr33n' | sudo -S alternatives --install /usr/bin/java java /usr/lib/jvm/java-21-openjdk-21.0.12.1.1-2.1.el9.x86_64/bin/java 2000"&lt;/span&gt;

&lt;span class="c"&gt;# Set Java 21 as default&lt;/span&gt;
ssh tony@stapp01 &lt;span class="s2"&gt;"echo 'Ir0nM@n' | sudo -S alternatives --set java /usr/lib/jvm/java-21-openjdk-21.0.12.1.1-2.1.el9.x86_64/bin/java"&lt;/span&gt;
ssh steve@stapp02 &lt;span class="s2"&gt;"echo 'Am3ric@' | sudo -S alternatives --set java /usr/lib/jvm/java-21-openjdk-21.0.12.1.1-2.1.el9.x86_64/bin/java"&lt;/span&gt;
ssh banner@stapp03 &lt;span class="s2"&gt;"echo 'BigGr33n' | sudo -S alternatives --set java /usr/lib/jvm/java-21-openjdk-21.0.12.1.1-2.1.el9.x86_64/bin/java"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Verify:&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;ssh tony@stapp01 &lt;span class="s2"&gt;"java -version"&lt;/span&gt;
ssh steve@stapp02 &lt;span class="s2"&gt;"java -version"&lt;/span&gt;
ssh banner@stapp03 &lt;span class="s2"&gt;"java -version"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;openjdk version "21.0.12.1" 2026-08-18 LTS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 7: Create Remote Root Directories
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;StrictHostKeyChecking&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no tony@stapp01 &lt;span class="s2"&gt;"mkdir -p /home/tony/jenkins"&lt;/span&gt;
ssh &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;StrictHostKeyChecking&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no steve@stapp02 &lt;span class="s2"&gt;"mkdir -p /home/steve/jenkins"&lt;/span&gt;
ssh &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;StrictHostKeyChecking&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no banner@stapp03 &lt;span class="s2"&gt;"mkdir -p /home/banner/jenkins"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 8: Get IP Addresses for Hostname Resolution
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh tony@stapp01 &lt;span class="s2"&gt;"hostname -I"&lt;/span&gt;
ssh steve@stapp02 &lt;span class="s2"&gt;"hostname -I"&lt;/span&gt;
ssh banner@stapp03 &lt;span class="s2"&gt;"hostname -I"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10.244.81.4
10.244.189.193
10.244.195.103
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 9: Add SSH Credentials in Jenkins
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Manage Jenkins&lt;/strong&gt; → &lt;strong&gt;Credentials&lt;/strong&gt; → &lt;strong&gt;System&lt;/strong&gt; → &lt;strong&gt;Global credentials&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Add Credentials&lt;/strong&gt; for each server:&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  App Server 1 Credentials:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Kind:&lt;/strong&gt; SSH Username with private key&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scope:&lt;/strong&gt; Global&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ID:&lt;/strong&gt; &lt;code&gt;stapp01-credentials&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Username:&lt;/strong&gt; &lt;code&gt;tony&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Private Key:&lt;/strong&gt; Enter directly (paste the private key from &lt;code&gt;~/.ssh/id_rsa&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;OK&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  App Server 2 Credentials:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Kind:&lt;/strong&gt; SSH Username with private key&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ID:&lt;/strong&gt; &lt;code&gt;stapp02-credentials&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Username:&lt;/strong&gt; &lt;code&gt;steve&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Private Key:&lt;/strong&gt; Enter directly&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;OK&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  App Server 3 Credentials:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Kind:&lt;/strong&gt; SSH Username with private key&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ID:&lt;/strong&gt; &lt;code&gt;stapp03-credentials&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Username:&lt;/strong&gt; &lt;code&gt;banner&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Private Key:&lt;/strong&gt; Enter directly&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;OK&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 10: Add Slave Nodes in Jenkins
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Add App_server_1 Node:
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Manage Jenkins&lt;/strong&gt; → &lt;strong&gt;Nodes&lt;/strong&gt; → &lt;strong&gt;New Node&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Enter name: &lt;code&gt;App_server_1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Permanent Agent&lt;/strong&gt; → &lt;strong&gt;OK&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Configure:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;App_server_1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remote root directory:&lt;/strong&gt; &lt;code&gt;/home/tony/jenkins&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Labels:&lt;/strong&gt; &lt;code&gt;stapp01&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Usage:&lt;/strong&gt; Use this node as much as possible&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Launch method:&lt;/strong&gt; Launch agents via SSH&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Host:&lt;/strong&gt; &lt;code&gt;stapp01.stratos.xfusioncorp.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Credentials:&lt;/strong&gt; &lt;code&gt;stapp01-credentials&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Host Key Verification Strategy:&lt;/strong&gt; Non-verifying Verification Strategy&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Advanced&lt;/strong&gt; and set:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;JavaPath:&lt;/strong&gt; &lt;code&gt;/usr/lib/jvm/java-21-openjdk-21.0.12.1.1-2.1.el9.x86_64/bin/java&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Save&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Add App_server_2 Node:
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Manage Jenkins&lt;/strong&gt; → &lt;strong&gt;Nodes&lt;/strong&gt; → &lt;strong&gt;New Node&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Enter name: &lt;code&gt;App_server_2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Configure:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;App_server_2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remote root directory:&lt;/strong&gt; &lt;code&gt;/home/steve/jenkins&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Labels:&lt;/strong&gt; &lt;code&gt;stapp02&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Host:&lt;/strong&gt; &lt;code&gt;stapp02.stratos.xfusioncorp.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Credentials:&lt;/strong&gt; &lt;code&gt;stapp02-credentials&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JavaPath:&lt;/strong&gt; &lt;code&gt;/usr/lib/jvm/java-21-openjdk-21.0.12.1.1-2.1.el9.x86_64/bin/java&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Save&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Add App_server_3 Node:
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Manage Jenkins&lt;/strong&gt; → &lt;strong&gt;Nodes&lt;/strong&gt; → &lt;strong&gt;New Node&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Enter name: &lt;code&gt;App_server_3&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Configure:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;App_server_3&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remote root directory:&lt;/strong&gt; &lt;code&gt;/home/banner/jenkins&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Labels:&lt;/strong&gt; &lt;code&gt;stapp03&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Host:&lt;/strong&gt; &lt;code&gt;10.244.195.103&lt;/code&gt; (use IP address since hostname doesn't resolve)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Credentials:&lt;/strong&gt; &lt;code&gt;stapp03-credentials&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JavaPath:&lt;/strong&gt; &lt;code&gt;/usr/lib/jvm/java-21-openjdk-21.0.12.1.1-2.1.el9.x86_64/bin/java&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Save&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 11: Verify All Nodes Are Online
&lt;/h3&gt;

&lt;p&gt;Go to &lt;strong&gt;Manage Jenkins&lt;/strong&gt; → &lt;strong&gt;Nodes&lt;/strong&gt;. All three nodes should show &lt;strong&gt;Online&lt;/strong&gt; status with a green checkmark.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr3a2ga92aqa2epu2xpzi.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr3a2ga92aqa2epu2xpzi.png" alt="Nodes online" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;




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

&lt;h3&gt;
  
  
  Issue 1: UnsupportedClassVersionError
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;UnsupportedClassVersionError&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;hudson&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;remoting&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nc"&gt;Launcher&lt;/span&gt; &lt;span class="n"&gt;has&lt;/span&gt; &lt;span class="n"&gt;been&lt;/span&gt; &lt;span class="n"&gt;compiled&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;more&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="nc"&gt;Java&lt;/span&gt; &lt;span class="nf"&gt;Runtime&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;file&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="mf"&gt;61.0&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="nc"&gt;Java&lt;/span&gt; &lt;span class="nc"&gt;Runtime&lt;/span&gt; &lt;span class="n"&gt;only&lt;/span&gt; &lt;span class="n"&gt;recognizes&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;file&lt;/span&gt; &lt;span class="n"&gt;versions&lt;/span&gt; &lt;span class="n"&gt;up&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="mf"&gt;55.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; Jenkins agent requires Java 17+ but the server has Java 11.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Install Java 21 and set it as 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="nb"&gt;sudo &lt;/span&gt;yum &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; java-21-openjdk
&lt;span class="nb"&gt;sudo &lt;/span&gt;alternatives &lt;span class="nt"&gt;--install&lt;/span&gt; /usr/bin/java java /usr/lib/jvm/java-21-openjdk-21.0.12.1.1-2.1.el9.x86_64/bin/java 2000
&lt;span class="nb"&gt;sudo &lt;/span&gt;alternatives &lt;span class="nt"&gt;--set&lt;/span&gt; java /usr/lib/jvm/java-21-openjdk-21.0.12.1.1-2.1.el9.x86_64/bin/java
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Issue 2: "has not been configured as an alternative"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/usr/lib/jvm/java-21-openjdk-21.0.12.1.1-2.el9.x86_64/bin/java has not been configured as an alternative for java
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; Java 21 was not registered as an alternative before using &lt;code&gt;--set&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Use &lt;code&gt;--install&lt;/code&gt; first, then &lt;code&gt;--set&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Issue 3: Wrong Java Path
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt; Path uses &lt;code&gt;-2.el9.x86_64&lt;/code&gt; but the actual path is &lt;code&gt;-2.1.el9.x86_64&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Verify the exact path:&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="nb"&gt;ls&lt;/span&gt; /usr/lib/jvm/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Issue 4: Name or Service Not Known
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;stapp03.stratos.xfusioncorp.com: Name or service not known
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; The hostname cannot be resolved from the Jenkins server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Use the IP address directly in the node configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Issue 5: Jenkins User Cannot Use Sudo
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jenkins is not in the sudoers file.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; The &lt;code&gt;jenkins&lt;/code&gt; user doesn't have sudo privileges.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Use IP addresses in node configuration instead of editing &lt;code&gt;/etc/hosts&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Configuration Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Node Name&lt;/th&gt;
&lt;th&gt;Label&lt;/th&gt;
&lt;th&gt;Remote Root Directory&lt;/th&gt;
&lt;th&gt;Host&lt;/th&gt;
&lt;th&gt;JavaPath&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;App_server_1&lt;/td&gt;
&lt;td&gt;stapp01&lt;/td&gt;
&lt;td&gt;/home/tony/jenkins&lt;/td&gt;
&lt;td&gt;stapp01.stratos.xfusioncorp.com&lt;/td&gt;
&lt;td&gt;Java 21&lt;/td&gt;
&lt;td&gt;Online&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App_server_2&lt;/td&gt;
&lt;td&gt;stapp02&lt;/td&gt;
&lt;td&gt;/home/steve/jenkins&lt;/td&gt;
&lt;td&gt;stapp02.stratos.xfusioncorp.com&lt;/td&gt;
&lt;td&gt;Java 21&lt;/td&gt;
&lt;td&gt;Online&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App_server_3&lt;/td&gt;
&lt;td&gt;stapp03&lt;/td&gt;
&lt;td&gt;/home/banner/jenkins&lt;/td&gt;
&lt;td&gt;10.244.195.103&lt;/td&gt;
&lt;td&gt;Java 21&lt;/td&gt;
&lt;td&gt;Online&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Key Learnings
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Java Version Compatibility&lt;/strong&gt;: Jenkins agents require Java 17 or higher. Java 21 is recommended.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alternatives Management&lt;/strong&gt;: Use &lt;code&gt;alternatives --install&lt;/code&gt; to register a new Java version, then &lt;code&gt;alternatives --set&lt;/code&gt; to make it default.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;JavaPath in Node Configuration&lt;/strong&gt;: For non-interactive SSH sessions, always specify the full Java binary path in the agent configuration.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hostname Resolution&lt;/strong&gt;: If a hostname cannot be resolved, use the IP address directly in the node configuration.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Jenkins User Permissions&lt;/strong&gt;: The &lt;code&gt;jenkins&lt;/code&gt; user may not have sudo access, so plan accordingly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SSH Key Setup&lt;/strong&gt;: Generate SSH keys for the &lt;code&gt;jenkins&lt;/code&gt; user (not root) and copy them to all target servers.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Benefits of Distributed Builds
&lt;/h2&gt;

&lt;p&gt;With slave nodes configured, you can now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Run jobs in parallel&lt;/strong&gt; across multiple servers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Target specific servers&lt;/strong&gt; using labels (&lt;code&gt;stapp01&lt;/code&gt;, &lt;code&gt;stapp02&lt;/code&gt;, &lt;code&gt;stapp03&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduce load&lt;/strong&gt; on the Jenkins master&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scale horizontally&lt;/strong&gt; by adding more agents&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Isolate environments&lt;/strong&gt; for different build types&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example: Using Node Labels in a Job
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'stapp01'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Deploy'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'echo "Deploying to App Server 1"'&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this challenge, we successfully:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generated SSH keys on the Jenkins server&lt;/li&gt;
&lt;li&gt;Copied SSH keys to all app servers&lt;/li&gt;
&lt;li&gt;Installed Java 21 on all app servers&lt;/li&gt;
&lt;li&gt;Registered and set Java 21 as the default version&lt;/li&gt;
&lt;li&gt;Created remote root directories on all app servers&lt;/li&gt;
&lt;li&gt;Added SSH credentials in Jenkins&lt;/li&gt;
&lt;li&gt;Added all three app servers as Jenkins slave nodes&lt;/li&gt;
&lt;li&gt;Configured labels and JavaPath for each node&lt;/li&gt;
&lt;li&gt;Verified all nodes are online and working&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This setup enables Jenkins to distribute build tasks across all app servers, improving performance and allowing server-specific automation tasks. The slave nodes are now ready to execute jobs assigned to them.&lt;/p&gt;

</description>
      <category>jenkins</category>
      <category>devops</category>
    </item>
    <item>
      <title>DNS Troubleshooting</title>
      <dc:creator>Janak Shrestha</dc:creator>
      <pubDate>Tue, 15 Sep 2026 07:00:09 +0000</pubDate>
      <link>https://dev.to/janak0ff/dns-troubleshooting-4n2d</link>
      <guid>https://dev.to/janak0ff/dns-troubleshooting-4n2d</guid>
      <description>&lt;p&gt;The system admins team of xFusionCorp Industries has noticed intermittent issues with DNS resolution in several apps . &lt;code&gt;App Server 2&lt;/code&gt; in &lt;code&gt;Stratos Datacenter&lt;/code&gt; is having some DNS resolution issues, so we want to add some additional DNS nameservers on this server.  &lt;/p&gt;

&lt;p&gt;As a temporary fix we have decided to go with Google public DNS (ipv4). Please make appropriate changes on this server.&lt;/p&gt;




&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Connect to App Server 2 (stapp02)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh steve@stapp02
&lt;span class="c"&gt;# Password: Am3ric@&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Switch to root
&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;su -
&lt;span class="c"&gt;# Password: Am3ric@&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Check current DNS configuration
&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;cat&lt;/span&gt; /etc/resolv.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Add Google Public DNS nameservers
&lt;/h3&gt;

&lt;p&gt;Google Public DNS IPv4 addresses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;8.8.8.8&lt;/code&gt; (Primary)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;8.8.4.4&lt;/code&gt; (Secondary)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Add Google DNS servers to resolv.conf&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"nameserver 8.8.8.8"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/resolv.conf
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"nameserver 8.8.4.4"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/resolv.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Verify the configuration
&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;cat&lt;/span&gt; /etc/resolv.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 6: Test DNS resolution
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Test with ping&lt;/span&gt;
ping &lt;span class="nt"&gt;-c&lt;/span&gt; 2 google.com

&lt;span class="c"&gt;# Test with nslookup&lt;/span&gt;
nslookup google.com

&lt;span class="c"&gt;# Test with dig&lt;/span&gt;
dig google.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  One-Line Commands
&lt;/h2&gt;

&lt;h3&gt;
  
  
  From jump host with password:
&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;echo&lt;/span&gt; &lt;span class="s1"&gt;'Am3ric@'&lt;/span&gt; | ssh steve@stapp02 &lt;span class="s2"&gt;"sudo -S bash -c 'echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;nameserver 8.8.8.8&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &amp;gt;&amp;gt; /etc/resolv.conf &amp;amp;&amp;amp; echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;nameserver 8.8.4.4&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &amp;gt;&amp;gt; /etc/resolv.conf &amp;amp;&amp;amp; cat /etc/resolv.conf'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using heredoc (Recommended):
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh steve@stapp02 &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
echo 'Am3ric@' | sudo -S bash -c '
echo "=== Current DNS configuration ==="
cat /etc/resolv.conf

echo ""
echo "=== Adding Google Public DNS ==="
echo "nameserver 8.8.8.8" &amp;gt;&amp;gt; /etc/resolv.conf
echo "nameserver 8.8.4.4" &amp;gt;&amp;gt; /etc/resolv.conf

echo ""
echo "=== Updated DNS configuration ==="
cat /etc/resolv.conf

echo ""
echo "=== Testing DNS resolution ==="
ping -c 2 google.com
'
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step-by-Step Interactive Commands
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Connect to stapp02&lt;/span&gt;
ssh steve@stapp02
&lt;span class="c"&gt;# Password: Am3ric@&lt;/span&gt;

&lt;span class="c"&gt;# Become root&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;su -
&lt;span class="c"&gt;# Password: Am3ric@&lt;/span&gt;

&lt;span class="c"&gt;# Step 1: Check current DNS configuration&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== Current DNS configuration ==="&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/resolv.conf

&lt;span class="c"&gt;# Step 2: Backup existing resolv.conf (optional but recommended)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== Backing up resolv.conf ==="&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; /etc/resolv.conf /etc/resolv.conf.backup

&lt;span class="c"&gt;# Step 3: Add Google DNS servers&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== Adding Google Public DNS ==="&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"nameserver 8.8.8.8"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/resolv.conf
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"nameserver 8.8.4.4"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/resolv.conf

&lt;span class="c"&gt;# Step 4: Verify the changes&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== Updated DNS configuration ==="&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/resolv.conf

&lt;span class="c"&gt;# Step 5: Test DNS resolution&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== Testing DNS resolution ==="&lt;/span&gt;
ping &lt;span class="nt"&gt;-c&lt;/span&gt; 2 google.com
nslookup google.com

&lt;span class="c"&gt;# Exit&lt;/span&gt;
&lt;span class="nb"&gt;exit
exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Expected Output
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;root@stapp02 ~]# &lt;span class="nb"&gt;cat&lt;/span&gt; /etc/resolv.conf
nameserver 10.0.0.2

&lt;span class="o"&gt;[&lt;/span&gt;root@stapp02 ~]# &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"nameserver 8.8.8.8"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/resolv.conf
&lt;span class="o"&gt;[&lt;/span&gt;root@stapp02 ~]# &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"nameserver 8.8.4.4"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/resolv.conf

&lt;span class="o"&gt;[&lt;/span&gt;root@stapp02 ~]# &lt;span class="nb"&gt;cat&lt;/span&gt; /etc/resolv.conf
nameserver 10.0.0.2
nameserver 8.8.8.8
nameserver 8.8.4.4

&lt;span class="o"&gt;[&lt;/span&gt;root@stapp02 ~]# ping &lt;span class="nt"&gt;-c&lt;/span&gt; 2 google.com
PING google.com &lt;span class="o"&gt;(&lt;/span&gt;142.250.192.46&lt;span class="o"&gt;)&lt;/span&gt; 56&lt;span class="o"&gt;(&lt;/span&gt;84&lt;span class="o"&gt;)&lt;/span&gt; bytes of data.
64 bytes from 142.250.192.46: &lt;span class="nv"&gt;icmp_seq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;115 &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10.2 ms
64 bytes from 142.250.192.46: &lt;span class="nv"&gt;icmp_seq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="nv"&gt;ttl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;115 &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10.5 ms

&lt;span class="nt"&gt;---&lt;/span&gt; google.com ping statistics &lt;span class="nt"&gt;---&lt;/span&gt;
2 packets transmitted, 2 received, 0% packet loss, &lt;span class="nb"&gt;time &lt;/span&gt;1001ms

&lt;span class="o"&gt;[&lt;/span&gt;root@stapp02 ~]# nslookup google.com
Server:         8.8.8.8
Address:        8.8.8.8#53

Non-authoritative answer:
Name:   google.com
Address: 142.250.192.46
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Alternative Methods
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Method 1: Using tee command
&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;echo&lt;/span&gt; &lt;span class="s2"&gt;"nameserver 8.8.8.8"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/resolv.conf
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"nameserver 8.8.4.4"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/resolv.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Method 2: Using cat with heredoc
&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;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/resolv.conf &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;
nameserver 8.8.8.8
nameserver 8.8.4.4
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Method 3: Using sed to replace existing nameservers
&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;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/^nameserver.*/nameserver 8.8.8.8/'&lt;/span&gt; /etc/resolv.conf
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"nameserver 8.8.4.4"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/resolv.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Method 4: Using nmcli (NetworkManager)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check connection name&lt;/span&gt;
nmcli connection show

&lt;span class="c"&gt;# Add DNS servers&lt;/span&gt;
nmcli connection modify &amp;lt;connection-name&amp;gt; ipv4.dns &lt;span class="s2"&gt;"8.8.8.8 8.8.4.4"&lt;/span&gt;
nmcli connection up &amp;lt;connection-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Method 5: Using nmtui (Text User Interface)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nmtui
&lt;span class="c"&gt;# Navigate to Edit a connection&lt;/span&gt;
&lt;span class="c"&gt;# Select the connection&lt;/span&gt;
&lt;span class="c"&gt;# Add DNS servers&lt;/span&gt;
&lt;span class="c"&gt;# Save and exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification Commands
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Check resolv.conf&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/resolv.conf

&lt;span class="c"&gt;# 2. Test DNS resolution with ping&lt;/span&gt;
ping &lt;span class="nt"&gt;-c&lt;/span&gt; 2 google.com

&lt;span class="c"&gt;# 3. Test with nslookup&lt;/span&gt;
nslookup google.com

&lt;span class="c"&gt;# 4. Test with dig&lt;/span&gt;
dig google.com

&lt;span class="c"&gt;# 5. Test with host&lt;/span&gt;
host google.com

&lt;span class="c"&gt;# 6. Check DNS resolution order&lt;/span&gt;
getent hosts google.com

&lt;span class="c"&gt;# 7. Check if DNS servers are reachable&lt;/span&gt;
ping &lt;span class="nt"&gt;-c&lt;/span&gt; 2 8.8.8.8
ping &lt;span class="nt"&gt;-c&lt;/span&gt; 2 8.8.4.4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Understanding Google Public DNS
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Google Public DNS IPv4 Addresses
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Address&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;8.8.8.8&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Primary DNS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;8.8.4.4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Secondary DNS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Google Public DNS IPv6 Addresses
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Address&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;2001:4860:4860::8888&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Primary DNS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;2001:4860:4860::8844&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Secondary DNS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Why Use Google Public DNS
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reliability&lt;/strong&gt;: Highly available and globally distributed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speed&lt;/strong&gt;: Fast resolution times&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: DNSSEC validation and protection against DNS poisoning&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privacy&lt;/strong&gt;: No logging of personal information&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free&lt;/strong&gt;: Available to everyone at no cost&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Important Notes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  resolv.conf May Be Overwritten
&lt;/h3&gt;

&lt;p&gt;On systems using NetworkManager or DHCP, &lt;code&gt;/etc/resolv.conf&lt;/code&gt; may be automatically overwritten. To make changes permanent, consider these approaches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option 1: Use NetworkManager&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;nmcli connection modify &amp;lt;connection-name&amp;gt; ipv4.dns &lt;span class="s2"&gt;"8.8.8.8 8.8.4.4"&lt;/span&gt;
nmcli connection modify &amp;lt;connection-name&amp;gt; ipv4.ignore-auto-dns &lt;span class="nb"&gt;yes
&lt;/span&gt;nmcli connection up &amp;lt;connection-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Option 2: Make resolv.conf immutable&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;chattr +i /etc/resolv.conf
&lt;span class="c"&gt;# To undo: chattr -i /etc/resolv.conf&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Option 3: Configure in network interface file&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="c"&gt;# For RHEL/CentOS&lt;/span&gt;
vi /etc/sysconfig/network-scripts/ifcfg-&amp;lt;interface&amp;gt;
&lt;span class="c"&gt;# Add: DNS1=8.8.8.8&lt;/span&gt;
&lt;span class="c"&gt;# Add: DNS2=8.8.4.4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;"Permission denied" when editing resolv.conf&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="c"&gt;# Use sudo or become root&lt;/span&gt;
   &lt;span class="nb"&gt;sudo &lt;/span&gt;su -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;DNS still not resolving&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="c"&gt;# Check if DNS servers are reachable&lt;/span&gt;
   ping &lt;span class="nt"&gt;-c&lt;/span&gt; 2 8.8.8.8

   &lt;span class="c"&gt;# Check network connectivity&lt;/span&gt;
   ping &lt;span class="nt"&gt;-c&lt;/span&gt; 2 8.8.4.4

   &lt;span class="c"&gt;# Restart network service&lt;/span&gt;
   systemctl restart NetworkManager
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;resolv.conf changes not persisting&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="c"&gt;# Check if NetworkManager is managing resolv.conf&lt;/span&gt;
   &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /etc/resolv.conf

   &lt;span class="c"&gt;# If symlink to NetworkManager, configure via nmcli&lt;/span&gt;
   nmcli connection modify &amp;lt;connection&amp;gt; ipv4.dns &lt;span class="s2"&gt;"8.8.8.8 8.8.4.4"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;DNS resolution slow&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="c"&gt;# Check which DNS server is being used&lt;/span&gt;
   nslookup google.com

   &lt;span class="c"&gt;# Check DNS response time&lt;/span&gt;
   dig google.com | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"Query time"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Cannot reach Google DNS&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="c"&gt;# Check firewall rules&lt;/span&gt;
   firewall-cmd &lt;span class="nt"&gt;--list-all&lt;/span&gt;

   &lt;span class="c"&gt;# Allow DNS traffic&lt;/span&gt;
   firewall-cmd &lt;span class="nt"&gt;--add-service&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dns &lt;span class="nt"&gt;--permanent&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;
  
  
  Complete Script (Run from Jump Host)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"========================================="&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Configuring Google Public DNS"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"========================================="&lt;/span&gt;

ssh steve@stapp02 &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
echo 'Am3ric@' | sudo -S bash -c '
echo ""
echo "=== Current DNS Configuration ==="
cat /etc/resolv.conf

echo ""
echo "=== Backing up resolv.conf ==="
cp /etc/resolv.conf /etc/resolv.conf.backup

echo ""
echo "=== Adding Google Public DNS ==="
echo "nameserver 8.8.8.8" &amp;gt;&amp;gt; /etc/resolv.conf
echo "nameserver 8.8.4.4" &amp;gt;&amp;gt; /etc/resolv.conf

echo ""
echo "=== Updated DNS Configuration ==="
cat /etc/resolv.conf

echo ""
echo "=== Testing DNS Resolution ==="
ping -c 2 google.com

echo ""
echo "========================================="
echo "✅ Google Public DNS configured successfully!"
echo "📌 Primary DNS: 8.8.8.8"
echo "📌 Secondary DNS: 8.8.4.4"
echo "========================================="
'
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Google Public DNS added&lt;/strong&gt;: &lt;code&gt;8.8.8.8&lt;/code&gt; and &lt;code&gt;8.8.4.4&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Configuration file&lt;/strong&gt;: &lt;code&gt;/etc/resolv.conf&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;DNS resolution tested&lt;/strong&gt;: Working correctly&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Backup created&lt;/strong&gt;: &lt;code&gt;/etc/resolv.conf.backup&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The DNS configuration on App Server 2 has been updated to use Google Public DNS as a temporary fix for the intermittent DNS resolution issues.&lt;/p&gt;

</description>
      <category>linux</category>
    </item>
    <item>
      <title>Linux Configure sudo</title>
      <dc:creator>Janak Shrestha</dc:creator>
      <pubDate>Tue, 15 Sep 2026 06:53:59 +0000</pubDate>
      <link>https://dev.to/janak0ff/linux-configure-sudo-8li</link>
      <guid>https://dev.to/janak0ff/linux-configure-sudo-8li</guid>
      <description>&lt;p&gt;We have some users on all app servers in&amp;nbsp;&lt;code&gt;Stratos Datacenter&lt;/code&gt;. Some of them have been assigned some new roles and responsibilities, therefore their users need to be upgraded with sudo access so that they can perform admin level tasks.  &lt;/p&gt;

&lt;p&gt;a. Provide sudo access to user&amp;nbsp;&lt;code&gt;siva&lt;/code&gt;&amp;nbsp;on all app servers.&lt;br&gt;&lt;br&gt;
b. Make sure you have set up password-less sudo for the user.&lt;/p&gt;




&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Connect to Each App Server and Configure sudo
&lt;/h3&gt;

&lt;h4&gt;
  
  
  App Server 1 (stapp01) - User: tony
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh tony@stapp01
&lt;span class="c"&gt;# Password: Ir0nM@n&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;su -
&lt;span class="c"&gt;# Password: Ir0nM@n&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"siva ALL=(ALL) NOPASSWD: ALL"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /etc/sudoers.d/siva
&lt;span class="nb"&gt;chmod &lt;/span&gt;440 /etc/sudoers.d/siva
&lt;span class="nb"&gt;exit
exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  App Server 2 (stapp02) - User: steve
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh steve@stapp02
&lt;span class="c"&gt;# Password: Am3ric@&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;su -
&lt;span class="c"&gt;# Password: Am3ric@&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"siva ALL=(ALL) NOPASSWD: ALL"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /etc/sudoers.d/siva
&lt;span class="nb"&gt;chmod &lt;/span&gt;440 /etc/sudoers.d/siva
&lt;span class="nb"&gt;exit
exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  App Server 3 (stapp03) - User: banner
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh banner@stapp03
&lt;span class="c"&gt;# Password: BigGr33n&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;su -
&lt;span class="c"&gt;# Password: BigGr33n&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"siva ALL=(ALL) NOPASSWD: ALL"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /etc/sudoers.d/siva
&lt;span class="nb"&gt;chmod &lt;/span&gt;440 /etc/sudoers.d/siva
&lt;span class="nb"&gt;exit
exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  One-Line Commands
&lt;/h2&gt;

&lt;h3&gt;
  
  
  App Server 1 (stapp01):
&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;echo&lt;/span&gt; &lt;span class="s1"&gt;'Ir0nM@n'&lt;/span&gt; | ssh tony@stapp01 &lt;span class="s2"&gt;"sudo -S bash -c 'echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;siva ALL=(ALL) NOPASSWD: ALL&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &amp;gt; /etc/sudoers.d/siva &amp;amp;&amp;amp; chmod 440 /etc/sudoers.d/siva'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  App Server 2 (stapp02):
&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;echo&lt;/span&gt; &lt;span class="s1"&gt;'Am3ric@'&lt;/span&gt; | ssh steve@stapp02 &lt;span class="s2"&gt;"sudo -S bash -c 'echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;siva ALL=(ALL) NOPASSWD: ALL&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &amp;gt; /etc/sudoers.d/siva &amp;amp;&amp;amp; chmod 440 /etc/sudoers.d/siva'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  App Server 3 (stapp03):
&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;echo&lt;/span&gt; &lt;span class="s1"&gt;'BigGr33n'&lt;/span&gt; | ssh banner@stapp03 &lt;span class="s2"&gt;"sudo -S bash -c 'echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;siva ALL=(ALL) NOPASSWD: ALL&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &amp;gt; /etc/sudoers.d/siva &amp;amp;&amp;amp; chmod 440 /etc/sudoers.d/siva'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using Heredoc (Recommended)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  App Server 1:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh tony@stapp01 &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
echo 'Ir0nM@n' | sudo -S bash -c '
echo "=== Configuring sudo for siva on stapp01 ==="
echo "siva ALL=(ALL) NOPASSWD: ALL" &amp;gt; /etc/sudoers.d/siva
chmod 440 /etc/sudoers.d/siva
echo "Verification:"
cat /etc/sudoers.d/siva
ls -la /etc/sudoers.d/siva
visudo -c
'
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  App Server 2:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh steve@stapp02 &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
echo 'Am3ric@' | sudo -S bash -c '
echo "=== Configuring sudo for siva on stapp02 ==="
echo "siva ALL=(ALL) NOPASSWD: ALL" &amp;gt; /etc/sudoers.d/siva
chmod 440 /etc/sudoers.d/siva
echo "Verification:"
cat /etc/sudoers.d/siva
ls -la /etc/sudoers.d/siva
visudo -c
'
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  App Server 3:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh banner@stapp03 &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
echo 'BigGr33n' | sudo -S bash -c '
echo "=== Configuring sudo for siva on stapp03 ==="
echo "siva ALL=(ALL) NOPASSWD: ALL" &amp;gt; /etc/sudoers.d/siva
chmod 440 /etc/sudoers.d/siva
echo "Verification:"
cat /etc/sudoers.d/siva
ls -la /etc/sudoers.d/siva
visudo -c
'
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Automated Script (Run from Jump Host)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"========================================="&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Configuring Password-less sudo for siva"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"========================================="&lt;/span&gt;

&lt;span class="nb"&gt;declare&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="nv"&gt;SERVERS&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
    &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"stapp01"&lt;/span&gt;&lt;span class="o"&gt;]=&lt;/span&gt;&lt;span class="s2"&gt;"tony:Ir0nM@n"&lt;/span&gt;
    &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"stapp02"&lt;/span&gt;&lt;span class="o"&gt;]=&lt;/span&gt;&lt;span class="s2"&gt;"steve:Am3ric@"&lt;/span&gt;
    &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"stapp03"&lt;/span&gt;&lt;span class="o"&gt;]=&lt;/span&gt;&lt;span class="s2"&gt;"banner:BigGr33n"&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;server &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="p"&gt;!SERVERS[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;':'&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; user pass &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SERVERS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$server&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== &lt;/span&gt;&lt;span class="nv"&gt;$server&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="s2"&gt;) ==="&lt;/span&gt;

    ssh &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="nv"&gt;$server&lt;/span&gt;&lt;span class="s2"&gt;"&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;
echo '&lt;/span&gt;&lt;span class="nv"&gt;$pass&lt;/span&gt;&lt;span class="sh"&gt;' | sudo -S bash -c '
echo "Configuring sudo for siva..."
echo "siva ALL=(ALL) NOPASSWD: ALL" &amp;gt; /etc/sudoers.d/siva
chmod 440 /etc/sudoers.d/siva

echo "Verification:"
echo "  File: &lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="sh"&gt;(cat /etc/sudoers.d/siva)"
echo "  Permissions: &lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="sh"&gt;(ls -la /etc/sudoers.d/siva | awk &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="sh"&gt;{print &lt;/span&gt;&lt;span class="se"&gt;\\\$&lt;/span&gt;&lt;span class="sh"&gt;1}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="sh"&gt;)"
echo "  Syntax: &lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="sh"&gt;(visudo -c 2&amp;gt;&amp;amp;1 | head -1)"
'
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;span class="k"&gt;done

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"========================================="&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ Password-less sudo configured for siva"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"========================================="&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step-by-Step Interactive Commands
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Connect to stapp01&lt;/span&gt;
ssh tony@stapp01
&lt;span class="c"&gt;# Password: Ir0nM@n&lt;/span&gt;

&lt;span class="c"&gt;# Become root&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;su -
&lt;span class="c"&gt;# Password: Ir0nM@n&lt;/span&gt;

&lt;span class="c"&gt;# Step 1: Create sudoers file for siva&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== Creating sudoers file for siva ==="&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"siva ALL=(ALL) NOPASSWD: ALL"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /etc/sudoers.d/siva

&lt;span class="c"&gt;# Step 2: Set correct permissions&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== Setting permissions ==="&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;440 /etc/sudoers.d/siva

&lt;span class="c"&gt;# Step 3: Verify contents&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== File contents ==="&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/sudoers.d/siva

&lt;span class="c"&gt;# Step 4: Check permissions&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== File permissions ==="&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /etc/sudoers.d/siva

&lt;span class="c"&gt;# Step 5: Validate sudoers syntax&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== Validating syntax ==="&lt;/span&gt;
visudo &lt;span class="nt"&gt;-c&lt;/span&gt;

&lt;span class="c"&gt;# Step 6: Test as siva user (if exists)&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== Testing sudo access ==="&lt;/span&gt;
su - siva &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"sudo whoami"&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"siva user not found or test failed"&lt;/span&gt;

&lt;span class="c"&gt;# Exit&lt;/span&gt;
&lt;span class="nb"&gt;exit
exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Expected Output
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;root@stapp01 ~]# &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"siva ALL=(ALL) NOPASSWD: ALL"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /etc/sudoers.d/siva
&lt;span class="o"&gt;[&lt;/span&gt;root@stapp01 ~]# &lt;span class="nb"&gt;chmod &lt;/span&gt;440 /etc/sudoers.d/siva

&lt;span class="o"&gt;[&lt;/span&gt;root@stapp01 ~]# &lt;span class="nb"&gt;cat&lt;/span&gt; /etc/sudoers.d/siva
siva &lt;span class="nv"&gt;ALL&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;ALL&lt;span class="o"&gt;)&lt;/span&gt; NOPASSWD: ALL

&lt;span class="o"&gt;[&lt;/span&gt;root@stapp01 ~]# &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /etc/sudoers.d/siva
&lt;span class="nt"&gt;-r--r-----&lt;/span&gt; 1 root root 30 Sep 11 10:00 /etc/sudoers.d/siva

&lt;span class="o"&gt;[&lt;/span&gt;root@stapp01 ~]# visudo &lt;span class="nt"&gt;-c&lt;/span&gt;
/etc/sudoers: parsed OK
/etc/sudoers.d/siva: parsed OK

&lt;span class="o"&gt;[&lt;/span&gt;root@stapp01 ~]# su - siva &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"sudo whoami"&lt;/span&gt;
root
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verification Commands
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Check sudoers file exists&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /etc/sudoers.d/siva

&lt;span class="c"&gt;# 2. Check file contents&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/sudoers.d/siva

&lt;span class="c"&gt;# 3. Check file permissions (should be 440)&lt;/span&gt;
&lt;span class="nb"&gt;stat&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"%a %U:%G %n"&lt;/span&gt; /etc/sudoers.d/siva

&lt;span class="c"&gt;# 4. Validate sudoers syntax&lt;/span&gt;
visudo &lt;span class="nt"&gt;-c&lt;/span&gt;

&lt;span class="c"&gt;# 5. Test sudo access as siva&lt;/span&gt;
su - siva &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"sudo whoami"&lt;/span&gt;
&lt;span class="c"&gt;# Expected: root&lt;/span&gt;

&lt;span class="c"&gt;# 6. Check sudo privileges&lt;/span&gt;
su - siva &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"sudo -l"&lt;/span&gt;
&lt;span class="c"&gt;# Expected: (ALL) NOPASSWD: ALL&lt;/span&gt;

&lt;span class="c"&gt;# 7. Verify on all servers&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;server &lt;span class="k"&gt;in &lt;/span&gt;stapp01 stapp02 stapp03&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== &lt;/span&gt;&lt;span class="nv"&gt;$server&lt;/span&gt;&lt;span class="s2"&gt; ==="&lt;/span&gt;
    ssh tony@&lt;span class="nv"&gt;$server&lt;/span&gt; &lt;span class="s2"&gt;"sudo cat /etc/sudoers.d/siva"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Understanding the sudoers Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Syntax Breakdown
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;siva &lt;span class="nv"&gt;ALL&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;ALL&lt;span class="o"&gt;)&lt;/span&gt; NOPASSWD: ALL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;siva&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Username&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ALL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hosts (all hosts)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;(ALL)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run as user (all users)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NOPASSWD:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No password required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ALL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Commands (all commands)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  File Permissions
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Permission&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;440&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;r--r-----&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read-only for owner and group&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Owner&lt;/td&gt;
&lt;td&gt;&lt;code&gt;root&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Root user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Group&lt;/td&gt;
&lt;td&gt;&lt;code&gt;root&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Root group&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Why Use /etc/sudoers.d/
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Cleaner than editing &lt;code&gt;/etc/sudoers&lt;/code&gt; directly&lt;/li&gt;
&lt;li&gt;Easier to manage individual user configurations&lt;/li&gt;
&lt;li&gt;Less risk of breaking the main sudoers file&lt;/li&gt;
&lt;li&gt;Files are automatically included if syntax is correct&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Alternative Methods
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Method 1: Using visudo to create file
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;visudo &lt;span class="nt"&gt;-f&lt;/span&gt; /etc/sudoers.d/siva
&lt;span class="c"&gt;# Add: siva ALL=(ALL) NOPASSWD: ALL&lt;/span&gt;
&lt;span class="c"&gt;# Save and exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Method 2: Using tee command
&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;echo&lt;/span&gt; &lt;span class="s2"&gt;"siva ALL=(ALL) NOPASSWD: ALL"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/sudoers.d/siva
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;440 /etc/sudoers.d/siva
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Method 3: Using printf
&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;printf&lt;/span&gt; &lt;span class="s2"&gt;"siva ALL=(ALL) NOPASSWD: ALL&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/sudoers.d/siva
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;440 /etc/sudoers.d/siva
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Method 4: Append to main sudoers file (not recommended)
&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;echo&lt;/span&gt; &lt;span class="s2"&gt;"siva ALL=(ALL) NOPASSWD: ALL"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/sudoers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;"siva is not in the sudoers file"&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="c"&gt;# Check if file was created&lt;/span&gt;
   &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /etc/sudoers.d/siva
   &lt;span class="c"&gt;# Check contents&lt;/span&gt;
   &lt;span class="nb"&gt;cat&lt;/span&gt; /etc/sudoers.d/siva
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;"sudo: /etc/sudoers.d/siva is mode 0644, should be 0440"&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="c"&gt;# Fix permissions&lt;/span&gt;
   &lt;span class="nb"&gt;chmod &lt;/span&gt;440 /etc/sudoers.d/siva
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;"syntax error near line 1"&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="c"&gt;# Check syntax&lt;/span&gt;
   visudo &lt;span class="nt"&gt;-c&lt;/span&gt;
   &lt;span class="c"&gt;# Fix the line&lt;/span&gt;
   &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"siva ALL=(ALL) NOPASSWD: ALL"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /etc/sudoers.d/siva
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;"siva user does not exist"&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="c"&gt;# Create the user first&lt;/span&gt;
   useradd siva
   &lt;span class="c"&gt;# Then configure sudo&lt;/span&gt;
   &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"siva ALL=(ALL) NOPASSWD: ALL"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /etc/sudoers.d/siva
   &lt;span class="nb"&gt;chmod &lt;/span&gt;440 /etc/sudoers.d/siva
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;"Permission denied" when creating file&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="c"&gt;# Use sudo or become root&lt;/span&gt;
   &lt;span class="nb"&gt;sudo &lt;/span&gt;su -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;File not being read&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="c"&gt;# Check if sudoers.d is included in main sudoers&lt;/span&gt;
   &lt;span class="nb"&gt;grep &lt;/span&gt;includedir /etc/sudoers
   &lt;span class="c"&gt;# Should show: #includedir /etc/sudoers.d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Complete Script (Run from Jump Host)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"========================================="&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Configuring Password-less sudo for siva"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"========================================="&lt;/span&gt;

&lt;span class="nb"&gt;declare&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="nv"&gt;SERVERS&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
    &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"stapp01"&lt;/span&gt;&lt;span class="o"&gt;]=&lt;/span&gt;&lt;span class="s2"&gt;"tony:Ir0nM@n"&lt;/span&gt;
    &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"stapp02"&lt;/span&gt;&lt;span class="o"&gt;]=&lt;/span&gt;&lt;span class="s2"&gt;"steve:Am3ric@"&lt;/span&gt;
    &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"stapp03"&lt;/span&gt;&lt;span class="o"&gt;]=&lt;/span&gt;&lt;span class="s2"&gt;"banner:BigGr33n"&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;server &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="p"&gt;!SERVERS[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;':'&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; user pass &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SERVERS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$server&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== &lt;/span&gt;&lt;span class="nv"&gt;$server&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="s2"&gt;) ==="&lt;/span&gt;

    ssh &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="nv"&gt;$server&lt;/span&gt;&lt;span class="s2"&gt;"&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;
echo '&lt;/span&gt;&lt;span class="nv"&gt;$pass&lt;/span&gt;&lt;span class="sh"&gt;' | sudo -S bash -c '
echo "Creating sudoers file..."
echo "siva ALL=(ALL) NOPASSWD: ALL" &amp;gt; /etc/sudoers.d/siva
chmod 440 /etc/sudoers.d/siva

echo "Verifying..."
echo "  Content: &lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="sh"&gt;(cat /etc/sudoers.d/siva)"
echo "  Permissions: &lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="sh"&gt;(stat -c "%a" /etc/sudoers.d/siva)"
echo "  Syntax check: &lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="sh"&gt;(visudo -c 2&amp;gt;&amp;amp;1 | grep siva)"

if id siva &amp;amp;&amp;gt;/dev/null; then
    echo "  Sudo test: &lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="sh"&gt;(su - siva -c "sudo whoami" 2&amp;gt;/dev/null)"
else
    echo "  Note: siva user does not exist yet"
fi

echo "✓ Configuration complete on &lt;/span&gt;&lt;span class="nv"&gt;$server&lt;/span&gt;&lt;span class="sh"&gt;"
'
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;span class="k"&gt;done

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"========================================="&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"✅ Password-less sudo configured for siva"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"========================================="&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Verification commands:"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  ssh tony@stapp01 'sudo cat /etc/sudoers.d/siva'"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  ssh steve@stapp02 'sudo cat /etc/sudoers.d/siva'"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  ssh banner@stapp03 'sudo cat /etc/sudoers.d/siva'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;sudoers file created&lt;/strong&gt;: &lt;code&gt;/etc/sudoers.d/siva&lt;/code&gt; on all servers&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Configuration&lt;/strong&gt;: &lt;code&gt;siva ALL=(ALL) NOPASSWD: ALL&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Permissions&lt;/strong&gt;: &lt;code&gt;440&lt;/code&gt; (r--r-----)&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Syntax validated&lt;/strong&gt;: Using &lt;code&gt;visudo -c&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Password-less sudo&lt;/strong&gt;: Enabled for siva&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The user &lt;code&gt;siva&lt;/code&gt; now has password-less sudo access on all App servers in the Stratos Datacenter.&lt;/p&gt;

</description>
      <category>linux</category>
    </item>
    <item>
      <title>Configure Jenkins Job for Package Installation</title>
      <dc:creator>Janak Shrestha</dc:creator>
      <pubDate>Mon, 14 Sep 2026 15:43:04 +0000</pubDate>
      <link>https://dev.to/janak0ff/configure-jenkins-job-for-package-installation-2o06</link>
      <guid>https://dev.to/janak0ff/configure-jenkins-job-for-package-installation-2o06</guid>
      <description>&lt;p&gt;Some new requirements have come up to install and configure some packages on the Nautilus infrastructure under Stratos Datacenter. The Nautilus DevOps team installed and configured a new Jenkins server so they wanted to create a Jenkins job to automate this task. Find below more details and complete the task accordingly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Access the Jenkins UI by clicking on the Jenkins button in the top bar. Log in using the credentials: username admin and password Adm!n321.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a new Jenkins job named install-packages and configure it with the following specifications:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Add a string parameter named PACKAGE.&lt;/p&gt;

&lt;p&gt;Configure the job to install a package specified in the $PACKAGE parameter on the storage server (Stratos Datacenter).&lt;/p&gt;

&lt;p&gt;Build the job at least once (e.g. with parameter PACKAGE=vim-enhanced) so the package is installed on the Storage server and can be verified.&lt;/p&gt;

&lt;p&gt;Note:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Ensure to install any required plugins and restart the Jenkins service if necessary. Opt for Restart Jenkins when installation is complete and no jobs are running on the plugin installation/update page. Refresh the UI page if needed after restarting the service.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Verify that the Jenkins job runs successfully on repeated executions to ensure reliability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Capture screenshots of your configuration for documentation and review purposes. Alternatively, use screen recording software like loom.com for comprehensive documentation and sharing.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step 1: Access Jenkins UI
&lt;/h2&gt;

&lt;p&gt;Click the &lt;strong&gt;Jenkins&lt;/strong&gt; button in the top bar and log in with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Username:&lt;/strong&gt; &lt;code&gt;admin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Password:&lt;/strong&gt; &lt;code&gt;Adm!n321&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 2: Install Required Plugin
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Publish Over SSH&lt;/strong&gt; plugin is recommended for this task, as it allows Jenkins to execute commands on remote servers directly .&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Manage Jenkins&lt;/strong&gt; → &lt;strong&gt;Plugins&lt;/strong&gt; → &lt;strong&gt;Available&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Search for &lt;strong&gt;"Publish Over SSH"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Check the box and click &lt;strong&gt;Install without restart&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;If prompted, select &lt;strong&gt;"Restart Jenkins when installation is complete and no jobs are running"&lt;/strong&gt; &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Refresh the UI page after Jenkins restarts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Configure SSH Connection to Storage Server
&lt;/h2&gt;

&lt;p&gt;Before creating the job, you need to set up SSH connectivity from Jenkins to &lt;code&gt;ststor01&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Manage Jenkins&lt;/strong&gt; → &lt;strong&gt;Configure System&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Scroll down to &lt;strong&gt;Publish over SSH&lt;/strong&gt; section&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Add&lt;/strong&gt; to create a new SSH server&lt;/li&gt;
&lt;li&gt;Configure with these details:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;ststor01&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hostname:&lt;/strong&gt; &lt;code&gt;ststor01&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Username:&lt;/strong&gt; &lt;code&gt;natasha&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remote Directory:&lt;/strong&gt; &lt;code&gt;/home/natasha&lt;/code&gt; (or leave blank)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Password:&lt;/strong&gt; &lt;code&gt;Bl@kW&lt;/code&gt; (or use key-based authentication)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Test Configuration&lt;/strong&gt; to verify connectivity &lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Save&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 4: Create the install-packages Job
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;From the dashboard, click &lt;strong&gt;New Item&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Enter name: &lt;code&gt;install-packages&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Freestyle project&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;OK&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 5: Add the PACKAGE Parameter
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;In the job configuration, scroll to &lt;strong&gt;General&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Check &lt;strong&gt;"This project is parameterized"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Add Parameter&lt;/strong&gt; → select &lt;strong&gt;String Parameter&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;Configure the parameter:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;PACKAGE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Default Value:&lt;/strong&gt; &lt;code&gt;vim-enhanced&lt;/code&gt; (or leave blank)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description:&lt;/strong&gt; &lt;code&gt;Enter the package name to install on the storage server&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Save&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 6: Configure Build Step to Install Package
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Scroll down to the &lt;strong&gt;Build&lt;/strong&gt; section&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Add build step&lt;/strong&gt; → select &lt;strong&gt;Send files or execute commands over SSH&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;Select the server: &lt;code&gt;ststor01&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;In the &lt;strong&gt;Exec command&lt;/strong&gt; field, add:
&lt;/li&gt;
&lt;/ol&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;yum &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nv"&gt;$PACKAGE&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nv"&gt;$PACKAGE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; The &lt;code&gt;natasha&lt;/code&gt; user needs passwordless sudo access. If you encounter a &lt;code&gt;sudo: sorry, you must have a tty to run sudo&lt;/code&gt; error, the Jenkins server admin must add this entry to the sudoers file on &lt;code&gt;ststor01&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;natasha &lt;span class="nv"&gt;ALL&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;ALL&lt;span class="o"&gt;)&lt;/span&gt; NOPASSWD: ALL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or alternatively, disable the TTY requirement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Defaults:natasha &lt;span class="o"&gt;!&lt;/span&gt;requiretty
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;Save&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 7: Build the Job
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;From the job page, click &lt;strong&gt;Build with Parameters&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;Enter a package name, e.g., &lt;code&gt;vim-enhanced&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Build&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Console Output&lt;/strong&gt; to monitor the build progress and verify success &lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 8: Verify Installation
&lt;/h2&gt;

&lt;p&gt;After the build succeeds, SSH into &lt;code&gt;ststor01&lt;/code&gt; and verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh natasha@ststor01
rpm &lt;span class="nt"&gt;-qa&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;vim-enhanced    &lt;span class="c"&gt;# For RHEL/CentOS&lt;/span&gt;
&lt;span class="c"&gt;# OR&lt;/span&gt;
dpkg &lt;span class="nt"&gt;-l&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;vim-enhanced    &lt;span class="c"&gt;# For Ubuntu/Debian&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 9: Test Repeated Executions
&lt;/h2&gt;

&lt;p&gt;Run the job again with different packages (e.g., &lt;code&gt;curl&lt;/code&gt;, &lt;code&gt;git&lt;/code&gt;) to ensure the job works reliably on repeated executions .&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Log in to Jenkins UI (admin / Adm!n321)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Install &lt;strong&gt;Publish Over SSH&lt;/strong&gt; plugin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Configure SSH connection to &lt;code&gt;ststor01&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Create Freestyle job named &lt;code&gt;install-packages&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Add String Parameter &lt;code&gt;PACKAGE&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Configure build step with SSH command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Build with parameter (e.g., &lt;code&gt;vim-enhanced&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Verify installation on storage server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;Test repeatedly for reliability&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Key Points:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;Publish Over SSH&lt;/strong&gt; plugin for reliable remote execution &lt;/li&gt;
&lt;li&gt;Ensure &lt;code&gt;natasha&lt;/code&gt; has passwordless sudo access on &lt;code&gt;ststor01&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;Test the job multiple times with different packages to ensure reliability &lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>jenkins</category>
    </item>
    <item>
      <title>Organize Jenkins Jobs with Folders</title>
      <dc:creator>Janak Shrestha</dc:creator>
      <pubDate>Mon, 14 Sep 2026 15:33:53 +0000</pubDate>
      <link>https://dev.to/janak0ff/organize-jenkins-jobs-with-folders-55</link>
      <guid>https://dev.to/janak0ff/organize-jenkins-jobs-with-folders-55</guid>
      <description>&lt;p&gt;xFusionCorp Industries' DevOps team aims to streamline the management of Jenkins jobs by organizing them into distinct folders based on their purpose. Complete the task following the provided requirements:&lt;/p&gt;

&lt;p&gt;1.Access the Jenkins UI by clicking on the Jenkins button in the top bar. Log in using the credentials: username admin and password Adm!n321.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create a new folder named Apache within the Jenkins UI.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Move the existing jobs httpd-php and services under the newly created Apache folder.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Note:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Ensure to install any required plugins and restart the Jenkins service if necessary. Opt for Restart Jenkins when installation is complete and no jobs are running on the plugin installation/update page.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Be aware that Jenkins UI may experience temporary unresponsiveness during the service restart. Refresh the UI page if needed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Capture screenshots of your work for documentation and review purposes. Alternatively, utilize screen recording software like loom.com for detailed documentation and sharing.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step 1: Access Jenkins UI
&lt;/h2&gt;

&lt;p&gt;Click the &lt;strong&gt;Jenkins&lt;/strong&gt; button on the top bar of your KodeKloud environment and log in with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Username:&lt;/strong&gt; &lt;code&gt;admin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Password:&lt;/strong&gt; &lt;code&gt;Adm!n321&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 2: Install the Folders Plugin
&lt;/h2&gt;

&lt;p&gt;The Folders plugin is required to create folders in Jenkins .&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Manage Jenkins&lt;/strong&gt; → &lt;strong&gt;Plugins&lt;/strong&gt; (or &lt;strong&gt;Manage Plugins&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;Click on the &lt;strong&gt;Available&lt;/strong&gt; tab&lt;/li&gt;
&lt;li&gt;Search for &lt;strong&gt;"Folders"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Check the box for the &lt;strong&gt;"Folders"&lt;/strong&gt; plugin (maintained by CloudBees) &lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Install without restart&lt;/strong&gt; or &lt;strong&gt;Download now and install after restart&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; If prompted, select &lt;strong&gt;"Restart Jenkins when installation is complete and no jobs are running"&lt;/strong&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After restart, log in again with the same credentials. If the UI becomes unresponsive, refresh the page .&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Create the "Apache" Folder
&lt;/h2&gt;

&lt;p&gt;Once the Folders plugin is installed and Jenkins has restarted:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;From the Jenkins dashboard, click &lt;strong&gt;New Item&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;Enter the name: &lt;strong&gt;Apache&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Folder&lt;/strong&gt; as the item type &lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;OK&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;On the configuration page, you can leave the Display Name same as the folder name&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Save&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 4: Move the "httpd-php" Job
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;From the dashboard, click on the &lt;strong&gt;httpd-php&lt;/strong&gt; job to open it&lt;/li&gt;
&lt;li&gt;In the left sidebar, click &lt;strong&gt;Move&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;In the destination field, select &lt;strong&gt;Apache&lt;/strong&gt; folder&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Move&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 5: Move the "services" Job
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;From the dashboard, click on the &lt;strong&gt;services&lt;/strong&gt; job&lt;/li&gt;
&lt;li&gt;In the left sidebar, click &lt;strong&gt;Move&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Apache&lt;/strong&gt; as the destination&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Move&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 6: Verify the Result
&lt;/h2&gt;

&lt;p&gt;After moving both jobs, your Jenkins dashboard should show:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Apache/
├── httpd-php
└── services
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can verify by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clicking on the &lt;strong&gt;Apache&lt;/strong&gt; folder to see both jobs inside &lt;/li&gt;
&lt;li&gt;Checking that the dashboard no longer shows &lt;code&gt;httpd-php&lt;/code&gt; and &lt;code&gt;services&lt;/code&gt; at the root level&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Troubleshooting Tips
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;If "Folder" option doesn't appear&lt;/strong&gt; during New Item creation, the plugin installation likely hasn't completed. Wait a few moments and refresh, or check &lt;strong&gt;Manage Jenkins&lt;/strong&gt; → &lt;strong&gt;Manage Plugins&lt;/strong&gt; → &lt;strong&gt;Installed&lt;/strong&gt; to confirm the Folders plugin is active .&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If Jenkins UI becomes stuck&lt;/strong&gt; during restart, simply refresh the browser page and log in again .&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Log in to Jenkins UI (admin / Adm!n321)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Install Folders plugin via Manage Plugins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Restart Jenkins if prompted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Create new Folder named "Apache"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Move &lt;code&gt;httpd-php&lt;/code&gt; job into Apache folder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Move &lt;code&gt;services&lt;/code&gt; job into Apache folder&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>jenkins</category>
    </item>
  </channel>
</rss>
