DEV Community

Cover image for Your Linux Server Is Slow. Don't Restart It Yet: Do This First
Asep Sayyad
Asep Sayyad

Posted on Originally published at asepsayyad007.in

Your Linux Server Is Slow. Don't Restart It Yet: Do This First

Rebooting clears the crime scene and hides the real bottleneck. Here is the non-destructive 60-second triage sequence to run before you touch the reboot button.

You connect over SSH to a production node and press the Enter key.

Nothing happens for two full seconds.

Then the cursor stumbles across the terminal, paints your username, and lags on every keystroke. Your Slack channels are flashing red with high-latency alerts from the load balancer, error rates are climbing, and a colleague on the incident bridge is already saying the words: "Just reboot the box."

Do not do it.

Rebooting a sluggish Linux server is an expensive admission of defeat. Worse, it is often a trap.

When you issue a reboot on a live, degraded system, you wipe volatile RAM state. You flush kernel ring buffers (dmesg) that hold hardware controller and memory warnings. You drop ephemeral network connection tables, kill inflight process traces, and erase the memory-backed filesystems in /run and /tmp.

And what happens next? If the real cause was an exhausted socket listen backlog, an unresponsive NFS storage mount, a runaway cron job, or memory thrashing under heavy swap, the node reboots, takes two minutes to start its services, and falls flat on its face the moment production traffic swings back.

You haven't resolved the incident. You just turned a reproducible five-minute diagnostic problem into an intermittent ghost bug that will strike again during peak traffic tomorrow.

Triage under fire is not about typing loud, disruptive commands to look busy. It is about disciplined observation before intervention.

Before you touch the reboot button or send destructive signals, run this systematic, non-destructive triage sequence. It takes under sixty seconds, costs virtually zero CPU overhead, and pins down the exact subsystem choking your system.


The 10-Second Baseline: Uptime, Core Count, and Load Trajectory

Start by checking whether the machine agrees with your external monitoring alerts. Synthetic health checks often flag a node as down simply because an external router jittered or an ingress proxy timed out.

Open your shell and run two commands:

uptime
nproc
Enter fullscreen mode Exit fullscreen mode

Sample output:

 14:22:08 up 88 days, 14:12,  2 users,  load average: 36.40, 18.20, 6.10
 8
Enter fullscreen mode Exit fullscreen mode

This output immediately tells you two critical facts: how many processor cores exist to service work, and which direction the load is heading.

Many engineers misinterpret load average as raw CPU utilization. It is not.

On Linux, load average measures the exponential moving average of processes in two specific kernel states: TASK_RUNNING (state R, processes actively running on a CPU or waiting in the run queue) and TASK_UNINTERRUPTIBLE (state D, processes blocked waiting on uninterruptible disk, network I/O, or hardware kernel locks).

On an 8-core host, a load average of 8.0 means every core is fully occupied with zero tasks queuing. A load of 36.4 means an average of 28 tasks were queuing for service over the past sixty seconds.

Now read the trajectory across the three timestamps (one, five, and fifteen minutes):

  • When the numbers read 36.4, 18.2, 6.1, load is actively surging upward. The fire is spreading right now.
  • When the numbers read 4.2, 16.5, 32.0, the worst of the spike has already cleared. The sluggishness you feel in your terminal is the tail end of queued work or swap reclamation, not an active disaster.
  • When all three numbers hover around 28.0, 27.8, 28.2, you are dealing with chronic, steady-state saturation.

A high load average tells you that a queue is backing up, but it does not tell you where that queue lives. It could be CPU starvation, disk I/O wait, or memory paging.

To find out, check the kernel's pressure telemetry.


Modern Linux Telemetry: Pressure Stall Information (PSI)

If your server runs Linux kernel 4.20 or newer (standard on Ubuntu 20.04+, Debian 11+, and RHEL 8+), you have access to Pressure Stall Information (PSI).

PSI is the single most accurate telemetry system built into modern Linux. Traditional utilization numbers can mislead you: a server with 98% CPU utilization might be running perfectly healthy, high-throughput database queries with zero user-facing delay. Conversely, a server with only 15% CPU utilization might be completely frozen because every thread is stuck waiting for a choked disk.

PSI solves this contradiction by measuring stall time: the percentage of wall-clock time that tasks were delayed waiting for resources.

Inspect the three pressure files in /proc:

grep -H . /proc/pressure/{cpu,memory,io}
Enter fullscreen mode Exit fullscreen mode

Sample output:

/proc/pressure/cpu:some avg10=2.10 avg60=1.40 avg300=0.80 total=1240182
/proc/pressure/memory:some avg10=64.20 avg60=42.10 avg300=12.00 total=8923011
/proc/pressure/memory:full avg10=38.50 avg60=18.30 avg300=4.10 total=4129841
/proc/pressure/io:some avg10=8.40 avg60=5.10 avg300=2.00 total=3120914
/proc/pressure/io:full avg10=1.20 avg60=0.80 avg300=0.20 total=410920
Enter fullscreen mode Exit fullscreen mode

Notice the two distinct metric lines: some and full.

The some metric indicates the percentage of time during which at least one task was stalled waiting for that resource while other tasks continued executing.

The full metric indicates total starvation: all runnable tasks on the system were simultaneously stalled waiting for memory or storage. During those windows, zero productive CPU cycles occurred. Notice that CPU only has a some metric, because if any task is executing on a CPU, the CPU is making progress.

Look at the sample output above:

  • CPU pressure is minimal (some avg10=2.10). CPU cycles are not your problem.
  • Memory pressure is alarming (some avg10=64.20, full avg10=38.50). For almost 40% of the past ten seconds, every process on the host was stalled waiting on memory page allocation or swap transfer.

In five seconds, without installing third-party agents or guessing, PSI isolated the exact physical bottleneck: memory starvation.

Now let us inspect the four primary failure domains individually to verify the underlying process mechanisms.


Subsystem 1: CPU Starvation, System Call Overhead, and CPU Steal

When CPU pressure is high, you need to know who is burning the cycles. Are they user applications, kernel context switches, or a noisy neighbor in your cloud environment?

Run vmstat and mpstat:

vmstat 1 5
Enter fullscreen mode Exit fullscreen mode

Sample output:

procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
14  0      0 120400  42100 812900    0    0     4    12 2840 4520 88 11  1  0  0
16  0      0 119800  42100 812900    0    0     0     0 2910 4810 90  9  1  0  0
15  0      0 120100  42100 812900    0    0     0     8 2880 4700 89 10  1  0  0
Enter fullscreen mode Exit fullscreen mode

Read the r column under procs. That is your run queue: the number of processes currently executing or waiting for CPU time slices. If r consistently exceeds your core count, you have genuine CPU saturation.

Next, glance at the cpu columns on the far right:

The us (user) column reflects application code execution. If us is above 85%, your application processes (such as Python workers, Java JVM threads, or Node.js instances) are burning cycles doing compute-heavy work.

The sy (system) column tracks kernel execution time. If sy spikes above 30%, application code is not the direct culprit. The kernel is spending massive time servicing system calls, resolving lock contention, or executing page faults. High sy often points to inefficient software hammering system calls like gettimeofday, epoll_ctl, or thrashing on thread synchronization locks (futex).

The st (steal) column is critical for cloud virtual machines (such as AWS EC2 or GCP Compute Engine). Steal time measures the percentage of physical CPU cycles that the underlying hypervisor took away from your VM to service other tenants on the same physical hardware.

If st climbs above 15% or 20%, your virtual machine is starved by noisy neighbors on the physical host. No amount of software tuning, caching, or code optimization will solve high steal time. You must migrate, resize, or redeploy the instance to a new hypervisor.

To pinpoint the exact processes consuming CPU, use pidstat:

pidstat -u 1 3
Enter fullscreen mode Exit fullscreen mode

Alternatively, run a targeted ps query sorted by CPU usage:

ps -eo pid,ppid,user,%cpu,%mem,comm --sort=-%cpu | head -n 10
Enter fullscreen mode Exit fullscreen mode

If you notice a single multi-threaded process reporting 750% CPU on an 8-core machine, remember that Linux sums CPU usage across all threads. To expand that process and see which specific threads are burning cycles, inspect the threads directly:

ps -eLf -q <PID>
Enter fullscreen mode Exit fullscreen mode

Subsystem 2: Memory Starvation and the Swap Thrashing Trap

When a server feels sluggish, junior engineers often open free -h, see 150MB in the free column, and assume the server is out of memory.

That is almost always a false alarm.

The Linux kernel treats unused memory as wasted memory. Any RAM not actively claimed by application heaps is aggressively allocated to page caches (buff/cache) to keep disk reads fast.

free -h
Enter fullscreen mode Exit fullscreen mode

Sample output:

               total        used        free      shared  buff/cache   available
Mem:            15Gi        12Gi       210Mi       1.2Gi       2.8Gi       1.8Gi
Swap:          4.0Gi       3.2Gi       819Mi
Enter fullscreen mode Exit fullscreen mode

Ignore the free column. Focus entirely on available.

The available metric is the kernel's estimate of how much memory can be handed to new or existing processes without forcing the system into swap. It includes easily reclaimable page cache and dentries.

The real disaster in memory management is not low free memory. It is swap thrashing.

When active memory demand exceeds physical RAM, the kernel starts writing anonymous memory pages to your swap device while simultaneously evicting file-backed page caches.

Look at vmstat 1 5 again, specifically the si (swap in) and so (swap out) columns:

procs -----------memory---------- ---swap-- -----io----
 r  b   swpd   free   buff  cache   si   so    bi    bo
 4  3 342100 110200  18200 142000 4820 5120  4890  5210
 5  4 349800 108400  18100 141200 5120 6210  5190  6300
Enter fullscreen mode Exit fullscreen mode

When si and so are both nonzero and continuously ticking in the thousands of kilobytes per second, your server is thrashing.

Every time a thread needs a page of memory, the kernel has to stall that thread, issue a disk I/O request to read it back from swap, and write another page out to make room. Because NVMe and SSD storage are orders of magnitude slower than DDR4 or DDR5 RAM, every thread on the system slows to a crawl. Your shell freezes because the bash process itself is being paged in and out of swap.

Next, check whether the kernel's Out-Of-Memory (OOM) killer has already begun terminating processes in the background:

dmesg -T | grep -i -E "oom|out of memory|killed process"
Enter fullscreen mode Exit fullscreen mode

If the OOM killer triggered, you will see output documenting the victim process name, PID, total virtual memory, and RSS footprint:

[Tue Oct  8 14:18:22 2026] Out of memory: Killed process 14210 (node) total-vm:4210984kB, anon-rss:3810240kB, file-rss:0kB, shmem-rss:0kB
Enter fullscreen mode Exit fullscreen mode

When you see this, you do not have a mysterious system degradation. You have a specific application process that exceeded its memory boundaries.


Subsystem 3: Uninterruptible Sleep (The "D" State) and Storage Latency

Have you ever tried to terminate a sluggish process using kill -9 <PID>, only to watch the process continue sitting in ps completely unchanged?

You did not break kill. You encountered a process stuck in uninterruptible sleep, represented by state D.

When a process executes a system call that requires waiting on physical hardware—such as reading a block from an SSD, waiting on a SAN controller, or issuing an RPC over an NFS mount—the kernel places that task into TASK_UNINTERRUPTIBLE.

In this state, the kernel explicitly refuses to deliver asynchronous signals to the process. Even SIGKILL cannot touch it. The kernel does this to prevent internal driver state corruption. The process will remain frozen until the hardware I/O request returns or times out.

To identify processes trapped in D state:

ps -eo pid,user,state,wchan:20,comm | grep -E ' (D) '
Enter fullscreen mode Exit fullscreen mode

Sample output:

 18420 www-data  D nfs4_wait_on_open    php-fpm
 18421 www-data  D nfs4_wait_on_open    php-fpm
 18429 www-data  D nfs4_wait_on_open    php-fpm
Enter fullscreen mode Exit fullscreen mode

The wchan column reveals the kernel function where the process is currently sleeping. In the snippet above, three PHP workers are stuck waiting on an NFS storage lock (nfs4_wait_on_open). The web server is slow not because PHP is broken, but because the remote NFS file server became unresponsive.

To see the exact line of kernel execution where a stuck process is blocked, read its kernel stack directly:

cat /proc/<PID>/stack
Enter fullscreen mode Exit fullscreen mode

Sample output:

[<0>] io_schedule+0x42/0x70
[<0>] nfs4_wait_on_open+0x98/0x120
[<0>] nfs4_file_open+0x140/0x210
[<0>] do_dentry_open+0x180/0x3a0
[<0>] vfs_open+0x2d/0x30
[<0>] path_openat+0x2c0/0x10a0
[<0>] do_filp_open+0x91/0x140
[<0>] do_sys_openat2+0x96/0x150
[<0>] __x64_sys_openat+0x54/0x90
[<0>] do_syscall_64+0x58/0x120
[<0>] entry_SYSCALL_64_after_hwframe+0x6e/0x0
Enter fullscreen mode Exit fullscreen mode

Notice the top frame: io_schedule. The process surrendered its CPU time slice and is waiting for the storage stack to respond.

To inspect overall storage subsystem performance, check disk await latency with iostat:

iostat -xz 1 3
Enter fullscreen mode Exit fullscreen mode

Sample output:

Device    r/s     w/s     rkB/s     wkB/s   r_await   w_await  aqu-sz  %util
nvme0n1  12.0  1450.0      48.0   98200.0      1.10    148.20   24.50  99.80
Enter fullscreen mode Exit fullscreen mode

Look closely at two metrics: w_await (average write wait time in milliseconds) and aqu-sz (average queue length).

Here, writes are taking 148 milliseconds to complete, and almost 25 I/O requests are stacking up in the device queue. For modern solid-state storage, write latencies above 10 or 15 milliseconds indicate serious drive saturation, controller throttling, or write-buffer exhaustion.

Do not be misled by %util. On modern NVMe drives that support 64,000 parallel queues, a disk can show 100% %util while maintaining sub-millisecond latencies under concurrent access. Always cross-reference %util with r_await and w_await.


The Hidden Storage Killer: Open Deleted Files

There is another common storage trap: df -h reports your root filesystem is at 100% capacity, but when you run du -sh /*, you can only account for 35GB of files.

Where is the missing disk space?

In Linux, deleting a file using rm simply unlinks its directory entry from the filesystem tree. If a running process (like an Nginx access logger or a database worker) still holds an open file descriptor pointing to that inode, the kernel preserves the underlying disk blocks.

The file disappears from ls and du, but the space remains 100% allocated.

To locate these unlinked, space-consuming file handles:

find /proc/*/fd -ls 2>/dev/null | grep '(deleted)' | sort -k7 -nr | head -n 5
Enter fullscreen mode Exit fullscreen mode

Sample output:

142091 10485760 -rw-r--r-- 1 root root 10737418240 Oct  8 12:10 /proc/2190/fd/3 (deleted)
Enter fullscreen mode Exit fullscreen mode

Process ID 2190 is holding open a 10GB file that was deleted from disk.

You do not need to kill the process or restart the daemon to get that disk space back. You can safely truncate the file descriptor through /proc in a single command:

: > /proc/2190/fd/3
Enter fullscreen mode Exit fullscreen mode

The colon operator (:) is the shell's built-in no-op. Redirecting it into the file descriptor instantly resets the file size to zero bytes, freeing the 10GB of storage space immediately without dropping active network connections or restarting the parent daemon.


Subsystem 4: Socket Queues, Connection Trackers, and Network Choke Points

Sometimes CPU usage is 10%, RAM is clear, and disks are idle, yet API requests take five seconds to connect.

When a server feels sluggish from the outside but shows empty system queues on the inside, inspect the network transport layer.

Run ss to inspect your TCP listen backlogs:

ss -lnt
Enter fullscreen mode Exit fullscreen mode

Sample output:

State      Recv-Q Send-Q  Local Address:Port   Peer Address:Port
LISTEN        512    128        0.0.0.0:8080        0.0.0.0:*
LISTEN          0    511        0.0.0.0:443         0.0.0.0:*
Enter fullscreen mode Exit fullscreen mode

For listening sockets (indicated by the LISTEN state), the numbers in Send-Q and Recv-Q take on a completely different meaning than for established data connections:

The Send-Q column indicates the maximum listen backlog limit configured for that socket (the backlog parameter passed to the listen() system call, capped by /proc/sys/net/core/somaxconn).

The Recv-Q column indicates the number of completed TCP handshakes currently waiting in the kernel's accept queue, waiting for the user-space application to call accept().

Look at port 8080 above: Recv-Q is 512, while Send-Q is 128.

The application's listen backlog is completely overflowing. Incoming client connection attempts have saturated the queue, and the Linux kernel is silently dropping new SYN or ACK packets. Clients perceive this as total server unresponsiveness, yet CPU and memory monitors show the box doing almost nothing.

When building network services, handling connection queues and socket backlogs is a frequent tuning requirement. If an application event loop blocks on synchronous disk operations or CPU-heavy JSON parsing, it stops calling accept(), causing Recv-Q to blow past Send-Q within milliseconds.

Next, check connection tracking table exhaustion:

cat /proc/sys/net/netfilter/nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_max
Enter fullscreen mode Exit fullscreen mode

If nf_conntrack_count approaches nf_conntrack_max, the Linux kernel's Netfilter firewall subsystem cannot track any new stateful network connections.

When the table fills, the kernel drops incoming packets immediately at the raw interface layer without sending a reset. External clients time out, while internal processes report connection failures.

Check kernel log buffers for confirmation:

dmesg -T | grep -i "nf_conntrack: table full"
Enter fullscreen mode Exit fullscreen mode

If you see this error, you can raise the table capacity dynamically in memory without restarting:

sysctl -w net.netfilter.nf_conntrack_max=262144
Enter fullscreen mode Exit fullscreen mode

Five Panic Reflexes That Make Outages Worse

During high-stress outages, human adrenaline is often more dangerous than the original bug.

Avoid these five destructive reflexes:

  1. Blindly sending kill -9 (SIGKILL): When you issue SIGKILL, the kernel unceremoniously strips process memory without notifying the application. Inflight write buffers are never flushed to disk, open network connections are abruptly severed without sending TCP resets or FIN packets, and distributed locks (such as Redis or etcd leases) remain locked until they expire. Always send SIGTERM (kill -15) first and give the application ten seconds to drain its state.

  2. Flushing page caches with drop_caches: Running echo 3 > /proc/sys/vm/drop_caches is a common misstep when memory looks low. Dropping clean page caches forces the kernel to purge in-memory caches of shared libraries, binaries, and frequently read indexes. The very next second, every running process must read those files directly from physical disk, creating an immediate, violent disk I/O spike that can push a struggling server into total lockup.

  3. Running unthrottled search tools across the filesystem: Typing find / -name "*.log" or du -sh /* on an I/O-starved machine reads millions of metadata dentries and inodes from disk. If storage is already choked or an NFS share is unresponsive, your search command joins the queue, locks file descriptors, and compounds the I/O freeze.

  4. Applying random sysctl snippets from forums: Changing kernel TCP buffer sizes, virtual memory ratios, or scheduler tunables in the middle of an incident introduces unknown variables. If you change three sysctl settings simultaneously, you will never know which one stabilized the machine or which one created a secondary memory leak.

  5. Executing a panic reboot: A reboot destroys the live /proc metrics, socket queues, and kernel traces needed to diagnose the issue. You trade twenty minutes of temporary relief for another outage tomorrow.


Controlled Stabilization: How to Take Control Safely

Once you isolate the runaway process or saturated subsystem, intervene using controlled, non-destructive tools.

If a runaway background worker or batch script is consuming 100% of your CPU cores, do not kill it immediately. Freeze it:

kill -SIGSTOP <PID>
Enter fullscreen mode Exit fullscreen mode

The SIGSTOP signal suspends process execution instantly. The kernel deschedules the process from the CPU run queue, and its CPU usage drops to 0% within microseconds.

Crucially, the process remains in memory: its memory allocations, network sockets, and file descriptors stay completely intact.

Freezing the process removes system pressure immediately, restoring responsiveness to your SSH session and giving your team time to inspect the process state:

lsof -p <PID>
cat /proc/<PID>/environ
Enter fullscreen mode Exit fullscreen mode

If you confirm the process was a rogue script that cannot be saved, terminate it cleanly:

kill -SIGTERM <PID>
Enter fullscreen mode Exit fullscreen mode

If you decide the process is legitimate but simply needs to run without starving interactive traffic, lower its scheduling priority using renice and ionice:

renice +19 -p <PID>
ionice -c 3 -p <PID>
Enter fullscreen mode Exit fullscreen mode

Setting a nice value of +19 instructs the Linux completely fair scheduler (CFS) to grant the process minimum CPU priority, while ionice -c 3 assigns it to the idle I/O scheduling class. It will only receive disk I/O when no other process on the machine requests the disk.

Finally, resume the frozen process:

kill -SIGCONT <PID>
Enter fullscreen mode Exit fullscreen mode

The process resumes execution in the background without starving production workloads.


An Interesting Historical Detail: The Origin of Uninterruptible Sleep

The TASK_UNINTERRUPTIBLE state (D state) in Linux dates back to the earliest days of Unix design and Linus Torvalds' early 0.9x kernel revisions in 1992.

Operating system designers faced a difficult physical reality: when a driver requests a raw sector transfer from a hard disk controller or programs a DMA bus channel, hardware registers are left in an active, transient state.

If the kernel allowed an asynchronous user-space signal (like an impatient engineer pressing Ctrl+C or sending SIGKILL) to interrupt the process mid-transfer, the kernel would have to unwind the system call while the hardware controller was still writing physical magnetic tracks. This risked bus locks, corrupted sector headers, and kernel panics.

By creating an uninterruptible sleep state, the kernel guaranteed that the hardware driver could always complete its physical hardware transaction safely before the calling process could be altered or killed.

The downside is the exact production behavior we experience today: when physical storage controllers lock up or network mounts stop responding, the kernel waits forever, creating processes that cannot be terminated until the machine is rebooted.


The 60-Second Non-Destructive Triage Cheatsheet

When your next server alert fires, resist the urge to reboot. Open your terminal and run this exact diagnostic sequence:

  • Step 1: Check baseline and trajectory
    • Command: uptime && nproc
    • Action: Compare 1-minute load against core count. Check if load is surging or recovering.
  • Step 2: Check resource stall pressure
    • Command: grep -H . /proc/pressure/{cpu,memory,io}
    • Action: Look for high avg10 numbers under some and full to isolate CPU, memory, or disk starvation.
  • Step 3: Check memory and swap thrashing
    • Command: free -h && vmstat 1 3
    • Action: Check available memory. If si and so in vmstat are actively ticking, swap is thrashing. Check OOM logs with dmesg -T | grep -i oom.
  • Step 4: Check disk wait latency and stuck processes
    • Command: iostat -xz 1 2 && ps -eo pid,state,wchan:20,comm | grep ' D '
    • Action: Watch for w_await > 20ms or processes stuck in D state. Read /proc/<pid>/stack on stuck tasks.
  • Step 5: Check network socket backlogs
    • Command: ss -lnt
    • Action: Verify that Recv-Q is not exceeding Send-Q on active application listening ports.
  • Step 6: Safely stabilize without killing
    • Command: kill -SIGSTOP <pid>
    • Action: Freeze rogue processes to restore terminal control before deciding whether to renice, truncate deleted file handles, or gracefully reload.

Wrapping Up

Rebooting a server feels productive because it temporarily resets running counters. But in production engineering, temporary relief without diagnosis is a liability.

The next time an incident bridge starts demanding a reboot, pause for sixty seconds. Measure load trajectory, inspect kernel pressure metrics, check for swap thrashing and D-state tasks, and verify socket backlogs.

You will find the real culprit, protect your production data, and save your team from troubleshooting the exact same outage tomorrow.

What is the strangest root cause you have ever uncovered on a sluggish Linux server that turned out not to be high CPU at all?


If this breakdown saved you hours of debugging or gave you something practical to use in production, consider buying me a coffee. Your support directly fuels independent, zero-fluff Linux and DevOps technical guides.


About the Author

Asep Sayyad is a Linux and DevOps engineer passionate about Linux administration, automation, cloud technologies, containers, and open-source software. He enjoys solving real-world infrastructure challenges and sharing practical knowledge through in-depth technical articles, tutorials, and hands-on guides.

His goal is to help aspiring and experienced engineers build stronger Linux and DevOps skills with content focused on real production scenarios rather than theory alone.

Connect with Me

Enjoyed this article?

If this guide saved you hours of debugging or gave you something practical for production, consider:

  • Buying me a coffee: Your support directly fuels independent, zero-fluff Linux and DevOps engineering breakdowns.
  • Starring my open-source projects on GitHub.
  • Sharing this article with fellow Linux and DevOps engineers.

You can also follow me for more practical content on Linux, DevOps, Cloud, Containers, Automation, and Open Source. Thanks for reading, and enjoy your learning!

© 2026 Asep Sayyad

Top comments (0)