<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Linux Pathfinder</title>
	<atom:link href="https://linuxpathfinder.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://linuxpathfinder.com</link>
	<description>Cutting-edge insights in the ever-evolving world of IT</description>
	<lastBuildDate>Sat, 27 Dec 2025 17:16:11 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://linuxpathfinder.com/wp-content/uploads/2025/12/linuxpathfinder-logo-150x150.png</url>
	<title>Linux Pathfinder</title>
	<link>https://linuxpathfinder.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Deploying a Production-Ready Next.js Full-Stack Application on Linux VPS</title>
		<link>https://linuxpathfinder.com/deploying-a-production-ready-next-js-full-stack-application-on-linux-vps/</link>
					<comments>https://linuxpathfinder.com/deploying-a-production-ready-next-js-full-stack-application-on-linux-vps/#respond</comments>
		
		<dc:creator><![CDATA[Asif Khan]]></dc:creator>
		<pubDate>Sat, 27 Dec 2025 16:54:25 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<guid isPermaLink="false">https://linuxpathfinder.com/?p=211</guid>

					<description><![CDATA[Setting up a modern Next.js app with server-side rendering, authentication, and database integration is not easy. It takes a lot of work to get all the parts working together. This guide shows you how to set up a Linux VPS...]]></description>
										<content:encoded><![CDATA[
<p>Setting up a modern Next.js app with server-side rendering, authentication, and database integration is not easy. It takes a lot of work to get all the parts working together. This guide shows you how to set up a Linux VPS to run a production-ready Next.js 16 full-stack application. It covers setting up PostgreSQL, NextAuth authentication, Prisma ORM, PM2 process management, and Nginx reverse proxy.</p>



<p>At the end of this tutorial, you&#8217;ll have a web app that works and can be accessed through HTTP and HTTPS. It will have automatic restarts, proper database migrations, and a modern, responsive dashboard interface.</p>



<h2 class="wp-block-heading">System Requirements and Prerequisites</h2>



<p>Before starting, ensure you have:</p>



<ul class="wp-block-list">
<li>A Linux VPS (Ubuntu 24.04 LTS or RHEL 9/AlmaLinux 9)</li>



<li>Root or sudo access</li>



<li>At least 2GB RAM and 20GB storage</li>



<li>SSH access configured</li>



<li>A domain name or public IP address</li>
</ul>



<h2 class="wp-block-heading">Installing Node.js 20 LTS</h2>



<p>Next.js 16 requires Node.js 20 or higher. Install using the NodeSource repository:</p>



<p><strong>Ubuntu/Debian:</strong></p>



<pre class="wp-block-code"><code>curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs</code></pre>



<p><strong>RHEL/CentOS/AlmaLinux:</strong></p>



<pre class="wp-block-code"><code>curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo bash -
sudo dnf install -y nodejs</code></pre>



<p>Verify the installation:</p>



<pre class="wp-block-code"><code>node --version
npm --version</code></pre>



<p>Expected output:</p>



<pre class="wp-block-code"><code>v20.11.1
10.2.4</code></pre>



<h2 class="wp-block-heading">Installing and Configuring PostgreSQL</h2>



<p>Install PostgreSQL 14 or higher for your distribution:</p>



<p><strong>Ubuntu/Debian:</strong></p>



<pre class="wp-block-code"><code>sudo apt update
sudo apt install -y postgresql postgresql-contrib
sudo systemctl start postgresql
sudo systemctl enable postgresql</code></pre>



<p><strong>RHEL/CentOS/AlmaLinux:</strong></p>



<pre class="wp-block-code"><code>sudo dnf install -y postgresql-server postgresql-contrib
sudo postgresql-setup --initdb
sudo systemctl start postgresql
sudo systemctl enable postgresql</code></pre>



<p>Create the application database and user:</p>



<pre class="wp-block-code"><code>sudo -u postgres psql</code></pre>



<p>Inside the PostgreSQL prompt:</p>



<pre class="wp-block-code"><code>CREATE DATABASE testapp_production;
CREATE USER appuser WITH PASSWORD 'AppPassword2024';
GRANT ALL PRIVILEGES ON DATABASE testapp_production TO appuser;
\q</code></pre>



<p>Configure PostgreSQL to accept password authentication. Edit <code>/var/lib/pgsql/data/pg_hba.conf</code> (RHEL) or <code>/etc/postgresql/14/main/pg_hba.conf</code> (Ubuntu):</p>



<pre class="wp-block-code"><code># Change peer to md5 for local connections
local   all             all                                     md5
host    all             all             127.0.0.1/32            md5</code></pre>



<p>Restart PostgreSQL:</p>



<pre class="wp-block-code"><code>sudo systemctl restart postgresql</code></pre>



<p>Test the connection:</p>



<pre class="wp-block-code"><code>PGPASSWORD=AppPassword2024 psql -U appuser -h localhost -d testapp_production -c "SELECT version();"</code></pre>



<p>Expected output:</p>



<pre class="wp-block-code"><code>                                                version
--------------------------------------------------------------------------------------------------------
 PostgreSQL 14.10 (Ubuntu 14.10-0ubuntu0.22.04.1) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0, 64-bit
(1 row)</code></pre>



<h2 class="wp-block-heading">Initializing the Next.js Project</h2>



<p>Create a new Next.js project with TypeScript and Tailwind CSS:</p>



<pre class="wp-block-code"><code>npx create-next-app@latest nextjs-app --typescript --tailwind --app --no-src-dir
cd nextjs-app</code></pre>



<p>Install core dependencies:</p>



<pre class="wp-block-code"><code>npm install @prisma/client@^5.22.0 next-auth@^4.24.13 bcrypt@^6.0.0 zod@^4.2.1 react-hook-form@^7.69.0
npm install -D prisma@^5.22.0 @types/bcrypt@^6.0.0</code></pre>



<p>The package.json should include these versions:</p>



<pre class="wp-block-code"><code>{
  "dependencies": {
    "@prisma/client": "^5.22.0",
    "next-auth": "^4.24.13",
    "bcrypt": "^6.0.0",
    "next": "^16.1.1",
    "react": "^19.2.3",
    "tailwindcss": "^3.4.0"
  }
}</code></pre>



<h2 class="wp-block-heading">Setting Up Prisma ORM</h2>



<p>Initialize Prisma with PostgreSQL:</p>



<pre class="wp-block-code"><code>npx prisma init</code></pre>



<p>This creates <code>prisma/schema.prisma</code>. Configure the database schema:</p>



<pre class="wp-block-code"><code>datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

generator client {
  provider = "prisma-client-js"
}

model User {
  id            String    @id @default(cuid())
  email         String    @unique
  name          String?
  password      String
  createdAt     DateTime  @default(now())
  updatedAt     DateTime  @updatedAt
  posts         Post&#91;]
}

model Post {
  id        String   @id @default(cuid())
  title     String
  content   String   @db.Text
  published Boolean  @default(false)
  authorId  String
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  author    User     @relation(fields: &#91;authorId], references: &#91;id], onDelete: Cascade)

  @@index(&#91;authorId])
}</code></pre>



<p>Create the <code>.env</code> file with your database connection:</p>



<pre class="wp-block-code"><code>DATABASE_URL="postgresql://appuser:AppPassword2024@localhost:5432/testapp_production"
NEXTAUTH_SECRET="your-secret-key-here"
NEXTAUTH_URL="http://85.29.10.87"
NODE_ENV="production"</code></pre>



<p>Generate a secure NextAuth secret:</p>



<pre class="wp-block-code"><code>openssl rand -base64 32</code></pre>



<p>Output example:</p>



<pre class="wp-block-code"><code>NGFR39BFGxtN6018TnjC4RoyYC+FUnyaBRD+Ae6P79o=</code></pre>



<p>Run the initial migration:</p>



<pre class="wp-block-code"><code>npx prisma migrate dev --name init</code></pre>



<p>Expected output:</p>



<pre class="wp-block-code"><code>Environment variables loaded from .env
Prisma schema loaded from prisma/schema.prisma
Datasource "db": PostgreSQL database "testapp_production", schema "public" at "localhost:5432"

Applying migration `20240307103000_init`

The following migration(s) have been created and applied from new schema changes:

migrations/
  └─ 20240307103000_init/
    └─ migration.sql

Your database is now in sync with your schema.

&#x2714; Generated Prisma Client (v5.22.0) to ./node_modules/@prisma/client in 169ms</code></pre>



<p>Verify tables were created:</p>



<pre class="wp-block-code"><code>PGPASSWORD=AppPassword2024 psql -U appuser -h localhost -d testapp_production -c '\dt'</code></pre>



<p>Output:</p>



<pre class="wp-block-code"><code>                List of relations
 Schema |        Name        | Type  |  Owner
--------+--------------------+-------+---------
 public | Post               | table | appuser
 public | User               | table | appuser
 public | _prisma_migrations | table | appuser
(3 rows)</code></pre>



<h2 class="wp-block-heading">Building the Application</h2>



<p>Update <code>package.json</code> scripts for production:</p>



<pre class="wp-block-code"><code>"scripts": {
  "dev": "next dev",
  "build": "prisma generate &amp;&amp; prisma migrate deploy &amp;&amp; next build",
  "start": "next start -p 3000",
  "migrate:deploy": "prisma migrate deploy"
}</code></pre>



<p>Build the application:</p>



<pre class="wp-block-code"><code>npm run build</code></pre>



<p>Expected output excerpt:</p>



<pre class="wp-block-code"><code>▲ Next.js 16.1.1 (Turbopack)
- Environments: .env

  Creating an optimized production build ...
✓ Compiled successfully in 6.1s
  Running TypeScript ...
  Collecting page data using 3 workers ...
  Generating static pages using 3 workers (0/9) ...
✓ Generating static pages using 3 workers (9/9) in 292.3ms
  Finalizing page optimization ...

Route (app)
┌ ○ /
├ ƒ /api/auth/&#91;...nextauth]
├ ƒ /dashboard
├ ○ /login
└ ○ /signup

ƒ  (Dynamic)  server-rendered on demand
○  (Static)   prerendered as static content</code></pre>



<h2 class="wp-block-heading">Installing and Configuring PM2</h2>



<p>PM2 is a production process manager for Node.js applications with automatic restarts and monitoring.</p>



<p><strong>Install PM2 globally:</strong></p>



<pre class="wp-block-code"><code>sudo npm install -g pm2</code></pre>



<p><strong>Ubuntu/Debian:</strong></p>



<pre class="wp-block-code"><code>sudo apt install -y pm2</code></pre>



<p><strong>RHEL/CentOS:</strong></p>



<pre class="wp-block-code"><code>sudo npm install -g pm2</code></pre>



<p>Create <code>ecosystem.config.js</code> in your project root:</p>



<pre class="wp-block-code"><code>module.exports = {
  apps: &#91;{
    name: 'nextjs-app',
    script: 'npm',
    args: 'start',
    instances: 1,
    exec_mode: 'fork',
    env: {
      NODE_ENV: 'production',
      PORT: 3000
    }
  }]
};</code></pre>



<p>Start the application with PM2:</p>



<pre class="wp-block-code"><code>pm2 start ecosystem.config.js</code></pre>



<p>Output:</p>



<pre class="wp-block-code"><code>&#91;PM2] Starting npm in fork mode (1 instance)
&#91;PM2] Done.
┌────┬───────────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┐
│ id │ name          │ namespace   │ version │ mode    │ pid      │ uptime │ ↺    │ status    │ cpu      │ mem      │
├────┼───────────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┤
│ 0  │ nextjs-app    │ default     │ N/A     │ fork    │ 25018    │ 0s     │ 0    │ online    │ 0%       │ 56.3mb   │
└────┴───────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┘</code></pre>



<p>Save the PM2 process list and configure auto-start on system boot:</p>



<pre class="wp-block-code"><code>pm2 save
pm2 startup systemd -u yourusername --hp /home/yourusername</code></pre>



<p>The output provides a command to run with sudo:</p>



<pre class="wp-block-code"><code>sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u devopslinx --hp /home/devopslinx</code></pre>



<p>Verify PM2 status:</p>



<pre class="wp-block-code"><code>pm2 list</code></pre>



<p>View real-time logs:</p>



<pre class="wp-block-code"><code>pm2 logs nextjs-app --lines 50</code></pre>



<p>Output example:</p>



<pre class="wp-block-code"><code>0|nextjs-a | ▲ Next.js 16.1.1
0|nextjs-a | - Local:         http://localhost:3000
0|nextjs-a | - Network:       http://85.29.10.87:3000
0|nextjs-a |
0|nextjs-a | ✓ Starting...
0|nextjs-a | ✓ Ready in 246ms</code></pre>



<h2 class="wp-block-heading">Configuring Nginx Reverse Proxy</h2>



<p>Install Nginx:</p>



<p><strong>Ubuntu/Debian:</strong></p>



<pre class="wp-block-code"><code>sudo apt install -y nginx</code></pre>



<p><strong>RHEL/CentOS:</strong></p>



<pre class="wp-block-code"><code>sudo dnf install -y nginx
sudo systemctl start nginx
sudo systemctl enable nginx</code></pre>



<p>Create Nginx configuration at <code>/etc/nginx/sites-available/nextjs-app</code>:</p>



<pre class="wp-block-code"><code>server {
    listen 80;
    listen &#91;::]:80;
    server_name 85.29.10.87;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }
}</code></pre>



<p>Enable the site (Ubuntu/Debian):</p>



<pre class="wp-block-code"><code>sudo ln -s /etc/nginx/sites-available/nextjs-app /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default</code></pre>



<p>For RHEL/CentOS, place the config in <code>/etc/nginx/conf.d/nextjs-app.conf</code>.</p>



<p>Test Nginx configuration:</p>



<pre class="wp-block-code"><code>sudo nginx -t</code></pre>



<p>Output:</p>



<pre class="wp-block-code"><code>nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful</code></pre>



<p>Reload Nginx:</p>



<pre class="wp-block-code"><code>sudo systemctl reload nginx</code></pre>



<h2 class="wp-block-heading">Configuring the Firewall</h2>



<p>Allow HTTP traffic through the firewall:</p>



<p><strong>Ubuntu (UFW):</strong></p>



<pre class="wp-block-code"><code>sudo ufw allow 80/tcp comment 'HTTP'
sudo ufw allow 443/tcp comment 'HTTPS'
sudo ufw status</code></pre>



<p>Output:</p>



<pre class="wp-block-code"><code>Status: active

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW       Anywhere                   # SSH
80/tcp                     ALLOW       Anywhere                   # HTTP
443/tcp                    ALLOW       Anywhere                   # HTTPS</code></pre>



<p><strong>RHEL/CentOS (firewalld):</strong></p>



<pre class="wp-block-code"><code>sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
sudo firewall-cmd --list-all</code></pre>



<p>Test the application:</p>



<pre class="wp-block-code"><code>curl -I http://85.29.10.87</code></pre>



<p>Expected response:</p>



<pre class="wp-block-code"><code>HTTP/1.1 200 OK
Server: nginx/1.24.0 (Ubuntu)
Content-Type: text/html; charset=utf-8
X-Powered-By: Next.js</code></pre>



<h2 class="wp-block-heading">Performance Monitoring</h2>



<p>Monitor PM2 processes in real-time:</p>



<pre class="wp-block-code"><code>pm2 monit</code></pre>



<p>This displays:</p>



<ul class="wp-block-list">
<li>CPU usage per process</li>



<li>Memory consumption</li>



<li>Process uptime</li>



<li>Real-time logs</li>
</ul>



<p>Check application metrics:</p>



<pre class="wp-block-code"><code>pm2 show nextjs-app</code></pre>



<p>Output includes:</p>



<pre class="wp-block-code"><code>Describing process with id 0 - name nextjs-app
┌───────────────────┬─────────────────────────────────────────────────┐
│ status            │ online                                          │
│ name              │ nextjs-app                                      │
│ namespace         │ default                                         │
│ version           │ N/A                                             │
│ restarts          │ 4                                               │
│ uptime            │ 8h                                              │
│ entire log path   │ /home/devopslinx/nextjs-app/logs/combined-0.log │
│ script path       │ /usr/bin/npm                                    │
│ script args       │ start                                           │
│ error log path    │ /home/devopslinx/nextjs-app/logs/err-0.log      │
│ out log path      │ /home/devopslinx/nextjs-app/logs/out-0.log      │
│ pid path          │ /home/devopslinx/.pm2/pids/nextjs-app-0.pid     │
│ interpreter       │ /usr/bin/node                                   │
│ interpreter args  │ N/A                                             │
│ script id         │ 0                                               │
│ exec cwd          │ /home/devopslinx/nextjs-app                     │
│ exec mode         │ fork_mode                                       │
│ node.js version   │ 20.19.6                                         │
│ node env          │ production                                      │
│ watch &amp; reload    │ ✘                                               │
│ unstable restarts │ 0                                               │
└───────────────────┴─────────────────────────────────────────────────┘</code></pre>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1024" height="572" src="https://linuxpathfinder.com/wp-content/uploads/2025/12/next.js_dashboard-2-1024x572.png" alt="" class="wp-image-221" srcset="https://linuxpathfinder.com/wp-content/uploads/2025/12/next.js_dashboard-2-1024x572.png 1024w, https://linuxpathfinder.com/wp-content/uploads/2025/12/next.js_dashboard-2-300x168.png 300w, https://linuxpathfinder.com/wp-content/uploads/2025/12/next.js_dashboard-2-768x429.png 768w, https://linuxpathfinder.com/wp-content/uploads/2025/12/next.js_dashboard-2-1536x858.png 1536w, https://linuxpathfinder.com/wp-content/uploads/2025/12/next.js_dashboard-2.png 1688w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h2 class="wp-block-heading">Conclusion</h2>



<p>You now have a production-ready Next.js full-stack app running on a Linux VPS with a PostgreSQL database, NextAuth authentication, PM2 process management, and an Nginx reverse proxy. The app restarts on crashes, survives server reboots, and serves traffic on HTTP port 80.</p>



<p>Major achievements are correct database migrations, secure authentication flow, process monitoring with PM2, and professional reverse proxy setup. For production, add SSL/TLS certificates with Let&#8217;s Encrypt, database backups, and monitoring with Prometheus and Grafana.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxpathfinder.com/deploying-a-production-ready-next-js-full-stack-application-on-linux-vps/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Mastering Zero-Downtime Deployments: Node.js and React Application Updates with PM2</title>
		<link>https://linuxpathfinder.com/mastering-zero-downtime-deployments-node-js-and-react-application/</link>
					<comments>https://linuxpathfinder.com/mastering-zero-downtime-deployments-node-js-and-react-application/#respond</comments>
		
		<dc:creator><![CDATA[Asif Khan]]></dc:creator>
		<pubDate>Sat, 27 Dec 2025 02:19:53 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<guid isPermaLink="false">https://linuxpathfinder.com/?p=205</guid>

					<description><![CDATA[Look, I&#8217;ll be honest with you &#8211; deploying changes into production used to make me nervous. If you make one mistake, your users get error pages, your boss gets angry emails, and you have to quickly undo changes at 2...]]></description>
										<content:encoded><![CDATA[
<p>Look, I&#8217;ll be honest with you &#8211; deploying changes into production used to make me nervous. If you make one mistake, your users get error pages, your boss gets angry emails, and you have to quickly undo changes at 2 AM. But here&#8217;s the deal: it doesn&#8217;t have to be like that.</p>



<p>I have been in charge of production deployments for years, and I have learned that the key is not just knowing the commands but also understanding the workflow. Today, I&#8217;m going to show you step by step how I update a working Node.js and React app without missing a single request. We will be using a functioning Todo app that runs on an operational VPS, and I&#8217;ll show you how to do it all.</p>



<p>This guide will help you sleep better at night, whether you&#8217;re fixing a bug quickly or adding a big new feature.</p>



<h2 class="wp-block-heading">Understanding Your Production-like Environment</h2>



<p>Before we do anything in production, let&#8217;s take a minute to get acquainted about what we&#8217;re working with. This is the stack we have to work with:</p>



<ul class="wp-block-list">
<li><strong>Nginx</strong> on port 80 &#8211; and handles incoming traffic and serves our React files.</li>



<li><strong>Node.js Backend</strong> &#8211; on port 5000 is our Express API, which is kept running by PM2.</li>



<li><strong>React Frontend</strong> &#8211; is made up of static files built with Vite. PostgreSQL stores all of our data.</li>



<li><strong>PostgreSQL</strong> &#8211; stores all of our data.</li>



<li><strong>PM2</strong> &#8211; the underrated hero that makes everything work.</li>
</ul>



<p>Nginx is like your front door, and React is like your living room that guests see.&nbsp;Your kitchen is Node.js&nbsp;where the real work gets done, and PostgreSQL is your storage room. PM2? That&#8217;s your security system making sure the kitchen is always open.</p>



<h3 class="wp-block-heading">Let&#8217;s Checkout What&#8217;s Running</h3>



<p>First, connect to your server using SSH. I&#8217;m using my VPS at 85.29.10.87:</p>



<pre class="wp-block-code"><code>ssh devopslinx@85.29.10.87
</code></pre>



<p>Now, let&#8217;s check what processes are actually running:</p>



<pre class="wp-block-code"><code>ps aux | grep -E 'node|nginx|postgres' | grep -v grep
</code></pre>



<p>Here&#8217;s what I see on my server:</p>



<pre class="wp-block-code"><code>postgres 1620071  0.0  0.3 223252 31360 ?        Ss   Mar02   0:16 /usr/lib/postgresql/16/bin/postgres -D /var/lib/postgresql/16/main
devopsl+ 1621070  0.2  0.8 11786920 72380 ?      Ssl  Mar02   8:36 node /home/devopslinx/todo-app/backend/server.js
root     1621722  0.0  0.0  11160  1976 ?        Ss   Mar02   0:00 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
www-data 1621723  0.0  0.0  12880  4920 ?        S    Mar02   0:00 nginx: worker process
</code></pre>



<p>PostgreSQL is humming along (PID 1620071), my Node.js backend is running (PID 1621070), and Nginx has its master and worker processes ready to handle traffic.</p>



<p>Now let&#8217;s check PM2 &#8211; this is where the magic happens:</p>



<pre class="wp-block-code"><code>pm2 list
</code></pre>



<pre class="wp-block-code"><code>┌────┬─────────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name        │ namespace   │ version │ mode    │ pid      │ uptime │ ↺    │ status    │ cpu      │ mem      │ user     │ watching │
├────┼─────────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0  │ todo-api    │ default     │ 1.0.0   │ fork    │ 1621070  │ 2D     │ 0    │ online    │ 0%       │ 70.2mb   │ devopsl… │ disabled │
└────┴─────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
</code></pre>



<p>Perfect! The app has been running for 2 days straight with zero restarts (see that ↺ column?). This is the baseline we want to maintain.</p>



<p>Quick sanity check on the app structure:</p>



<pre class="wp-block-code"><code>ls -la ~/todo-app/
</code></pre>



<pre class="wp-block-code"><code>total 16
drwxr-xr-x 4 devopslinx devopslinx 4096 Mar 02 02:27 .
drwxr-xr-x 9 devopslinx devopslinx 4096 Mar 02 02:30 ..
drwxr-xr-x 3 devopslinx devopslinx 4096 Mar 02 05:28 backend
drwxr-xr-x 3 devopslinx devopslinx 4096 Mar 02 05:26 frontend
</code></pre>



<p>Let&#8217;s verify the database is responding:</p>



<pre class="wp-block-code"><code>PGPASSWORD=todopass123 psql -h localhost -U todouser -d tododb -c 'SELECT COUNT(*) FROM todos;'
</code></pre>



<pre class="wp-block-code"><code> count
-------
     3
(1 row)
</code></pre>



<p>And finally, let&#8217;s ping the API to make sure it&#8217;s responding:</p>



<pre class="wp-block-code"><code>curl -s http://localhost:5000/api/health
</code></pre>



<pre class="wp-block-code"><code>{"status":"OK","message":"Server is running"}
</code></pre>



<p>Alright, everything looks good. We&#8217;re ready to deploy some changes.</p>



<h2 class="wp-block-heading">Deploying Backend Changes (The Safe Way)</h2>



<p>Here&#8217;s where most people mess up. They SSH in, edit the file, restart the server, and hope for the best. Don&#8217;t do that. Let me show you the professional approach.</p>



<h3 class="wp-block-heading">Step 1: Always Backup First</h3>



<p>I cannot stress this enough &#8211; ALWAYS backup before you touch production code. I&#8217;ve been saved by backups more times than I care to admit:</p>



<pre class="wp-block-code"><code>cd ~/todo-app/backend
cp server.js server.js.backup.$(date +%Y%m%d-%H%M%S)
</code></pre>



<p>Let&#8217;s verify it worked:</p>



<pre class="wp-block-code"><code>ls -lh server.js*
</code></pre>



<pre class="wp-block-code"><code>-rwxr-xr-x 1 devopslinx devopslinx 3.3K Mar 02 02:30 server.js
-rwxr-xr-x 1 devopslinx devopslinx 3.3K Mar 02 10:45 server.js.backup.20240302-104523
</code></pre>



<p>Good. Now if something goes wrong, we have a timestamped backup we can quickly restore.</p>



<h3 class="wp-block-heading">Step 2: Making the Code Change</h3>



<p>For this example, I&#8217;m adding a statistics endpoint to the API. Let&#8217;s see what we&#8217;re working with first:</p>



<pre class="wp-block-code"><code>head -20 ~/todo-app/backend/server.js
</code></pre>



<pre class="wp-block-code"><code>const express = require('express');
const cors = require('cors');
const pool = require('./db');
require('dotenv').config();

const app = express();
const PORT = process.env.PORT || 5000;

app.use(cors());
app.use(express.json());

// Health check endpoint
app.get('/api/health', (req, res) =&gt; {
  res.json({ status: 'OK', message: 'Server is running' });
});

// Get all todos
app.get('/api/todos', async (req, res) =&gt; {
  try {
</code></pre>



<p>Now I&#8217;ll add my new endpoint. First, let me create the code snippet:</p>



<pre class="wp-block-code"><code>cat &gt; /tmp/stats_endpoint.txt &lt;&lt; 'EOF'

// Get todo statistics
app.get('/api/stats', async (req, res) =&gt; {
  try {
    const stats = await pool.query(
      'SELECT COUNT(*) as total, SUM(CASE WHEN completed = true THEN 1 ELSE 0 END) as completed, SUM(CASE WHEN completed = false THEN 1 ELSE 0 END) as pending FROM todos'
    );

    res.json({
      total: parseInt(stats.rows&#91;0].total),
      completed: parseInt(stats.rows&#91;0].completed),
      pending: parseInt(stats.rows&#91;0].pending)
    });
  } catch (err) {
    console.error(err.message);
    res.status(500).json({ error: 'Server error' });
  }
});
EOF
</code></pre>



<p>Now I&#8217;ll insert it into the server file right after the health check:</p>



<pre class="wp-block-code"><code>head -15 server.js &gt; /tmp/server_new.js &amp;&amp; \
echo "" &gt;&gt; /tmp/server_new.js &amp;&amp; \
cat /tmp/stats_endpoint.txt &gt;&gt; /tmp/server_new.js &amp;&amp; \
echo "" &gt;&gt; /tmp/server_new.js &amp;&amp; \
tail -n +16 server.js &gt;&gt; /tmp/server_new.js &amp;&amp; \
mv /tmp/server_new.js server.js
</code></pre>



<p>Let&#8217;s double-check the new code is in there:</p>



<pre class="wp-block-code"><code>head -35 server.js
</code></pre>



<pre class="wp-block-code"><code>const express = require('express');
const cors = require('cors');
const pool = require('./db');
require('dotenv').config();

const app = express();
const PORT = process.env.PORT || 5000;

app.use(cors());
app.use(express.json());

// Health check endpoint
app.get('/api/health', (req, res) =&gt; {
  res.json({ status: 'OK', message: 'Server is running' });
});

// Get todo statistics
app.get('/api/stats', async (req, res) =&gt; {
  try {
    const stats = await pool.query(
      'SELECT COUNT(*) as total, SUM(CASE WHEN completed = true THEN 1 ELSE 0 END) as completed, SUM(CASE WHEN completed = false THEN 1 ELSE 0 END) as pending FROM todos'
    );

    res.json({
      total: parseInt(stats.rows&#91;0].total),
      completed: parseInt(stats.rows&#91;0].completed),
      pending: parseInt(stats.rows&#91;0].pending)
    });
  } catch (err) {
    console.error(err.message);
    res.status(500).json({ error: 'Server error' });
  }
});
</code></pre>



<p>Perfect! The new endpoint is in place.</p>



<h3 class="wp-block-heading">Step 3: The Zero-Downtime Reload</h3>



<p>This is the part that used to scare me until I learned about PM2&#8217;s reload feature. Unlike restart, which just kills and restarts your app (causing downtime), reload is graceful. Here&#8217;s what happens:</p>



<ol class="wp-block-list">
<li>PM2 starts a new instance of your app</li>



<li>Waits for it to be ready</li>



<li>Routes new traffic to the new instance</li>



<li>Gracefully shuts down the old instance</li>



<li>Your users never see an error</li>
</ol>



<p>Watch this:</p>



<pre class="wp-block-code"><code>pm2 reload todo-api
</code></pre>



<pre class="wp-block-code"><code>Use --update-env to update environment variables
&#91;PM2] Applying action reloadProcessId on app &#91;todo-api](ids: &#91; 0 ])
&#91;PM2] &#91;todo-api](0) ✓
</code></pre>



<p>That checkmark is what you want to see. Now let&#8217;s verify:</p>



<pre class="wp-block-code"><code>pm2 status
</code></pre>



<pre class="wp-block-code"><code>┌────┬─────────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name        │ namespace   │ version │ mode    │ pid      │ uptime │ ↺    │ status    │ cpu      │ mem      │ user     │ watching │
├────┼─────────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0  │ todo-api    │ default     │ 1.0.0   │ fork    │ 1703070  │ 9s     │ 1    │ online    │ 0%       │ 58.5mb   │ devopsl… │ disabled │
└────┴─────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
</code></pre>



<p>See what happened?<br>&#8211; <strong>PID changed</strong> from 1621070 to 1703070 (we got a fresh process)<br>&#8211; <strong>Uptime reset</strong> to 9 seconds (brand new instance)<br>&#8211; <strong>Restart counter</strong> went from 0 to 1<br>&#8211; <strong>Status</strong> stayed online the entire time</p>



<p>That&#8217;s zero-downtime deployment in action.</p>



<h3 class="wp-block-heading">Step 4: Testing the Changes</h3>



<p>Never assume the deployment worked. Always test:</p>



<pre class="wp-block-code"><code>curl -s http://localhost:5000/api/stats
</code></pre>



<pre class="wp-block-code"><code>{"total":3,"completed":0,"pending":3}
</code></pre>



<p>Excellent! The new endpoint works. But wait &#8211; that&#8217;s just the backend. Let&#8217;s make sure it&#8217;s accessible through Nginx:</p>



<pre class="wp-block-code"><code>curl -s http://85.29.10.87/api/stats
</code></pre>



<pre class="wp-block-code"><code>{"total":3,"completed":0,"pending":3}
</code></pre>



<p>Perfect! And let&#8217;s verify we didn&#8217;t break anything:</p>



<pre class="wp-block-code"><code>curl -s http://85.29.10.87/api/health
</code></pre>



<pre class="wp-block-code"><code>{"status":"OK","message":"Server is running"}
</code></pre>



<p>All good. Now check the logs to make sure there are no hidden errors:</p>



<pre class="wp-block-code"><code>pm2 logs todo-api --lines 20 --nostream
</code></pre>



<pre class="wp-block-code"><code>&#91;TAILING] Tailing last 20 lines for &#91;todo-api] process
/home/devopslinx/.pm2/logs/todo-api-error.log last 20 lines:
/home/devopslinx/.pm2/logs/todo-api-out.log last 20 lines:
0|todo-api | Server is running on port 5000
0|todo-api | Server is running on port 5000
</code></pre>



<p>Clean logs, no errors. This is a successful deployment.</p>



<h3 class="wp-block-heading">Step 5: Monitor for a Bit</h3>



<p>I like to keep an eye on the app for a few minutes after deployment. PM2 makes this easy:</p>



<pre class="wp-block-code"><code>pm2 monit
</code></pre>



<p>This opens a real-time dashboard showing CPU, memory, and logs. Press Ctrl+C when you&#8217;re satisfied everything is stable.</p>



<p>You can also check detailed process info:</p>



<pre class="wp-block-code"><code>pm2 show todo-api
</code></pre>



<pre class="wp-block-code"><code>Describing process with id 0 - name todo-api
┌───────────────────┬──────────────────────────────────────────────────┐
│ status            │ online                                           │
│ name              │ todo-api                                         │
│ restarts          │ 1                                                │
│ uptime            │ 5m                                               │
│ script path       │ /home/devopslinx/todo-app/backend/server.js      │
│ script args       │ N/A                                              │
│ error log path    │ /home/devopslinx/.pm2/logs/todo-api-error.log    │
│ out log path      │ /home/devopslinx/.pm2/logs/todo-api-out.log      │
│ pid path          │ /home/devopslinx/.pm2/pids/todo-api-0.pid        │
│ mode              │ fork_mode                                        │
└───────────────────┴──────────────────────────────────────────────────┘
</code></pre>



<p>Everything looks solid. Deployment complete!</p>



<h2 class="wp-block-heading">Deploying Frontend Changes</h2>



<p>Frontend deployments are actually simpler than backend because there&#8217;s no process to restart. Nginx serves static files, so we just need to upload new ones.</p>



<h3 class="wp-block-heading">Step 1: Build on Your Local Machine</h3>



<p>On your development machine, make your React changes and build:</p>



<pre class="wp-block-code"><code>cd /path/to/your/local/todo-frontend
npm run build
</code></pre>



<pre class="wp-block-code"><code>vite v4.5.0 building for production...
✓ 342 modules transformed.
dist/index.html                   0.45 kB │ gzip:  0.30 kB
dist/assets/index-DCTgUJ87.css    4.01 kB │ gzip:  1.24 kB
dist/assets/index-5F_pVDlP.js   196.25 kB │ gzip: 63.45 kB
✓ built in 3.42s
</code></pre>



<p>Your optimized production files are now in the <code>dist/</code> folder.</p>



<h3 class="wp-block-heading">Step 2: Backup Current Frontend</h3>



<p>On the server, create a backup:</p>



<pre class="wp-block-code"><code>cd ~/todo-app
cp -r frontend frontend.backup.$(date +%Y%m%d-%H%M%S)
</code></pre>



<p>Verify:</p>



<pre class="wp-block-code"><code>ls -ld frontend*
</code></pre>



<pre class="wp-block-code"><code>drwxr-xr-x 3 devopslinx devopslinx 4096 Mar 02 05:26 frontend
drwxr-xr-x 3 devopslinx devopslinx 4096 Mar 02 11:15 frontend.backup.20240302-111530
</code></pre>



<h3 class="wp-block-heading">Step 3: Upload the New Build</h3>



<p>From your local machine, use scp to upload:</p>



<pre class="wp-block-code"><code>scp -r dist/* devopslinx@85.29.10.87:~/todo-app/frontend/
</code></pre>



<pre class="wp-block-code"><code>index.html                                    100%  455     8.2KB/s   00:00
index-DCTgUJ87.css                            100% 4013    72.1KB/s   00:00
index-5F_pVDlP.js                             100%  196KB   3.2MB/s   00:00
vite.svg                                      100% 1497    27.5KB/s   00:00
</code></pre>



<p>Or if you prefer rsync (I do, because it&#8217;s faster for updates):</p>



<pre class="wp-block-code"><code>rsync -avz --delete dist/ devopslinx@85.29.10.87:~/todo-app/frontend/
</code></pre>



<pre class="wp-block-code"><code>sending incremental file list
./
index.html
assets/
assets/index-DCTgUJ87.css
assets/index-5F_pVDlP.js

sent 201,245 bytes  received 92 bytes  134,224.67 bytes/sec
total size is 200,712  speedup is 1.00
</code></pre>



<h3 class="wp-block-heading">Step 4: Verify the Upload</h3>



<p>Back on the server, check the files:</p>



<pre class="wp-block-code"><code>ls -lah ~/todo-app/frontend/
</code></pre>



<pre class="wp-block-code"><code>total 20K
drwxr-xr-x 3 devopslinx devopslinx 4.0K Mar 02 11:18 .
drwxr-xr-x 4 devopslinx devopslinx 4.0K Mar 02 02:27 ..
drwxr-xr-x 2 devopslinx devopslinx 4.0K Mar 02 11:18 assets
-rw-r--r-- 1 devopslinx devopslinx  455 Mar 02 11:18 index.html
-rw-r--r-- 1 devopslinx devopslinx 1.5K Mar 02 11:18 vite.svg
</code></pre>



<p>Test that Nginx is serving the new files:</p>



<pre class="wp-block-code"><code>curl -I http://85.29.10.87/
</code></pre>



<pre class="wp-block-code"><code>HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Sat, 02 Mar 2024 11:20:15 GMT
Content-Type: text/html
Content-Length: 455
Last-Modified: Sat, 02 Mar 2024 11:18:42 GMT
Connection: keep-alive
ETag: "676d8b82-1c7"
Accept-Ranges: bytes
</code></pre>



<p>The <code>Last-Modified</code> timestamp matches our upload time &#8211; Nginx is serving the new files immediately. No restart needed!</p>



<h3 class="wp-block-heading">Step 5: Clear Browser Cache</h3>



<p>Here&#8217;s a gotcha that trips up a lot of people: browsers cache static files aggressively. Your users might not see the changes right away. To force a refresh:</p>



<ul class="wp-block-list">
<li>Chrome/Firefox: <code>Ctrl+Shift+R</code> (Windows/Linux) or <code>Cmd+Shift+R</code> (Mac)</li>



<li>Or open in incognito/private mode</li>
</ul>



<p>Pro tip: Vite automatically generates hashed filenames (like <code>index-5F_pVDlP.js</code>) which acts as cache-busting. When you change the code and rebuild, the filename changes, forcing browsers to download the new version.</p>



<h2 class="wp-block-heading">Database Migrations (Proceed with Caution)</h2>



<p>Database changes are where things can really go wrong if you&#8217;re not careful. Here&#8217;s my battle-tested approach:</p>



<h3 class="wp-block-heading">Step 1: Backup, Backup, Backup</h3>



<p>Did I mention you should backup? Because you should REALLY backup before touching the database:</p>



<pre class="wp-block-code"><code>pg_dump -h localhost -U todouser tododb &gt; ~/backup_$(date +%Y%m%d_%H%M%S).sql
</code></pre>



<p>Verify it:</p>



<pre class="wp-block-code"><code>ls -lh ~/backup_*.sql | tail -1
</code></pre>



<pre class="wp-block-code"><code>-rw-rw-r-- 1 devopslinx devopslinx 99K Mar 02 11:25 backup_20240302_112530.sql
</code></pre>



<h3 class="wp-block-heading">Step 2: Test in a Safe Environment First</h3>



<p>Never test migrations directly in production. Create a test database:</p>



<pre class="wp-block-code"><code>PGPASSWORD=todopass123 psql -h localhost -U todouser -d tododb -c 'CREATE DATABASE tododb_test TEMPLATE tododb;'
</code></pre>



<p>Test your migration there first, make sure it works, then proceed.</p>



<h3 class="wp-block-heading">Step 3: Run the Migration</h3>



<p>Connect to the production database:</p>



<pre class="wp-block-code"><code>PGPASSWORD=todopass123 psql -h localhost -U todouser -d tododb
</code></pre>



<p>Let&#8217;s say we&#8217;re adding a priority field to todos:</p>



<pre class="wp-block-code"><code>ALTER TABLE todos ADD COLUMN priority VARCHAR(20) DEFAULT 'medium';
</code></pre>



<pre class="wp-block-code"><code>ALTER TABLE
</code></pre>



<p>Verify the change:</p>



<pre class="wp-block-code"><code>\d todos
</code></pre>



<pre class="wp-block-code"><code>                                        Table "public.todos"
   Column    |            Type             | Collation | Nullable |              Default
-------------+-----------------------------+-----------+----------+-----------------------------------
 id          | integer                     |           | not null | nextval('todos_id_seq'::regclass)
 title       | character varying(255)      |           | not null |
 description | text                        |           |          |
 completed   | boolean                     |           |          | false
 created_at  | timestamp without time zone |           |          | CURRENT_TIMESTAMP
 priority    | character varying(20)       |           |          | 'medium'::character varying
</code></pre>



<p>Exit PostgreSQL:</p>



<pre class="wp-block-code"><code>\q
</code></pre>



<h3 class="wp-block-heading">Step 4: Update Code if Needed</h3>



<p>If your schema change requires code updates, deploy those backend changes using the PM2 reload workflow we covered earlier.</p>



<p>The beauty of adding a column with a DEFAULT value is that it&#8217;s backward compatible &#8211; the existing code keeps working while the new code can start using the new field.</p>



<h3 class="wp-block-heading">Step 5: Verify Everything Works</h3>



<p>Query the updated table:</p>



<pre class="wp-block-code"><code>PGPASSWORD=todopass123 psql -h localhost -U todouser -d tododb -c 'SELECT id, title, priority FROM todos LIMIT 3;'
</code></pre>



<pre class="wp-block-code"><code> id |        title        | priority
----+---------------------+----------
  1 | Welcome to Todo App | medium
  2 | Learn React         | medium
  3 | Learn Node.js       | medium
(3 rows)
</code></pre>



<p>All existing todos got the default value. Perfect!</p>



<h2 class="wp-block-heading">When Things Go Wrong: Rollback Procedures</h2>



<p>Let&#8217;s face it &#8211; sometimes deployments don&#8217;t go as planned. Here&#8217;s how to quickly recover.</p>



<h3 class="wp-block-heading">Rolling Back Backend Code</h3>



<p>Restore your backup:</p>



<pre class="wp-block-code"><code>cd ~/todo-app/backend
cp server.js.backup.20240302-104523 server.js
pm2 reload todo-api
</code></pre>



<p>Test to make sure the rollback worked:</p>



<pre class="wp-block-code"><code>curl -s http://localhost:5000/api/stats
</code></pre>



<p>If you get a 404 error, the rollback succeeded (assuming /api/stats was the new endpoint you added).</p>



<h3 class="wp-block-heading">Rolling Back Frontend</h3>



<p>Delete the new files and restore the backup:</p>



<pre class="wp-block-code"><code>cd ~/todo-app
rm -rf frontend/*
cp -r frontend.backup.20240302-111530/* frontend/
</code></pre>



<p>Force refresh your browser to see the old version.</p>



<h3 class="wp-block-heading">Rolling Back Database Changes</h3>



<p>Restore from your backup:</p>



<pre class="wp-block-code"><code>PGPASSWORD=todopass123 psql -h localhost -U todouser -d tododb &lt; ~/backup_20240302_112530.sql
</code></pre>



<p>For simple schema changes, you can revert manually:</p>



<pre class="wp-block-code"><code>PGPASSWORD=todopass123 psql -h localhost -U todouser -d tododb -c 'ALTER TABLE todos DROP COLUMN priority;'
</code></pre>



<h2 class="wp-block-heading">Essential PM2 Commands (Your Daily Toolkit)</h2>



<p>Here are the PM2 commands I use most often:</p>



<h3 class="wp-block-heading">Deployment Commands</h3>



<p>Zero-downtime reload (my go-to for deployments):</p>



<pre class="wp-block-code"><code>pm2 reload todo-api
</code></pre>



<p>Quick restart with brief downtime:</p>



<pre class="wp-block-code"><code>pm2 restart todo-api
</code></pre>



<p>Restart and update environment variables:</p>



<pre class="wp-block-code"><code>pm2 restart todo-api --update-env
</code></pre>



<p>Stop the application:</p>



<pre class="wp-block-code"><code>pm2 stop todo-api
</code></pre>



<p>Start it back up:</p>



<pre class="wp-block-code"><code>pm2 start todo-api
</code></pre>



<h3 class="wp-block-heading">Monitoring Commands</h3>



<p>Watch logs in real-time:</p>



<pre class="wp-block-code"><code>pm2 logs todo-api
</code></pre>



<p>View last 50 log lines:</p>



<pre class="wp-block-code"><code>pm2 logs todo-api --lines 50 --nostream
</code></pre>



<p>Real-time CPU and memory monitoring:</p>



<pre class="wp-block-code"><code>pm2 monit
</code></pre>



<p>Detailed process information:</p>



<pre class="wp-block-code"><code>pm2 show todo-api
</code></pre>



<p>List all processes:</p>



<pre class="wp-block-code"><code>pm2 list
</code></pre>



<h3 class="wp-block-heading">Log Management</h3>



<p>View only errors:</p>



<pre class="wp-block-code"><code>pm2 logs todo-api --err
</code></pre>



<p>View only output:</p>



<pre class="wp-block-code"><code>pm2 logs todo-api --out
</code></pre>



<p>Clear all logs:</p>



<pre class="wp-block-code"><code>pm2 flush
</code></pre>



<h3 class="wp-block-heading">Process Management</h3>



<p>Save current process list:</p>



<pre class="wp-block-code"><code>pm2 save
</code></pre>



<p>Restore saved processes:</p>



<pre class="wp-block-code"><code>pm2 resurrect
</code></pre>



<p>Remove process from PM2:</p>



<pre class="wp-block-code"><code>pm2 delete todo-api
</code></pre>



<h2 class="wp-block-heading">Troubleshooting Real Deployment Issues</h2>



<p>Let me share three problems I&#8217;ve actually encountered and how I solved them.</p>



<h3 class="wp-block-heading">Problem 1: PM2 Reload Fails Immediately</h3>



<p>So you run <code>pm2 reload todo-api</code> and instead of seeing that beautiful checkmark, your app shows &#8220;errored&#8221;. Here&#8217;s what I do:</p>



<p>Check the status:</p>



<pre class="wp-block-code"><code>pm2 status
</code></pre>



<pre class="wp-block-code"><code>┌────┬─────────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id │ name        │ namespace   │ version │ mode    │ pid      │ uptime │ ↺    │ status    │ cpu      │ mem      │ user     │ watching │
├────┼─────────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0  │ todo-api    │ default     │ 1.0.0   │ fork    │ 0        │ 0      │ 15   │ errored   │ 0%       │ 0b       │ devopsl… │ disabled │
└────┴─────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘
</code></pre>



<p>That restart counter at 15 tells me PM2 is repeatedly trying to start the app but it keeps crashing. Time to check the logs:</p>



<pre class="wp-block-code"><code>pm2 logs todo-api --lines 30 --nostream
</code></pre>



<pre class="wp-block-code"><code>0|todo-api | Error: Cannot find module 'express'
0|todo-api |     at Function.Module._resolveFilename (internal/modules/cjs/loader.js:815:15)
</code></pre>



<p>Ah! Missing dependencies. This usually happens when you pull code changes but forget to run npm install:</p>



<pre class="wp-block-code"><code>cd ~/todo-app/backend
npm install
pm2 restart todo-api
</code></pre>



<p>Problem solved.</p>



<h3 class="wp-block-heading">Problem 2: New Endpoint Returns 502 Bad Gateway</h3>



<p>You deployed the new code, PM2 shows online, but hitting the endpoint gives you a 502 error. Frustrating, right?</p>



<p>First, test the backend directly:</p>



<pre class="wp-block-code"><code>curl -v http://localhost:5000/api/stats
</code></pre>



<p>If this works but <code>http://85.29.10.87/api/stats</code> returns 502, the issue is with Nginx, not your code.</p>



<p>Check the Nginx error logs:</p>



<pre class="wp-block-code"><code>sudo tail -f /var/log/nginx/error.log
</code></pre>



<pre class="wp-block-code"><code>2024/03/02 11:45:23 &#91;error] 1621723#1621723: *234 connect() failed (111: Connection refused) while connecting to upstream, client: 192.168.12.10, server: 85.29.10.87, request: "GET /api/stats HTTP/1.1", upstream: "http://127.0.0.1:5000/api/stats"
</code></pre>



<p>&#8220;Connection refused&#8221; means Nginx can&#8217;t connect to your backend. Let&#8217;s check if Node.js is actually listening:</p>



<pre class="wp-block-code"><code>pm2 status
sudo ss -tlnp | grep :5000
</code></pre>



<p>If port 5000 isn&#8217;t listening, your backend crashed. Restart it:</p>



<pre class="wp-block-code"><code>pm2 restart todo-api
</code></pre>



<h3 class="wp-block-heading">Problem 3: Users Still See Old Frontend</h3>



<p>You deployed new frontend files but users are complaining they don&#8217;t see the changes. I&#8217;ve been there!</p>



<p><strong>Solution 1</strong>: Browser cache is the most common culprit. Have them hard refresh with <code>Ctrl+Shift+R</code>.</p>



<p><strong>Solution 2</strong>: Verify the files actually uploaded. SSH into the server:</p>



<pre class="wp-block-code"><code>ls -lah ~/todo-app/frontend/index.html
</code></pre>



<p>Check the timestamp. If it&#8217;s old, your upload didn&#8217;t work. Try uploading again.</p>



<p><strong>Solution 3</strong>: Make sure Nginx is pointing to the right directory:</p>



<pre class="wp-block-code"><code>sudo grep "root" /etc/nginx/sites-enabled/todo-app
</code></pre>



<p>Should show:</p>



<pre class="wp-block-code"><code>        root /home/devopslinx/todo-app/frontend;
</code></pre>



<p>If the path is wrong, fix the Nginx config and reload:</p>



<pre class="wp-block-code"><code>sudo systemctl reload nginx
</code></pre>



<h2 class="wp-block-heading">Best Practices I&#8217;ve Learned the Hard Way</h2>



<h3 class="wp-block-heading">Use Version Control</h3>



<p>Don&#8217;t edit files directly on the server and hope for the best. Use Git:</p>



<pre class="wp-block-code"><code>git add .
git commit -m "Add statistics endpoint"
git push origin main
</code></pre>



<p>On the server:</p>



<pre class="wp-block-code"><code>cd ~/todo-app/backend
git pull origin main
pm2 reload todo-api
</code></pre>



<p>This gives you a proper audit trail and makes rollbacks trivial.</p>



<h3 class="wp-block-heading">Implement Health Checks</h3>



<p>Before declaring victory, verify the deployment actually works. I use a simple script:</p>



<pre class="wp-block-code"><code>#!/bin/bash
# health-check.sh

if curl -s http://localhost:5000/api/health | grep -q "OK"; then
    echo "✓ Health check passed"
    exit 0
else
    echo "✗ Health check failed"
    exit 1
fi
</code></pre>



<p>Run it after deployment:</p>



<pre class="wp-block-code"><code>bash health-check.sh &amp;&amp; echo "Deployment successful"
</code></pre>



<h3 class="wp-block-heading">Monitor Resource Usage</h3>



<p>After deploying, keep an eye on memory and CPU:</p>



<pre class="wp-block-code"><code>pm2 monit
</code></pre>



<p>Or watch status updates:</p>



<pre class="wp-block-code"><code>watch -n 2 'pm2 status'
</code></pre>



<p>Sometimes new code is less efficient than you thought.</p>



<h3 class="wp-block-heading">Use Staged Deployments</h3>



<p>For critical apps, deploy to staging first:</p>



<pre class="wp-block-code"><code># Deploy to staging
pm2 start server.js --name todo-api-staging --env staging

# Test thoroughly
curl http://localhost:5001/api/stats

# If all good, deploy to production
pm2 reload todo-api
</code></pre>



<h3 class="wp-block-heading">Automate Common Tasks</h3>



<p>Create a deployment script to avoid mistakes:</p>



<pre class="wp-block-code"><code>#!/bin/bash
# deploy.sh

echo "Starting deployment..."

# Backup
cp server.js server.js.backup.$(date +%Y%m%d-%H%M%S)

# Pull latest code
git pull origin main

# Install dependencies
npm install --production

# Reload application
pm2 reload todo-api

# Health check
sleep 3
if curl -s http://localhost:5000/api/health | grep -q "OK"; then
    echo "✓ Deployment successful"
else
    echo "✗ Deployment failed, rolling back"
    cp server.js.backup.* server.js
    pm2 reload todo-api
    exit 1
fi
</code></pre>



<p>Make it executable:</p>



<pre class="wp-block-code"><code>chmod +x deploy.sh
./deploy.sh
</code></pre>



<h2 class="wp-block-heading">Wrapping Up</h2>



<p>Deployments don&#8217;t have to be scary, you know. The most important thing is to have a good workflow and stick to it:</p>



<ol class="wp-block-list">
<li><strong>Always backup</strong> before making any changes in production.</li>



<li><strong>Use PM2 reload</strong> for backend updates with no downtime.</li>



<li><strong>Test immediately</strong> after deployment, don&#8217;t assume it worked.</li>



<li><strong>Monitor logs</strong> for at least a few minutes after deployment</li>



<li><strong>Know how to rollback</strong> quickly to where you were when things go wrong.</li>
</ol>



<p>You can update your Node.js backend without losing any requests with PM2&#8217;s reload feature. Nginx serves static files directly, so changes to the frontend happen right away. And if you have the right backups, your system will always be up and running in less than 30 seconds.</p>



<p>I&#8217;ve been using this process for years in dozens of production apps. It has saved me from a lot of sleepless nights and having to roll back things in a hurry. I hope it does the same for you.</p>



<p>Happy deployment!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxpathfinder.com/mastering-zero-downtime-deployments-node-js-and-react-application/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Understanding systemd Service Management: A Comprehensive Guide</title>
		<link>https://linuxpathfinder.com/understanding-systemd-service-management-a-comprehensive-guide-2/</link>
					<comments>https://linuxpathfinder.com/understanding-systemd-service-management-a-comprehensive-guide-2/#respond</comments>
		
		<dc:creator><![CDATA[Asif Khan]]></dc:creator>
		<pubDate>Fri, 26 Dec 2025 14:04:19 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<guid isPermaLink="false">https://linuxpathfinder.com/?p=203</guid>

					<description><![CDATA[Systemd is now the default init system and service manager for most modern Linux distributions. If you want to be a good Linux system administrator, you need to know how to use systemd. This is true whether you&#8217;re in charge...]]></description>
										<content:encoded><![CDATA[
<p>Systemd is now the default init system and service manager for most modern Linux distributions. If you want to be a good Linux system administrator, you need to know how to use systemd. This is true whether you&#8217;re in charge of web servers, databases, or custom applications. This guide has real-world examples and hands-on exercises to help you learn how to manage systemd services.</p>



<h2 class="wp-block-heading">What is systemd?</h2>



<p>Systemctl is the main command for working with systemd. It controls the systemd system and service manager. Unit files with the .service extension define services in systemd. These files are usually found in /lib/systemd/system/ or /etc/systemd/system/.<a href="https://github.com/openlinux01/documentation/blob/main/systemd-service-management.md#what-is-systemd"></a></p>



<p>systemd is an init system and system manager that is now the standard for most major Linux distributions, such as RHEL, CentOS, Ubuntu, and Debian. It took the place of older init systems like SysV init and Upstart. It has advanced dependency management, the ability to start services in parallel, and the ability to activate services on demand.</p>



<h2 class="wp-block-heading">Installing and checking systemd</h2>



<p><a href="https://github.com/openlinux01/documentation/blob/main/systemd-service-management.md#installing-and-verifying-systemd"></a>Most modern versions of Linux already have systemd installed. You can check the version and installation of systemd on your computer:</p>



<pre class="wp-block-code"><code>systemctl --version</code></pre>



<p>Sample output:</p>



<pre class="wp-block-code"><code>systemd 249 (249.11-0ubuntu3.12)
+PAM +AUDIT +SELINUX +APPARMOR +IMA +SMACK +SECCOMP +GCRYPT +GNUTLS +OPENSSL +ACL +BLKID +CURL +ELFUTILS +FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP +LIBFDISK +PCRE2 -PWQUALITY -P11KIT -QRENCODE +BZIP2 +LZ4 +XZ +ZLIB +ZSTD -XKBCOMMON +UTMP +SYSVINIT default-hierarchy=unified</code></pre>



<p>To check if systemd is running as PID 1 (the init process):</p>



<pre class="wp-block-code"><code>ps -p 1</code></pre>



<p>Output:</p>



<pre class="wp-block-code"><code>PID TTY          TIME CMD
  1 ?        00:00:12 systemd</code></pre>



<h2 class="wp-block-heading">Basic systemd Commands<a href="https://github.com/openlinux01/documentation/blob/main/systemd-service-management.md#basic-systemd-commands"></a></h2>



<h3 class="wp-block-heading">Starting and Stopping Services<a href="https://github.com/openlinux01/documentation/blob/main/systemd-service-management.md#starting-and-stopping-services"></a></h3>



<p>To start a service immediately:</p>



<pre class="wp-block-code"><code>sudo systemctl start nginx</code></pre>



<p>To stop a running service:</p>



<pre class="wp-block-code"><code>sudo systemctl stop nginx</code></pre>



<p>To restart a service (stops and starts):</p>



<pre class="wp-block-code"><code>sudo systemctl restart apache2</code></pre>



<p>To reload the configuration without stopping the service:</p>



<pre class="wp-block-code"><code>sudo systemctl reload nginx</code></pre>



<p>If you&#8217;re not sure if a service supports reload, use this:</p>



<pre class="wp-block-code"><code>sudo systemctl reload-or-restart mysql</code></pre>



<h3 class="wp-block-heading">Turning Services On and Off<a href="https://github.com/openlinux01/documentation/blob/main/systemd-service-management.md#enabling-and-disabling-services"></a></h3>



<p>Enabling a service sets it up so that it starts up automatically when the computer boots up:</p>



<pre class="wp-block-code"><code>sudo systemctl enable nginx</code></pre>



<p>Output:</p>



<pre class="wp-block-code"><code>Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service → /lib/systemd/system/nginx.service.</code></pre>



<p>To disable automatic startup:</p>



<pre class="wp-block-code"><code>sudo systemctl disable apache2</code></pre>



<p>You can enable and start a service in one command:</p>



<pre class="wp-block-code"><code>sudo systemctl enable --now mysql</code></pre>



<h3 class="wp-block-heading">Checking Service Status<a href="https://github.com/openlinux01/documentation/blob/main/systemd-service-management.md#checking-service-status"></a></h3>



<p>The status command gives you a lot of information about a service:</p>



<pre class="wp-block-code"><code>systemctl status sshd</code></pre>



<p>Sample output for a running service:</p>



<pre class="wp-block-code"><code>● sshd.service - OpenSSH server daemon
     Loaded: loaded (/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)
     Active: active (running) since Mon 2024-12-16 09:23:14 UTC; 2 days ago
       Docs: man:sshd(8)
             man:sshd_config(5)
   Main PID: 1842 (sshd)
      Tasks: 1 (limit: 4615)
     Memory: 5.2M
        CPU: 1.234s
     CGroup: /system.slice/sshd.service
             └─1842 "sshd: /usr/sbin/sshd -D &#91;listener] 0 of 10-100 startups"

Dec 16 09:23:14 webserver systemd&#91;1]: Starting OpenSSH server daemon...
Dec 16 09:23:14 webserver sshd&#91;1842]: Server listening on 0.0.0.0 port 22.
Dec 16 09:23:14 webserver sshd&#91;1842]: Server listening on :: port 22.
Dec 16 09:23:14 webserver systemd&#91;1]: Started OpenSSH server daemon.</code></pre>



<p>Key fields explained:</p>



<ul class="wp-block-list">
<li><strong>Loaded</strong>: Shows unit file location and whether service is enabled</li>



<li><strong>Active</strong>: Current running state with uptime</li>



<li><strong>Main PID</strong>: Process ID of the main service process</li>



<li><strong>Tasks</strong>: Number of running tasks/threads</li>



<li><strong>Memory/CPU</strong>: Resource usage</li>



<li><strong>CGroup</strong>: Control group hierarchy</li>



<li><strong>Log entries</strong>: Recent systemd journal entries</li>
</ul>



<p>To check if a service is active:</p>



<pre class="wp-block-code"><code>systemctl is-active nginx</code></pre>



<p>Output:&nbsp;<code>active</code>&nbsp;or&nbsp;<code>inactive</code></p>



<p>To check if a service is enabled:</p>



<pre class="wp-block-code"><code>systemctl is-enabled nginx</code></pre>



<p>Output:&nbsp;<code>enabled</code>,&nbsp;<code>disabled</code>, or&nbsp;<code>static</code></p>



<p>List all running services:</p>



<pre class="wp-block-code"><code>systemctl list-units --type=service --state=running</code></pre>



<p>List all services (running and stopped):</p>



<pre class="wp-block-code"><code>systemctl list-units --type=service --all</code></pre>



<h2 class="wp-block-heading">Structure of a Service Unit<a href="https://github.com/openlinux01/documentation/blob/main/systemd-service-management.md#understanding-systemd-unit-files"></a></h2>



<h3 class="wp-block-heading">Service Unit Structure<a href="https://github.com/openlinux01/documentation/blob/main/systemd-service-management.md#service-unit-structure"></a></h3>



<p>The three main parts of a basic service unit file are [Unit], [Service], and [Install]. For a custom web app, here&#8217;s an example:</p>



<pre class="wp-block-code"><code>cat /etc/systemd/system/myapp.service</code></pre>



<pre class="wp-block-code"><code>&#91;Unit]
Description=My Web Application
After=network.target mysql.service
Requires=mysql.service

&#91;Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/node /opt/myapp/server.js
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal

&#91;Install]
WantedBy=multi-user.target</code></pre>



<p>Key directives explained:</p>



<ul class="wp-block-list">
<li><strong>After</strong>: Makes sure the service starts after the specified units</li>



<li><strong>Requires</strong>: Hard dependency—if the dependency fails, the service won&#8217;t start.</li>



<li><strong>Type</strong>: The type of service (simple, forking, oneshot, notify, or dbus)</li>



<li><strong>ExecStart</strong>: The command to start the service</li>



<li><strong>Restart</strong>: When to restart (never, always, when something goes wrong, or when something goes wrong)</li>



<li><strong>WantedBy</strong>: Target that should have this service</li>
</ul>



<h3 class="wp-block-heading">Generating Custom Services<a href="https://github.com/openlinux01/documentation/blob/main/systemd-service-management.md#creating-custom-services"></a></h3>



<p>Let&#8217;s make a custom service for a Python application:</p>



<pre class="wp-block-code"><code>sudo nano /etc/systemd/system/python-api.service</code></pre>



<p>Content:</p>



<pre class="wp-block-code"><code>&#91;Unit]
Description=Python REST API Service
After=network-online.target
Wants=network-online.target

&#91;Service]
Type=simple
User=apiuser
WorkingDirectory=/opt/python-api
Environment="PATH=/opt/python-api/venv/bin"
ExecStart=/opt/python-api/venv/bin/python app.py
Restart=always
RestartSec=5

&#91;Install]
WantedBy=multi-user.target</code></pre>



<p>After creating the unit file, reload systemd and start the service:</p>



<pre class="wp-block-code"><code>sudo systemctl daemon-reload
sudo systemctl start python-api
sudo systemctl enable python-api</code></pre>



<p>Verify the service is running:</p>



<pre class="wp-block-code"><code>systemctl status python-api</code></pre>



<h2 class="wp-block-heading">Advanced systemd Operations<a href="https://github.com/openlinux01/documentation/blob/main/systemd-service-management.md#advanced-systemd-operations"></a></h2>



<h3 class="wp-block-heading">Managing Dependencies<a href="https://github.com/openlinux01/documentation/blob/main/systemd-service-management.md#managing-dependencies"></a></h3>



<p>View service dependencies:</p>



<pre class="wp-block-code"><code>systemctl list-dependencies nginx</code></pre>



<p>Output:</p>



<pre class="wp-block-code"><code>nginx.service
● ├─system.slice
● ├─network.target
● │ ├─network-online.target
● │ └─network-pre.target
● └─sysinit.target
●   ├─dev-hugepages.mount
●   ├─dev-mqueue.mount</code></pre>



<p>Show reverse dependencies (what depends on this service):</p>



<pre class="wp-block-code"><code>systemctl list-dependencies nginx --reverse</code></pre>



<h3 class="wp-block-heading">Using journalctl to look at logs<a href="https://github.com/openlinux01/documentation/blob/main/systemd-service-management.md#viewing-logs-with-journalctl"></a></h3>



<p>For centralized logging, systemd works with journald. Check out the service logs:</p>



<pre class="wp-block-code"><code>journalctl -u nginx</code></pre>



<p>Follow logs in real-time:</p>



<pre class="wp-block-code"><code>journalctl -u nginx -f</code></pre>



<p>Show logs from the last hour:</p>



<pre class="wp-block-code"><code>journalctl -u mysql --since "1 hour ago"</code></pre>



<p>Show logs between specific times:</p>



<pre class="wp-block-code"><code>journalctl -u sshd --since "2024-12-16 09:00:00" --until "2024-12-16 10:00:00"</code></pre>



<p>Sample output:</p>



<pre class="wp-block-code"><code>Dec 16 09:23:45 webserver sshd&#91;3421]: Accepted publickey for admin from 192.168.1.100 port 54332 ssh2: RSA SHA256:abc123...
Dec 16 09:24:12 webserver sshd&#91;3456]: pam_unix(sshd:session): session opened for user admin(uid=1000) by (uid=0)
Dec 16 09:45:33 webserver sshd&#91;3456]: pam_unix(sshd:session): session closed for user admin</code></pre>



<p>Show only errors:</p>



<pre class="wp-block-code"><code>journalctl -u apache2 -p err</code></pre>



<p>Display logs with full output (no truncation):</p>



<pre class="wp-block-code"><code>journalctl -u nginx --no-pager</code></pre>



<h3 class="wp-block-heading">Services for Masking<a href="https://github.com/openlinux01/documentation/blob/main/systemd-service-management.md#masking-services"></a></h3>



<p>Masking stops a service from starting up on its own or by hand:</p>



<pre class="wp-block-code"><code>sudo systemctl mask apache2</code></pre>



<p>Output:</p>



<pre class="wp-block-code"><code>Created symlink /etc/systemd/system/apache2.service → /dev/null.</code></pre>



<p>If you have two services that want the same port (like Apache and Nginx both wanting port 80), this is helpful. Unmask to let you start over:</p>



<pre class="wp-block-code"><code>sudo systemctl unmask apache2</code></pre>



<h2 class="wp-block-heading">Troubleshooting in the Real World Scenarios<a href="https://github.com/openlinux01/documentation/blob/main/systemd-service-management.md#real-world-troubleshooting-scenarios"></a></h2>



<h3 class="wp-block-heading">Scenario 1: Service Fails to Start After Update<a href="https://github.com/openlinux01/documentation/blob/main/systemd-service-management.md#scenario-1-service-fails-to-start-after-update"></a></h3>



<p><strong>Problem</strong>: After a system update, nginx fails to start.</p>



<p>Investigation steps:</p>



<pre class="wp-block-code"><code>systemctl status nginx</code></pre>



<p>Output shows:</p>



<pre class="wp-block-code"><code>● nginx.service - A high performance web server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
     Active: failed (Result: exit-code) since Wed 2024-12-18 14:22:10 UTC; 30s ago
    Process: 5234 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=1/FAILURE)
        CPU: 45ms</code></pre>



<p>Check detailed logs:</p>



<pre class="wp-block-code"><code>journalctl -u nginx -n 50</code></pre>



<p>Output reveals:</p>



<pre class="wp-block-code"><code>Dec 18 14:22:10 webserver nginx&#91;5234]: nginx: &#91;emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
Dec 18 14:22:10 webserver nginx&#91;5234]: nginx: configuration file /etc/nginx/nginx.conf test failed
Dec 18 14:22:10 webserver systemd&#91;1]: nginx.service: Control process exited, code=exited, status=1/FAILURE</code></pre>



<p><strong>Solution</strong>: Port 80 is already in use. Find the conflicting process:</p>



<pre class="wp-block-code"><code>sudo ss -tlnp | grep :80</code></pre>



<p>Output:</p>



<pre class="wp-block-code"><code>LISTEN 0      511          0.0.0.0:80        0.0.0.0:*    users:(("apache2",pid=1234,fd=4))</code></pre>



<p>Apache2 is using port 80. Stop it and start nginx:</p>



<pre class="wp-block-code"><code>sudo systemctl stop apache2
sudo systemctl disable apache2
sudo systemctl start nginx</code></pre>



<h3 class="wp-block-heading">Scenario 2: The Service Keeps Restarting<a href="https://github.com/openlinux01/documentation/blob/main/systemd-service-management.md#scenario-2-service-keeps-restarting"></a></h3>



<p><strong>Problem</strong>: A custom application service keeps crashing and restarting.</p>



<p>Check status:</p>



<pre class="wp-block-code"><code>systemctl status myapp</code></pre>



<p>Output:</p>



<pre class="wp-block-code"><code>● myapp.service - My Web Application
     Loaded: loaded (/etc/systemd/system/myapp.service; enabled; vendor preset: enabled)
     Active: activating (auto-restart) (Result: exit-code) since Wed 2024-12-18 15:10:45 UTC; 3s ago
    Process: 6789 ExecStart=/usr/bin/node /opt/myapp/server.js (code=exited, status=1/FAILURE)
   Main PID: 6789 (code=exited, status=1/FAILURE)
        CPU: 234ms</code></pre>



<p>View recent restart attempts:</p>



<pre class="wp-block-code"><code>journalctl -u myapp -n 100</code></pre>



<p>Output shows repeated failures:</p>



<pre class="wp-block-code"><code>Dec 18 15:10:42 webserver node&#91;6789]: Error: Cannot find module 'express'
Dec 18 15:10:42 webserver systemd&#91;1]: myapp.service: Main process exited, code=exited, status=1/FAILURE
Dec 18 15:10:42 webserver systemd&#91;1]: myapp.service: Failed with result 'exit-code'.</code></pre>



<p><strong>Solution</strong>: Missing Node.js dependencies. Install them and restart:</p>



<pre class="wp-block-code"><code>cd /opt/myapp
npm install
sudo systemctl restart myapp</code></pre>



<p>If you need to stop the restart loop temporarily:</p>



<pre class="wp-block-code"><code>sudo systemctl stop myapp</code></pre>



<h3 class="wp-block-heading">Scenario 3: Database Service Won&#8217;t Start After Crash<a href="https://github.com/openlinux01/documentation/blob/main/systemd-service-management.md#scenario-3-database-service-wont-start-after-crash"></a></h3>



<p><strong>Problem</strong>: MySQL won&#8217;t start after an unexpected server reboot.</p>



<pre class="wp-block-code"><code>systemctl status mysql</code></pre>



<p>Output:</p>



<pre class="wp-block-code"><code>● mysql.service - MySQL Community Server
     Loaded: loaded (/lib/systemd/system/mysql.service; enabled; vendor preset: enabled)
     Active: failed (Result: exit-code) since Wed 2024-12-18 16:05:12 UTC; 2min ago
    Process: 2341 ExecStartPre=/usr/share/mysql/mysql-systemd-start pre (code=exited, status=0/SUCCESS)
    Process: 2349 ExecStart=/usr/sbin/mysqld (code=exited, status=1/FAILURE)
   Main PID: 2349 (code=exited, status=1/FAILURE)
     Status: "Server startup in progress"</code></pre>



<p>Check MySQL error log:</p>



<pre class="wp-block-code"><code>journalctl -u mysql -n 200</code></pre>



<p>Output:</p>



<pre class="wp-block-code"><code>Dec 18 16:05:12 dbserver mysqld&#91;2349]: &#91;ERROR] InnoDB: Unable to lock ./ibdata1 error: 11
Dec 18 16:05:12 dbserver mysqld&#91;2349]: &#91;ERROR] InnoDB: Check that you do not already have another mysqld process</code></pre>



<p>Check for existing MySQL processes:</p>



<pre class="wp-block-code"><code>ps aux | grep mysqld</code></pre>



<p><strong>Solution</strong>: Stale PID file or lock. Remove it and restart:</p>



<pre class="wp-block-code"><code>sudo rm /var/run/mysqld/mysqld.pid
sudo rm /var/lib/mysql/*.pid
sudo systemctl start mysql</code></pre>



<p>Verify it&#8217;s running:</p>



<pre class="wp-block-code"><code>systemctl status mysql</code></pre>



<h2 class="wp-block-heading">Conclusion<a href="https://github.com/openlinux01/documentation/blob/main/systemd-service-management.md#conclusion"></a></h2>



<p>Systemd is now the standard for managing services on modern Linux distributions. It has powerful tools for controlling, monitoring, and fixing services. You can easily manage any Linux system if you learn how to use systemctl commands, understand how unit files are structured, and know how to use journalctl to find and fix common problems.</p>



<p>Important points:</p>



<ul class="wp-block-list">
<li>For immediate service control, use systemctl start, stop, or restart.</li>



<li>Use systemctl enable to turn on services so that they start up automatically when the computer boots up.</li>



<li>When you&#8217;re having problems, always use systemctl status and journalctl -u.</li>



<li>Make your own unit files for your apps in /etc/systemd/system/.</li>



<li>After changing unit files, run systemctl daemon-reload.</li>



<li>Use journalctl to look at all of your logs in one place.</li>
</ul>



<p>These basic systemd commands will help you keep Linux systems stable and reliable in production environments, whether you&#8217;re in charge of web servers, databases</p>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxpathfinder.com/understanding-systemd-service-management-a-comprehensive-guide-2/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Deploying a Full-Stack Node.js Application on Ubuntu Server Without Docker</title>
		<link>https://linuxpathfinder.com/deploying-a-full-stack-node-js-application-on-ubuntu-server-without-docker/</link>
					<comments>https://linuxpathfinder.com/deploying-a-full-stack-node-js-application-on-ubuntu-server-without-docker/#respond</comments>
		
		<dc:creator><![CDATA[Asif Khan]]></dc:creator>
		<pubDate>Fri, 26 Dec 2025 13:53:04 +0000</pubDate>
				<category><![CDATA[Docker]]></category>
		<category><![CDATA[Linux]]></category>
		<guid isPermaLink="false">https://linuxpathfinder.com/?p=198</guid>

					<description><![CDATA[Deploying a full-stack application directly on a Linux server without the use of containers was one of the most instructive experiences I have encountered recently. Certainly, Docker is highly popular at present, but there is a certain satisfaction in maintaining...]]></description>
										<content:encoded><![CDATA[
<p>Deploying a full-stack application directly on a Linux server without the use of containers was one of the most instructive experiences I have encountered recently. Certainly, Docker is highly popular at present, but there is a certain satisfaction in maintaining full control over your infrastructure and being able to perform direct debugging in a production environment. Allow me to guide you through the process of deploying a React UI alongside a Node.js backend and a PostgreSQL database on Ubuntu 24.04.</p>



<h2 class="wp-block-heading">Reasons for Deploying Without Containers</h2>



<p>We all are aware of Docker&#8217;s popularity. However, it is important to recognise that direct deployment on Ubuntu offers several significant advantages that are often overlooked. The application starts faster, troubleshooting is way easier when you have direct file access, there&#8217;s less resource overhead (which matters when you&#8217;re on a budget), and logging integration with systemd is straightforward. If you&#8217;re working on a small to medium application or you&#8217;re just getting started with DevOps, this approach gives you a solid foundation before jumping into the containerised world.</p>



<h2 class="wp-block-heading">Prerequisites Required Prior to Commencing</h2>



<p>Things You Need to Do Before You Start</p>



<p>Make sure that these basic parts are taken care of:</p>



<ul class="wp-block-list">
<li>Ubuntu 24.04 LTS server with admin rights</li>



<li>You need at least 2GB of RAM and 10GB of disc space</li>



<li>SSH access set up. You can get root or sudo access.</li>
</ul>



<p>For this example, I&#8217;m using a simple Todo List app that has a React frontend, an Express.js backend, and a PostgreSQL database. Perfect for getting the hang of things.</p>



<h2 class="wp-block-heading">Setting Up Node.js the Right Way</h2>



<p>Ubuntu&#8217;s default repositories have older Node.js versions, and trust me, you don&#8217;t want to deal with version mismatches. Here&#8217;s how I installed the latest LTS version from NodeSource:</p>



<pre class="wp-block-code"><code>curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs</code></pre>



<p>Quick verification to make sure everything&#8217;s working:</p>



<pre class="wp-block-code"><code>node --version
npm --version</code></pre>



<p>You should see something like v20.19.6 and 10.8.2. Perfect!</p>



<h2 class="wp-block-heading">Getting PostgreSQL Up and Running</h2>



<p>PostgreSQL is my go-to database for production apps. Installation is straightforward:</p>



<pre class="wp-block-code"><code>sudo apt install -y postgresql postgresql-contrib</code></pre>



<p>I always check if the service is actually running (can&#8217;t tell you how many times I&#8217;ve forgotten this step):</p>



<pre class="wp-block-code"><code>psql --version
sudo systemctl status postgresql</code></pre>



<p>You&#8217;ll see PostgreSQL 16.11 running, which is solid and stable.</p>



<h2 class="wp-block-heading">Nginx for Web Serving and Reverse Proxy</h2>



<p>Nginx is going to serve your static React files and proxy API requests to the Node.js backend. It&#8217;s fast and reliable:</p>



<pre class="wp-block-code"><code>sudo apt install -y nginx
nginx -v</code></pre>



<p>Should show nginx version 1.24.0.</p>



<h2 class="wp-block-heading">PM2 &#8211; Your Application&#8217;s Guardian Angel</h2>



<p>PM2 is seriously one of the best tools out there. It keeps your Node.js app running 24/7 and automatically restarts it if something goes wrong:</p>



<pre class="wp-block-code"><code>sudo npm install -g pm2
pm2 --version</code></pre>



<p>Version 6.0.14 is what I&#8217;m running.</p>



<h2 class="wp-block-heading">Configuring PostgreSQL (The Fun Part)</h2>



<p>Now let&#8217;s set up the database. I like to create a dedicated database and user for each application &#8211; it&#8217;s just cleaner:</p>



<pre class="wp-block-code"><code>sudo -u postgres psql -c "CREATE DATABASE tododb;"
sudo -u postgres psql -c "CREATE USER todouser WITH PASSWORD 'todopass123';"
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE tododb TO todouser;"
sudo -u postgres psql -d tododb -c "GRANT ALL ON SCHEMA public TO todouser;"</code></pre>



<p>Creating the table structure:</p>



<pre class="wp-block-code"><code>sudo -u postgres psql -d tododb -c "CREATE TABLE todos (
  id SERIAL PRIMARY KEY,
  title VARCHAR(255) NOT NULL,
  description TEXT,
  completed BOOLEAN DEFAULT false,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);"</code></pre>



<p>I always add some test data to make sure everything works:</p>



<pre class="wp-block-code"><code>sudo -u postgres psql -d tododb -c "INSERT INTO todos (title, description, completed) VALUES
  ('Welcome to Todo App', 'This is your first todo item', false),
  ('Learn React', 'Build amazing frontend applications', false),
  ('Learn Node.js', 'Create powerful backend APIs', true);"</code></pre>



<h2 class="wp-block-heading">Setting Up the Backend</h2>



<p>Time to get the Node.js backend in place. I organize my projects like this:</p>



<pre class="wp-block-code"><code>mkdir -p ~/todo-app/backend
cd ~/todo-app/backend</code></pre>



<p>Here&#8217;s my package.json &#8211; keeping dependencies minimal and focused:</p>



<pre class="wp-block-code"><code>{
  "name": "todo-backend",
  "version": "1.0.0",
  "description": "Todo app backend API",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "dependencies": {
    "express": "^4.18.2",
    "pg": "^8.11.3",
    "cors": "^2.8.5",
    "dotenv": "^16.3.1"
  }
}</code></pre>



<p>Installing dependencies:</p>



<pre class="wp-block-code"><code>npm install</code></pre>



<p>Pro tip: Always use environment variables for sensitive data. Here&#8217;s my .env setup:</p>



<pre class="wp-block-code"><code>cat &gt; .env &lt;&lt; EOF
PORT=5000
DB_USER=todouser
DB_HOST=localhost
DB_NAME=tododb
DB_PASSWORD=todopass123
DB_PORT=5432
EOF</code></pre>



<p><strong>Important:</strong> In production, use stronger passwords! Consider tools like AWS Secrets Manager or dotenv-vault.</p>



<h2 class="wp-block-heading">Launching with PM2</h2>



<p>This is where the magic happens. PM2 will keep your app running forever:</p>



<pre class="wp-block-code"><code>pm2 start server.js --name todo-api</code></pre>



<p>You&#8217;ll see a nice table showing your app is online. Beautiful!</p>



<p>Now, make sure it starts automatically on server reboots (I forgot this once and had a fun surprise after maintenance):</p>



<pre class="wp-block-code"><code>pm2 startup systemd
# Run the command it gives you
pm2 save</code></pre>



<p>Some useful PM2 commands I use all the time:</p>



<pre class="wp-block-code"><code>pm2 list          # See all running apps
pm2 logs todo-api # View real-time logs
pm2 monit         # Monitor resources
pm2 restart todo-api # Restart the app</code></pre>



<h2 class="wp-block-heading">Building the React Frontend</h2>



<p>Time to create the production build of your React app:</p>



<pre class="wp-block-code"><code>cd ~/todo-app/frontend
npm run build</code></pre>



<p>Vite builds it super fast &#8211; usually done in under a second. The optimized files go into the <code>dist</code> directory.</p>



<h2 class="wp-block-heading">Configuring Nginx (The Critical Part)</h2>



<p>This is where a lot of people get stuck, so I&#8217;ll walk through it carefully. Create your Nginx config:</p>



<pre class="wp-block-code"><code>sudo nano /etc/nginx/sites-available/todo-app</code></pre>



<p>Here&#8217;s the configuration I use:</p>



<pre class="wp-block-code"><code>server {
    listen 80;
    server_name your_server_ip;

    # Serve React frontend
    location / {
        root /home/yourusername/todo-app/frontend/dist;
        index index.html;
        try_files $uri $uri/ /index.html;
    }

    # Proxy API requests to backend
    location /api {
        proxy_pass http://localhost:5000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }
}</code></pre>



<p>Enable the site:</p>



<pre class="wp-block-code"><code>sudo ln -s /etc/nginx/sites-available/todo-app /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default</code></pre>



<p>Always test the config before restarting (learned this lesson the hard way):</p>



<pre class="wp-block-code"><code>sudo nginx -t
sudo systemctl restart nginx</code></pre>



<h2 class="wp-block-heading">The Permission Headache (And How I Fixed It)</h2>



<p>If you see a 500 error, it&#8217;s probably permissions. Check the Nginx error log:</p>



<pre class="wp-block-code"><code>sudo tail -20 /var/log/nginx/error.log</code></pre>



<p>You&#8217;ll likely see &#8220;Permission denied&#8221; errors. Here&#8217;s the fix:</p>



<pre class="wp-block-code"><code>chmod 755 /home/yourusername
chmod -R 755 ~/todo-app</code></pre>



<p>This gave me a headache for about 30 minutes before I figured it out!</p>



<h2 class="wp-block-heading">Testing Everything</h2>



<p>Let&#8217;s make sure everything works. Test the backend directly:</p>



<pre class="wp-block-code"><code>curl http://localhost:5000/api/health
curl http://localhost:5000/api/todos</code></pre>



<p>Test through Nginx:</p>



<pre class="wp-block-code"><code>curl http://localhost/api/health</code></pre>



<p>And from outside your server:</p>



<pre class="wp-block-code"><code>curl http://your_server_ip/api/health</code></pre>



<p>Open your browser and visit your server IP &#8211; you should see your Todo app!</p>



<h2 class="wp-block-heading">Troubleshooting Common Issues</h2>



<p><strong>App keeps crashing?</strong> Check PM2 logs:</p>



<pre class="wp-block-code"><code>pm2 logs todo-api --lines 50</code></pre>



<p><strong>Database connection errors?</strong> Test PostgreSQL:</p>



<pre class="wp-block-code"><code>psql -h localhost -U todouser -d tododb</code></pre>



<p><strong>502 Bad Gateway?</strong> Make sure your backend is running:</p>



<pre class="wp-block-code"><code>pm2 status
netstat -tulpn | grep 5000</code></pre>



<h2 class="wp-block-heading">Security Best Practices</h2>



<p>Don&#8217;t skip this part! Set up a basic firewall:</p>



<pre class="wp-block-code"><code>sudo ufw allow 22/tcp    # SSH
sudo ufw allow 80/tcp    # HTTP
sudo ufw allow 443/tcp   # HTTPS
sudo ufw enable</code></pre>



<p>Change your database password to something strong:</p>



<pre class="wp-block-code"><code>sudo -u postgres psql -c "ALTER USER todouser WITH PASSWORD 'YourStrongPasswordHere';"
</code></pre>



<p>Add security headers to Nginx (inside your server block):</p>



<pre class="wp-block-code"><code>add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;</code></pre>



<h2 class="wp-block-heading">Performance Tips</h2>



<p>Want better performance? Run PM2 in cluster mode:</p>



<pre class="wp-block-code"><code>pm2 delete todo-api
pm2 start server.js -i max --name todo-api</code></pre>



<p>This runs one instance per CPU core &#8211; really helps with load handling.</p>



<p>Add Nginx caching for static files:</p>



<pre class="wp-block-code"><code>location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}</code></pre>



<h2 class="wp-block-heading">Maintenance and Updates</h2>



<p>When you need to deploy updates:</p>



<pre class="wp-block-code"><code>cd ~/todo-app/backend
npm install  # If dependencies changed
pm2 restart todo-api</code></pre>



<p>For frontend updates:</p>



<pre class="wp-block-code"><code>cd ~/todo-app/frontend
npm run build</code></pre>



<p>Regular database backups are crucial:</p>



<pre class="wp-block-code"><code>pg_dump -U todouser -h localhost tododb &gt; backup_$(date +%Y%m%d).sql</code></pre>



<p>To restore:</p>



<pre class="wp-block-code"><code>psql -U todouser -h localhost tododb &lt; backup_20251224.sql</code></pre>



<h2 class="wp-block-heading">Conclusion</h2>



<p>And at the last. We have successfully deployed full-stack Node.js app on Ubuntu 24.04 without Docker. You can do the same for production: PM2 keeps things alive, Nginx serves fast, and PostgreSQL keeps data safe.</p>



<p>Key takeaways from my experience:<br>PM2 is a must-have for keeping your application running 24/7. Nginx is high performance and efficient for static file serving and reverse proxying. PostgreSQL with proper user isolation is a solid database.<br>File permissions can be a problem, check them first.<br>Regular monitoring and backups avoid future problems</p>



<p>I recommend Let&#8217;s Encrypt for SSL certificates for production, automated backups, Grafana for monitoring, and log rotation. But this way you have control over your infrastructure, and it&#8217;s simple and easy to debug.<br>If you have any problems, let me know, I have probably been there before.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxpathfinder.com/deploying-a-full-stack-node-js-application-on-ubuntu-server-without-docker/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Installing and Configuring Windows Subsystem for Linux (WSL) Distros</title>
		<link>https://linuxpathfinder.com/installing-and-configuring-windows-subsystem-for-linux-wsl-distros/</link>
					<comments>https://linuxpathfinder.com/installing-and-configuring-windows-subsystem-for-linux-wsl-distros/#respond</comments>
		
		<dc:creator><![CDATA[Asif Khan]]></dc:creator>
		<pubDate>Thu, 08 Feb 2024 10:03:34 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<guid isPermaLink="false"></guid>

					<description><![CDATA[Flexibility is essential in the dynamic fields of Linux administration and DevOps. By bridging the gap between the Windows and Linux environments, Windows Subsystem for Linux (WSL) facilitates the development, testing, and deployment of cross-platform applications for professionals. Knowing how...]]></description>
										<content:encoded><![CDATA[<p>Flexibility is essential in the dynamic fields of Linux administration and DevOps. By bridging the gap between the Windows and Linux environments, Windows Subsystem for Linux (WSL) facilitates the development, testing, and deployment of cross-platform applications for professionals. Knowing how to install and setup WSL can enhance your workflow, regardless of your level of experience as a Linux administrator or DevOps engineer.</p>
<p>There are two versions:</p>
<ul>
<li><strong>WSL 1:</strong> Faster and lighter file input/output between Linux and Windows.</li>
<li><strong>WSL 2:</strong> A full Linux kernel that is recommended for its improved performance, Docker compatibility, and authentic Linux behavior.</li>
</ul>
<h2>Why Use WSL for Testing?</h2>
<p>WSL allows you to operate complete Linux distributions directly on your Windows computer without the need for a virtual machine or dual-boot configuration. Here is the rationale for why WSL represents a significant advancement in testing:</p>
<ul>
<li><strong>Seamless Integration:</strong> Utilize Linux tools, scripts, and applications natively within the Windows environment.</li>
<li><strong>Resource Efficiency:</strong> Utilize fewer resources than conventional virtual machines, enabling quicker deployment and reduced memory consumption.</li>
<li><strong>Improved Productivity:</strong> Streamline cross-platform testing without departing from your primary operating system.</li>
<li><strong>DevOps Alignment:</strong> Replicate production environments to facilitate precise testing and effective troubleshooting.</li>
</ul>
<h2>Step-by-Step Installation Guide</h2>
<h3>1. Enable WSL on Windows</h3>
<p>First, enable the Windows Subsystem for Linux feature:</p>
<p>Run <strong>PowerShell as Administrator</strong> and execute:</p>
<pre>
wsl --install
</pre>
<p>This installs:</p>
<ul>
<li>
<p>WSL 2 backend</p>
</li>
<li>
<p>Latest Ubuntu LTS distro by default</p>
</li>
</ul>
<p>After installation:</p>
<pre>
wsl --set-default-version 2
</pre>
<h3>Optional: Install a Specific Linux Distro</h3>
<p>To see available distros:</p>
<pre>
wsl --list --online
The following is a list of valid distributions that can be installed.
Install using 'wsl.exe --install &lt;Distro&gt;'.

NAME                            FRIENDLY NAME
AlmaLinux-8                     AlmaLinux OS 8
AlmaLinux-9                     AlmaLinux OS 9
AlmaLinux-Kitten-10             AlmaLinux OS Kitten 10
AlmaLinux-10                    AlmaLinux OS 10
Debian                          Debian GNU/Linux
FedoraLinux-43                  Fedora Linux 43
FedoraLinux-42                  Fedora Linux 42
SUSE-Linux-Enterprise-15-SP7    SUSE Linux Enterprise 15 SP7
SUSE-Linux-Enterprise-16.0      SUSE Linux Enterprise 16.0
Ubuntu                          Ubuntu
Ubuntu-24.04                    Ubuntu 24.04 LTS
archlinux                       Arch Linux
kali-linux                      Kali Linux Rolling
openSUSE-Tumbleweed             openSUSE Tumbleweed
openSUSE-Leap-16.0              openSUSE Leap 16.0
Ubuntu-20.04                    Ubuntu 20.04 LTS
Ubuntu-22.04                    Ubuntu 22.04 LTS
OracleLinux_7_9                 Oracle Linux 7.9
OracleLinux_8_10                Oracle Linux 8.10
OracleLinux_9_5                 Oracle Linux 9.5
openSUSE-Leap-15.6              openSUSE Leap 15.6
SUSE-Linux-Enterprise-15-SP6    SUSE Linux Enterprise 15 SP6</pre>
<p>I have to install a different Linux distribution, specifically Oracle Linux 9, which is what I need to do. Let&#8217;s move forward with the installation in accordance with the instructions.</p>
<pre>
wsl --install --distribution OracleLinux_9_5
wsl: Using legacy distribution registration. Consider using a tar based distribution instead.
Downloading: Oracle Linux 9.5
Oracle Linux 9.5 has been downloaded.
Distribution successfully installed. It can be launched via 'wsl.exe -d Oracle Linux 9.5'
Launching Oracle Linux 9.5...
Installing, this may take a few minutes...
Please create a default UNIX user account. The username does not need to match your Windows username.
For more information visit: https://aka.ms/wslusers
Enter new UNIX username: asif
Changing password for user asif.
New password:
BAD PASSWORD: The password is shorter than 8 characters
Retype new password:
passwd: all authentication tokens updated successfully.
Installation successful!</pre>
<h2>Managing WSL Distros</h2>
<h3>List Installed Distros</h3>
<pre>
wsl --list --verbose
</pre>
<p>or shorthand:</p>
<pre>
wsl -l -v

&nbsp; NAME&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; STATE&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;VERSION
* Ubuntu-24.04&nbsp; &nbsp; &nbsp; &nbsp; Stopped&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;2
&nbsp; FedoraLinux-43&nbsp; &nbsp; &nbsp; Stopped&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;2
&nbsp; Ubuntu&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Stopped&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;2
&nbsp; OracleLinux_8_10&nbsp; &nbsp; Stopped&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;2
&nbsp; OracleLinux_9_5&nbsp; &nbsp; &nbsp;Stopped&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;2
&nbsp; Ubuntu-22.04&nbsp; &nbsp; &nbsp; &nbsp; Stopped&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;2
</pre>
<h3>Set Default Distro</h3>
<pre>
wsl --setdefault OracleLinux_9_5
The operation completed successfully.

wsl --list --verbose
  NAME                STATE           VERSION
* OracleLinux_9_5     Stopped         2
  FedoraLinux-43      Stopped         2
  Ubuntu              Stopped         2
  Ubuntu-24.04        Stopped         2
  OracleLinux_8_10    Stopped         2
  Ubuntu-22.04        Stopped         2</pre>
<h3>Set WSL Version (1 or 2)</h3>
<pre>
wsl --set-version &lt;distro-name&gt; 2
</pre>
<h3>Launch a Specific Distro</h3>
<pre>
wsl -d Ubuntu-24.04
user@DESKTOP:~$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:&nbsp; &nbsp; Ubuntu 24.04.1 LTS
Release:&nbsp; &nbsp; &nbsp; &nbsp; 24.04
Codename:&nbsp; &nbsp; &nbsp; &nbsp;noble
</pre>
<h3>Terminate the instance</h3>
<pre>
wsl --terminate FedoraLinux-43</pre>
<pre>
wsl --list --verbose
&nbsp; NAME&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; STATE&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;VERSION
* OracleLinux_9_5&nbsp; &nbsp; &nbsp;Stopped&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;2
&nbsp; FedoraLinux-43&nbsp; &nbsp; &nbsp; Running&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;2
&nbsp; Ubuntu&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Stopped&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;2
&nbsp; Ubuntu-24.04&nbsp; &nbsp; &nbsp; &nbsp; Stopped&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;2
&nbsp; OracleLinux_8_10&nbsp; &nbsp; Stopped&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;2
&nbsp; Ubuntu-22.04&nbsp; &nbsp; &nbsp; &nbsp; Stopped&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;2

wsl --terminate FedoraLinux-43
The operation completed successfully.

wsl --list --verbose
&nbsp; NAME&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; STATE&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;VERSION
* OracleLinux_9_5&nbsp; &nbsp; &nbsp;Stopped&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;2
&nbsp; FedoraLinux-43&nbsp; &nbsp; &nbsp; Stopped&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;2
&nbsp; Ubuntu&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Stopped&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;2
&nbsp; Ubuntu-24.04&nbsp; &nbsp; &nbsp; &nbsp; Stopped&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;2
&nbsp; OracleLinux_8_10&nbsp; &nbsp; Stopped&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;2
&nbsp; Ubuntu-22.04&nbsp; &nbsp; &nbsp; &nbsp; Stopped&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;2
</pre>
<h3>Shut down all WSL instances</h3>
<pre>
wsl --shutdown
</pre>
<h3>Unregister (delete) the distribution</h3>
<pre>
wsl --unregister Ubuntu-22.04
Unregistering.
The operation completed successfully.

wsl --list --verbose
  NAME                STATE           VERSION
* OracleLinux_9_5     Stopped         2
  FedoraLinux-43      Stopped         2
  Ubuntu              Stopped         2
  Ubuntu-24.04        Stopped         2
  OracleLinux_8_10    Stopped         2</pre>
<pre>
&nbsp;</pre>
<h2>Starting and Stopping WSL</h2>
<h3>Start WSL</h3>
<pre>
wsl
</pre>
<h3>Start a Specific Distro</h3>
<pre>
wsl -d Ubuntu-24.04
</pre>
<h3>Stop All Running Instances</h3>
<pre>
wsl --shutdown
</pre>
<h2>Export, Import, and Backup WSL</h2>
<h3>Export a Distro (for Backup)</h3>
<pre>
wsl --export Ubuntu-24.04 "D:\01\WSL_Backups\Ubuntu24_Snapshot.tar"
</pre>
<h3>Import a Distro (Restore)</h3>
<pre>
wsl --import MyUbuntu D:\WSL\MyUbuntu D:\01\WSL_Backups\Ubuntu24_Snapshot.tar --version 2
</pre>
<h3>Unregister (Delete) a Distro</h3>
<pre>
wsl --unregister FedoraLinux-43
Unregistering.
The operation completed successfully.

wsl --list --verbose
  NAME                STATE           VERSION
* OracleLinux_9_5     Stopped         2
  Ubuntu              Stopped         2
  Ubuntu-24.04        Stopped         2
  OracleLinux_8_10    Stopped         2
</pre>
<h2>Useful WSL Configuration Commands</h2>
<h3>View WSL Config</h3>
<pre>
cat /etc/wsl.conf
</pre>
<p>Example configuration file:</p>
<pre>
[boot]
systemd=true

[network]
generateResolvConf=false
</pre>
<h3>Enable systemd in WSL</h3>
<p>If systemd services like systemctl don’t work:<br />
Edit /etc/wsl.conf inside your distro:</p>
<pre>
sudo nano /etc/wsl.conf
</pre>
<p>Add:</p>
<pre>
[boot]
systemd=true
</pre>
<p>Then run:</p>
<pre>
wsl --shutdown
</pre>
<p>and restart the distro.</p>
<h2>Accessing Windows Files from WSL</h2>
<p>Your Windows drives are available under /mnt:</p>
<pre>
cd /mnt/c
cd /mnt/d
</pre>
<p>You can also open Windows apps from WSL:</p>
<h2>Using WSL for DevOps or Cloud Tools</h2>
<p>You can install:</p>
<ul>
<li>
<p><strong>Docker Desktop</strong> (uses WSL2 backend)</p>
</li>
<li>
<p><strong>kubectl</strong>, <strong>minikube</strong>, or <strong>kind</strong> for Kubernetes</p>
</li>
<li>
<p><strong>Terraform</strong>, <strong>Ansible</strong>, <strong>AWS CLI</strong>, etc.</p>
</li>
</ul>
<pre>
sudo apt update &amp;&amp; sudo apt install -y docker.io kubectl ansible</pre>
<h2>Summary</h2>
<p>Windows Subsystem for Linux (WSL) is an option to take into consideration if you want to create a development environment on Windows that is similar to that of Linux. Through the use of WSL, you are able to work with Windows apps while simultaneously running a full Linux distribution. You may start utilizing WSL by:</p>
<ul>
<li>You can activate Windows Subsystem for Linux (WSL) by going into the settings for Windows Features.</li>
<li>Through the Microsoft Store, you can get your preferred Linux distribution up and running successfully.</li>
<li>Your development environment should be configured in the same manner as it would be on Linux.</li>
</ul>
<p>When creating across many platforms, this setup provides a more consistent experience for the user. You can find thorough information by checking the Microsoft&#8217;s&nbsp;<a href="https://learn.microsoft.com/en-us/windows/wsl/">official WSL documentation</a>.</p>
<p><!-- notionvc: 0478b64d-fc62-4542-9490-45b100275a64 --></p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxpathfinder.com/installing-and-configuring-windows-subsystem-for-linux-wsl-distros/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>How to Use and Master the Nmap Command in Linux</title>
		<link>https://linuxpathfinder.com/how-to-use-and-master-the-nmap-command-in-linux/</link>
					<comments>https://linuxpathfinder.com/how-to-use-and-master-the-nmap-command-in-linux/#respond</comments>
		
		<dc:creator><![CDATA[Asif Khan]]></dc:creator>
		<pubDate>Sat, 03 Feb 2024 12:07:34 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<guid isPermaLink="false"></guid>

					<description><![CDATA[Introduction Nmap, short for Network Mapper, is a robust, open-source utility.&#160;Network Mapper is a sophisticated program for network discovery, security auditing, and service upgrade management. Network administrators and security professionals use it to scan systems, discover hosts, detect open ports,...]]></description>
										<content:encoded><![CDATA[<h2>Introduction</h2>
<p>Nmap, short for Network Mapper, is a robust, open-source utility.&nbsp;Network Mapper is a sophisticated program for network discovery, security auditing, and service upgrade management. Network administrators and security professionals use it to scan systems, discover hosts, detect open ports, and locate networked services. Mastering nmap can help you diagnose network faults and improve network security.</p>
<p>This tutorial will walk you through the fundamentals, advanced capabilities, and best practices of using nmap on Linux.</p>
<h2>Installation of Nmap</h2>
<p>Most Linux distributions include nmap in their official repositories. To install it, use:</p>
<ul>
<li><strong>Debian/Ubuntu:&nbsp;</strong>sudo apt update&nbsp;sudo apt install nmap</li>
<li><strong>CentOS/RHEL:&nbsp;</strong>sudo yum install nmap</li>
<li><strong>Fedora/OEL:&nbsp;</strong>sudo dnf install nmap</li>
</ul>
<h2>Basic Usage</h2>
<h3>a. Scanning a Single Host</h3>
<pre>
nmap &lt;target&gt;
</pre>
<p><strong>Example:</strong></p>
<pre>
nmap 192.168.100.10
Starting Nmap 7.92 ( https://nmap.org )
Nmap scan report for 192.168.100.10
Host is up (0.000013s latency).
Not shown: 999 closed tcp ports (reset)
PORT   STATE SERVICE
22/tcp open  ssh
Nmap done: 1 IP address (1 host up) scanned in 0.23 seconds

nmap 8.8.8.8
Starting Nmap 7.92 ( https://nmap.org )
Nmap scan report for dns.google (8.8.8.8)
Host is up (0.040s latency).
Not shown: 997 filtered tcp ports (no-response), 1 filtered tcp ports (net-unreach)
PORT    STATE SERVICE
53/tcp  open  domain
443/tcp open  https
Nmap done: 1 IP address (1 host up) scanned in 4.98 seconds</pre>
<p>This command scans the default 1,000 ports on the target.</p>
<h3>b. Scanning Multiple Hosts</h3>
<pre>
nmap 192.168.1.1 192.168.1.2
nmap 192.168.1.1-10
nmap 192.168.1.*

nmap 8.8.8.8 8.8.4.4
Starting Nmap 7.92 ( https://nmap.org )
Nmap scan report for dns.google (8.8.8.8)
Host is up (0.056s latency).
Not shown: 997 filtered tcp ports (no-response), 1 filtered tcp ports (net-unreach)
PORT    STATE SERVICE
53/tcp  open  domain
443/tcp open  https

Nmap scan report for dns.google (8.8.4.4)
Host is up (0.056s latency).
Not shown: 998 filtered tcp ports (no-response)
PORT    STATE SERVICE
53/tcp  open  domain
443/tcp open  https
Nmap done: 2 IP addresses (2 hosts up) scanned in 18.76 seconds</pre>
<h3>c. Scanning a Range of IP Addresses</h3>
<pre>
nmap 192.168.1.1-20
</pre>
<p>or</p>
<pre>
nmap 192.168.1.0/24

nmap 192.168.10.0/24
Starting Nmap 7.92 ( https://nmap.org )
Nmap scan report for 192.168.10.1
Host is up (0.014s latency).
Not shown: 995 closed tcp ports (reset)
PORT   STATE    SERVICE
21/tcp filtered ftp
22/tcp filtered ssh
23/tcp filtered telnet
53/tcp open     domain
80/tcp open     http
MAC Address: (New Technologies)

Nmap scan report for 192.168.10.50
Host is up (0.00070s latency).
Not shown: 998 filtered tcp ports (no-response)
PORT     STATE SERVICE
135/tcp  open  msrpc
2179/tcp open  vmrdp
MAC Address:

Nmap scan report for 192.168.10.60
Host is up (0.00070s latency).
All 1000 scanned ports on 192.168.10.60 are in ignored states.
Not shown: 1000 closed tcp ports (reset)
MAC Address:

Nmap scan report for 192.168.10.70
Host is up (0.0000090s latency).
Not shown: 999 closed tcp ports (reset)
PORT   STATE SERVICE
22/tcp open  ssh

Nmap done: 256 IP addresses (4 hosts up) scanned in 22.36 seconds</pre>
<h2>Port Scanning Techniques</h2>
<h3>a. Scan Specific Ports</h3>
<pre>
nmap -p 22,80,443 192.168.10.1

Starting Nmap 7.92 ( https://nmap.org )
Nmap scan report for 192.168.10.1
Host is up (0.0016s latency).

PORT    STATE    SERVICE
22/tcp  filtered ssh
80/tcp  open     http
443/tcp closed   https
MAC Address: (New Technologies)

Nmap done: 1 IP address (1 host up) scanned in 1.39 seconds</pre>
<h3>b. Full Port Scan (1-65535)</h3>
<pre>
nmap -p- 192.168.10.1
Starting Nmap 7.92 ( https://nmap.org )
Nmap scan report for 192.168.10.1
Host is up (0.020s latency).
Not shown: 65527 closed tcp ports (reset)
PORT      STATE    SERVICE
21/tcp    filtered ftp
22/tcp    filtered ssh
23/tcp    filtered telnet
80/tcp    open     http
17998/tcp open     unknown
37443/tcp open     unknown
37444/tcp open     unknown
Nmap done: 1 IP address (1 host up) scanned in 13.07 seconds</pre>
<h3>c. Fast Scan</h3>
<p>Scan only the top 100 common ports:</p>
<pre>
nmap -F 192.168.10.1</pre>
<h2>Service and Version Detection</h2>
<p>To detect running services and their versions:</p>
<pre>
nmap -sV 192.168.10.1
Starting Nmap 7.92 ( https://nmap.org )
Nmap scan report for 192.168.10.1
Host is up (0.0097s latency).
Not shown: 995 closed tcp ports (reset)
PORT   STATE    SERVICE  VERSION
21/tcp filtered ftp
22/tcp filtered ssh
23/tcp filtered telnet
80/tcp open     ssl/http
SF-Port53-TCP:V=7.92%I=7%D=1
SF:(DNSVersionBindReqTCP,44vers
SF:ion\x04bind\0\0\x10\0
SF:0\x0c\0\x02\0\x03\0\0
Service detection performed.
Nmap done: 1 IP address (1 host up) scanned in 18.15 seconds</pre>
<h2>Operating System Detection</h2>
<p>Try to identify the target’s operating system:</p>
<pre>
nmap -O 192.168.10.1
Starting Nmap 7.92 ( https://nmap.org )
Nmap scan report for 192.168.10.1
Host is up (0.0012s latency).
Not shown: 999 closed tcp ports (reset)
PORT   STATE SERVICE
22/tcp open  ssh
Device type: general purpose
Running: Linux 4.X|5.X
OS CPE: cpe:/o:linux:linux_kernel:4 cpe:/o:linux:linux_kernel:5
OS details: Linux 4.15 - 5.6
Network Distance: 1 hop
OS detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 1.79 seconds</pre>
<p>Combine service and OS detection:</p>
<pre>
nmap -A 192.168.10.1
Starting Nmap 7.92 ( https://nmap.org )
Nmap scan report for 192.168.10.1
Host is up (0.00056s latency).
Not shown: 999 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 9.6p1 Ubuntu 3ubuntu13.11 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   256 9f:90:98:f:1f:87:96:b3:73:e3:f1:8a:ee (ECDSA)
|_  256 35:55:ef:e:af:32:b2:c1:51:69:e4:30:e5 (ED25519)
Device type: general purpose
Running: Linux 4.X|5.X
OS CPE: cpe:/o:linux:linux_kernel:4 cpe:/o:linux:linux_kernel:5
OS details: Linux 4.15 - 5.6
Network Distance: 1 hop
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE
HOP RTT     ADDRESS
1   0.56 ms 192.168.10.1
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 2.56 seconds</pre>
<h2>Advanced Scanning Options</h2>
<h3>a. Stealth Scan (SYN Scan)</h3>
<pre>
sudo nmap -sS 192.168.1.1</pre>
<h3>b. UDP Scan</h3>
<pre>
sudo nmap -sU 192.168.1.1
Starting Nmap 7.92 ( https://nmap.org )
Nmap scan report for 192.168.1.1
Host is up (0.000018s latency).
Not shown: 999 closed udp ports (port-unreach)
PORT     STATE         SERVICE
5353/udp open|filtered zeroconf
Nmap done: 1 IP address (1 host up) scanned in 1.34 seconds</pre>
<h3>c. Aggressive Scan</h3>
<pre>
nmap -A 192.168.1.1
Starting Nmap 7.92 ( https://nmap.org )
Nmap scan report for 192.168.1.1
Host is up (0.000058s latency).
Not shown: 999 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.7 (protocol 2.0)
| ssh-hostkey:
|   256 5c:c4:66:2b:70:e3:11:8b:13:14:03:88 (ECDSA)
|_  256 a0:80:d6:53:32:6c:c3:a8:25:e3:be:40 (ED25519)
Device type: general purpose
Running: Linux 2.6.X
OS CPE: cpe:/o:linux:linux_kernel:2.6.32
OS details: Linux 2.6.32
Network Distance: 0 hops
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 2.40 seconds</pre>
<p>This enables OS detection, version detection, script scanning, and traceroute.</p>
<h3>d. Using Nmap Scripts</h3>
<p>Nmap has a powerful scripting engine (NSE):</p>
<pre>
nmap --script=default 192.168.1.1
</pre>
<p>Or use a specific script:</p>
<pre>
nmap --script=http-enum 192.168.1.1</pre>
<h2>Output Formats</h2>
<ul>
<li><strong>Normal Output:</strong> (default)</li>
<li><strong>Grepable Output:&nbsp;</strong>nmap -oG output.txt 192.168.1.1</li>
<li><strong>XML Output:&nbsp;</strong>nmap -oX output.xml 192.168.1.1</li>
<li><strong>All Formats:&nbsp;</strong>nmap -oA output 192.168.1.1<br />
	List of files<br />
	ls<br />
	output.gnmap&nbsp; output.nmap&nbsp; output.txt&nbsp; output.xml</li>
</ul>
<h2>Tips for Mastering Nmap</h2>
<ul>
<li><strong>Practice Regularly:</strong> Try different scan types and options on test networks.</li>
<li><strong>Read the Documentation:</strong> man nmap and the <a href="https://nmap.org/book/man.html" rel="noreferrer" target="_blank">official Nmap documentation</a> are invaluable.</li>
<li><strong>Explore NSE Scripts:</strong> There are hundreds of scripts for various tasks—enumeration, vulnerability detection, brute force, etc.</li>
<li><strong>Stay Legal:</strong> Only scan networks you own or have explicit permission to test.</li>
</ul>
<h2>Useful Nmap Examples</h2>
<ul>
<li>Scan all devices on your subnet:&nbsp;nmap -sn 192.168.1.0/24</li>
<li>Detect firewall rules:&nbsp;nmap -sA 192.168.1.1</li>
<li>Scan for vulnerabilities:&nbsp;nmap &#8211;script vuln 192.168.1.1</li>
</ul>
<h2>Conclusion</h2>
<p>When it comes to auditing network security and exploring networks, Nmap is a strong and flexible tool. Gaining expertise requires consistent effort and a thirst for knowledge. You may quickly and easily map out networks, identify weaknesses, and protect your infrastructure if you are familiar with its many features and possibilities.</p>
<p>Always use Nmap in a responsible and ethical manner.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxpathfinder.com/how-to-use-and-master-the-nmap-command-in-linux/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Comprehensive Guide: Installing and Managing Java 17 on Linux</title>
		<link>https://linuxpathfinder.com/comprehensive-guide-installing-and-managing-java-17-on-linux/</link>
					<comments>https://linuxpathfinder.com/comprehensive-guide-installing-and-managing-java-17-on-linux/#respond</comments>
		
		<dc:creator><![CDATA[Asif Khan]]></dc:creator>
		<pubDate>Mon, 29 Jan 2024 02:37:34 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<guid isPermaLink="false"></guid>

					<description><![CDATA[Java is still one of the best and most popular programming languages out there; it powers mobile apps and enterprise software. This tutorial will help you install and verify Java 17, compile and run Java programs, work with JAR files,...]]></description>
										<content:encoded><![CDATA[<p>Java is still one of the best and most popular programming languages out there; it powers mobile apps and enterprise software. This tutorial will help you install and verify Java 17, compile and run Java programs, work with JAR files, and configure your environment variables on a Linux machine. Now, let&#8217;s dive into it.</p>
<h2>Installing Java 17 on Linux</h2>
<p>Easy way to install Java 17 is with your system’s package manager. For most distributions, OpenJDK is recommended.</p>
<h3>For Ubuntu/Debian:</h3>
<p>Update Your System</p>
<pre>
sudo apt update &amp;&amp; sudo apt upgrade -y
</pre>
<p>Install OpenJDK 17</p>
<pre>
sudo apt install openjdk-17-jdk -y
</pre>
<h3>For CentOS/RHEL/Fedora:</h3>
<pre>
sudo dnf update -y</pre>
<p>Enable EPEL Repository (if needed)</p>
<pre>
sudo dnf install epel-release -y
sudo dnf install java-17-openjdk-devel
</pre>
<h2>Verifying Java Installation</h2>
<p>After installation, verify your Java version by running:</p>
<pre>
java -version

openjdk version "17.0.17" LTS
OpenJDK Runtime Environment (Red_Hat-17.0.17.0.10-1.0.1) (build 17.0.17+10-LTS)
OpenJDK 64-Bit Server VM (Red_Hat-17.0.17.0.10-1.0.1) (build 17.0.17+10-LTS, mixed mode, sharing)
</pre>
<p>You should see an output indicating Java 17 is installed.</p>
<h2>Creating a Java Program in Linux</h2>
<p>Use any text editor to create a new Java source file. For example, using nano:</p>
<pre>
nano HelloWorld.java</pre>
<p>Insert the following code:</p>
<pre>
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}
</pre>
<p>Save and exit the editor (for nano: press Ctrl+O, then Enter, then Ctrl+X).</p>
<h2>Compiling and Running a Java Program</h2>
<p>To compile your Java program, run:</p>
<pre>
javac HelloWorld.java
ls
HelloWorld.class&nbsp; HelloWorld.java
</pre>
<p>This generates a HelloWorld.class file. To execute the program:</p>
<pre>
java HelloWorld
</pre>
<p>You should see:</p>
<pre>
Hello, World!</pre>
<h2>Finding the Java Installation Path</h2>
<p>To determine where Java is installed, run:</p>
<pre>
which java
/usr/bin/java
</pre>
<p>This shows the symbolic link. For the actual installation directory, use:</p>
<pre>
readlink -f $(which java)
/usr/lib/jvm/java-17-openjdk-17.0.17.0.10-1.0.1.el9.x86_64/bin/java
</pre>
<p>Or, to list all installed Java versions with their paths:</p>
<pre>
update-alternatives --config java
There is 1 program that provides 'java'.
  Selection    Command
-----------------------------------------------
*+ 1           java-17-openjdk.x86_64 (/usr/lib/jvm/java-17-openjdk-17.0.17.0.10-1.0.1.el9.x86_64/bin/java)
Enter to keep the current selection[+], or type selection number: 1
</pre>
<h2>Creating a JAR File Using a Java Program</h2>
<p>First, ensure your program is compiled (.class files exist). Then, use the jar command:</p>
<pre>
jar cvf HelloWorld.jar HelloWorld.class
added manifest
adding: HelloWorld.class(in = 427) (out= 290)(deflated 32%)
</pre>
<ul>
<li>c creates a new JAR file.</li>
<li>v enables verbose output.</li>
<li>f specifies the filename.</li>
</ul>
<p>To create an executable JAR, include a manifest specifying the main class:</p>
<pre>
echo "Main-Class: HelloWorld" &gt; manifest.txt
jar cfm HelloWorld.jar manifest.txt HelloWorld.class
ls
HelloWorld.class&nbsp; HelloWorld.jar&nbsp; HelloWorld.java&nbsp; manifest.txt
</pre>
<h2>Executing a JAR File in Linux</h2>
<p>To run your JAR file:</p>
<pre>
java -jar HelloWorld.jar
</pre>
<p>You should again see:</p>
<pre>
Hello, World!</pre>
<h2>Setting JAVA_HOME in Linux</h2>
<p>Setting JAVA_HOME ensures that development tools and scripts can locate your Java installation.</p>
<ol start="1">
<li>
<p>Find your Java installation directory:</p>
<pre>
sudo update-alternatives --config java
</pre>
<p>The path will look something like /usr/lib/jvm/java-17-openjdk-17</p>
</li>
<li>
<p>Open your shell profile (.bashrc, .bash_profile, or .profile) in a text editor:</p>
<pre>
nano ~/.bashrc
</pre>
</li>
<li>
<p>Add the following line at the end:</p>
<pre>
export JAVA_HOME="/usr/lib/jvm/java-17-openjdk-17.0.17.0.10-1.0.1.el9.x86_64"
export PATH="$JAVA_HOME/bin:$PATH"
</pre>
</li>
<li>
<p>Apply the changes:</p>
<pre>
source ~/.bashrc
</pre>
</li>
<li>
<p>Verify:</p>
<pre>
echo $JAVA_HOME
/usr/lib/jvm/java-17-openjdk-17.0.17.0.10-1.0.1.el9.x86_64
</pre>
</li>
</ol>
<h2>Conclusion</h2>
<p>You may now efficiently create, compile, and package Java applications on your Linux machine with Java 17 installed. Having a solid grasp of these fundamental stages guarantees a seamless development experience, regardless of whether you&#8217;re working with basic apps or huge corporate solutions. More new setups and in-depth tutorials are coming soon!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxpathfinder.com/comprehensive-guide-installing-and-managing-java-17-on-linux/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>How to Set Up Passwordless SSH, Rsync, and SCP Commands in Linux</title>
		<link>https://linuxpathfinder.com/how-to-set-up-passwordless-ssh-rsync-and-scp-commands-in-linux/</link>
					<comments>https://linuxpathfinder.com/how-to-set-up-passwordless-ssh-rsync-and-scp-commands-in-linux/#respond</comments>
		
		<dc:creator><![CDATA[Asif Khan]]></dc:creator>
		<pubDate>Mon, 22 Jan 2024 04:30:34 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<guid isPermaLink="false"></guid>

					<description><![CDATA[Linux users and administrators need to be able to transfer files and access them from a distance safely and quickly. SSH, rsync, and scp are all best tools for managing things from far away and keeping data in sync. But...]]></description>
										<content:encoded><![CDATA[<p>Linux users and administrators need to be able to transfer files and access them from a distance safely and quickly. SSH, rsync, and scp are all best tools for managing things from far away and keeping data in sync. But typing in passwords all the time can be boring and impractical, especially for scripts or automated tasks. This is where passwordless authentication comes in. It makes it easier to get in while keeping security high.</p>
<p>In this article, we&#8217;ll talk about how to set up passwordless access with SSH key pairs and how this helps rsync and scp work.</p>
<h2>Understanding Passwordless SSH Authentication</h2>
<p><strong>Passwordless SSH</strong> enables users to log in to remote Linux systems securely without entering a password each time. This is achieved using a pair of cryptographic keys:</p>
<ul>
<li><strong>Private Key:</strong> Stays on your local machine and must be kept secure.</li>
<li><strong>Public Key:</strong> Shared with the remote server.</li>
</ul>
<p>When properly configured, the SSH protocol uses these keys to authenticate the user, eliminating manual password entry.</p>
<h2>Step 1: Generate SSH Key Pair</h2>
<p>On your local machine, open a terminal and run:</p>
<pre>
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
or
ssh-keygen -t rsa -b 4096</pre>
<ul>
<li>Press Enter to accept the default file location.</li>
<li>Optionally, set a passphrase for added security.</li>
</ul>
<p>This creates two files in your ~/.ssh/ directory:</p>
<ul>
<li>id_rsa (private key)</li>
<li>id_rsa.pub (public key)</li>
</ul>
<h2>Step 2: Copy the Public Key to the Remote Server</h2>
<p>To enable passwordless login, copy your public key to the remote server’s authorized keys:</p>
<pre>
ssh-copy-id user@remote_host
</pre>
<ul>
<li>Replace user with your remote username and remote_host with the server’s IP or hostname.</li>
</ul>
<p>Alternatively, if ssh-copy-id is unavailable:</p>
<pre>
cat ~/.ssh/id_rsa.pub | ssh user@remote_host 'cat &gt;&gt; ~/.ssh/authorized_keys'
</pre>
<h2>Step 3: Test Passwordless SSH Login</h2>
<p>Try logging into the remote server:</p>
<pre>
ssh user@remote_host
</pre>
<p>If everything is set up correctly, you should be granted access without being prompted for a password.</p>
<h2>Using Passwordless SSH with rsync and scp</h2>
<p>Now that passwordless SSH is enabled, you can leverage it with rsync and scp for seamless file transfers and backups.</p>
<h3>Rsync Example</h3>
<p>Synchronize a local directory to a remote server:</p>
<pre>
rsync -avz /local/dir/ user@remote_host:/remote/dir/
</pre>
<p><em>No password prompt will appear, enabling automated backups or deployments.</em></p>
<h3>SCP Example</h3>
<p>Copy a file to a remote server:</p>
<pre>
scp /path/to/local/file user@remote_host:/path/to/remote/
</pre>
<p>Copy a file from the remote server:</p>
<pre>
scp user@remote_host:/path/to/remote/file /path/to/local/
</pre>
<h2>Security Best Practices</h2>
<ul>
<li><strong>Protect Your Private Key:</strong> Never share your private key and always use strong filesystem permissions (chmod 600 ~/.ssh/id_rsa).</li>
<li><strong>Consider a Passphrase:</strong> For additional security, set a passphrase when generating your SSH key.</li>
<li><strong>Limit SSH Access:</strong> Use firewalls and SSH configuration (/etc/ssh/sshd_config) to restrict who can log in.</li>
<li><strong>Use SSH Agent:</strong> If you use a passphrase, ssh-agent can cache your decrypted key during a session for convenience.</li>
</ul>
<h2>Conclusion</h2>
<p>Setting up passwordless SSH authentication makes Linux environments much more productive, especially when using rsync and scp. You can safely and quickly automate file transfers, backups, and remote operations with these tools.<br />
By doing the things above, you&#8217;ll make your Linux management tasks easier and faster than ever.</p>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxpathfinder.com/how-to-set-up-passwordless-ssh-rsync-and-scp-commands-in-linux/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Understanding the ss Command in Linux: A Modern Tool for Network Analysis</title>
		<link>https://linuxpathfinder.com/understanding-the-ss-command-in-linux-a-modern-tool-for-network-analysis/</link>
					<comments>https://linuxpathfinder.com/understanding-the-ss-command-in-linux-a-modern-tool-for-network-analysis/#respond</comments>
		
		<dc:creator><![CDATA[Asif Khan]]></dc:creator>
		<pubDate>Thu, 18 Jan 2024 04:30:34 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<guid isPermaLink="false"></guid>

					<description><![CDATA[Having the right tools is very important for managing and fixing networks on Linux systems. The ss (socket statistics) command is one of the most powerful and useful tools for looking into sockets, connections, and network statistics. In this post,...]]></description>
										<content:encoded><![CDATA[<p>Having the right tools is very important for managing and fixing networks on Linux systems. The ss (socket statistics) command is one of the most powerful and useful tools for looking into sockets, connections, and network statistics. In this post, we&#8217;ll talk about what the &#8220;ss&#8221; command is, what it does, and how to use it in real life.</p>
<h3>What is the ss Command?</h3>
<p>The ss command is a tool that dumps socket statistics and shows information that is similar to what you would get with the older netstat tool. But &#8220;ss&#8221; is faster, gives more information, and is now the best way to look at network sockets on modern Linux distributions.</p>
<h3>Why Choose ss Over netstat?</h3>
<p>While netstat has been a staple for network diagnostics, it is now deprecated in many distributions in favor of ss. Here’s why:</p>
<ul>
<li><strong>Speed:</strong> ss leverages /proc files directly, making it significantly faster.</li>
<li><strong>Detail:</strong> It provides more detailed information about sockets.</li>
<li><strong>Active Development:</strong> ss is actively maintained, ensuring compatibility with the latest Linux kernels and features.</li>
</ul>
<h3>Basic Syntax</h3>
<p>The basic syntax for the ss command is:</p>
<pre>
ss [options]

</pre>
<p>You can combine options to tailor the output to your needs.</p>
<h2>Common Use Cases and Examples</h2>
<h3>
1. Display All Connections</h3>
<p>To display all current TCP, UDP, and Unix socket connections:</p>
<pre>
ss -a

</pre>
<h3>2. Show Listening Sockets</h3>
<p>To list all listening sockets (commonly used by servers):</p>
<pre>
ss -l

</pre>
<h3>3. Filter by Protocol</h3>
<p>To view only TCP connections:</p>
<pre>
ss -t

</pre>
<p>For UDP connections:</p>
<pre>
ss -u

</pre>
<h3>4. Show Process Information</h3>
<p>To display the processes using each socket:</p>
<pre>
ss -p

</pre>
<h3>5. Display Listening TCP Ports with Process Info</h3>
<pre>
ss -ltp

</pre>
<h3>6. Find Connections on a Specific Port</h3>
<p>For example, to see connections on port 80:</p>
<pre>
ss -t state listening '( sport = :80 )'

</pre>
<h3>7. Show All IPv4 and IPv6 Connections</h3>
<pre>
ss -4   # IPv4 only
ss -6   # IPv6 only

</pre>
<h2>Key Options and Flags</h2>
<ul>
<li>a: Show all sockets (listening and non-listening)</li>
<li>l: Display only listening sockets</li>
<li>t: Show TCP sockets</li>
<li>u: Show UDP sockets</li>
<li>p: Show process using socket</li>
<li>n: Don’t resolve service names</li>
</ul>
<h3>Conclusion</h3>
<p>The &#8220;ss&#8221; command is an important tool for anyone who works with Linux systems or networks. It is the modern standard for investigating and fixing socket problems because it is fast, flexible, and has a lot of features. If you learn how to use &#8220;ss,&#8221; you can quickly see what&#8217;s going on with your system&#8217;s network and fix connection problems quickly. The ss command should be in your Linux toolkit if you want to debug a service, keep an eye on active connections, or make sure security.</p>
<h4>Further Reading:</h4>
<ul>
<li>To see the full documentation, type man ss in your terminal or go to the <a href="https://man7.org/linux/man-pages/man8/ss.8.html">Linux man pages online</a>.</li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxpathfinder.com/understanding-the-ss-command-in-linux-a-modern-tool-for-network-analysis/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Unlocking Network Insights with the netstat Command in Linux</title>
		<link>https://linuxpathfinder.com/unlocking-network-insights-with-the-netstat-command-in-linux/</link>
					<comments>https://linuxpathfinder.com/unlocking-network-insights-with-the-netstat-command-in-linux/#respond</comments>
		
		<dc:creator><![CDATA[Asif Khan]]></dc:creator>
		<pubDate>Wed, 10 Jan 2024 06:30:34 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<guid isPermaLink="false"></guid>

					<description><![CDATA[It&#8217;s very important to know what&#8217;s going on with your network when you&#8217;re in charge of Linux systems. The netstat command is a classic tool that lets you see what&#8217;s going on with your system&#8217;s network activity, whether you&#8217;re a...]]></description>
										<content:encoded><![CDATA[<p>It&#8217;s very important to know what&#8217;s going on with your network when you&#8217;re in charge of Linux systems. The netstat command is a classic tool that lets you see what&#8217;s going on with your system&#8217;s network activity, whether you&#8217;re a veteran system administrator or just starting out with Linux. We&#8217;ll talk about what netstat is, how it works, and some real-life examples that will help you get the most out of it in this article.</p>
<h3>What Is netstat?</h3>
<p>The command-line tool &#8220;netstat&#8221; stands for &#8220;network statistics&#8221; and gives you information about network connections, routing tables, interface statistics, masquerade connections, and multicast memberships. Most Unix-like operating systems, like Linux, can use it.</p>
<p>In recent years, newer tools like ss have come out, but netstat is still a must-have for many administrators because it is simple to use and works with a wide range of systems.</p>
<h3>Why Use netstat?</h3>
<p>Network issues can be elusive, and troubleshooting them often requires insight into which ports are open, which services are listening, and where connections are being made. The netstat command gives you a real-time snapshot of this information, making it invaluable for:</p>
<ul>
<li>Diagnosing connectivity problems</li>
<li>Identifying open ports and active services</li>
<li>Monitoring network performance</li>
<li>Investigating security concerns</li>
</ul>
<h3>Basic Usage</h3>
<p>To get started, open your terminal and type:</p>
<pre>
netstat

</pre>
<p>By default, this displays a list of active connections, but the real power of netstat comes from its various options. Let’s look at some of the most useful ones.</p>
<h3>1. Display All Connections and Listening Ports</h3>
<pre>
netstat -a

</pre>
<p>This command shows all active connections and the ports on which the computer is listening.</p>
<h3>2. Show Listening Ports Only</h3>
<pre>
netstat -l

</pre>
<p>If you’re interested in services waiting for incoming connections, this option filters out only the listening ports.</p>
<h3>3. Display Numerical Addresses</h3>
<pre>
netstat -n

</pre>
<p>By default, netstat tries to resolve hostnames and service names, which can slow things down. The -n flag keeps things fast and simple by showing raw IP addresses and port numbers.</p>
<h3>4. Show Process IDs</h3>
<pre>
netstat -p

</pre>
<p>This option adds the PID and name of the program to the output, which is especially useful for tracking down which application is using a specific port.</p>
<h3>5. Combine Options for Detailed Output</h3>
<p>A common combination is:</p>
<pre>
netstat -tulnp

</pre>
<p>Here’s what these flags mean:</p>
<ul>
<li>t: TCP connections</li>
<li>u: UDP connections</li>
<li>l: Listening ports</li>
<li>n: Numerical addresses</li>
<li>p: Show process information</li>
</ul>
<p>This command gives a concise, detailed overview of all active listening TCP and UDP ports with numerical addresses and associated processes.</p>
<h3>Example Output</h3>
<p>Here’s what a sample output might look like:</p>
<pre>
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1023/sshd
tcp6       0      0 :::80                   :::*                    LISTEN      1204/apache2

</pre>
<p>This tells you which protocols are being used, which addresses and ports are in play, and which processes are responsible.</p>
<h3>Modern Alternatives</h3>
<p>It&#8217;s important to note that even though netstat is still widely used, some newer distributions recommend using the ss command instead because it is faster and has more features. netstat is still a good backup, though, especially on older systems.</p>
<h3>Conclusion</h3>
<p>Anyone who works with Linux systems needs the netstat command. It is easy to find network problems, keep an eye on activity, and keep a safe environment with its simple output and flexible options. If you want to know what&#8217;s going on behind the scenes or if you&#8217;re having problems, you should learn how to use netstat.<!-- notionvc: a8a92328-515a-434a-9fc2-0a3bb0c25c40 --></p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxpathfinder.com/unlocking-network-insights-with-the-netstat-command-in-linux/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
