<?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>Forem: Wycliffe A. Onyango</title>
    <description>The latest articles on Forem by Wycliffe A. Onyango (@wycliffealphus).</description>
    <link>https://forem.com/wycliffealphus</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1481182%2F40f7af6a-d8c9-49df-a265-956c8ffb229d.jpeg</url>
      <title>Forem: Wycliffe A. Onyango</title>
      <link>https://forem.com/wycliffealphus</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/wycliffealphus"/>
    <language>en</language>
    <item>
      <title>You Bought a Domain. Now Why Does It Feel Like You Need Three?</title>
      <dc:creator>Wycliffe A. Onyango</dc:creator>
      <pubDate>Sun, 18 Jan 2026 09:17:35 +0000</pubDate>
      <link>https://forem.com/wycliffealphus/you-bought-a-domain-now-why-does-it-feel-like-you-need-three-5ej7</link>
      <guid>https://forem.com/wycliffealphus/you-bought-a-domain-now-why-does-it-feel-like-you-need-three-5ej7</guid>
      <description>&lt;p&gt;You’ve done the hard part. You built a frontend that looks incredible. You finished a backend that actually works. You even pulled the trigger and bought that professional &lt;strong&gt;.com&lt;/strong&gt; or &lt;strong&gt;.co.ke&lt;/strong&gt; you’ve been eyeing.&lt;/p&gt;

&lt;p&gt;But now, you’re stuck.&lt;/p&gt;

&lt;p&gt;You’re staring at the dashboard of your domain registrar, and the realization is hitting you: &lt;strong&gt;"I have one domain name, but I have three different things to show the world."&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need a &lt;strong&gt;Portfolio&lt;/strong&gt; to land the job.&lt;/li&gt;
&lt;li&gt;You need an &lt;strong&gt;App&lt;/strong&gt; to show the product.&lt;/li&gt;
&lt;li&gt;You need an &lt;strong&gt;API&lt;/strong&gt; to power the whole thing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You might be wondering: &lt;em&gt;Did I make a mistake? Do I need to buy three different domains? How am I supposed to jam a React frontend, a Node.js backend, and a static portfolio into one single address?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stop.&lt;/strong&gt; You don’t need more domains. You just need to realize that you didn't buy a single room—you bought the whole plot of land.&lt;/p&gt;




&lt;h2&gt;
  
  
  The "Subdomain" Superpower
&lt;/h2&gt;

&lt;p&gt;Right now, you’re likely looking at your domain as a static file. But your domain is actually a &lt;strong&gt;namespace&lt;/strong&gt;. Think of it as a house. Your main domain is the front door (the Lobby), but you can add as many side doors as you want.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Lobby (&lt;code&gt;yourdomain.com&lt;/code&gt;):&lt;/strong&gt; This is for your Portfolio. It’s your handshake with the world.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Workshop (&lt;code&gt;app.yourdomain.com&lt;/code&gt;):&lt;/strong&gt; This is where your application lives. It’s a separate space for users to interact with your code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Engine Room (&lt;code&gt;api.yourdomain.com&lt;/code&gt;):&lt;/strong&gt; This is where your backend sits. It’s tucked away, doing the heavy lifting without cluttering your frontend.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The "DNS Map" in Your Hands
&lt;/h2&gt;

&lt;p&gt;If you’re looking at settings like &lt;strong&gt;A Records&lt;/strong&gt;, &lt;strong&gt;CNAMEs&lt;/strong&gt;, and &lt;strong&gt;Nameservers&lt;/strong&gt; and feeling like you’re about to break the internet—take a breath.&lt;/p&gt;

&lt;p&gt;DNS is just a map. Your job is simply to draw the lines:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Point the "root" (@)&lt;/strong&gt; to wherever you decide to host your portfolio.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a CNAME record named `app&lt;/strong&gt;` and point it to your frontend host.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a CNAME record named `api&lt;/strong&gt;` and point it to your backend host.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The moment you do this, you stop being someone who just "writes code" and you become a &lt;strong&gt;System Architect&lt;/strong&gt;. You are now managing a professional-grade ecosystem where every part of your stack has its own home.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Final "Handshake" (CORS)
&lt;/h2&gt;

&lt;p&gt;Once you get those subdomains live, you’ll likely hit one last wall. Your frontend will try to talk to your backend, and the browser will stop it. It will say: &lt;em&gt;"I don't trust this connection."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Don't panic. This is the &lt;strong&gt;CORS&lt;/strong&gt; (Cross-Origin Resource Sharing) talk.&lt;/p&gt;

&lt;p&gt;Even though your App and your API share a "last name," they are separate origins. You must go into your backend code and explicitly say: &lt;em&gt;"I trust my workshop. Let &lt;code&gt;app.yourdomain.com&lt;/code&gt; in."&lt;/em&gt; It’s the final handshake that connects your engine to your interface.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Takeaway
&lt;/h2&gt;

&lt;p&gt;If you’re frustrated today because your domain feels "too small" for your project, remember this: &lt;strong&gt;Your domain is as big as you make it.&lt;/strong&gt; You have the power to host a portfolio, a production app, and a robust API all under one roof. You aren't just pointing URLs; you're building a professional infrastructure.&lt;/p&gt;

&lt;p&gt;Stop staring at the "Site Not Found" error. Go to your DNS manager, add those records, and turn your one domain into a powerhouse.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>devops</category>
      <category>development</category>
    </item>
    <item>
      <title>100 Days of DevOps: Day 79</title>
      <dc:creator>Wycliffe A. Onyango</dc:creator>
      <pubDate>Mon, 24 Nov 2025 07:49:34 +0000</pubDate>
      <link>https://forem.com/wycliffealphus/100-days-of-devops-day-79-58ik</link>
      <guid>https://forem.com/wycliffealphus/100-days-of-devops-day-79-58ik</guid>
      <description>&lt;h2&gt;
  
  
  Automated CI/CD for Nautilus Application
&lt;/h2&gt;

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

&lt;p&gt;This article confirms the successful implementation of a robust Continuous Deployment (CD) pipeline using Jenkins. The solution ensures that any code push to the Git repository's &lt;code&gt;master&lt;/code&gt; branch instantly triggers a build, deploying the complete application contents to the Storage Server (&lt;code&gt;ststor01&lt;/code&gt;) under the required ownership of user &lt;code&gt;sarah&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  I. Infrastructure Setup Commands (Prerequisites)
&lt;/h3&gt;

&lt;p&gt;These commands ensure the necessary passwordless access and user accounts are in place before deployment. They must be executed on the respective servers using administrative privileges (e.g., as &lt;code&gt;jenkins&lt;/code&gt; or &lt;code&gt;natasha&lt;/code&gt; via &lt;code&gt;sudo&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  A. Jenkins Server Setup (to enable passwordless SSH)
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;jenkins&lt;/code&gt; user's public key must be generated and transferred to the remote &lt;code&gt;natasha&lt;/code&gt; account.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command (Run on Jenkins Server as &lt;code&gt;jenkins&lt;/code&gt;)&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;ssh-keygen&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generates the private (&lt;code&gt;id_ed25519&lt;/code&gt;) and public (&lt;code&gt;id_ed25519.pub&lt;/code&gt;) key pair.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ssh-copy-id -i /var/lib/jenkins/.ssh/id_ed25519.pub natasha@ststor01.stratos.xfusioncorp.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Transfers the public key to the Storage Server for passwordless login as &lt;code&gt;natasha&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  B. Storage Server Setup (ststor01)
&lt;/h3&gt;

&lt;p&gt;These steps are crucial for automation (Natasha's &lt;code&gt;sudo&lt;/code&gt; rights) and the final file ownership/verification (Sarah's account).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command (Run on Storage Server as &lt;code&gt;natasha&lt;/code&gt; via &lt;code&gt;sudo&lt;/code&gt;)&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;sudo visudo&lt;/code&gt; (and add line below)&lt;/td&gt;
&lt;td&gt;Configures passwordless &lt;code&gt;sudo&lt;/code&gt; for &lt;code&gt;natasha&lt;/code&gt; to allow the script to run &lt;code&gt;chown&lt;/code&gt; and &lt;code&gt;tar&lt;/code&gt; without prompts.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;natasha ALL=(ALL) NOPASSWD: ALL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The line added to &lt;code&gt;/etc/sudoers&lt;/code&gt; file.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo useradd sarah&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Creates the user &lt;code&gt;sarah&lt;/code&gt; (required for file ownership and Task 3).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo passwd sarah&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sets the login password for &lt;code&gt;sarah&lt;/code&gt; (required for Task 3 SSH).&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  II. Jenkins Job Configuration &amp;amp; Execution
&lt;/h2&gt;

&lt;h3&gt;
  
  
  A. Build Trigger Setup (Webhook)
&lt;/h3&gt;

&lt;p&gt;The job &lt;code&gt;nautilus-app-deployment&lt;/code&gt; was configured to use the &lt;strong&gt;Generic Webhook Trigger&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Value/URL Used&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Jenkins URL Endpoint&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;http://172.16.238.19:8080/generic-webhook-trigger/invoke?token=&amp;lt;SECRET_TOKEN&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Gitea Webhook Target&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The Jenkins URL above was pasted into the Git repository's webhook settings.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Gitea Trigger&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Set to trigger on &lt;strong&gt;Push Events&lt;/strong&gt; to the &lt;strong&gt;&lt;code&gt;master&lt;/code&gt;&lt;/strong&gt; branch.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  B. Execution Shell Script (&lt;code&gt;deployment_script.sh&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;The following script was placed in the Jenkins job's &lt;strong&gt;Execute shell&lt;/strong&gt; step. It uses &lt;code&gt;tar&lt;/code&gt; piped over &lt;code&gt;ssh&lt;/code&gt; to efficiently transfer the files, as &lt;code&gt;rsync&lt;/code&gt; was not available on the Jenkins host.&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="c"&gt;# ==============================================================================&lt;/span&gt;
&lt;span class="c"&gt;# Jenkins Deployment Script for Storage Server (ststor01)&lt;/span&gt;
&lt;span class="c"&gt;# ==============================================================================&lt;/span&gt;

&lt;span class="c"&gt;# Target Server details&lt;/span&gt;
&lt;span class="nv"&gt;STORAGE_SERVER_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"natasha"&lt;/span&gt; &lt;span class="c"&gt;# Assuming we use the listed user to SSH into the server&lt;/span&gt;
&lt;span class="nv"&gt;STORAGE_SERVER_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ststor01.stratos.xfusioncorp.com"&lt;/span&gt;
&lt;span class="nv"&gt;DEPLOY_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/www/html"&lt;/span&gt;

&lt;span class="c"&gt;# SSH Options for non-interactive execution:&lt;/span&gt;
&lt;span class="c"&gt;# 1. StrictHostKeyChecking=no: Avoids the "Host key verification failed" error on first connect.&lt;/span&gt;
&lt;span class="c"&gt;# 2. UserKnownHostsFile=/dev/null: Ensures the keys are not written to the known_hosts file.&lt;/span&gt;
&lt;span class="nv"&gt;SSH_OPTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting deployment process to &lt;/span&gt;&lt;span class="nv"&gt;$STORAGE_SERVER_HOST&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$DEPLOY_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# 1. SSH into the Storage Server and set ownership to 'sarah'.&lt;/span&gt;
&lt;span class="c"&gt;# This ensures that the subsequent file operations performed by 'natasha' on the directory&lt;/span&gt;
&lt;span class="c"&gt;# will not affect the final deployed files' required ownership.&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"1. Changing ownership of &lt;/span&gt;&lt;span class="nv"&gt;$DEPLOY_PATH&lt;/span&gt;&lt;span class="s2"&gt; to user 'sarah' on the Storage Server..."&lt;/span&gt;
ssh &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SSH_OPTS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;STORAGE_SERVER_USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;@&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;STORAGE_SERVER_HOST&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="s2"&gt;"sudo chown -R sarah:sarah &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DEPLOY_PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Ownership change successful."&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Failed to change ownership on the remote server. Check SSH keys, sudo rights."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# 2. Deploy the new code using 'tar' piped over SSH.&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"2. Deploying content from Jenkins workspace (&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;WORKSPACE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) to &lt;/span&gt;&lt;span class="nv"&gt;$DEPLOY_PATH&lt;/span&gt;&lt;span class="s2"&gt; using tar over SSH..."&lt;/span&gt;

&lt;span class="c"&gt;# A. Execute 'tar' locally to create a compressed archive of the workspace, excluding .git.&lt;/span&gt;
&lt;span class="c"&gt;# B. Pipe the output (-) directly to the remote 'ssh' connection.&lt;/span&gt;
&lt;span class="c"&gt;# C. On the remote server:&lt;/span&gt;
&lt;span class="c"&gt;#    i. Use 'sudo' to remove all existing files in the deployment path for a clean slate.&lt;/span&gt;
&lt;span class="c"&gt;#    ii. Pipe the incoming compressed data to 'sudo tar -xzf -' to extract it into the path.&lt;/span&gt;
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-czf&lt;/span&gt; - &lt;span class="nt"&gt;--exclude&lt;/span&gt; &lt;span class="s1"&gt;'.git'&lt;/span&gt; &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;WORKSPACE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; | &lt;span class="se"&gt;\&lt;/span&gt;
    ssh &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SSH_OPTS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;STORAGE_SERVER_USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;@&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;STORAGE_SERVER_HOST&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="s2"&gt;"
        # Use sudo for file deletion and extraction
        sudo rm -rf &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DEPLOY_PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/* &amp;amp;&amp;amp; sudo tar -xzf - -C &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DEPLOY_PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
    "&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Deployment successful on &lt;/span&gt;&lt;span class="nv"&gt;$STORAGE_SERVER_HOST&lt;/span&gt;&lt;span class="s2"&gt;."&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Deployment failed during file transfer (tar/ssh pipe). Check permissions/connectivity."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Deployment finished successfully."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  III. Final Verification
&lt;/h2&gt;

&lt;p&gt;These commands are used to push the code and trigger the fully automated pipeline.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command (Run on Storage Server as &lt;code&gt;sarah&lt;/code&gt;)&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;ssh sarah@ststor01.stratos.xfusioncorp.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Log in to the Storage Server as the user &lt;code&gt;sarah&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd web&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Navigate to the cloned repository.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;echo "Welcome to the xFusionCorp Industries" &amp;gt; index.html&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Modify the content of the index file as required.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git add index.html&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stage the modified file.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git commit -m "Updated homepage content for deployment"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Commit the change locally.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git push origin master&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Triggers the webhook&lt;/strong&gt; and starts the Jenkins job, completing the CD cycle.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>devops</category>
      <category>jenkins</category>
      <category>cicd</category>
      <category>ci</category>
    </item>
    <item>
      <title>100 Days of DevOps: Day 78</title>
      <dc:creator>Wycliffe A. Onyango</dc:creator>
      <pubDate>Tue, 28 Oct 2025 20:25:33 +0000</pubDate>
      <link>https://forem.com/wycliffealphus/100-days-of-devops-day-78-1i48</link>
      <guid>https://forem.com/wycliffealphus/100-days-of-devops-day-78-1i48</guid>
      <description>&lt;h2&gt;
  
  
  Jenkins Conditional Pipeline Deployment for Nautilus Web App
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Objective&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The goal of this task was to configure a &lt;strong&gt;Jenkins Pipeline&lt;/strong&gt; job that automatically deploys a &lt;strong&gt;static website&lt;/strong&gt; from the &lt;strong&gt;Gitea repository&lt;/strong&gt; depending on the parameters selected to the &lt;strong&gt;Nautilus Application Servers&lt;/strong&gt;, using the &lt;strong&gt;Storage Server (&lt;code&gt;ststor01&lt;/code&gt;)&lt;/strong&gt; as the Jenkins agent.&lt;/p&gt;

&lt;p&gt;The deployed web application should be accessible directly from the root URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://8091-port-7l43zyf3btna3i6q.labs.kodekloud.com/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and &lt;strong&gt;not&lt;/strong&gt; from a subdirectory like &lt;code&gt;/web_app&lt;/code&gt;.&lt;/p&gt;




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

&lt;h3&gt;
  
  
  &lt;strong&gt;1. Install and Configure Jenkins Plugins&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To ensure the pipeline could interact with Git and remote agents, the following plugins were verified and installed via &lt;strong&gt;Manage Jenkins → Plugins&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Git Plugin&lt;/strong&gt; → enables Jenkins to clone and pull from Git repositories.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SSH Agent Plugin&lt;/strong&gt; → required for communication between Jenkins and remote agents.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pipeline Plugin&lt;/strong&gt; → allows scripted and declarative pipelines to run.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After installation, Jenkins was restarted to apply changes.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;2. Configure Jenkins Credentials&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Instead of using an SSH key, the connection to the Storage Server was configured to use &lt;strong&gt;username and password authentication&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Manage Jenkins → Credentials → System → Global Credentials → Add Credentials&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select &lt;strong&gt;“Username and Password”&lt;/strong&gt; as the credential type.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enter:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&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;Password:&lt;/strong&gt; &lt;em&gt;(used password provided in the task)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ID:&lt;/strong&gt; &lt;code&gt;ststor01-password&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;4.Click &lt;strong&gt;Save&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;These credentials were later linked to the Storage Server node so Jenkins could authenticate via SSH using &lt;code&gt;natasha&lt;/code&gt;’s account.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;3. Add Jenkins Agent Node (Storage Server)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;A new node was created to allow Jenkins to deploy code directly on the Storage Server.&lt;/p&gt;

&lt;p&gt;Steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Manage Jenkins → Nodes → New Node&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Configure the node with:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;Storage Server&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remote Root Directory:&lt;/strong&gt; &lt;code&gt;/var/www/html&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Labels:&lt;/strong&gt; &lt;code&gt;ststor01&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Launch method:&lt;/strong&gt; &lt;em&gt;Launch agents via SSH&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Host:&lt;/strong&gt; IP or hostname of the Storage Server&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Credentials:&lt;/strong&gt; select &lt;code&gt;natasha (ststor01-password)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3.Save and verify the node connects successfully and shows &lt;strong&gt;“Online.”&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;4. Verify Environment on Storage Server&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;On the Storage Server, verify prerequisites and directory setup:&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;whoami&lt;/span&gt;
&lt;span class="c"&gt;# natasha&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; /var/www/html
git &lt;span class="nt"&gt;--version&lt;/span&gt;   &lt;span class="c"&gt;# confirm git installed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apache was already running on port 8080 and serving from &lt;code&gt;/var/www/html&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;5. Access Jenkins and Gitea&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Logged into both systems using provided credentials:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Jenkins:&lt;/strong&gt;&lt;/p&gt;

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


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Gitea:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Repository URL: &lt;code&gt;https://80-port-7l43zyf3btna3i6q.labs.kodekloud.com/sarah/web_app&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Username: &lt;code&gt;sarah&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Password: &lt;em&gt;(used as provided)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Confirmed repository contained &lt;code&gt;master&lt;/code&gt; and &lt;code&gt;feature&lt;/code&gt; branches.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;6. Create the Jenkins Pipeline Job&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;New Item → Pipeline&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Name it &lt;strong&gt;&lt;code&gt;nautilus-webapp-job&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Enable &lt;strong&gt;“This project is parameterized”&lt;/strong&gt; and add a &lt;strong&gt;String Parameter&lt;/strong&gt;:&lt;/li&gt;
&lt;/ol&gt;

&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; &lt;code&gt;Branch to deploy (master or feature)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;4.Under the &lt;strong&gt;Pipeline script&lt;/strong&gt; section, paste the working Groovy script below.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;7. Final Working Jenkins Pipeline Script&lt;/strong&gt;
&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;'ststor01'&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="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;branchName&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="na"&gt;trim&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;branchName&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;branchName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"master"&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;branchName&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"master"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;branchName&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&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;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Invalid BRANCH parameter: ${branchName}. Use '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;sh&lt;/span&gt; &lt;span class="s2"&gt;"""
            set -e
            echo "=== Deployment started for branch: ${branchName} ==="

            cd /var/www/html

            if [ ! -d .git ]; then
                echo "Repository not found. Cloning..."
                git clone https://80-port-7l43zyf3btna3i6q.labs.kodekloud.com/sarah/web_app.git .
            fi

            echo "Fetching and resetting to ${branchName}"
            git fetch origin
            git checkout -f ${branchName} || git checkout -b ${branchName} origin/${branchName}
            git reset --hard origin/${branchName}

            echo "=== Deployment complete at /var/www/html ==="
        """&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;p&gt;This script:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatically clones the repository if missing.&lt;/li&gt;
&lt;li&gt;Fetches and synchronizes the correct branch (&lt;code&gt;master&lt;/code&gt; or &lt;code&gt;feature&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Deploys directly to &lt;code&gt;/var/www/html&lt;/code&gt;, ensuring no subdirectory path like &lt;code&gt;/web_app&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;8. Run and Verify the Pipeline&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Triggered the job with parameter:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected Console 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;=== Deployment started for branch: master ===
Fetching and resetting to master
HEAD is now at e12c8f4 Updated index.html
=== Deployment complete at /var/www/html ===
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Job completed successfully with no workspace errors.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;9. Validation&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Opened the web app at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://8091-port-7l43zyf3btna3i6q.labs.kodekloud.com/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confirmed the content loaded correctly &lt;strong&gt;from the root URL&lt;/strong&gt; (no &lt;code&gt;/web_app&lt;/code&gt; subpath), proving that deployment targeted the proper directory.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Key Notes&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Installed and verified &lt;strong&gt;Git&lt;/strong&gt;, &lt;strong&gt;SSH Agent&lt;/strong&gt;, and &lt;strong&gt;Pipeline&lt;/strong&gt; plugins before running jobs.&lt;/li&gt;
&lt;li&gt;Used &lt;strong&gt;username + password authentication&lt;/strong&gt; for the &lt;code&gt;natasha&lt;/code&gt; account (no SSH key).&lt;/li&gt;
&lt;li&gt;Always ensure node labels match (&lt;code&gt;ststor01&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The workspace issues were avoided by deploying directly under &lt;code&gt;/var/www/html&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;BRANCH&lt;/code&gt; parameter allows switching between &lt;code&gt;master&lt;/code&gt; and &lt;code&gt;feature&lt;/code&gt; easily.&lt;/li&gt;
&lt;/ul&gt;

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

</description>
      <category>devops</category>
      <category>jenkins</category>
      <category>ci</category>
      <category>cicd</category>
    </item>
    <item>
      <title>100 Days of DevOps: Day 77</title>
      <dc:creator>Wycliffe A. Onyango</dc:creator>
      <pubDate>Fri, 24 Oct 2025 14:38:19 +0000</pubDate>
      <link>https://forem.com/wycliffealphus/100-days-of-devops-day-77-48d8</link>
      <guid>https://forem.com/wycliffealphus/100-days-of-devops-day-77-48d8</guid>
      <description>&lt;h2&gt;
  
  
  CI/CD Deployment of Web Application using Jenkins Pipeline
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Objective
&lt;/h3&gt;

&lt;p&gt;Automate deployment of a static website from a Gitea repository to the shared document root on the Storage Server using a Jenkins Pipeline with a single stage named Deploy.&lt;/p&gt;




&lt;h2&gt;
  
  
  Work Completed
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Jenkins UI Access
&lt;/h3&gt;

&lt;p&gt;Jenkins was already installed and running. Configuration was performed through the Jenkins web interface:&lt;/p&gt;

&lt;p&gt;Login credentials:&lt;/p&gt;

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

&lt;p&gt;No Jenkins server installation steps were required.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Required Jenkins Plugin Installation
&lt;/h3&gt;

&lt;p&gt;Several plugins needed for source code retrieval and pipeline execution were installed from:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Manage Jenkins → Plugins → Available Plugins&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Installed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Git plugin&lt;/li&gt;
&lt;li&gt;SSH Agent plugin&lt;/li&gt;
&lt;li&gt;Pipeline plugin&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Jenkins was restarted from the UI to apply the changes.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Java 21 Installation
&lt;/h3&gt;

&lt;p&gt;Java was required for proper Jenkins agent execution. On the Storage Server, Java 21 was installed using:&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;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; java-21-openjdk
java &lt;span class="nt"&gt;-version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Java installed successfully.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Add Storage Server as Jenkins Agent
&lt;/h3&gt;

&lt;p&gt;Jenkins needed a remote execution environment to deploy directly to the shared site directory.&lt;/p&gt;

&lt;p&gt;Performed via:&lt;br&gt;
&lt;strong&gt;Manage Jenkins → Nodes → New Node&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node name: &lt;strong&gt;Storage Server&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Remote root directory: &lt;code&gt;/var/www/html/workspace&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Label: &lt;strong&gt;ststor01&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Launch method: &lt;strong&gt;Launch agent via execution of command on the agent node&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Jenkins generated a command, which was executed on the Storage Server to bring the agent online.&lt;/p&gt;
&lt;h4&gt;
  
  
  Credentials Setup
&lt;/h4&gt;

&lt;p&gt;Under:&lt;br&gt;
Manage Jenkins → Credentials&lt;/p&gt;

&lt;p&gt;A secure SSH credential was added to allow the controller to authenticate to the Storage Server. This ensured a reliable and secure connection for deployments.&lt;/p&gt;


&lt;h3&gt;
  
  
  5. Folder Permissions on Storage Server
&lt;/h3&gt;

&lt;p&gt;To allow Jenkins agent to write deployed files:&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 chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; natasha:natasha /var/www/html
&lt;span class="nb"&gt;sudo chmod&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 755 /var/www/html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensured successful delivery of web content.&lt;/p&gt;




&lt;h3&gt;
  
  
  6. Create Jenkins Pipeline Job
&lt;/h3&gt;

&lt;p&gt;Job Type: &lt;strong&gt;Pipeline&lt;/strong&gt;&lt;br&gt;
Job Name: &lt;strong&gt;xfusion-webapp-job&lt;/strong&gt;&lt;br&gt;
(Not a Multibranch Pipeline)&lt;/p&gt;

&lt;h4&gt;
  
  
  Pipeline Script Used
&lt;/h4&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;'ststor01'&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;git&lt;/span&gt; &lt;span class="nl"&gt;url:&lt;/span&gt; &lt;span class="s1"&gt;'https://80-port-qtjnzi2lylpawcus.labs.kodekloud.com/sarah/web_app'&lt;/span&gt;
                &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'cp -r index.html /var/www/html/'&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;p&gt;Details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single stage named &lt;strong&gt;Deploy&lt;/strong&gt; (case-sensitive requirement met)&lt;/li&gt;
&lt;li&gt;Pulls source code from &lt;code&gt;web_app&lt;/code&gt; repository in Gitea&lt;/li&gt;
&lt;li&gt;Deployed directly to final document root&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  7. Successful Deployment Execution
&lt;/h3&gt;

&lt;p&gt;Triggered via &lt;strong&gt;Build Now&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Console output confirmed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Git repository successfully cloned&lt;/li&gt;
&lt;li&gt;Deployment action executed on Storage Server&lt;/li&gt;
&lt;li&gt;index.html delivered properly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Final status: &lt;strong&gt;SUCCESS&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The website displayed correctly via the Load Balancer main URL without extra subdirectories.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Result
&lt;/h2&gt;

&lt;p&gt;A complete CI/CD deployment pipeline now updates the website automatically with each build. The system uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Jenkins pipeline job&lt;/li&gt;
&lt;li&gt;SSH connected Storage Server agent&lt;/li&gt;
&lt;li&gt;Secure credentials&lt;/li&gt;
&lt;li&gt;Direct deployment to /var/www/html&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This enables developers to publish updates efficiently and consistently.&lt;/p&gt;

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

</description>
      <category>devops</category>
      <category>jenkins</category>
      <category>cicd</category>
      <category>ci</category>
    </item>
    <item>
      <title>100 Days of DevOps: Day 76</title>
      <dc:creator>Wycliffe A. Onyango</dc:creator>
      <pubDate>Thu, 23 Oct 2025 06:07:34 +0000</pubDate>
      <link>https://forem.com/wycliffealphus/100-days-of-devops-day-76-1fi7</link>
      <guid>https://forem.com/wycliffealphus/100-days-of-devops-day-76-1fi7</guid>
      <description>&lt;h2&gt;
  
  
  Jenkins Security Configuration: Project-Based Authorization
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Project Summary
&lt;/h3&gt;

&lt;p&gt;This article documents the configuration process to grant fine-grained, job-specific permissions to new developers, &lt;strong&gt;sam&lt;/strong&gt; and &lt;strong&gt;rohan&lt;/strong&gt;, on the &lt;strong&gt;Packages&lt;/strong&gt; job within the xFusionCorp Industries Jenkins instance. The task utilized the &lt;strong&gt;Project-based Matrix Authorization Strategy&lt;/strong&gt; to ensure the Principle of Least Privilege.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Users:&lt;/strong&gt; &lt;code&gt;admin&lt;/code&gt;, &lt;code&gt;sam&lt;/code&gt;, &lt;code&gt;rohan&lt;/code&gt; exist in the Jenkins Security Realm.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Job:&lt;/strong&gt; &lt;code&gt;Packages&lt;/code&gt; job exists.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plugin:&lt;/strong&gt; &lt;strong&gt;Matrix Authorization Strategy Plugin&lt;/strong&gt; installed and Jenkins restarted.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Stage 1: Global Security Configuration (The Parent ACL)
&lt;/h2&gt;

&lt;p&gt;The initial attempt failed because the users lacked the fundamental global permission to view the Jenkins UI. This step rectifies that to ensure successful login.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  1.1 Activate Authorization Strategy
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; Log in as &lt;strong&gt;&lt;code&gt;admin&lt;/code&gt;&lt;/strong&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; then &lt;strong&gt;Configure Global Security&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Under the &lt;strong&gt;Authorization&lt;/strong&gt; section, select &lt;strong&gt;Project-based Matrix Authorization Strategy&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  1.2 Grant Global Read Access
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Overall/Read&lt;/strong&gt; permission is the minimum requirement for any user to successfully log in and see the Jenkins dashboard.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; In the &lt;strong&gt;Global&lt;/strong&gt; permission matrix:&lt;/li&gt;
&lt;li&gt;Add user &lt;strong&gt;&lt;code&gt;sam&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add user &lt;strong&gt;&lt;code&gt;rohan&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For both &lt;code&gt;sam&lt;/code&gt; and &lt;code&gt;rohan&lt;/code&gt;, &lt;strong&gt;check only the box for Overall then Read&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click &lt;strong&gt;Save&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This configuration ensures the users can log in, thus resolving the &lt;strong&gt;"missing the Overall/Read permission"&lt;/strong&gt; error.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnsidsj4bam3gg5nte6qu.png" alt="read permission" width="800" height="408"&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Stage 2: Packages Job Configuration (The Child ACL)
&lt;/h2&gt;

&lt;p&gt;This stage applies the specific job-level permissions as required by the development team.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Navigate to the Jenkins dashboard and open the &lt;strong&gt;Packages&lt;/strong&gt; job.&lt;/li&gt;
&lt;li&gt; Click &lt;strong&gt;Configure&lt;/strong&gt; in the left sidebar.&lt;/li&gt;
&lt;li&gt; Scroll to the &lt;strong&gt;Authorization&lt;/strong&gt; section and &lt;strong&gt;check the box for "Enable project-based security."&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  2.1 Configure Inheritance Strategy (Requirement A)
&lt;/h3&gt;

&lt;p&gt;Under the Authorization settings, the required inheritance rule was applied:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Select Inheritance Strategy then Inherit permissions from parent ACL.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;This setting correctly combines the global Overall/Read permission with the project-specific permissions defined below.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2.2 Grant Specific Job Permissions (Requirements B &amp;amp; C)
&lt;/h3&gt;

&lt;p&gt;The final, specific permissions were granted in the project-level matrix:&lt;/p&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;Requirement&lt;/th&gt;
&lt;th&gt;Job/Read&lt;/th&gt;
&lt;th&gt;Job/Build&lt;/th&gt;
&lt;th&gt;Job/Configure&lt;/th&gt;
&lt;th&gt;Job/Cancel&lt;/th&gt;
&lt;th&gt;Job/Update&lt;/th&gt;
&lt;th&gt;SCM/Tag&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;sam&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Build, Configure, Read&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;rohan&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Build, Cancel, Configure, Read, Update, Tag&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;4.Click &lt;strong&gt;Save&lt;/strong&gt; to apply the configuration to the &lt;strong&gt;Packages&lt;/strong&gt; job.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnffuikv0mnz2e943cnfv.png" alt="Packages" width="800" height="320"&gt;
&lt;/h2&gt;

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

&lt;p&gt;The configuration is now complete and verified:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;sam&lt;/strong&gt; can log in and perform read, build, and configure actions on the &lt;strong&gt;Packages&lt;/strong&gt; job.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;rohan&lt;/strong&gt; can log in and has full control over the &lt;strong&gt;Packages&lt;/strong&gt; job, including the ability to cancel builds and tag SCM revisions.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Neither user has global administrative access, adhering to security best practices.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The successful implementation demonstrates the correct application of the &lt;strong&gt;Project-based Matrix Authorization Strategy&lt;/strong&gt; for granular access control in the Jenkins environment.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>jenkins</category>
      <category>cicd</category>
      <category>ci</category>
    </item>
    <item>
      <title>100 Days of DevOps: Day 75</title>
      <dc:creator>Wycliffe A. Onyango</dc:creator>
      <pubDate>Wed, 22 Oct 2025 07:19:12 +0000</pubDate>
      <link>https://forem.com/wycliffealphus/100-days-of-devops-day-75-221</link>
      <guid>https://forem.com/wycliffealphus/100-days-of-devops-day-75-221</guid>
      <description>&lt;h2&gt;
  
  
  Jenkins Agent Node Configuration
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Project Goal
&lt;/h2&gt;

&lt;p&gt;The primary goal was to successfully connect three Linux Application Servers (&lt;strong&gt;stapp01&lt;/strong&gt;, &lt;strong&gt;stapp02&lt;/strong&gt;, &lt;strong&gt;stapp03&lt;/strong&gt;) as permanent Jenkins agent nodes to the Jenkins controller using the SSH method.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Status
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;SUCCESS.&lt;/strong&gt; All three agent nodes (&lt;strong&gt;App_server_1&lt;/strong&gt;, &lt;strong&gt;App_server_2&lt;/strong&gt;, and &lt;strong&gt;App_server_3&lt;/strong&gt;) are now online, connected to the Jenkins controller, and ready to accept build jobs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisite&lt;/strong&gt;&lt;/p&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;&lt;strong&gt;Install SSH Plugin&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Navigate to &lt;strong&gt;Manage Jenkins&lt;/strong&gt; then &lt;strong&gt;Manage Plugins&lt;/strong&gt; then &lt;strong&gt;Available Plugins&lt;/strong&gt;. Search for and install the &lt;strong&gt;"SSH Agents plugin"&lt;/strong&gt; (or similar, depending on Jenkins version/naming).&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Core Jenkins UI Configuration (Per Node)
&lt;/h2&gt;

&lt;p&gt;The following settings were applied when creating each new permanent node in Jenkins (&lt;strong&gt;Manage Jenkins&lt;/strong&gt; then &lt;strong&gt;Manage Nodes&lt;/strong&gt; then &lt;strong&gt;New Node&lt;/strong&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Credentials Setup
&lt;/h3&gt;

&lt;p&gt;Before configuring the node, the SSH credentials for each agent user were added to Jenkins.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Navigate to &lt;strong&gt;Manage Jenkins&lt;/strong&gt; then &lt;strong&gt;Credentials&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt; Click &lt;strong&gt;Add Credentials&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Set &lt;strong&gt;Kind&lt;/strong&gt; to &lt;code&gt;Username with private key&lt;/code&gt; (or &lt;code&gt;SSH Username with password&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; Input the &lt;strong&gt;Username&lt;/strong&gt; (&lt;code&gt;tony&lt;/code&gt;, &lt;code&gt;steve&lt;/code&gt;, or &lt;code&gt;banner&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; Provide the corresponding &lt;strong&gt;Private Key&lt;/strong&gt; (or Password).&lt;/li&gt;
&lt;li&gt; Assign an easily identifiable &lt;strong&gt;ID&lt;/strong&gt; (e.g., &lt;code&gt;tony-ssh-key&lt;/code&gt;). This ID is selected during node configuration.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  2. Node Configuration Table
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Configuration Field&lt;/th&gt;
&lt;th&gt;App_server_1 (stapp01)&lt;/th&gt;
&lt;th&gt;App_server_2 (stapp02)&lt;/th&gt;
&lt;th&gt;App_server_3 (stapp03)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Node Name&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;App_server_1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;App_server_2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;App_server_3&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Remote Root Directory&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/home/tony/jenkins-agent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/home/steve/jenkins-agent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/home/banner/jenkins-agent&lt;/code&gt;&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;&lt;code&gt;stapp01&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;stapp02&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;stapp03&lt;/code&gt;&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;&lt;code&gt;Launch agent via SSH&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Launch agent via SSH&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Launch agent via SSH&lt;/code&gt;&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;
&lt;strong&gt;&lt;code&gt;stapp01&lt;/code&gt;&lt;/strong&gt; (or its IP address)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;stapp02&lt;/code&gt;&lt;/strong&gt; (or its IP address)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;stapp03&lt;/code&gt;&lt;/strong&gt; (or its IP address)&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;&lt;strong&gt;Select the &lt;code&gt;tony&lt;/code&gt; credential ID&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Select the &lt;code&gt;steve&lt;/code&gt; credential ID&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Select the &lt;code&gt;banner&lt;/code&gt; credential ID&lt;/strong&gt;&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;
&lt;code&gt;Non-verifying Verification Strategy&lt;/code&gt; (or preferred method)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Non-verifying Verification Strategy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Non-verifying Verification Strategy&lt;/code&gt;&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;&lt;code&gt;Only build jobs with label expressions matching this node&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Only build jobs with label expressions matching this node&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Only build jobs with label expressions matching this node&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Key Challenge Resolution: Java 21 Configuration Steps
&lt;/h2&gt;

&lt;p&gt;The initial failure was due to the Jenkins SSH agent session failing to find the correct Java executable. The resolution involved guaranteeing that &lt;strong&gt;Java 21&lt;/strong&gt; was explicitly set up for the system and the agent users on all three App Servers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install Java 21:&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="nb"&gt;sudo &lt;/span&gt;yum &lt;span class="nb"&gt;install &lt;/span&gt;java-21-openjdk &lt;span class="nt"&gt;-y&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final Step: Agent Launch
&lt;/h2&gt;

&lt;p&gt;With all environment and Jenkins configurations complete, the nodes were successfully launched from the Jenkins UI, resolving the original dependency and credential errors, and transitioning all agents to an &lt;strong&gt;Online&lt;/strong&gt; status.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwmc1uwo0ick2sw0k1jsb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwmc1uwo0ick2sw0k1jsb.png" alt="general" width="800" height="224"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbbvrhjv8tazly57k5cp6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbbvrhjv8tazly57k5cp6.png" alt="one" width="800" height="261"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6q7fqj0zu5rwxmd9553k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6q7fqj0zu5rwxmd9553k.png" alt="two" width="800" height="261"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr64thdrgvdov9aykqyh0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr64thdrgvdov9aykqyh0.png" alt="three" width="800" height="261"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>jenkins</category>
      <category>cicd</category>
      <category>ci</category>
    </item>
    <item>
      <title>100 Days of DevOps: Day 74</title>
      <dc:creator>Wycliffe A. Onyango</dc:creator>
      <pubDate>Tue, 21 Oct 2025 06:57:33 +0000</pubDate>
      <link>https://forem.com/wycliffealphus/100-days-of-devops-day-74-1a3o</link>
      <guid>https://forem.com/wycliffealphus/100-days-of-devops-day-74-1a3o</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Automating Database Backup in Jenkins&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Overview&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The Jenkins job, &lt;code&gt;database-backup&lt;/code&gt;, has been successfully implemented to automate database backups for &lt;code&gt;kodekloud_db01&lt;/code&gt; and securely transfer the dump to the Backup Server. This solution successfully navigated a major administrative hurdle: the &lt;strong&gt;Jenkins Server lacked the &lt;code&gt;mysqldump&lt;/code&gt; utility&lt;/strong&gt;, and the &lt;code&gt;jenkins&lt;/code&gt; user was blocked from using &lt;code&gt;sudo&lt;/code&gt; to install it.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;I. Problem &amp;amp; Solution Strategy&lt;/strong&gt;
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;The Challenge&lt;/th&gt;
&lt;th&gt;The Solution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;mysqldump: command not found&lt;/code&gt;&lt;/strong&gt; on Jenkins Server.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Remote Execution:&lt;/strong&gt; Use SSH to execute &lt;code&gt;mysqldump&lt;/code&gt; on the Database Server (&lt;code&gt;stdb01&lt;/code&gt;) where the utility exists.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Password prompts&lt;/strong&gt; break automated jobs.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Passwordless SSH:&lt;/strong&gt; Configure key-based authentication from the Jenkins Server to both target servers.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Infrastructure Details Used&lt;/strong&gt;
&lt;/h3&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;Hostname&lt;/th&gt;
&lt;th&gt;User&lt;/th&gt;
&lt;th&gt;Password&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;Jenkins&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jenkins.stratos.xfusioncorp.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jenkins&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;j@rv!s&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CI/CD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Database&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;stdb01.stratos.xfusioncorp.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;peter&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Sp!dy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;DB Server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Backup&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;stbkp01.stratos.xfusioncorp.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;clint&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;H@wk3y3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Backup Server&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;II. Command Line Steps (Prerequisites)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;All following commands are executed while SSHed into the &lt;strong&gt;Jenkins Server&lt;/strong&gt; as the &lt;strong&gt;&lt;code&gt;jenkins&lt;/code&gt;&lt;/strong&gt; user.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 1: Generate the SSH Key Pair&lt;/strong&gt;
&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;# 1. Generate an RSA 4096-bit key pair.&lt;/span&gt;
jenkins@jenkins:~&lt;span class="nv"&gt;$ &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="c"&gt;# IMPORTANT: Press ENTER twice to leave the passphrase EMPTY!&lt;/span&gt;

&lt;span class="c"&gt;# Key files are saved to: /var/lib/jenkins/.ssh/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Step 2: Install Public Key on Target Servers&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The public key (&lt;code&gt;/var/lib/jenkins/.ssh/id_rsa.pub&lt;/code&gt;) is installed on both target machines to enable passwordless access.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Target&lt;/th&gt;
&lt;th&gt;User&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Password to Enter&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DB Server&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;peter&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ssh-copy-id -i /var/lib/jenkins/.ssh/id_rsa.pub peter@stdb01.stratos.xfusioncorp.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Sp!dy&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Backup Server&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;clint&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ssh-copy-id -i /var/lib/jenkins/.ssh/id_rsa.pub clint@stbkp01.stratos.xfusioncorp.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;H@wk3y3&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;III. Jenkins Job Configuration&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 3: Job Setup and Scheduling&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Create Job:&lt;/strong&gt; Create a new &lt;strong&gt;Freestyle Project&lt;/strong&gt; named &lt;code&gt;database-backup&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Scheduling:&lt;/strong&gt; In the &lt;strong&gt;Build Triggers&lt;/strong&gt; section, enable &lt;strong&gt;"Build periodically"&lt;/strong&gt; and set the schedule:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*/10 * * * *
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 4: Execute Shell Script (Remote Execution)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In the &lt;strong&gt;Build&lt;/strong&gt; section, add an &lt;strong&gt;"Execute shell"&lt;/strong&gt; build step and use the following script. This script executes &lt;code&gt;mysqldump&lt;/code&gt; remotely on the DB Server and pipes the output back to the Jenkins 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="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="c"&gt;# --- Database Details ---&lt;/span&gt;
&lt;span class="nv"&gt;DB_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"kodekloud_db01"&lt;/span&gt;
&lt;span class="nv"&gt;DB_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"kodekloud_roy"&lt;/span&gt;
&lt;span class="nv"&gt;DB_PASS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"asdfgdsd"&lt;/span&gt;
&lt;span class="nv"&gt;DB_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"stdb01.stratos.xfusioncorp.com"&lt;/span&gt;
&lt;span class="nv"&gt;DB_USER_SSH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"peter"&lt;/span&gt; 

&lt;span class="c"&gt;# --- Backup Details ---&lt;/span&gt;
&lt;span class="nv"&gt;BACKUP_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"stbkp01.stratos.xfusioncorp.com"&lt;/span&gt;
&lt;span class="nv"&gt;BACKUP_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"clint"&lt;/span&gt;
&lt;span class="nv"&gt;BACKUP_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/home/clint/db_backups"&lt;/span&gt;

&lt;span class="c"&gt;# --- File Naming ---&lt;/span&gt;
&lt;span class="nv"&gt;DATE_FORMAT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%F&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;DUMP_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"db_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DATE_FORMAT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.sql"&lt;/span&gt;

&lt;span class="c"&gt;# 1. Execute DUMP REMOTELY on the DB Server and PIPE output to the Jenkins Server&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Executing remote database dump on &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DB_HOST&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; and piping output..."&lt;/span&gt;
ssh &lt;span class="nt"&gt;-T&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DB_USER_SSH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;@&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DB_HOST&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"mysqldump -u &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DB_USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; -p&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DB_PASS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DB_NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DUMP_FILE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt; &lt;span class="nt"&gt;-ne&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Remote database dump failed. Check SSH key setup or DB credentials."&lt;/span&gt;
    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DUMP_FILE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Database dump successful and saved locally: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DUMP_FILE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# 2. Copy the dump file from the Jenkins Server to the Backup Server&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Copying &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DUMP_FILE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; to Backup Server (&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BACKUP_HOST&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)..."&lt;/span&gt;
scp &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DUMP_FILE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BACKUP_USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;@&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BACKUP_HOST&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;:&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BACKUP_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"File successfully copied to &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BACKUP_HOST&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BACKUP_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DUMP_FILE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="c"&gt;# Clean up the local dump file&lt;/span&gt;
    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DUMP_FILE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ERROR: Failed to copy the dump file via SCP."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;IV. Final Result&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The job successfully ran and achieved the required outcome:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[database-backup] $ /bin/bash /tmp/jenkins...sh
Executing remote database dump on stdb01.stratos.xfusioncorp.com and piping output...
Database dump successful and saved locally: db_2025-10-21.sql
Copying db_2025-10-21.sql to Backup Server (stbkp01.stratos.xfusioncorp.com)...
File successfully copied to stbkp01.stratos.xfusioncorp.com:/home/clint/db_backups/db_2025-10-21.sql
Finished: SUCCESS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

</description>
      <category>jenkins</category>
      <category>devops</category>
      <category>cicd</category>
      <category>ci</category>
    </item>
    <item>
      <title>100 Days of DevOps: Day 73</title>
      <dc:creator>Wycliffe A. Onyango</dc:creator>
      <pubDate>Mon, 20 Oct 2025 09:06:05 +0000</pubDate>
      <link>https://forem.com/wycliffealphus/100-days-of-devops-day-73-2o7i</link>
      <guid>https://forem.com/wycliffealphus/100-days-of-devops-day-73-2o7i</guid>
      <description>&lt;h2&gt;
  
  
  Automated Apache Log Collection for xFusionCorp Industries
&lt;/h2&gt;

&lt;p&gt;I successfully implemented the &lt;strong&gt;&lt;code&gt;copy-logs&lt;/code&gt;&lt;/strong&gt; Jenkins job to automatically collect Apache access and error logs. This provides critical log data for immediate troubleshooting, bridging the gap until the full centralized logging system is complete.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Initial Access and Plugin Installation
&lt;/h3&gt;

&lt;p&gt;The first step involves accessing the Jenkins UI and ensuring the necessary remote execution plugin is installed.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Access Jenkins:&lt;/strong&gt; Log in using username &lt;strong&gt;&lt;code&gt;admin&lt;/code&gt;&lt;/strong&gt; and password &lt;strong&gt;&lt;code&gt;Adm!n321&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Install Plugin:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Manage Jenkins&lt;/strong&gt; then &lt;strong&gt;Manage Plugins&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Go to the &lt;strong&gt;Available&lt;/strong&gt; tab and search for &lt;strong&gt;Publish Over SSH&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select the plugin and click &lt;strong&gt;Install without restart&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;After installation, click &lt;strong&gt;Restart Jenkins&lt;/strong&gt; (if necessary and no jobs are running).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  2. Configure Global SSH Server Details
&lt;/h3&gt;

&lt;p&gt;After the plugin is installed, the target servers must be configured in Jenkins' global settings.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Navigate to &lt;strong&gt;Manage Jenkins&lt;/strong&gt; then &lt;strong&gt;Configure System&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Scroll down to the &lt;strong&gt;Publish over SSH&lt;/strong&gt; section and click &lt;strong&gt;Add&lt;/strong&gt;.&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;Server&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Hostname/IP&lt;/th&gt;
&lt;th&gt;Username&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;&lt;strong&gt;App Server 1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;app-server-1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;172.16.238.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tony&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Ir0nN@M&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Storage Server&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;(Not strictly required for Jenkins connection, but credentials are needed for &lt;code&gt;scp&lt;/code&gt;)&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;172.16.238.15&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;natasha&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Bl@kW&lt;/code&gt;&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;Test Configuration&lt;/strong&gt; to verify connectivity (for &lt;code&gt;app-server-1&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; Click &lt;strong&gt;Save&lt;/strong&gt; at the bottom of the page.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  3. Configure SSH Key Authentication (Prerequisite)
&lt;/h3&gt;

&lt;p&gt;To ensure the non-interactive &lt;code&gt;scp&lt;/code&gt; command works, SSH keys must be set up &lt;em&gt;manually&lt;/em&gt; between the execution user (&lt;code&gt;tony&lt;/code&gt; on &lt;code&gt;stapp01&lt;/code&gt;) and the destination user (&lt;code&gt;natasha&lt;/code&gt; on &lt;code&gt;ststor01&lt;/code&gt;).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;SSH into App Server 1 (&lt;code&gt;stapp01&lt;/code&gt;)&lt;/strong&gt; as user &lt;code&gt;tony&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Generate Key Pair:&lt;/strong&gt; If no key exists, generate one (use default file location and no passphrase for automation):&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; rsa
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Copy Public Key:&lt;/strong&gt; Copy the key to the Storage Server's authorized keys file. You will be prompted for &lt;code&gt;natasha&lt;/code&gt;'s password (&lt;code&gt;Bl@kW&lt;/code&gt;) for this one-time setup:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-copy-id natasha@172.16.238.15
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  4. Create and Configure the Jenkins Job (&lt;code&gt;copy-logs&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;The final step is to build the Jenkins job using the established connectivity.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create Job:&lt;/strong&gt; On the Jenkins dashboard, click &lt;strong&gt;New Item&lt;/strong&gt;, name it &lt;strong&gt;&lt;code&gt;copy-logs&lt;/code&gt;&lt;/strong&gt;, and select &lt;strong&gt;Freestyle project&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Schedule Build:&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Go to the &lt;strong&gt;Build Triggers&lt;/strong&gt; section.&lt;/li&gt;
&lt;li&gt;Check &lt;strong&gt;Build periodically&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Enter the schedule: &lt;code&gt;*/5 * * * *&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3.&lt;strong&gt;Configure Build Step:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go 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; then &lt;strong&gt;Execute shell script on remote host using SSH&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSH Server:&lt;/strong&gt; Select &lt;code&gt;app-server-1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Command:&lt;/strong&gt; Enter the following script to execute the file transfer:&lt;/p&gt;




&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Define variables&lt;/span&gt;
&lt;span class="nv"&gt;LOG_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/log/httpd"&lt;/span&gt; &lt;span class="c"&gt;# Confirmed Apache log path on CentOS&lt;/span&gt;
&lt;span class="nv"&gt;DEST_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"natasha"&lt;/span&gt;
&lt;span class="nv"&gt;DEST_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"172.16.238.15"&lt;/span&gt;
&lt;span class="nv"&gt;DEST_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/usr/src/data/"&lt;/span&gt;

&lt;span class="c"&gt;# Executes scp using the previously set up SSH keys&lt;/span&gt;
scp &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LOG_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/access_log &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LOG_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/error_log &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DEST_USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;@&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DEST_HOST&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;:&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DEST_PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;4.&lt;strong&gt;Save:&lt;/strong&gt; Click &lt;strong&gt;Save&lt;/strong&gt; to finalize the job configuration.&lt;/p&gt;

&lt;p&gt;The job is now running every five minutes, securely transferring the required Apache logs and achieving a &lt;strong&gt;SUCCESS&lt;/strong&gt; status.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>jenkins</category>
      <category>cicd</category>
      <category>ci</category>
    </item>
    <item>
      <title>100 Days of DevOps: Day 72</title>
      <dc:creator>Wycliffe A. Onyango</dc:creator>
      <pubDate>Mon, 20 Oct 2025 07:48:57 +0000</pubDate>
      <link>https://forem.com/wycliffealphus/100-days-of-devops-day-72-5c5f</link>
      <guid>https://forem.com/wycliffealphus/100-days-of-devops-day-72-5c5f</guid>
      <description>&lt;h2&gt;
  
  
  Mastering Jenkins' Parameterized Builds
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Introduction
&lt;/h3&gt;

&lt;p&gt;Understanding Jenkins' core functionalities is crucial. Today's task is to create and run a simple parameterized Jenkins job. This exercise not only familiarizes us with the Jenkins UI but also demonstrates how to build jobs that accept user input, making them highly flexible and reusable.&lt;/p&gt;

&lt;p&gt;This article details the step-by-step process of configuring and executing a parameterized Jenkins job, highlighting the configuration points and showcasing the successful execution.&lt;/p&gt;

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

&lt;p&gt;The objective was to create a Jenkins Freestyle project named &lt;code&gt;parameterized-job&lt;/code&gt; with two parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;String Parameter:&lt;/strong&gt; &lt;code&gt;Stage&lt;/code&gt; (default value: &lt;code&gt;Build&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choice Parameter:&lt;/strong&gt; &lt;code&gt;env&lt;/code&gt; (choices: &lt;code&gt;Development&lt;/code&gt;, &lt;code&gt;Staging&lt;/code&gt;, &lt;code&gt;Production&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The job needed to execute a shell command that echoes the values passed to these parameters. Finally, we had to build the job at least once, specifically setting the &lt;code&gt;env&lt;/code&gt; parameter to &lt;code&gt;Production&lt;/code&gt;, to ensure its successful operation.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Step-by-Step Implementation
&lt;/h3&gt;

&lt;h4&gt;
  
  
  3.1. Accessing Jenkins and Creating a New Item
&lt;/h4&gt;

&lt;p&gt;After logging into the Jenkins UI, the first step was to initiate the creation of a new job.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;From the Jenkins dashboard, click on &lt;strong&gt;"New Item"&lt;/strong&gt; in the left-hand navigation pane.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the "Enter an item name" field, type &lt;code&gt;parameterized-job&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select &lt;strong&gt;"Freestyle project"&lt;/strong&gt; as the item type.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click &lt;strong&gt;"OK"&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  3.2. Configuring Parameters
&lt;/h4&gt;

&lt;p&gt;With the job shell created, the next crucial step was to make it parameterized. This involved adding a String parameter and a Choice parameter.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;On the job configuration page, check the box &lt;strong&gt;"This project is parameterized"&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click &lt;strong&gt;"Add Parameter"&lt;/strong&gt; and select &lt;strong&gt;"String Parameter"&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Set &lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;Stage&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Set &lt;strong&gt;Default Value:&lt;/strong&gt; &lt;code&gt;Build&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3.Click &lt;strong&gt;"Add Parameter"&lt;/strong&gt; again and select &lt;strong&gt;"Choice Parameter"&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set &lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;env&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Enter the choices (one per line):&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Development
Staging
Production
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3.3. Defining the Build Step (Shell Command)
&lt;/h4&gt;

&lt;p&gt;To demonstrate that the parameters are being correctly received, a simple shell command was added to echo their values.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Scroll down to the &lt;strong&gt;"Build"&lt;/strong&gt; section.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click &lt;strong&gt;"Add build step"&lt;/strong&gt; and select &lt;strong&gt;"Execute shell"&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the "Command" text area, enter the following:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"The 'Stage' parameter value is: &lt;/span&gt;&lt;span class="nv"&gt;$Stage&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;"The 'env' parameter value is: &lt;/span&gt;&lt;span class="nv"&gt;$env&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally, click &lt;strong&gt;"Save"&lt;/strong&gt; at the bottom of the page to apply all changes.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  3.4. Building the Job with Specific Parameters
&lt;/h4&gt;

&lt;p&gt;The final step was to execute the job, ensuring we passed &lt;code&gt;Production&lt;/code&gt; as the value for the &lt;code&gt;env&lt;/code&gt; parameter.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;On the &lt;code&gt;parameterized-job&lt;/code&gt; dashboard, click &lt;strong&gt;"Build with Parameters"&lt;/strong&gt; in the left-hand menu.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;On the build form, leave &lt;strong&gt;"Stage"&lt;/strong&gt; as its default (&lt;code&gt;Build&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For the &lt;strong&gt;"env"&lt;/strong&gt; dropdown, select &lt;strong&gt;"Production"&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click the &lt;strong&gt;"Build"&lt;/strong&gt; button.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  4. Verification and Console Output
&lt;/h3&gt;

&lt;p&gt;After the build was triggered, we could monitor its progress and review the output to confirm that the parameters were correctly passed and processed.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; In the "Build History" on the left, click on the latest build number (e.g., &lt;code&gt;#1&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; Click &lt;strong&gt;"Console Output"&lt;/strong&gt; to view the build logs.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The console output clearly shows the shell commands executing and echoing the parameter values as expected:&lt;/p&gt;

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

&lt;h3&gt;
  
  
  5. Conclusion
&lt;/h3&gt;

&lt;p&gt;This exercise successfully demonstrated the creation and execution of a parameterized Jenkins job. We now understand how to define various types of parameters, integrate them into build steps, and trigger builds with specific inputs. This foundational knowledge is essential for developing more complex and dynamic CI/CD pipelines.&lt;/p&gt;

&lt;p&gt;The job ran successfully, confirming that Jenkins correctly interpreted and utilized the provided parameters, marking the completion of this task.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>programming</category>
      <category>jenkins</category>
      <category>cicd</category>
    </item>
    <item>
      <title>100 Days of DevOps: Day 71</title>
      <dc:creator>Wycliffe A. Onyango</dc:creator>
      <pubDate>Fri, 17 Oct 2025 06:04:46 +0000</pubDate>
      <link>https://forem.com/wycliffealphus/100-days-of-devops-day-71-1092</link>
      <guid>https://forem.com/wycliffealphus/100-days-of-devops-day-71-1092</guid>
      <description>&lt;h2&gt;
  
  
  Automating Package Installations with Jenkins
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Introduction
&lt;/h3&gt;

&lt;p&gt;The Nautilus DevOps team at Stratos Datacenter required an automated solution for installing and configuring packages on their storage server (&lt;code&gt;ststor01&lt;/code&gt;). This article details the process of creating and configuring a Jenkins job, named &lt;code&gt;install-packages&lt;/code&gt;, to fulfill this requirement, utilizing the &lt;strong&gt;Publish Over SSH&lt;/strong&gt; plugin for remote execution.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Access to Jenkins UI (URL provided by the lab environment).&lt;/li&gt;
&lt;li&gt;Jenkins admin credentials: &lt;code&gt;username: admin&lt;/code&gt;, &lt;code&gt;password: Adm!n321&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Storage server (&lt;code&gt;ststor01&lt;/code&gt;) credentials: &lt;code&gt;username: natasha&lt;/code&gt;, &lt;code&gt;password: Bl@kW&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Publish Over SSH&lt;/code&gt; Jenkins plugin installed and configured.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Step-by-Step Implementation
&lt;/h3&gt;

&lt;h4&gt;
  
  
  3.1. Access Jenkins and Initial Login
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt; Open the Jenkins UI by clicking the Jenkins button in the top bar or navigating to the provided Jenkins URL.&lt;/li&gt;
&lt;li&gt; Log in using the administrative credentials:

&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;h4&gt;
  
  
  3.2. Configure Publish Over SSH Plugin for Remote Connectivity
&lt;/h4&gt;

&lt;p&gt;The core of this task involves executing commands on a remote server (&lt;code&gt;ststor01&lt;/code&gt;). The &lt;code&gt;Publish Over SSH&lt;/code&gt; plugin facilitates this. The initial setup requires configuring the connection details for the target storage server, including its authentication.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Navigate to &lt;strong&gt;Manage Jenkins&lt;/strong&gt; &amp;gt; &lt;strong&gt;Configure System&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scroll down to the &lt;strong&gt;Publish over SSH&lt;/strong&gt; section.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click the &lt;strong&gt;"Add"&lt;/strong&gt; button under &lt;strong&gt;SSH Servers&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fill in the details for the &lt;code&gt;ststor01&lt;/code&gt; storage server:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;ststor01&lt;/code&gt; (or a descriptive name like &lt;code&gt;Nautilus_Storage&lt;/code&gt;)&lt;br&gt;
&lt;strong&gt;Hostname:&lt;/strong&gt; &lt;code&gt;ststor01.stratos.xfusioncorp.com&lt;/code&gt; (as per infrastructure details)&lt;br&gt;
&lt;strong&gt;Username:&lt;/strong&gt; &lt;code&gt;natasha&lt;/code&gt; (as per infrastructure details)&lt;br&gt;
&lt;strong&gt;Remote Directory:&lt;/strong&gt; &lt;code&gt;/tmp&lt;/code&gt; (a common temporary directory)&lt;/p&gt;

&lt;p&gt;5.&lt;strong&gt;Enable Password Authentication:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click the &lt;strong&gt;"Advanced..."&lt;/strong&gt; button to expand further options for the &lt;code&gt;ststor01&lt;/code&gt; entry.&lt;/li&gt;
&lt;li&gt;Check the box: &lt;strong&gt;"Use password authentication, or use a different key"&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;In the &lt;strong&gt;Passphrase / Password&lt;/strong&gt; field that appears, enter the password: &lt;code&gt;Bl@kW&lt;/code&gt; (from infrastructure details).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;6.Click the &lt;strong&gt;"Test Configuration"&lt;/strong&gt; button for the &lt;code&gt;ststor01&lt;/code&gt; server. It should display &lt;strong&gt;"Success"&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;7.Click &lt;strong&gt;"Apply"&lt;/strong&gt; and then &lt;strong&gt;"Save"&lt;/strong&gt; at the bottom of the &lt;strong&gt;Configure System&lt;/strong&gt; page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F77dhbn6rysszpiz84els.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F77dhbn6rysszpiz84els.png" alt="Details" width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  3.3. Create the &lt;code&gt;install-packages&lt;/code&gt; Jenkins Job
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;From the Jenkins Dashboard, click &lt;strong&gt;"New Item"&lt;/strong&gt; on the left-hand navigation pane.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Item Name:&lt;/strong&gt; Enter &lt;code&gt;install-packages&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Type:&lt;/strong&gt; Select &lt;strong&gt;"Freestyle project"&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click &lt;strong&gt;"OK"&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
  
  
  3.4. Configure Job Parameters
&lt;/h4&gt;

&lt;p&gt;To make the job flexible, a string parameter will be added to allow users to specify which package to install.&lt;/p&gt;

&lt;p&gt;1.In the job configuration page, check the box: &lt;strong&gt;"This project is parameterized"&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;2.Click &lt;strong&gt;"Add Parameter"&lt;/strong&gt; and select &lt;strong&gt;"String Parameter"&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Name:&lt;/strong&gt; &lt;code&gt;PACKAGE&lt;/code&gt;&lt;br&gt;
 &lt;strong&gt;Default Value:&lt;/strong&gt; &lt;code&gt;net-tools&lt;/code&gt; (or &lt;code&gt;httpd&lt;/code&gt;, &lt;code&gt;git&lt;/code&gt; for initial testing)&lt;br&gt;
 &lt;strong&gt;Description:&lt;/strong&gt; The name of the package to install on the storage server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F884huf6fqbdmq3yddaz7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F884huf6fqbdmq3yddaz7.png" alt="Job" width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  3.5. Configure the Build Step for Remote Execution
&lt;/h4&gt;

&lt;p&gt;This is where the actual package installation command is defined, using the previously configured SSH connection.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Scroll down to the &lt;strong&gt;"Build"&lt;/strong&gt; section of the job configuration.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click &lt;strong&gt;"Add build step"&lt;/strong&gt; and select &lt;strong&gt;"Send files or execute commands over SSH"&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SSH Server:&lt;/strong&gt; Select &lt;code&gt;ststor01&lt;/code&gt; (or the name you assigned) from the dropdown list.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the &lt;strong&gt;"Exec command"&lt;/strong&gt; textarea, enter the following shell script. This script passes the &lt;code&gt;natasha&lt;/code&gt; user's password to &lt;code&gt;sudo&lt;/code&gt; using &lt;code&gt;sudo -S&lt;/code&gt;, which is crucial for privilege elevation on &lt;code&gt;ststor01&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="c"&gt;# Define the password for sudo escalation (sensitive, but necessary for automation via script)&lt;/span&gt;
&lt;span class="nv"&gt;PACKAGE_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Bl@kW"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting installation of package: &lt;/span&gt;&lt;span class="nv"&gt;$PACKAGE&lt;/span&gt;&lt;span class="s2"&gt; on ststor01..."&lt;/span&gt;

&lt;span class="c"&gt;# Use 'echo $PACKAGE_PASSWORD | sudo -S' to pass the password to sudo&lt;/span&gt;
&lt;span class="c"&gt;# The '-S' flag tells sudo to read the password from standard input.&lt;/span&gt;
&lt;span class="c"&gt;# 'yum install -y' is used for CentOS/RHEL systems (like ststor01) to install packages.&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$PACKAGE_PASSWORD&lt;/span&gt; | &lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-S&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="c"&gt;# Check the exit code of the 'yum install' command&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"SUCCESS: Package '&lt;/span&gt;&lt;span class="nv"&gt;$PACKAGE&lt;/span&gt;&lt;span class="s2"&gt;' installed successfully."&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"FAILURE: Package '&lt;/span&gt;&lt;span class="nv"&gt;$PACKAGE&lt;/span&gt;&lt;span class="s2"&gt;' installation failed. Check server logs."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;/ol&gt;

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

&lt;p&gt;5.Click &lt;strong&gt;"Save"&lt;/strong&gt; at the bottom of the job configuration page.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Testing and Verification
&lt;/h3&gt;

&lt;p&gt;After configuring the job, it's essential to test its functionality and reliability.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Navigate to the &lt;code&gt;install-packages&lt;/code&gt; job page.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click &lt;strong&gt;"Build with Parameters"&lt;/strong&gt; on the left menu.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the &lt;strong&gt;PACKAGE&lt;/strong&gt; field, enter a package name that is likely not installed (e.g., &lt;code&gt;git&lt;/code&gt;, &lt;code&gt;htop&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click &lt;strong&gt;"Build"&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Monitor the build in the &lt;strong&gt;Build History&lt;/strong&gt; section. Once it completes, click on the build number (e.g., &lt;code&gt;#1&lt;/code&gt;) and then select &lt;strong&gt;"Console Output"&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Verify that the console output displays &lt;code&gt;SUCCESS: Package '...' installed successfully.&lt;/code&gt; and &lt;code&gt;Finished: SUCCESS&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;7.&lt;strong&gt;Run the job again&lt;/strong&gt; with a different package (e.g., &lt;code&gt;net-tools&lt;/code&gt;) to confirm the job's reliability and repeated successful execution. This ensures the configuration is robust.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Conclusion
&lt;/h3&gt;

&lt;p&gt;By following these steps, a robust Jenkins job named &lt;code&gt;install-packages&lt;/code&gt; has been successfully created. This job automates the process of installing specified packages on the &lt;code&gt;ststor01&lt;/code&gt; storage server within the Stratos Datacenter infrastructure, significantly improving operational efficiency for the Nautilus DevOps team. The use of the &lt;code&gt;Publish Over SSH&lt;/code&gt; plugin combined with parameterized builds offers a flexible and secure way to manage remote package installations.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>jenkins</category>
      <category>cicd</category>
      <category>ci</category>
    </item>
    <item>
      <title>100 Days of DevOps: Day 70 (New)</title>
      <dc:creator>Wycliffe A. Onyango</dc:creator>
      <pubDate>Thu, 16 Oct 2025 06:31:33 +0000</pubDate>
      <link>https://forem.com/wycliffealphus/100-days-of-devops-day-70-new-6p5</link>
      <guid>https://forem.com/wycliffealphus/100-days-of-devops-day-70-new-6p5</guid>
      <description>&lt;h2&gt;
  
  
  Jenkins User Access Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Objective&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The goal of this task was to &lt;strong&gt;set up secure user access&lt;/strong&gt; in Jenkins by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating a new user named &lt;strong&gt;Jim&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Enabling &lt;strong&gt;Project-based Matrix Authorization Strategy&lt;/strong&gt; for fine-grained permissions.&lt;/li&gt;
&lt;li&gt;Granting &lt;strong&gt;read-only&lt;/strong&gt; access to Jim.&lt;/li&gt;
&lt;li&gt;Removing all permissions from &lt;strong&gt;Anonymous&lt;/strong&gt; users.&lt;/li&gt;
&lt;li&gt;Retaining full &lt;strong&gt;administrative privileges&lt;/strong&gt; for the admin account.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This configuration ensures that only authenticated users can access Jenkins and that permissions are strictly role-based.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Steps Performed&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 1: Login to Jenkins&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Access Jenkins UI using your browser.&lt;/li&gt;
&lt;li&gt;Log in using:&lt;/li&gt;
&lt;/ol&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;h3&gt;
  
  
  &lt;strong&gt;Step 2: Create a New User (Jim)&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to:
&lt;strong&gt;Manage Jenkins → Users → Create User&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Fill in the details:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Username:&lt;/strong&gt; &lt;code&gt;jim&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Password:&lt;/strong&gt; &lt;code&gt;YchZHRcLkL&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full Name:&lt;/strong&gt; &lt;code&gt;Jim&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Create User&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Step 3: Install Matrix Authorization Plugin&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to:
&lt;strong&gt;Manage Jenkins → Plugins → 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;Click &lt;strong&gt;Install without restart&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Once done, select:
&lt;strong&gt;Restart Jenkins when installation is complete and no jobs are running.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Wait until the Jenkins login page reloads.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 4: Configure Global Security&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to:
&lt;strong&gt;Manage Jenkins → Security → Configure Global Security&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;Authorization&lt;/strong&gt;, select:
✅ &lt;em&gt;Project-based Matrix Authorization Strategy&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;In the permission table, assign as follows:&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Click &lt;strong&gt;Save&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 5: Configure Job-level Access&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open any existing Jenkins job.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Configure&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Scroll down to &lt;strong&gt;Build Permissions&lt;/strong&gt; or &lt;strong&gt;Enable project-based security&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Add user &lt;code&gt;jim&lt;/code&gt; and grant only &lt;strong&gt;Read&lt;/strong&gt; access.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Save&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 6: Verify Permissions&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Log out from &lt;code&gt;admin&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Log in as &lt;strong&gt;jim&lt;/strong&gt; using:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Username: &lt;code&gt;jim&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Password: &lt;code&gt;YchZHRcLkL&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Confirm that Jim can only:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;View dashboard and job details.&lt;/li&gt;
&lt;li&gt;Cannot configure or trigger builds.&lt;/li&gt;
&lt;/ul&gt;

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

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

&lt;h2&gt;
  
  
  &lt;strong&gt;Results&lt;/strong&gt;
&lt;/h2&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;Permissions&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;&lt;strong&gt;admin&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Full administrative control&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;jim&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Read-only (view jobs, no modification)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;anonymous&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No access&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;✅ Jenkins user access control successfully configured and verified.&lt;br&gt;
This enhances the &lt;strong&gt;security and accountability&lt;/strong&gt; within the Nautilus CI/CD environment.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Notes&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Future team members should be added using the same controlled process.&lt;/li&gt;
&lt;li&gt;Always ensure &lt;strong&gt;Matrix Authorization&lt;/strong&gt; is active before modifying user roles.&lt;/li&gt;
&lt;li&gt;It is recommended to take a full Jenkins configuration backup after major security changes.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>jenkins</category>
      <category>devops</category>
      <category>cicd</category>
      <category>automation</category>
    </item>
    <item>
      <title>100 Days of DevOps: Day 70</title>
      <dc:creator>Wycliffe A. Onyango</dc:creator>
      <pubDate>Wed, 15 Oct 2025 08:10:48 +0000</pubDate>
      <link>https://forem.com/wycliffealphus/100-days-of-devops-day-70-23g3</link>
      <guid>https://forem.com/wycliffealphus/100-days-of-devops-day-70-23g3</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Installing Git and GitLab Plugins on Jenkins via the UI&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Overview&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;As part of the Nautilus DevOps setup, I configured a new &lt;strong&gt;Jenkins server&lt;/strong&gt; to handle CI/CD jobs.&lt;br&gt;
The initial goal was to install two essential plugins — &lt;strong&gt;Git&lt;/strong&gt; and &lt;strong&gt;GitLab&lt;/strong&gt; — which allow Jenkins to integrate with source control systems for continuous integration.&lt;/p&gt;

&lt;p&gt;During installation via the Jenkins UI, I ran into several &lt;strong&gt;dependency errors&lt;/strong&gt;, but eventually discovered that a simple &lt;strong&gt;Jenkins restart&lt;/strong&gt; solved the issue completely.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Step 1: Accessing Jenkins&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;I started by logging in to Jenkins through the web interface:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Login credentials:&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;Once logged in, the Jenkins dashboard appeared — the starting point for all administrative actions.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Step 2: Navigating to Plugin Manager&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;From the left-hand sidebar, I selected:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Manage Jenkins → Plugins&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This opened the Jenkins &lt;strong&gt;Plugin Manager&lt;/strong&gt;, where plugins can be installed, updated, or removed.&lt;br&gt;
I switched to the &lt;strong&gt;Available plugins&lt;/strong&gt; tab and searched for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Git&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GitLab&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I selected both and clicked &lt;strong&gt;Install without restart&lt;/strong&gt; to begin installation.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Step 3: Encountering the Installation Errors&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;While the installation began, several red error messages appeared:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Failed to load: Jenkins Git client plugin (git-client 6.2.1)
 - Failed to load: Mina SSHD API :: Common (mina-sshd-api-common)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and later:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Failed to load: Mina SSHD API :: Common
 - Update required: BouncyCastle API Plugin to version 2.30.1.80 or higher
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first, this looked like a dependency issue — as if Jenkins was missing a required plugin.&lt;br&gt;
However, after checking the &lt;strong&gt;Installed Plugins&lt;/strong&gt; tab, I noticed that the &lt;strong&gt;BouncyCastle API plugin&lt;/strong&gt; was already installed.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 4: The Fix — Restart Jenkins&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Instead of downloading anything manually, I decided to try a &lt;strong&gt;restart&lt;/strong&gt; directly from the Jenkins UI.&lt;/p&gt;

&lt;p&gt;To do this, I navigated to:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Manage Jenkins → Restart Jenkins when installation is complete and no jobs are running&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Jenkins restarted automatically.&lt;br&gt;
After waiting for the login page to reappear, I signed in again and returned to the &lt;strong&gt;Plugin Manager&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 5: Reinstalling Plugins After Restart&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Once Jenkins was back online, I retried the installation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Manage Jenkins → Plugins → Available tab&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I searched again for &lt;strong&gt;Git&lt;/strong&gt; and &lt;strong&gt;GitLab&lt;/strong&gt;, selected both, and clicked &lt;strong&gt;Install without restart&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This time, the installation completed &lt;strong&gt;successfully&lt;/strong&gt;, with all plugins showing green checkmarks and no errors.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 6: Verification&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;After the installation finished, I went to:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Manage Jenkins → Plugins → Installed tab&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I confirmed the following plugins were now active:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Git plugin&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Git Client plugin&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mina SSHD API Common&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;BouncyCastle API plugin&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitLab plugin&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All dependencies were properly loaded after the restart.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 7: Outcome&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Jenkins now has &lt;strong&gt;Git&lt;/strong&gt; and &lt;strong&gt;GitLab&lt;/strong&gt; plugins installed and working.&lt;/li&gt;
&lt;li&gt;All dependency errors resolved through a simple UI restart.&lt;/li&gt;
&lt;li&gt;The environment is ready for setting up CI/CD pipelines and GitLab webhooks.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>jenkins</category>
      <category>cicd</category>
      <category>git</category>
    </item>
  </channel>
</rss>
