<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Dejan-Panovski on Linuxize</title><link>https://linuxize.com/authors/dejan-panovski/</link><description>Recent content in Dejan-Panovski on Linuxize</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><managingEditor>hello@linuxize.com (Linuxize)</managingEditor><webMaster>hello@linuxize.com (Linuxize)</webMaster><lastBuildDate>Thu, 11 Jun 2026 09:40:00 +0200</lastBuildDate><atom:link href="https://linuxize.com/authors/dejan-panovski/index.xml" rel="self" type="application/rss+xml"/><image><url>https://linuxize.com/icons/icon-512x512.png</url><title>Linuxize</title><link>https://linuxize.com/</link></image><item><title>Linux Pipes Explained: How to Use the | Operator</title><link>https://linuxize.com/post/linux-pipes-explained/</link><pubDate>Thu, 11 Jun 2026 09:40:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/linux-pipes-explained/</guid><category>bash</category><category>linux commands</category><description>Linux pipes connect command output to another command's input. Learn the | operator, pipefail, stderr piping, xargs, named pipes, and process substitution.</description><content:encoded>&lt;p&gt;A pipe in Linux is a one-way channel that connects the standard output of one process to the standard input of another. The shell builds the channel for you when you type a &lt;code&gt;|&lt;/code&gt; character between two commands. The first command writes; the second command reads. Both run at the same time, and data flows between them through the kernel without ever touching disk.&lt;/p&gt;
&lt;p&gt;The pipe is one of the oldest and most useful ideas in Unix. It is what makes a small command such as &lt;code&gt;grep&lt;/code&gt; or &lt;a href="https://linuxize.com/post/sort-command-in-linux/"&gt;sort&lt;/a&gt;
feel like part of a much larger toolbox. Knowing how pipes behave, where they fit in a shell pipeline, and when to reach for related tools such as &lt;code&gt;xargs&lt;/code&gt; and process substitution makes shell work more effective.&lt;/p&gt;
&lt;p&gt;This guide explains how to build pipelines, handle errors and exit statuses, and use related features such as named pipes and process substitution.&lt;/p&gt;
&lt;h2 id="pipe-syntax"&gt;Pipe Syntax &lt;a class="headline-link" href="#pipe-syntax" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The general syntax for a shell pipeline is:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="txt"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;txt&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;command1 | command2 [| command3 ...]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Each command passes its standard output to the standard input of the command on its right. A pipeline can contain two commands or a longer chain, and the shell runs all stages concurrently.&lt;/p&gt;
&lt;h2 id="how-a-pipe-works"&gt;How a Pipe Works &lt;a class="headline-link" href="#how-a-pipe-works" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A shell pipeline of the form &lt;code&gt;cmd1 | cmd2&lt;/code&gt; follows these steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The shell creates an anonymous pipe with the &lt;code&gt;pipe(2)&lt;/code&gt; system call. The call returns two file descriptors: one for reading and one for writing.&lt;/li&gt;
&lt;li&gt;The shell starts the commands and connects the first command&amp;rsquo;s standard output (file descriptor 1) to the write end of the pipe.&lt;/li&gt;
&lt;li&gt;The second command reads from the pipe through its standard input (file descriptor 0), while the kernel handles the data flow between them.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Linux pipes have a limited in-kernel buffer. The default is commonly 16 memory pages, or 64 KiB on systems with 4 KiB pages, but applications should not rely on a fixed capacity. When the buffer fills, the writer waits until the reader consumes more data.&lt;/p&gt;
&lt;h2 id="a-first-example"&gt;A First Example &lt;a class="headline-link" href="#a-first-example" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The following pipeline generates three service names and filters them with &lt;code&gt;grep&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;%s\n&amp;#39;&lt;/span&gt; ssh.service NetworkManager cron.service &lt;span class="p"&gt;|&lt;/span&gt; grep -i network&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;NetworkManager&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;printf&lt;/code&gt; writes one service name per line. The pipe sends those lines to &lt;code&gt;grep&lt;/code&gt;, which prints the line containing &lt;code&gt;network&lt;/code&gt;. The &lt;code&gt;-i&lt;/code&gt; option makes the match case-insensitive.&lt;/p&gt;
&lt;p&gt;The same pattern works with any command that writes to standard output and any command that reads from standard input.&lt;/p&gt;
&lt;h2 id="pipes-pass-data-not-files"&gt;Pipes Pass Data, Not Files &lt;a class="headline-link" href="#pipes-pass-data-not-files" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A pipe carries a stream of bytes, not a file name or command-line argument. The receiving command reads that stream from standard input.&lt;/p&gt;
&lt;p&gt;For example, the following command sends the text &lt;code&gt;/etc/hosts&lt;/code&gt; to &lt;code&gt;wc&lt;/code&gt;. It does not ask &lt;code&gt;wc&lt;/code&gt; to open that file:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;%s\n&amp;#39;&lt;/span&gt; /etc/hosts &lt;span class="p"&gt;|&lt;/span&gt; wc -l&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The result is &lt;code&gt;1&lt;/code&gt; because &lt;code&gt;wc&lt;/code&gt; received one line of text. To count lines in the file, pass the path as an argument with &lt;code&gt;wc -l /etc/hosts&lt;/code&gt;. Commands such as &lt;code&gt;rm&lt;/code&gt;, &lt;code&gt;mv&lt;/code&gt;, and &lt;code&gt;stat&lt;/code&gt; also expect paths as arguments rather than reading them from standard input.&lt;/p&gt;
&lt;h2 id="combining-pipes-with-xargs"&gt;Combining Pipes with xargs &lt;a class="headline-link" href="#combining-pipes-with-xargs" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://linuxize.com/post/linux-xargs-command/"&gt;xargs&lt;/a&gt;
reads items from standard input and turns them into arguments for another command. This example creates three empty files:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;%s\0&amp;#39;&lt;/span&gt; report-1.txt report-2.txt report-3.txt &lt;span class="p"&gt;|&lt;/span&gt; xargs -0 touch&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The null character separates each name, and &lt;code&gt;xargs -0&lt;/code&gt; converts the three items into arguments for &lt;code&gt;touch&lt;/code&gt;. The same null-delimited pattern safely handles spaces and newlines in file names.&lt;/p&gt;
&lt;p&gt;Before deleting files found by &lt;code&gt;find&lt;/code&gt;, print the matches and inspect them:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;find . -type f -name &lt;span class="s1"&gt;&amp;#39;*.log&amp;#39;&lt;/span&gt; -mtime +30 -print&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;When the list is correct, use &lt;code&gt;-print0&lt;/code&gt; with &lt;code&gt;xargs -0&lt;/code&gt;. The &lt;code&gt;-r&lt;/code&gt; option prevents GNU &lt;code&gt;xargs&lt;/code&gt; from running &lt;code&gt;rm&lt;/code&gt; when no files match:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;find . -type f -name &lt;span class="s1"&gt;&amp;#39;*.log&amp;#39;&lt;/span&gt; -mtime +30 -print0 &lt;span class="p"&gt;|&lt;/span&gt; xargs -0 -r rm --&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;--&lt;/code&gt; marker prevents a path beginning with a hyphen from being interpreted as an &lt;code&gt;rm&lt;/code&gt; option.&lt;/p&gt;
&lt;h2 id="reading-the-exit-status-of-a-pipeline"&gt;Reading the Exit Status of a Pipeline &lt;a class="headline-link" href="#reading-the-exit-status-of-a-pipeline" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Bash returns the exit status of the last command in a pipeline by default. A failure in an earlier stage can therefore go unnoticed:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;grep something /missing/file 2&amp;gt;/dev/null &lt;span class="p"&gt;|&lt;/span&gt; wc -l
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;0
0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;grep&lt;/code&gt; failed because the file did not exist, but &lt;code&gt;wc -l&lt;/code&gt; succeeded, so the pipeline status was &lt;code&gt;0&lt;/code&gt;. Enable &lt;code&gt;pipefail&lt;/code&gt; when the script must detect failures in any stage:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; -o pipefail
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;grep something /missing/file 2&amp;gt;/dev/null &lt;span class="p"&gt;|&lt;/span&gt; wc -l
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;0
2&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With &lt;code&gt;pipefail&lt;/code&gt; enabled, the pipeline returns the status of the rightmost command that failed, or &lt;code&gt;0&lt;/code&gt; if every command succeeded.&lt;/p&gt;
&lt;p&gt;To inspect every stage individually, use the &lt;code&gt;PIPESTATUS&lt;/code&gt; array:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ls /missing 2&amp;gt;/dev/null &lt;span class="p"&gt;|&lt;/span&gt; grep foo
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PIPESTATUS&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;2 1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;PIPESTATUS[0]&lt;/code&gt; is &lt;code&gt;ls&lt;/code&gt; and &lt;code&gt;PIPESTATUS[1]&lt;/code&gt; is &lt;code&gt;grep&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="pipes-and-standard-error"&gt;Pipes and Standard Error &lt;a class="headline-link" href="#pipes-and-standard-error" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A pipe only carries standard output (file descriptor 1). Standard error (file descriptor 2) goes straight to the terminal:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ls /missing &lt;span class="p"&gt;|&lt;/span&gt; wc -l&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;ls: cannot access &amp;#39;/missing&amp;#39;: No such file or directory
0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The error message bypassed the pipe. To send errors through the same pipe, redirect standard error to standard output with &lt;code&gt;2&amp;gt;&amp;amp;1&lt;/code&gt; before the pipe:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ls /missing 2&amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; tee log.txt&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;a href="https://linuxize.com/post/linux-tee-command/"&gt;&lt;code&gt;tee&lt;/code&gt; command&lt;/a&gt;
writes the redirected error stream to &lt;code&gt;log.txt&lt;/code&gt; and displays it in the terminal. Bash also provides &lt;code&gt;|&amp;amp;&lt;/code&gt; as shorthand for &lt;code&gt;2&amp;gt;&amp;amp;1 |&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ls /missing &lt;span class="p"&gt;|&amp;amp;&lt;/span&gt; tee log.txt&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For more redirection patterns and details about ordering, see how to &lt;a href="https://linuxize.com/post/bash-redirect-stderr-stdout/"&gt;redirect stderr to stdout&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="named-pipes-fifos"&gt;Named Pipes (FIFOs) &lt;a class="headline-link" href="#named-pipes-fifos" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;|&lt;/code&gt; operator creates an anonymous pipe that vanishes when the pipeline ends. A named pipe lives on the filesystem and lets unrelated processes share data. Create one with &lt;code&gt;mkfifo&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkfifo /tmp/linuxize-pipe&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;In the first terminal, start the reader:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cat /tmp/linuxize-pipe&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;In a second terminal, write to the FIFO:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;%s\n&amp;#39;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;hello&amp;#34;&lt;/span&gt; &amp;gt; /tmp/linuxize-pipe&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;hello&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The first terminal prints &lt;code&gt;hello&lt;/code&gt;. Opening a FIFO normally blocks until the other end connects, so start the reader and writer in separate terminals. Named pipes are useful for communication between scripts and processes that do not share a parent.&lt;/p&gt;
&lt;p&gt;Remove the FIFO when finished:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rm /tmp/linuxize-pipe&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="process-substitution"&gt;Process Substitution &lt;a class="headline-link" href="#process-substitution" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When a command expects one or more file paths instead of standard input, process substitution can expose command output through a file-like path:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;diff &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;%s\n&amp;#39;&lt;/span&gt; alpha beta&lt;span class="o"&gt;)&lt;/span&gt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;%s\n&amp;#39;&lt;/span&gt; alpha gamma&lt;span class="o"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;diff&lt;/code&gt; receives two generated paths as arguments and compares the output from both &lt;code&gt;printf&lt;/code&gt; commands. Bash implements those paths with &lt;code&gt;/dev/fd&lt;/code&gt; entries or named pipes, depending on the operating system.&lt;/p&gt;
&lt;p&gt;The reverse form, &lt;code&gt;&amp;gt;(command)&lt;/code&gt;, provides a file-like destination. The following pipeline saves a compressed copy while continuing to filter the original stream:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;some_command &lt;span class="p"&gt;|&lt;/span&gt; tee &amp;gt;&lt;span class="o"&gt;(&lt;/span&gt;gzip &amp;gt; out.gz&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep error&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Process substitution is available in Bash, ksh, and zsh, but it is not part of POSIX &lt;code&gt;sh&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="useful-pipe-based-one-liners"&gt;Useful Pipe-Based One-Liners &lt;a class="headline-link" href="#useful-pipe-based-one-liners" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A handful of pipelines come up again and again in shell work:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ps -eo pid,comm,%cpu --sort&lt;span class="o"&gt;=&lt;/span&gt;-%cpu &lt;span class="p"&gt;|&lt;/span&gt; head -n &lt;span class="m"&gt;6&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;ps&lt;/code&gt; sorts processes by CPU use, and &lt;code&gt;head&lt;/code&gt; keeps the header plus the five busiest processes.&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;awk &lt;span class="s1"&gt;&amp;#39;{print $1}&amp;#39;&lt;/span&gt; access.log &lt;span class="p"&gt;|&lt;/span&gt; sort &lt;span class="p"&gt;|&lt;/span&gt; uniq -c &lt;span class="p"&gt;|&lt;/span&gt; sort -nr &lt;span class="p"&gt;|&lt;/span&gt; head&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This pipeline extracts client addresses from a common Nginx or Apache access log, counts duplicates, and prints the most frequent addresses first.&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;du -h --max-depth&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; . 2&amp;gt;/dev/null &lt;span class="p"&gt;|&lt;/span&gt; sort -h&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The output shows disk use for the current directory and its immediate subdirectories, sorted from smallest to largest.&lt;/p&gt;
&lt;p&gt;Each pipeline reads as a sentence: do this, then this, then this. The flow matches how the data moves between processes.&lt;/p&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Send output to another command&lt;/td&gt;
&lt;td&gt;&lt;code&gt;command1 | command2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build a multi-stage pipeline&lt;/td&gt;
&lt;td&gt;&lt;code&gt;command1 | command2 | command3&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pipe stdout and stderr in Bash&lt;/td&gt;
&lt;td&gt;&lt;code&gt;command1 |&amp;amp; command2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enable pipeline failure detection&lt;/td&gt;
&lt;td&gt;&lt;code&gt;set -o pipefail&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read every Bash pipeline status&lt;/td&gt;
&lt;td&gt;&lt;code&gt;printf '%s\n' &amp;quot;${PIPESTATUS[@]}&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Convert null-delimited input to arguments&lt;/td&gt;
&lt;td&gt;&lt;code&gt;command1 | xargs -0 command2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Save and continue a stream&lt;/td&gt;
&lt;td&gt;&lt;code&gt;command1 | tee file | command2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Create a named pipe&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mkfifo /tmp/name&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compare generated output as files&lt;/td&gt;
&lt;td&gt;&lt;code&gt;diff &amp;lt;(command1) &amp;lt;(command2)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;A variable set in a pipeline is empty afterward&lt;/strong&gt;&lt;br&gt;
In Bash, each command in a multi-command pipeline normally runs in its own subshell. Variables set there do not survive in the parent shell:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;data&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; x
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;%s&amp;gt;\n&amp;#39;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$x&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;&amp;lt;&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Use a here-string or redirect process substitution into &lt;code&gt;read&lt;/code&gt; so it runs in the current shell:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;read&lt;/span&gt; -r x &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;data&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;%s&amp;gt;\n&amp;#39;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$x&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;&amp;lt;data&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;A command reports a broken pipe&lt;/strong&gt;&lt;br&gt;
A writer receives &lt;code&gt;SIGPIPE&lt;/code&gt; when the reader closes early, which often happens when &lt;code&gt;head&lt;/code&gt; has collected enough lines. This can be expected, but do not hide all errors with a broad &lt;code&gt;2&amp;gt;/dev/null&lt;/code&gt;. Check the individual statuses with &lt;code&gt;PIPESTATUS&lt;/code&gt;, especially when &lt;code&gt;pipefail&lt;/code&gt; is enabled.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The next command ignores piped file names&lt;/strong&gt;&lt;br&gt;
Tools such as &lt;code&gt;rm&lt;/code&gt;, &lt;code&gt;mv&lt;/code&gt;, and &lt;code&gt;stat&lt;/code&gt; expect paths as arguments. Use &lt;code&gt;xargs -0&lt;/code&gt; with null-delimited input, and preview destructive operations before running them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Output appears in delayed batches&lt;/strong&gt;&lt;br&gt;
Some programs use larger output buffers when writing to a pipe instead of a terminal. GNU &lt;code&gt;grep&lt;/code&gt; supports &lt;code&gt;--line-buffered&lt;/code&gt;, and GNU &lt;code&gt;stdbuf -oL command&lt;/code&gt; can request line-buffered output from programs that use standard C streams. These options are not portable to every Unix system or effective with every command.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Linux pipes let you connect focused commands without intermediate files. Use &lt;code&gt;pipefail&lt;/code&gt; when every stage must succeed, &lt;code&gt;xargs&lt;/code&gt; when a command needs arguments, and process substitution or named pipes when a simple pipeline does not fit the task.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/linux-pipes-explained/featured_hu_1f869179c82aaea9.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>How to Deploy a Node.js Application on Ubuntu 26.04</title><link>https://linuxize.com/post/how-to-deploy-a-nodejs-application-on-ubuntu-26-04/</link><pubDate>Wed, 10 Jun 2026 09:30:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/how-to-deploy-a-nodejs-application-on-ubuntu-26-04/</guid><category>nodejs</category><category>ubuntu</category><description>Deploy a Node.js application on Ubuntu 26.04 using PM2 as the process manager and Nginx as a reverse proxy, with a Let's Encrypt TLS certificate.</description><content:encoded>&lt;p&gt;Running &lt;code&gt;node app.js&lt;/code&gt; on a developer laptop is one thing; getting the same code to stay up on a production server, restart after a crash, survive a reboot, and answer requests on standard HTTPS ports is another. A working Node.js deployment on Ubuntu 26.04 has three moving parts: the runtime, a process manager that keeps the app alive, and a reverse proxy that handles TLS and routes traffic to the application.&lt;/p&gt;
&lt;p&gt;This guide explains how to deploy a Node.js application on Ubuntu 26.04 using PM2 as the process manager and Nginx as the reverse proxy, with a Let&amp;rsquo;s Encrypt certificate in front.&lt;/p&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites &lt;a class="headline-link" href="#prerequisites" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before starting you need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ubuntu 26.04 with a user that has &lt;code&gt;sudo&lt;/code&gt; privileges&lt;/li&gt;
&lt;li&gt;A domain name with an A record pointing at the server&amp;rsquo;s public IP&lt;/li&gt;
&lt;li&gt;Ports 80 and 443 open in the firewall&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We will use &lt;code&gt;example.com&lt;/code&gt; and an app that listens on port &lt;code&gt;3000&lt;/code&gt;. Substitute your real values.&lt;/p&gt;
&lt;h2 id="step-1-install-nodejs"&gt;Step 1: Install Node.js &lt;a class="headline-link" href="#step-1-install-nodejs" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Ubuntu 26.04 includes Node.js in its repositories, but NodeSource makes it easier to install a specific upstream LTS release. The commands below install Node.js 24.x, the current LTS line at publication time.&lt;/p&gt;
&lt;p&gt;Install the required packages and import the NodeSource signing key:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install -y ca-certificates curl gnupg
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo mkdir -p /etc/apt/keyrings
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; sudo gpg --dearmor --yes -o /etc/apt/keyrings/nodesource.gpg&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Add the Node.js 24.x repository:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;NODE_MAJOR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;24&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_&lt;/span&gt;&lt;span class="nv"&gt;$NODE_MAJOR&lt;/span&gt;&lt;span class="s2"&gt;.x nodistro main&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; sudo tee /etc/apt/sources.list.d/nodesource.list&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Update the package index and install Node.js:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install -y nodejs&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Confirm both binaries are in place:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;node --version
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm --version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;v24.16.0
11.16.0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The exact patch versions will change as Node.js and npm updates are released. For other installation methods, see our guide on &lt;a href="https://linuxize.com/post/how-to-install-node-js-on-ubuntu-26-04/"&gt;installing Node.js and npm on Ubuntu 26.04&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="step-2-prepare-a-deploy-user-and-directory"&gt;Step 2: Prepare a Deploy User and Directory &lt;a class="headline-link" href="#step-2-prepare-a-deploy-user-and-directory" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Production apps should run as a non-root account that owns only its own files. Create a dedicated user:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo adduser --system --group --home /var/www/app --shell /bin/bash app&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;--system&lt;/code&gt; flag creates an account with password login disabled, which is appropriate for a service account. Administrators can still run deployment commands as this user through &lt;code&gt;sudo&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Set up the deployment directory:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo mkdir -p /var/www/app
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo chown -R app:app /var/www/app&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="step-3-get-the-application-onto-the-server"&gt;Step 3: Get the Application onto the Server &lt;a class="headline-link" href="#step-3-get-the-application-onto-the-server" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Most deployments pull the code from Git. Clone the repository as the &lt;code&gt;app&lt;/code&gt; user:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install -y git
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo -H -u app git clone https://github.com/example/myapp.git /var/www/app&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Install dependencies inside that directory:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo -H -u app bash -c &lt;span class="s1"&gt;&amp;#39;cd /var/www/app &amp;amp;&amp;amp; npm ci --omit=dev&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;npm ci&lt;/code&gt; is the right command for deploys: it reads &lt;code&gt;package-lock.json&lt;/code&gt; and refuses to fall back when the lock file is out of sync. &lt;code&gt;--omit=dev&lt;/code&gt; skips development-only dependencies, which keeps the production install smaller.&lt;/p&gt;
&lt;p&gt;If the application has a build step that depends on development packages, install all dependencies first, build the project, and then remove development-only packages:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo -H -u app bash -c &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;cd /var/www/app &amp;amp;&amp;amp; npm ci &amp;amp;&amp;amp; npm run build &amp;amp;&amp;amp; npm prune --omit=dev&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For private repositories, configure an SSH deploy key or a short-lived access token for the &lt;code&gt;app&lt;/code&gt; account. Do not store repository credentials in the application directory.&lt;/p&gt;
&lt;p&gt;For a quick test without a real repository, write a minimal Express app instead:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo -H -u app bash -c &lt;span class="s1"&gt;&amp;#39;cd /var/www/app &amp;amp;&amp;amp; npm init -y &amp;amp;&amp;amp; npm install express&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo -H -u app nano /var/www/app/server.js&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="js"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;/var/www/app/server.js&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-yellow-100 text-yellow-700 dark:bg-yellow-900 dark:text-yellow-300"&gt;js&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;express&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Hello from Node.js on Ubuntu 26.04&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;127.0.0.1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`App listening on http://127.0.0.1:&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Notice that the listen call binds to &lt;code&gt;127.0.0.1&lt;/code&gt;, not &lt;code&gt;0.0.0.0&lt;/code&gt;. The reverse proxy will reach the app over loopback, and binding to localhost keeps the application off the public network entirely.&lt;/p&gt;
&lt;h2 id="step-4-install-pm2-and-run-the-app"&gt;Step 4: Install PM2 and Run the App &lt;a class="headline-link" href="#step-4-install-pm2-and-run-the-app" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;PM2 is a Node.js process manager that handles restarts, logs, and clustering. Install it globally:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo npm install -g pm2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Configure any required environment variables before starting the process. Keep API keys, database passwords, and other secrets outside the Git repository, and add local &lt;code&gt;.env&lt;/code&gt; files to &lt;code&gt;.gitignore&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Start the application under PM2 as the &lt;code&gt;app&lt;/code&gt; user:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo -H -u app pm2 start /var/www/app/server.js --name myapp --time&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;[PM2] Starting /var/www/app/server.js in fork_mode (1 instance)
[PM2] Done.
┌─────┬────────┬─────────────┬─────────┬─────────┬──────────┬────────┐
│ id │ name │ namespace │ version │ mode │ pid │ uptime │
├─────┼────────┼─────────────┼─────────┼─────────┼──────────┼────────┤
│ 0 │ myapp │ default │ 1.0.0 │ fork │ 12345 │ 0s │
└─────┴────────┴─────────────┴─────────┴─────────┴──────────┴────────┘&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Verify the app responds on loopback:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl http://127.0.0.1:3000&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Hello from Node.js on Ubuntu 26.04&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To make PM2 start the app at boot, generate the systemd unit and freeze the process list:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo env &lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;:/usr/bin pm2 startup systemd -u app --hp /var/www/app
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo -H -u app pm2 save&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The first command writes and enables &lt;code&gt;pm2-app.service&lt;/code&gt;; the second saves the current process list so PM2 knows what to start. Confirm the unit is enabled:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl status pm2-app&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="step-5-install-and-configure-nginx"&gt;Step 5: Install and Configure Nginx &lt;a class="headline-link" href="#step-5-install-and-configure-nginx" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Install Nginx:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Create a server block that proxies requests to the Node.js app:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nano /etc/nginx/sites-available/example.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="nginx"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;/etc/nginx/sites-available/example.com&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300"&gt;nginx&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="s"&gt;[::]:80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;example.com&lt;/span&gt; &lt;span class="s"&gt;www.example.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://127.0.0.1:3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The headers preserve the original host, client IP, and protocol for the application. If the app uses WebSockets, add the upgrade settings from the &lt;a href="https://linuxize.com/post/nginx-reverse-proxy/"&gt;Nginx reverse proxy guide&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;Enable the site and test the configuration:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nginx -t
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl reload nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Open the firewall:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo ufw allow &lt;span class="s1"&gt;&amp;#39;Nginx Full&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Visit &lt;code&gt;http://example.com&lt;/code&gt; and confirm the page renders. Requests now travel from the browser to Nginx on port 80, then to Node.js on loopback port 3000.&lt;/p&gt;
&lt;h2 id="step-6-add-https-with-certbot"&gt;Step 6: Add HTTPS with Certbot &lt;a class="headline-link" href="#step-6-add-https-with-certbot" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Install Certbot and the Nginx plugin:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install certbot python3-certbot-nginx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo certbot --nginx -d example.com -d www.example.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Certbot rewrites the server block to terminate TLS, adds an HTTP-to-HTTPS redirect, and registers a renewal timer. Verify:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -I https://example.com
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo certbot renew --dry-run&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The first command should return a successful HTTP status over HTTPS. The renewal dry run confirms that Certbot can renew the certificate before it expires.&lt;/p&gt;
&lt;h2 id="step-7-manage-the-app-with-pm2"&gt;Step 7: Manage the App with PM2 &lt;a class="headline-link" href="#step-7-manage-the-app-with-pm2" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once the deployment is live, the day-to-day operations all go through PM2:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo -H -u app pm2 ls
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo -H -u app pm2 logs myapp
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo -H -u app pm2 restart myapp
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo -H -u app pm2 stop myapp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;PM2 writes application output under &lt;code&gt;/var/www/app/.pm2/logs&lt;/code&gt;. Install the log rotation module so those files do not grow without limit:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo -H -u app pm2 install pm2-logrotate
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo -H -u app pm2 &lt;span class="nb"&gt;set&lt;/span&gt; pm2-logrotate:max_size 10M
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo -H -u app pm2 &lt;span class="nb"&gt;set&lt;/span&gt; pm2-logrotate:retain &lt;span class="m"&gt;7&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To deploy a new version, pull only fast-forward changes, install dependencies, and restart the process:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo -H -u app bash -c &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;cd /var/www/app &amp;amp;&amp;amp; git pull --ff-only &amp;amp;&amp;amp; npm ci --omit=dev&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo -H -u app pm2 restart myapp --update-env
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo -H -u app pm2 save&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This guide starts the app in fork mode, so &lt;code&gt;pm2 restart&lt;/code&gt; briefly interrupts requests. Zero-downtime &lt;code&gt;pm2 reload&lt;/code&gt; requires cluster mode with at least two instances:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo -H -u app pm2 delete myapp
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo -H -u app pm2 start /var/www/app/server.js --name myapp -i &lt;span class="m"&gt;2&lt;/span&gt; --time
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo -H -u app pm2 save
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo -H -u app pm2 reload myapp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Cluster mode works well for stateless HTTP applications. Apps that keep sessions, scheduled jobs, or WebSocket state in memory need an external store or additional coordination before running multiple instances.&lt;/p&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;502 Bad Gateway&lt;/code&gt; from Nginx&lt;/strong&gt;&lt;br&gt;
The Node.js app is not running, not listening on the expected port, or bound to the wrong address. Check &lt;code&gt;sudo -H -u app pm2 ls&lt;/code&gt; and confirm the process is online, then run &lt;code&gt;sudo ss -ltnp | grep 3000&lt;/code&gt; to confirm it is listening on loopback.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PM2 does not start at boot&lt;/strong&gt;&lt;br&gt;
The systemd unit was not enabled, or &lt;code&gt;pm2 save&lt;/code&gt; was skipped. Check &lt;code&gt;sudo systemctl status pm2-app&lt;/code&gt;, re-run the startup command, and then run &lt;code&gt;sudo -H -u app pm2 save&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Certbot cannot validate the domain&lt;/strong&gt;&lt;br&gt;
Confirm that the domain&amp;rsquo;s A and AAAA records point to this server, port 80 is reachable, and &lt;code&gt;sudo ufw status&lt;/code&gt; shows &lt;code&gt;Nginx Full&lt;/code&gt; allowed. If an incorrect AAAA record points elsewhere, Let&amp;rsquo;s Encrypt validation can fail even when the A record is correct.&lt;/p&gt;
&lt;h2 id="faq"&gt;FAQ &lt;a class="headline-link" href="#faq" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Why use Nginx in front of Node.js?&lt;/strong&gt;&lt;br&gt;
Nginx handles the public ports and TLS certificate while the Node.js process stays on an unprivileged local port. It also gives you one place for request limits, static files, access logs, and routing several applications by domain.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Can I run multiple Node.js apps on the same server?&lt;/strong&gt;&lt;br&gt;
Yes. Give each app its own port, its own PM2 entry, and its own Nginx server block. The reverse proxy routes by hostname so each app gets the requests for its domain only.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Should I use PM2 or systemd directly?&lt;/strong&gt;&lt;br&gt;
Both work. PM2 adds Node.js-specific process controls, clustering, and reloads, while systemd is simpler when you only need to supervise one process. In this setup, systemd starts PM2 at boot and PM2 manages the application.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A production Node.js deployment on Ubuntu 26.04 uses PM2 to keep the application running and Nginx to handle public HTTP and HTTPS traffic. Keep the app bound to loopback, rotate its logs, and test the update procedure before the server carries production traffic.&lt;/p&gt;
&lt;p&gt;For related setup, see our guides on &lt;a href="https://linuxize.com/post/how-to-install-node-js-on-ubuntu-26-04/"&gt;installing Node.js on Ubuntu 26.04&lt;/a&gt;
and &lt;a href="https://linuxize.com/post/nginx-reverse-proxy/"&gt;configuring Nginx as a reverse proxy&lt;/a&gt;
.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/how-to-deploy-a-nodejs-application-on-ubuntu-26-04/featured_hu_41dd5c8c68c35fa0.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>Bash Array Operations: Length, Search, Slice, and Reverse</title><link>https://linuxize.com/post/bash-array-operations/</link><pubDate>Tue, 09 Jun 2026 10:10:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/bash-array-operations/</guid><category>bash</category><description>Practical Bash array operations for checking length, finding values, slicing, reversing, iterating, appending, prepending, and removing elements.</description><content:encoded>&lt;p&gt;Once you know how to declare a Bash array, the next tasks are usually finding its length, checking whether it contains a value, extracting a slice, or changing the element order. Bash supports all of these operations, but the expansion syntax can be difficult to remember.&lt;/p&gt;
&lt;p&gt;This guide covers the indexed-array operations you will use most often in scripts. For array declaration, element access, and associative arrays, start with the &lt;a href="https://linuxize.com/post/bash-arrays/"&gt;Bash arrays guide&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="sample-array"&gt;Sample Array &lt;a class="headline-link" href="#sample-array" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The examples use the following array:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;fruits&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;apple&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;banana&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;dragon fruit&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;date&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;elderberry&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Each quoted value is one element, including &lt;code&gt;dragon fruit&lt;/code&gt;, and the indices run from &lt;code&gt;0&lt;/code&gt; through &lt;code&gt;4&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="get-the-array-length"&gt;Get the Array Length &lt;a class="headline-link" href="#get-the-array-length" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use &lt;code&gt;${#name[@]}&lt;/code&gt; to get the number of elements:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;fruits&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;5&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The result is &lt;code&gt;5&lt;/code&gt; because the array contains five elements. &lt;code&gt;${#name[*]}&lt;/code&gt; returns the same count, but &lt;code&gt;@&lt;/code&gt; is the safer default for other array operations because it preserves individual elements when quoted.&lt;/p&gt;
&lt;p&gt;To get the character length of one element, include its index:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;fruits&lt;/span&gt;&lt;span class="p"&gt;[1]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;6&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This counts the six characters in &lt;code&gt;banana&lt;/code&gt;, not the number of array elements.&lt;/p&gt;
&lt;h2 id="check-if-an-array-contains-a-value"&gt;Check If an Array Contains a Value &lt;a class="headline-link" href="#check-if-an-array-contains-a-value" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Bash does not have a built-in &lt;code&gt;contains&lt;/code&gt; operator. The reliable approach is to loop over the elements and compare each one with the target value:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;contains&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="nv"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;shift&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;local&lt;/span&gt; element
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; element in &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$element&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$target&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; contains &lt;span class="s2"&gt;&amp;#34;dragon fruit&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;fruits&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;found&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;not found&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;found&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The function returns status &lt;code&gt;0&lt;/code&gt; when it finds an exact match and status &lt;code&gt;1&lt;/code&gt; otherwise. Passing &lt;code&gt;&amp;quot;${fruits[@]}&amp;quot;&lt;/code&gt; keeps values containing spaces intact.&lt;/p&gt;
&lt;p&gt;Avoid joining the array into one string and testing it with a regular expression. That shortcut can report false matches when a search term appears inside another element or when elements contain the chosen separator.&lt;/p&gt;
&lt;h2 id="slice-an-array"&gt;Slice an Array &lt;a class="headline-link" href="#slice-an-array" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Array slicing uses the following expansion:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="txt"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;txt&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;${name[@]:offset:length}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;offset&lt;/code&gt; selects where the slice starts, and &lt;code&gt;length&lt;/code&gt; is the number of elements to return. This example takes three elements starting at index &lt;code&gt;1&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;slice&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;fruits&lt;/span&gt;&lt;span class="p"&gt;[@]:&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;3&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;%s&amp;gt;\n&amp;#39;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;&amp;lt;banana&amp;gt;
&amp;lt;dragon fruit&amp;gt;
&amp;lt;date&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Assigning the result to another array preserves the three values as separate elements.&lt;/p&gt;
&lt;p&gt;A negative offset counts from the end. Add a space before the negative number so Bash does not parse &lt;code&gt;:-&lt;/code&gt; as the default-value operator:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;last_two&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;fruits&lt;/span&gt;&lt;span class="p"&gt;[@]: -2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;%s&amp;gt;\n&amp;#39;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;last_two&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;&amp;lt;date&amp;gt;
&amp;lt;elderberry&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Omit &lt;code&gt;length&lt;/code&gt; to take every remaining element from the offset to the end.&lt;/p&gt;
&lt;h2 id="reverse-an-array"&gt;Reverse an Array &lt;a class="headline-link" href="#reverse-an-array" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Bash does not provide a reverse operator. The following loop reads the array indices from the end and appends each value to a new array:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;indices&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="p"&gt;!fruits[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;reversed&lt;/span&gt;&lt;span class="o"&gt;=()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;indices&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; - 1&lt;span class="p"&gt;;&lt;/span&gt; i &amp;gt;&lt;span class="o"&gt;=&lt;/span&gt; 0&lt;span class="p"&gt;;&lt;/span&gt; i--&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;reversed&lt;/span&gt;&lt;span class="o"&gt;+=(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;fruits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;indices&lt;/span&gt;&lt;span class="p"&gt;[i]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;%s&amp;gt;\n&amp;#39;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;reversed&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;&amp;lt;elderberry&amp;gt;
&amp;lt;date&amp;gt;
&amp;lt;dragon fruit&amp;gt;
&amp;lt;banana&amp;gt;
&amp;lt;apple&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Using the actual index list makes this pattern work with both contiguous arrays and sparse arrays that have gaps between indices.&lt;/p&gt;
&lt;p&gt;Tools such as &lt;code&gt;rev&lt;/code&gt; and &lt;code&gt;tac&lt;/code&gt; solve different problems. &lt;code&gt;rev&lt;/code&gt; reverses characters on each input line, while &lt;code&gt;tac&lt;/code&gt; reverses line order. Neither one operates directly on Bash array elements.&lt;/p&gt;
&lt;h2 id="iterate-over-values-and-indices"&gt;Iterate Over Values and Indices &lt;a class="headline-link" href="#iterate-over-values-and-indices" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When you only need the values, expand the array with &lt;code&gt;&amp;quot;${name[@]}&amp;quot;&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; fruit in &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;fruits&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$fruit&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Quoting the expansion ensures that &lt;code&gt;dragon fruit&lt;/code&gt; remains one loop item.&lt;/p&gt;
&lt;p&gt;When you need both the index and value, use &lt;code&gt;${!name[@]}&lt;/code&gt; to expand the index list:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; i in &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="p"&gt;!fruits[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;fruits&lt;/span&gt;&lt;span class="p"&gt;[i]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;0: apple
1: banana
2: dragon fruit
3: date
4: elderberry&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Iterating over indices is useful when you need to update elements in place or preserve gaps in a sparse array.&lt;/p&gt;
&lt;h2 id="append-and-prepend-elements"&gt;Append and Prepend Elements &lt;a class="headline-link" href="#append-and-prepend-elements" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Append one or more elements with the &lt;code&gt;+=&lt;/code&gt; operator:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;fruits&lt;/span&gt;&lt;span class="o"&gt;+=(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;fig&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;grape&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The new values receive the next available indices at the end of the array.&lt;/p&gt;
&lt;p&gt;To prepend a value, create a new array expansion with the new element first:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;fruits&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;apricot&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;fruits&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This assignment rebuilds the array with contiguous indices starting at &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can also assign a value at a specific index:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;fruits&lt;span class="o"&gt;[&lt;/span&gt;10&lt;span class="o"&gt;]=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;kiwi&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This creates a sparse array if indices &lt;code&gt;7&lt;/code&gt; through &lt;code&gt;9&lt;/code&gt; do not exist.&lt;/p&gt;
&lt;h2 id="remove-array-elements"&gt;Remove Array Elements &lt;a class="headline-link" href="#remove-array-elements" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use &lt;code&gt;unset&lt;/code&gt; with an index to remove one element:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;unset&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fruits[2]&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Quote the argument so the brackets are not treated as a filename pattern. The remaining elements keep their original indices, which leaves a gap where index &lt;code&gt;2&lt;/code&gt; was removed.&lt;/p&gt;
&lt;p&gt;To rebuild the array with contiguous indices, assign its current values back to it:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;fruits&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;fruits&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This preserves the values and renumbers them from &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To remove the entire array, pass only its name:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;unset&lt;/span&gt; fruits&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For a printable quick reference, see the &lt;a href="https://linuxize.com/cheatsheet/bash/"&gt;Bash cheatsheet&lt;/a&gt;
.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Syntax&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Number of elements&lt;/td&gt;
&lt;td&gt;&lt;code&gt;${#arr[@]}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Length of one element&lt;/td&gt;
&lt;td&gt;&lt;code&gt;${#arr[index]}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;All values&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;quot;${arr[@]}&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;All indices&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;quot;${!arr[@]}&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slice&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;quot;${arr[@]:offset:length}&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Last two elements&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;quot;${arr[@]: -2}&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Append elements&lt;/td&gt;
&lt;td&gt;&lt;code&gt;arr+=(x y)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prepend an element&lt;/td&gt;
&lt;td&gt;&lt;code&gt;arr=(x &amp;quot;${arr[@]}&amp;quot;)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Set a specific index&lt;/td&gt;
&lt;td&gt;&lt;code&gt;arr[index]=value&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remove one element&lt;/td&gt;
&lt;td&gt;&lt;code&gt;unset 'arr[index]'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reindex after removal&lt;/td&gt;
&lt;td&gt;&lt;code&gt;arr=(&amp;quot;${arr[@]}&amp;quot;)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remove the array&lt;/td&gt;
&lt;td&gt;&lt;code&gt;unset arr&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="common-pitfalls"&gt;Common Pitfalls &lt;a class="headline-link" href="#common-pitfalls" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Always use curly braces when accessing an indexed element. &lt;code&gt;$fruits[1]&lt;/code&gt; expands &lt;code&gt;$fruits&lt;/code&gt; and then prints the literal text &lt;code&gt;[1]&lt;/code&gt;, while &lt;code&gt;${fruits[1]}&lt;/code&gt; expands the element at index &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Inside double quotes, &lt;code&gt;&amp;quot;${fruits[@]}&amp;quot;&lt;/code&gt; expands each element as a separate word. &lt;code&gt;&amp;quot;${fruits[*]}&amp;quot;&lt;/code&gt; joins all elements into one string using the first character of &lt;code&gt;IFS&lt;/code&gt;. Use the &lt;code&gt;@&lt;/code&gt; form for loops, function arguments, copies, and most other array operations.&lt;/p&gt;
&lt;p&gt;Do not assume that array indices are contiguous after using &lt;code&gt;unset&lt;/code&gt; or assigning a distant index. Iterate over &lt;code&gt;&amp;quot;${!fruits[@]}&amp;quot;&lt;/code&gt; when the actual index values matter.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Bash array expansions cover length checks, slices, index iteration, and most everyday updates. Use quoted &lt;code&gt;@&lt;/code&gt; expansions to preserve element boundaries, and iterate over the real index list when an array may contain gaps.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/bash-array-operations/featured_hu_7c87cf995e170db8.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>pnpm Command: Install and Manage Node.js Packages</title><link>https://linuxize.com/post/pnpm-command/</link><pubDate>Mon, 08 Jun 2026 10:30:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/pnpm-command/</guid><category>nodejs</category><category>linux commands</category><description>This pnpm command guide covers installation, project setup, dependency management, scripts, lockfiles, npm migration, and workspace filtering.</description><content:encoded>&lt;p&gt;Large Node.js projects can consume a surprising amount of disk space when every project keeps its own copies of the same packages. &lt;code&gt;pnpm&lt;/code&gt; addresses this by storing package files in a shared content-addressable store and linking them into each project. It uses the npm registry and works with the same &lt;code&gt;package.json&lt;/code&gt; files while providing stricter dependency handling and built-in workspace support.&lt;/p&gt;
&lt;p&gt;This guide explains how to install pnpm on Linux, macOS, and Windows, then use it to create projects, install and update packages, run scripts, migrate from npm, and work across a monorepo.&lt;/p&gt;
&lt;h2 id="syntax"&gt;Syntax &lt;a class="headline-link" href="#syntax" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The general form of the command is:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="txt"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;txt&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm &amp;lt;command&amp;gt; [options]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Most pnpm commands have direct npm equivalents. For example, &lt;code&gt;pnpm install&lt;/code&gt; installs project dependencies, &lt;code&gt;pnpm add&lt;/code&gt; records a new dependency, and &lt;code&gt;pnpm run&lt;/code&gt; executes a script from &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="how-pnpm-stores-packages"&gt;How pnpm Stores Packages &lt;a class="headline-link" href="#how-pnpm-stores-packages" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;pnpm keeps package files in a shared store on your machine. Projects link to those files instead of copying the same package contents into every &lt;code&gt;node_modules&lt;/code&gt; directory. If ten projects use the same package version, pnpm stores its files once.&lt;/p&gt;
&lt;p&gt;The resulting &lt;code&gt;node_modules&lt;/code&gt; layout also prevents packages from importing dependencies they did not declare. This stricter behavior helps expose missing dependency declarations that npm&amp;rsquo;s flat dependency layout can sometimes hide.&lt;/p&gt;
&lt;p&gt;You still commit &lt;code&gt;package.json&lt;/code&gt; and the &lt;code&gt;pnpm-lock.yaml&lt;/code&gt; lockfile to version control. The shared store and generated &lt;code&gt;node_modules&lt;/code&gt; directory should not be committed.&lt;/p&gt;
&lt;h2 id="install-pnpm"&gt;Install pnpm &lt;a class="headline-link" href="#install-pnpm" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;pnpm 11 requires Node.js 22.13 or newer when you install it through npm or Corepack. Check your current version first:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;node --version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If you need a newer release, follow our guide to &lt;a href="https://linuxize.com/post/how-to-install-node-js-on-ubuntu-26-04/"&gt;install Node.js and npm on Ubuntu 26.04&lt;/a&gt;
, or use a Node.js version manager on other systems.&lt;/p&gt;
&lt;h3 id="install-with-corepack"&gt;Install with Corepack &lt;a class="headline-link" href="#install-with-corepack" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Corepack manages package-manager versions and can select the pnpm release recorded in a project&amp;rsquo;s &lt;code&gt;package.json&lt;/code&gt;. Update Corepack first to avoid outdated signature data:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm install --global corepack@latest&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Enable the pnpm shim:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;corepack &lt;span class="nb"&gt;enable&lt;/span&gt; pnpm&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Pin pnpm for the current project:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;corepack use pnpm@latest-11&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This adds a &lt;code&gt;packageManager&lt;/code&gt; entry with an exact pnpm version to &lt;code&gt;package.json&lt;/code&gt;, which helps contributors and CI jobs use the same release.&lt;/p&gt;
&lt;h3 id="install-with-npm"&gt;Install with npm &lt;a class="headline-link" href="#install-with-npm" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you prefer a regular global package, install the current pnpm 11 release with:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm install --global pnpm@latest-11&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This method requires a compatible Node.js version. It does not automatically enforce a project-specific pnpm version.&lt;/p&gt;
&lt;h3 id="install-the-standalone-version"&gt;Install the Standalone Version &lt;a class="headline-link" href="#install-the-standalone-version" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The standalone installer does not require Node.js to be present. On Linux, macOS, and other POSIX systems, run:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -fsSL https://get.pnpm.io/install.sh &lt;span class="p"&gt;|&lt;/span&gt; sh -&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="note callout callout-warning"&gt;
&lt;div class="callout-header"&gt;&lt;svg role="img" aria-hidden="true" class="callout-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"&gt;
&lt;path d="M10 20C4.477 20 0 15.523 0 10S4.477 0 10 0s10 4.477 10 10-4.477 10-10 10zm0-2c4.418 0 8-3.582 8-8s-3.582-8-8-8-8 3.582-8 8 3.582 8 8 8zm-.5-5h1c.276 0 .5.224.5.5v1c0 .276-.224.5-.5.5h-1c-.276 0-.5-.224-.5-.5v-1c0-.276.224-.5.5-.5zm0-8h1c.276 0 .5.224.5.5V8l-.5 3-1 .5L9 8V5.5c0-.276.224-.5.5-.5z"&gt;&lt;/path&gt;
&lt;/svg&gt;
&lt;span class="callout-title"&gt;Warning&lt;/span&gt;&lt;/div&gt;
&lt;div class="callout-body"&gt;Piping a remote script into a shell runs code from the network as your user. Review the script before running it on a production system, or download it first and inspect the local file.&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;On Windows PowerShell, use:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="powershell"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;powershell&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Invoke-WebRequest&lt;/span&gt; &lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;pnpm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;ps1&lt;/span&gt; &lt;span class="n"&gt;-UseBasicParsing&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;Invoke-Expression&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Open a new terminal after the installer finishes, then verify the command:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm --version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;11.5.2&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="initialize-a-project"&gt;Initialize a Project &lt;a class="headline-link" href="#initialize-a-project" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Create a directory and initialize a &lt;code&gt;package.json&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkdir example-app
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; example-app
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm init&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;pnpm prompts for package metadata and writes the result to &lt;code&gt;package.json&lt;/code&gt;. If you already have a project created with npm, pnpm can use its existing &lt;code&gt;package.json&lt;/code&gt; without any conversion.&lt;/p&gt;
&lt;h2 id="install-packages"&gt;Install Packages &lt;a class="headline-link" href="#install-packages" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Add a runtime dependency with &lt;code&gt;pnpm add&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm add express&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;pnpm installs the package, records it under &lt;code&gt;dependencies&lt;/code&gt; in &lt;code&gt;package.json&lt;/code&gt;, and creates or updates &lt;code&gt;pnpm-lock.yaml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Add development tools with &lt;code&gt;-D&lt;/code&gt; (short for &lt;code&gt;--save-dev&lt;/code&gt;):&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm add -D eslint&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To install a specific package version, append it after &lt;code&gt;@&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm add express@5.1.0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;When you clone an existing pnpm project, install everything declared in its package files with:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm install&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For CI or another reproducible environment, prevent changes to the lockfile:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm install --frozen-lockfile&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The command fails when &lt;code&gt;package.json&lt;/code&gt; and &lt;code&gt;pnpm-lock.yaml&lt;/code&gt; do not agree instead of resolving a new dependency tree.&lt;/p&gt;
&lt;h2 id="install-global-packages"&gt;Install Global Packages &lt;a class="headline-link" href="#install-global-packages" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Install a command-line tool globally with &lt;code&gt;-g&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm add -g http-server&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If pnpm reports that it cannot find a global binary directory, run:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm setup&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Open a new terminal so the updated &lt;code&gt;PATH&lt;/code&gt; takes effect, then repeat the global install. Keep application dependencies local to each project and reserve global installs for commands you need across projects.&lt;/p&gt;
&lt;h2 id="update-and-inspect-packages"&gt;Update and Inspect Packages &lt;a class="headline-link" href="#update-and-inspect-packages" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Check which dependencies have newer versions available:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm outdated&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Update dependencies within the version ranges in &lt;code&gt;package.json&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm update&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To update one package to its latest published release and change its declared range, run:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm update express --latest&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;List the direct dependencies in the current project with:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm list --depth &lt;span class="m"&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;When you need to understand why a package is present, use:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm why express&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="remove-packages"&gt;Remove Packages &lt;a class="headline-link" href="#remove-packages" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Remove a dependency from &lt;code&gt;node_modules&lt;/code&gt;, &lt;code&gt;package.json&lt;/code&gt;, and the lockfile with:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm remove express&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For a globally installed package, add &lt;code&gt;-g&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm remove -g http-server&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="run-scripts-and-package-binaries"&gt;Run Scripts and Package Binaries &lt;a class="headline-link" href="#run-scripts-and-package-binaries" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;pnpm reads the &lt;code&gt;scripts&lt;/code&gt; section of &lt;code&gt;package.json&lt;/code&gt; just like npm. Given this file:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="json"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;package.json&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-amber-100 text-amber-700 dark:bg-amber-900 dark:text-amber-300"&gt;json&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;scripts&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;start&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;node index.js&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;lint&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;eslint .&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Run a named script with:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm run lint&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You can omit &lt;code&gt;run&lt;/code&gt; when the script name does not conflict with a pnpm command:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm start&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Use &lt;code&gt;pnpm exec&lt;/code&gt; to run a binary installed in the current project:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm &lt;span class="nb"&gt;exec&lt;/span&gt; eslint .&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For a one-off package that you do not want to add as a dependency, use &lt;code&gt;pnpm dlx&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm dlx create-vite@latest my-app&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;pnpm exec&lt;/code&gt; runs an existing local dependency, while &lt;code&gt;pnpm dlx&lt;/code&gt; fetches a package temporarily and runs its command.&lt;/p&gt;
&lt;h2 id="migrate-a-project-from-npm"&gt;Migrate a Project from npm &lt;a class="headline-link" href="#migrate-a-project-from-npm" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;From a project containing &lt;code&gt;package-lock.json&lt;/code&gt;, generate a pnpm lockfile with:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm import&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The command reads the npm lockfile and creates &lt;code&gt;pnpm-lock.yaml&lt;/code&gt;. Install the imported dependency tree next:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm install&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Run the project&amp;rsquo;s tests and build before removing &lt;code&gt;package-lock.json&lt;/code&gt;. Commit &lt;code&gt;pnpm-lock.yaml&lt;/code&gt;, and update CI commands from &lt;code&gt;npm ci&lt;/code&gt; to &lt;code&gt;pnpm install --frozen-lockfile&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If the repository records its package manager, set the current pnpm version with:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;corepack use pnpm@latest&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="use-pnpm-workspaces"&gt;Use pnpm Workspaces &lt;a class="headline-link" href="#use-pnpm-workspaces" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A pnpm workspace groups multiple packages in one repository. Define its package locations in &lt;code&gt;pnpm-workspace.yaml&lt;/code&gt; at the repository root:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="yaml"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;pnpm-workspace.yaml&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-orange-100 text-orange-700 dark:bg-orange-900 dark:text-orange-300"&gt;yaml&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;packages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s2"&gt;&amp;#34;apps/*&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s2"&gt;&amp;#34;packages/*&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Install dependencies for every workspace package from the root:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm install&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Run a script recursively across all packages that define it:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm --recursive run build&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Use &lt;code&gt;--filter&lt;/code&gt; when a command should target one package:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm --filter web-app add zod
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pnpm --filter web-app run test&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Filters can select packages by name, directory, dependency relationship, or changed files, which makes them useful in larger monorepos and CI jobs.&lt;/p&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Check pnpm version&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pnpm --version&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Initialize a project&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pnpm init&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Install project dependencies&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pnpm install&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Install from an unchanged lockfile&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pnpm install --frozen-lockfile&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Add a dependency&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pnpm add &amp;lt;pkg&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Add a development dependency&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pnpm add -D &amp;lt;pkg&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Add a global package&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pnpm add -g &amp;lt;pkg&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Show outdated packages&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pnpm outdated&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Update packages&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pnpm update&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Update one package to latest&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pnpm update &amp;lt;pkg&amp;gt; --latest&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remove a package&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pnpm remove &amp;lt;pkg&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run a script&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pnpm run &amp;lt;script&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run a local package binary&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pnpm exec &amp;lt;command&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run a package temporarily&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pnpm dlx &amp;lt;pkg&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List direct dependencies&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pnpm list --depth 0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Explain an installed package&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pnpm why &amp;lt;pkg&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Import an npm lockfile&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pnpm import&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run a command in all workspaces&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pnpm --recursive &amp;lt;command&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Target one workspace package&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pnpm --filter &amp;lt;name&amp;gt; &amp;lt;command&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;pnpm: command not found after installation&lt;/strong&gt;&lt;br&gt;
Open a new terminal so your shell reloads its &lt;code&gt;PATH&lt;/code&gt;. For a standalone or global setup, run &lt;code&gt;pnpm setup&lt;/code&gt;, restart the terminal, and check again with &lt;code&gt;pnpm --version&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ERR_PNPM_UNSUPPORTED_ENGINE&lt;/strong&gt;&lt;br&gt;
The installed pnpm or one of the project packages requires a different Node.js version. pnpm 11 installed through npm or Corepack requires Node.js 22.13 or newer. Upgrade Node.js or use the standalone pnpm installer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A package import worked with npm but fails with pnpm&lt;/strong&gt;&lt;br&gt;
The code may rely on a transitive dependency that is not declared in its own &lt;code&gt;package.json&lt;/code&gt;. Add the missing package as a direct dependency instead of relying on npm&amp;rsquo;s flatter &lt;code&gt;node_modules&lt;/code&gt; layout.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ERR_PNPM_OUTDATED_LOCKFILE in CI&lt;/strong&gt;&lt;br&gt;
The committed &lt;code&gt;pnpm-lock.yaml&lt;/code&gt; does not match &lt;code&gt;package.json&lt;/code&gt;. Run &lt;code&gt;pnpm install&lt;/code&gt; locally, review and commit the updated lockfile, then rerun &lt;code&gt;pnpm install --frozen-lockfile&lt;/code&gt; in CI.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;pnpm uses the familiar npm package ecosystem while reducing duplicate package storage and enforcing clearer dependency declarations. If you are comparing workflows, see the &lt;a href="https://linuxize.com/post/npm-command/"&gt;npm command guide&lt;/a&gt;
for the equivalent npm commands and lockfile behavior.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/pnpm-command/featured_hu_5550d213ba2ac01d.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>How to Install Ansible on Ubuntu 26.04 and Write Your First Playbook</title><link>https://linuxize.com/post/how-to-install-ansible-on-ubuntu-26-04/</link><pubDate>Sun, 07 Jun 2026 09:40:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/how-to-install-ansible-on-ubuntu-26-04/</guid><category>ansible</category><category>ubuntu</category><description>Step-by-step instructions for installing Ansible on Ubuntu 26.04, configuring an inventory, and running your first playbook against remote hosts over SSH.</description><content:encoded>&lt;p&gt;When you manage more than a handful of servers, running the same commands on each one over SSH stops scaling. Ansible solves that by describing the desired state of your infrastructure in YAML files, then applying that state across many hosts in parallel. There is no agent to install on the targets and no central server to maintain; Ansible only needs SSH access and Python on the managed hosts.&lt;/p&gt;
&lt;p&gt;This guide explains how to install Ansible on Ubuntu 26.04, set up an inventory of remote hosts, run ad hoc commands, and write a small playbook that installs and starts Nginx on every host in a group.&lt;/p&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Install Ansible (apt)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt install ansible&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Show version&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ansible --version&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ping all hosts&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ansible all -m ping&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run an ad hoc command&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ansible web -a &amp;quot;uptime&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run a playbook&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ansible-playbook site.yml&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check an established playbook&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ansible-playbook --check --diff site.yml&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Limit to one host&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ansible-playbook site.yml --limit web1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List inventory hosts&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ansible-inventory --list&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Edit a vault file&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ansible-vault edit secrets.yml&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Install a collection&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ansible-galaxy collection install community.general&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites &lt;a class="headline-link" href="#prerequisites" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before installing Ansible, make sure you have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A control node running Ubuntu 26.04 with a &lt;a href="https://linuxize.com/post/how-to-create-a-sudo-user-on-ubuntu/"&gt;user with sudo privileges&lt;/a&gt;
. This is the machine where you run the &lt;code&gt;ansible&lt;/code&gt; commands.&lt;/li&gt;
&lt;li&gt;One or more managed hosts you can reach over SSH from the control node. Ansible runs on the managed hosts using the Python interpreter that ships with Ubuntu, so no extra setup is required there.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://linuxize.com/post/how-to-set-up-ssh-keys-on-ubuntu-20-04/"&gt;SSH key-based authentication&lt;/a&gt;
from the control node to each managed host. Password authentication works but is slower and limits automation.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="step-1-install-ansible"&gt;Step 1: Install Ansible &lt;a class="headline-link" href="#step-1-install-ansible" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Ansible is available in the Ubuntu 26.04 repositories. The &lt;code&gt;ansible&lt;/code&gt; package installs &lt;code&gt;ansible-core&lt;/code&gt; plus a curated set of community collections:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install ansible&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Confirm the install and the Python version Ansible is using:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ansible --version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The output shows the Ansible version, the Python interpreter path, and the location of the default config file:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;ansible [core 2.20.x]
config file = /etc/ansible/ansible.cfg
configured module search path = [&amp;#39;/home/user/.ansible/plugins/modules&amp;#39;, &amp;#39;/usr/share/ansible/plugins/modules&amp;#39;]
ansible python module location = /usr/lib/python3/dist-packages/ansible
python version = 3.14.x&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Ubuntu 26.04 provides the Ansible 13 community package with &lt;code&gt;ansible-core&lt;/code&gt; 2.20. The exact patch versions may be newer after regular package updates.&lt;/p&gt;
&lt;p&gt;If you need a release that is newer than the Ubuntu package, install Ansible in an isolated environment with &lt;code&gt;pipx&lt;/code&gt;. This avoids changing packages in the system Python environment:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install pipx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pipx ensurepath
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pipx install --include-deps ansible&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Start a new shell after &lt;code&gt;pipx ensurepath&lt;/code&gt; if the &lt;code&gt;ansible&lt;/code&gt; command is not immediately available. The Ansible PPA does not currently publish an Ubuntu 26.04 package, so do not use it for this release.&lt;/p&gt;
&lt;h2 id="step-2-create-an-inventory-file"&gt;Step 2: Create an Inventory File &lt;a class="headline-link" href="#step-2-create-an-inventory-file" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The inventory is the list of hosts Ansible can talk to. Create a working directory for your project and a small inventory:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkdir -p ~/ansible-demo
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/ansible-demo
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nano inventory.ini&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Add the following content. Replace the IP addresses with your own:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="ini"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-slate-200 text-slate-700 dark:bg-slate-600 dark:text-slate-300"&gt;ini&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[web]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;web1 ansible_host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;192.168.1.21&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;web2 ansible_host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;192.168.1.22&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[db]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;db1 ansible_host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;192.168.1.31&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[all:vars]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;ansible_user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;ubuntu&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;ansible_python_interpreter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/usr/bin/python3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;[web]&lt;/code&gt; and &lt;code&gt;[db]&lt;/code&gt; sections define groups. Each entry is a host alias followed by connection variables. The &lt;code&gt;[all:vars]&lt;/code&gt; block sets defaults for every host. Set &lt;code&gt;ansible_user&lt;/code&gt; to the SSH user on the managed hosts (often &lt;code&gt;ubuntu&lt;/code&gt; on cloud images or your own user on a private fleet).&lt;/p&gt;
&lt;p&gt;Tell Ansible to use this inventory by default by creating a project-level config:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nano ansible.cfg&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Add:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="ini"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-slate-200 text-slate-700 dark:bg-slate-600 dark:text-slate-300"&gt;ini&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[defaults]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;inventory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;./inventory.ini&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Ansible keeps SSH host-key checking enabled by default. Before the first Ansible run, connect to each server with SSH, verify its fingerprint, and accept the key:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh ubuntu@192.168.1.21&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Repeat this for each managed host. Verifying host keys protects the control node from connecting to an impersonated server.&lt;/p&gt;
&lt;h2 id="step-3-test-the-connection"&gt;Step 3: Test the Connection &lt;a class="headline-link" href="#step-3-test-the-connection" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use the &lt;code&gt;ping&lt;/code&gt; module to confirm Ansible can reach every host in the inventory. Despite the name, this module does not use ICMP; it logs in over SSH and runs a tiny Python module on the target:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ansible all -m ping&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;A successful run prints a green &lt;code&gt;pong&lt;/code&gt; for each host:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;web1 | SUCCESS =&amp;gt; {
&amp;#34;ansible_facts&amp;#34;: {
&amp;#34;discovered_interpreter_python&amp;#34;: &amp;#34;/usr/bin/python3&amp;#34;
},
&amp;#34;changed&amp;#34;: false,
&amp;#34;ping&amp;#34;: &amp;#34;pong&amp;#34;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you see &lt;code&gt;UNREACHABLE&lt;/code&gt;, check the SSH key, the user name in &lt;code&gt;ansible_user&lt;/code&gt;, and basic connectivity with &lt;code&gt;ssh ubuntu@192.168.1.21&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can scope commands to a group instead of &lt;code&gt;all&lt;/code&gt;. For example, list disk usage on every web host:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ansible web -a &lt;span class="s2"&gt;&amp;#34;df -h /&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;-a&lt;/code&gt; flag runs an ad hoc command using the default &lt;code&gt;command&lt;/code&gt; module. Use &lt;code&gt;-m shell&lt;/code&gt; instead when the command relies on pipes, redirection, or shell expansion.&lt;/p&gt;
&lt;h2 id="step-4-write-your-first-playbook"&gt;Step 4: Write Your First Playbook &lt;a class="headline-link" href="#step-4-write-your-first-playbook" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A playbook is a YAML file that describes the desired state of your hosts. Create one that installs Nginx and makes sure the service is running on every web host:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nano site.yml&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Add the following content:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="yaml"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-orange-100 text-orange-700 dark:bg-orange-900 dark:text-orange-300"&gt;yaml&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Configure web servers&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;hosts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;web&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;become&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Install Nginx&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ansible.builtin.apt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;nginx&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;present&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;update_cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Ensure Nginx is running and enabled&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ansible.builtin.service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;nginx&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;started&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Deploy a custom landing page&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ansible.builtin.copy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;lt;h1&amp;gt;Managed by Ansible&amp;lt;/h1&amp;gt;\n&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/var/www/html/index.html&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;root&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;root&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;0644&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The play targets the &lt;code&gt;web&lt;/code&gt; group and uses &lt;code&gt;become: true&lt;/code&gt; so each task runs as root through &lt;code&gt;sudo&lt;/code&gt;. The three tasks install the &lt;code&gt;nginx&lt;/code&gt; package, make sure the service is started and enabled at boot, and replace the default index page with a small marker.&lt;/p&gt;
&lt;h2 id="step-5-run-the-playbook"&gt;Step 5: Run the Playbook &lt;a class="headline-link" href="#step-5-run-the-playbook" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Check the YAML and playbook structure before changing the managed hosts:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ansible-playbook --syntax-check site.yml&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;When the syntax check passes, run the playbook:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ansible-playbook site.yml&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The output ends with a per-host summary that looks like this:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;PLAY RECAP *********************************************************************
web1 : ok=4 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
web2 : ok=4 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;changed=3&lt;/code&gt; count means Ansible applied three of the four tasks (the gather facts task counts as &lt;code&gt;ok&lt;/code&gt; only). Run the playbook in check mode after the initial configuration:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ansible-playbook --check --diff site.yml&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The check should report &lt;code&gt;changed=0&lt;/code&gt; because the hosts already match the playbook. Check mode is a simulation, and some modules do not fully support it, so review its output before relying on it for production changes.&lt;/p&gt;
&lt;p&gt;Open &lt;code&gt;http://&amp;lt;web_host_ip&amp;gt;&lt;/code&gt; in a browser and you should see the custom landing page.&lt;/p&gt;
&lt;h2 id="step-6-use-ansible-vault-for-secrets"&gt;Step 6: Use ansible-vault for Secrets &lt;a class="headline-link" href="#step-6-use-ansible-vault-for-secrets" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Most playbooks reference passwords or API tokens. &lt;code&gt;ansible-vault&lt;/code&gt; encrypts variable files at rest so you can keep them in version control:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ansible-vault create secrets.yml&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Set a strong password when prompted. Add encrypted variables to the file, for example:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="yaml"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-orange-100 text-orange-700 dark:bg-orange-900 dark:text-orange-300"&gt;yaml&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;db_password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;s3cret-value&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="note callout callout-warning"&gt;
&lt;div class="callout-header"&gt;&lt;svg role="img" aria-hidden="true" class="callout-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"&gt;
&lt;path d="M10 20C4.477 20 0 15.523 0 10S4.477 0 10 0s10 4.477 10 10-4.477 10-10 10zm0-2c4.418 0 8-3.582 8-8s-3.582-8-8-8-8 3.582-8 8 3.582 8 8 8zm-.5-5h1c.276 0 .5.224.5.5v1c0 .276-.224.5-.5.5h-1c-.276 0-.5-.224-.5-.5v-1c0-.276.224-.5.5-.5zm0-8h1c.276 0 .5.224.5.5V8l-.5 3-1 .5L9 8V5.5c0-.276.224-.5.5-.5z"&gt;&lt;/path&gt;
&lt;/svg&gt;
&lt;span class="callout-title"&gt;Warning&lt;/span&gt;&lt;/div&gt;
&lt;div class="callout-body"&gt;The example above uses a placeholder password. Replace it with a real strong value, do not commit unencrypted credentials, and store the vault password in a password manager or in a file referenced by &lt;code&gt;--vault-password-file&lt;/code&gt; outside the repository.&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Reference the vault file from a play with &lt;code&gt;vars_files&lt;/code&gt; and run the playbook with &lt;code&gt;--ask-vault-pass&lt;/code&gt; or &lt;code&gt;--vault-password-file&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="yaml"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-orange-100 text-orange-700 dark:bg-orange-900 dark:text-orange-300"&gt;yaml&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Configure web servers&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;hosts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;web&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;become&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;vars_files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;secrets.yml&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Run the playbook and enter the vault password when prompted:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ansible-playbook site.yml --ask-vault-pass&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Ansible Vault protects data at rest. Use &lt;code&gt;no_log: true&lt;/code&gt; on tasks that may print decrypted secrets, and never commit a vault password file to version control.&lt;/p&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;UNREACHABLE&lt;/code&gt; errors on every host&lt;/strong&gt;&lt;br&gt;
Ansible cannot SSH into the targets. Run &lt;code&gt;ssh ansible_user@host&lt;/code&gt; manually to confirm the key, the user, and the network path. Check that &lt;code&gt;ansible_user&lt;/code&gt; in the inventory matches the user that owns the authorized SSH key.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;sudo: a password is required&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
The remote user requires a password for &lt;code&gt;sudo&lt;/code&gt; and you did not pass one. Run the play with &lt;code&gt;--ask-become-pass&lt;/code&gt; or configure passwordless sudo for the deploy user on the managed hosts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tasks fail with &lt;code&gt;Could not find python interpreter&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
The discovery process picked the wrong path. Set &lt;code&gt;ansible_python_interpreter=/usr/bin/python3&lt;/code&gt; in the inventory &lt;code&gt;[all:vars]&lt;/code&gt; block to pin it to the Ubuntu default.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Host key verification failed&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
The server key is missing from &lt;code&gt;~/.ssh/known_hosts&lt;/code&gt;, or it changed since the previous connection. Connect with SSH and verify the fingerprint. If the server was rebuilt, remove only its stale entry with &lt;code&gt;ssh-keygen -R 192.168.1.21&lt;/code&gt;, then reconnect and verify the new key.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Playbook reports &lt;code&gt;changed&lt;/code&gt; on every run for the same task&lt;/strong&gt;&lt;br&gt;
A task is not idempotent. Re-check the module you used. For example, prefer &lt;code&gt;ansible.builtin.copy&lt;/code&gt; or &lt;code&gt;ansible.builtin.template&lt;/code&gt; over &lt;code&gt;command: echo ... &amp;gt; file&lt;/code&gt;, because shell redirection always reports &lt;code&gt;changed&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="faq"&gt;FAQ &lt;a class="headline-link" href="#faq" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;What is the difference between Ansible and ansible-core?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;ansible-core&lt;/code&gt; is the engine and the small set of built-in modules. The &lt;code&gt;ansible&lt;/code&gt; package adds a curated bundle of community collections on top of &lt;code&gt;ansible-core&lt;/code&gt;. Most users install &lt;code&gt;ansible&lt;/code&gt; and pull additional collections with &lt;code&gt;ansible-galaxy&lt;/code&gt; as needed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Do I need to install anything on the managed hosts?&lt;/strong&gt;&lt;br&gt;
Only Python and an SSH server, both of which ship by default on Ubuntu. See &lt;a href="https://linuxize.com/post/how-to-install-python-on-ubuntu-26-04/"&gt;How to Install Python on Ubuntu 26.04&lt;/a&gt;
if you need to confirm the interpreter is present.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Can I run Ansible against the local machine?&lt;/strong&gt;&lt;br&gt;
Yes. Add a &lt;code&gt;localhost ansible_connection=local&lt;/code&gt; entry to the inventory, or run a playbook with &lt;code&gt;ansible-playbook -i localhost, -c local site.yml&lt;/code&gt;. The trailing comma after &lt;code&gt;localhost&lt;/code&gt; tells Ansible to treat the value as an inline inventory rather than a file path.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How do I structure a real project?&lt;/strong&gt;&lt;br&gt;
Split logic into roles under a &lt;code&gt;roles/&lt;/code&gt; directory, group host variables under &lt;code&gt;group_vars/&lt;/code&gt;, and host-specific values under &lt;code&gt;host_vars/&lt;/code&gt;. Keep &lt;code&gt;site.yml&lt;/code&gt; short and import roles per group. The Ansible documentation calls this layout a &amp;ldquo;best-practices project&amp;rdquo;.&lt;/p&gt;
&lt;h2 id="next-steps"&gt;Next Steps &lt;a class="headline-link" href="#next-steps" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You now have a working Ansible setup on Ubuntu 26.04 and a first playbook that configures a group of web servers. From here you can break tasks into roles, parameterize plays with variables and templates, and add CI to run &lt;code&gt;ansible-playbook --check&lt;/code&gt; on every push. You can also use the same inventory and roles to prepare hosts before you &lt;a href="https://linuxize.com/post/how-to-install-docker-on-ubuntu-26-04/"&gt;install Docker on Ubuntu 26.04&lt;/a&gt;
.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/how-to-install-ansible-on-ubuntu-26-04/featured_hu_1245e306ef0ba588.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>Ansible Cheatsheet</title><link>https://linuxize.com/cheatsheet/ansible/</link><pubDate>Sun, 07 Jun 2026 00:00:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/cheatsheet/ansible/</guid><description>Quick reference for Ansible commands, inventories, playbooks, modules, and vault</description><content:encoded>&lt;div class="card"&gt;
&lt;h3 id="install--verify"&gt;Install &amp;amp; Verify &lt;a class="headline-link" href="#install--verify" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Set up Ansible on the control node. See the full &lt;a href="https://linuxize.com/post/how-to-install-ansible-on-ubuntu-26-04/"&gt;Ansible install guide&lt;/a&gt;
.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo apt install ansible&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install on Ubuntu, Debian, and derivatives&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo dnf install ansible&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install on Fedora, RHEL, and derivatives&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pipx install --include-deps ansible&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install latest in an isolated environment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible --version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show version and config file path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible-config dump&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show all active configuration settings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible-config dump --only-changed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show only non-default settings&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="inventory"&gt;Inventory &lt;a class="headline-link" href="#inventory" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Define and inspect the hosts Ansible manages.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible-inventory --list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the full inventory as JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible-inventory --graph&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show groups and hosts as a tree&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible all --list-hosts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List every host in the inventory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible web --list-hosts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List hosts in the &lt;code&gt;web&lt;/code&gt; group&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible -i inventory.ini all -m ping&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use a specific inventory file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible -i 'host,' all -m ping&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use an inline inventory (note the comma)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="inventory-file-ini"&gt;Inventory File (INI) &lt;a class="headline-link" href="#inventory-file-ini" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Basic structure of a static &lt;code&gt;inventory.ini&lt;/code&gt; file.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Syntax&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[web]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Define a host group&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;web1 ansible_host=192.168.1.21&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Host alias with connection address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[all:vars]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Variables applied to every host&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible_user=ubuntu&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SSH user for the connection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible_python_interpreter=/usr/bin/python3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pin the remote Python path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[prod:children]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Group made up of other groups&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="ad-hoc-commands"&gt;Ad Hoc Commands &lt;a class="headline-link" href="#ad-hoc-commands" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Run a single module against hosts without a playbook.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible all -m ping&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Test SSH and Python on every host&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible web -a &amp;quot;uptime&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run a command (default &lt;code&gt;command&lt;/code&gt; module)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible web -m shell -a &amp;quot;ps aux | grep nginx&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use the shell module for pipes and redirection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible web -b -m apt -a &amp;quot;name=nginx state=present&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install a package as root (&lt;code&gt;-b&lt;/code&gt; = become)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible web -m service -a &amp;quot;name=nginx state=restarted&amp;quot; -b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Restart a service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible web -m copy -a &amp;quot;src=a.conf dest=/etc/a.conf&amp;quot; -b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Copy a file to the hosts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible web -m setup&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Gather and print all host facts&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="running-playbooks"&gt;Running Playbooks &lt;a class="headline-link" href="#running-playbooks" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Apply a playbook with &lt;a href="https://linuxize.com/post/how-to-install-ansible-on-ubuntu-26-04/"&gt;ansible-playbook&lt;/a&gt;
.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible-playbook site.yml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run a playbook&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible-playbook --syntax-check site.yml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Validate YAML and structure only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible-playbook --check --diff site.yml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Dry run and show would-be changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible-playbook site.yml --limit web1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run against a single host&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible-playbook site.yml --tags deploy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run only tasks with a tag&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible-playbook site.yml --skip-tags slow&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Skip tasks with a tag&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible-playbook site.yml --start-at-task &amp;quot;name&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Begin at a named task&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible-playbook site.yml -e &amp;quot;var=value&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pass an extra variable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible-playbook site.yml -K&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prompt for the become (sudo) password&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="common-modules"&gt;Common Modules &lt;a class="headline-link" href="#common-modules" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Frequently used built-in modules in tasks.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Module&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible.builtin.apt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Manage packages on Debian-based systems&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible.builtin.dnf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Manage packages on RHEL-based systems&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible.builtin.service&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start, stop, enable, and restart services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible.builtin.copy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Copy a file to managed hosts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible.builtin.template&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Render a Jinja2 template to a file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible.builtin.file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set path state, owner, group, and mode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible.builtin.lineinfile&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ensure a line is present in a file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible.builtin.user&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create and manage user accounts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible.builtin.git&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check out a Git repository&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible.builtin.systemd_service&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Manage systemd units directly&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="playbook-keywords"&gt;Playbook Keywords &lt;a class="headline-link" href="#playbook-keywords" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Core directives used inside a play or task.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Keyword&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hosts:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Target group or host pattern for the play&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;become: true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run tasks with privilege escalation (sudo)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;vars:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Define variables for the play&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;vars_files:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Load variables from external files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tasks:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List of tasks to run in order&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;handlers:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tasks triggered by &lt;code&gt;notify&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;notify:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Trigger a handler when a task changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;when:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run a task only if a condition is true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;loop:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Repeat a task over a list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;register:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Save a task result to a variable&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="ansible-vault"&gt;Ansible Vault &lt;a class="headline-link" href="#ansible-vault" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Encrypt secrets so they are safe to commit.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible-vault create secrets.yml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a new encrypted file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible-vault edit secrets.yml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Edit an encrypted file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible-vault view secrets.yml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;View without editing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible-vault encrypt vars.yml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Encrypt an existing plaintext file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible-vault decrypt vars.yml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Decrypt a file back to plaintext&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible-vault rekey secrets.yml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change the vault password&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible-playbook site.yml --ask-vault-pass&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prompt for the vault password at run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible-playbook site.yml --vault-password-file .pass&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read the vault password from a file&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="galaxy--collections"&gt;Galaxy &amp;amp; Collections &lt;a class="headline-link" href="#galaxy--collections" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Install and manage roles and collections.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible-galaxy collection install community.general&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install a collection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible-galaxy collection list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List installed collections&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible-galaxy role install geerlingguy.nginx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install a role from Galaxy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible-galaxy role list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List installed roles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible-galaxy install -r requirements.yml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install from a requirements file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ansible-galaxy init my_role&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Scaffold a new role directory&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;</content:encoded></item><item><title>OpenSSL Cheatsheet</title><link>https://linuxize.com/cheatsheet/openssl/</link><pubDate>Sat, 06 Jun 2026 10:40:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/cheatsheet/openssl/</guid><description>Quick reference for OpenSSL commands for keys, CSRs, certificates, format conversion, TLS testing, random data, and hashes</description><content:encoded>&lt;div class="card"&gt;
&lt;h3 id="version-and-help"&gt;Version and Help &lt;a class="headline-link" href="#version-and-help" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Check the installed OpenSSL version and find command help.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-use-openssl/"&gt;&lt;code&gt;openssl version&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Show OpenSSL version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl version -a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show build details and paths&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl help&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List available commands&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl req -help&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show help for the req command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl x509 -help&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show help for certificate inspection&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="private-keys"&gt;Private Keys &lt;a class="headline-link" href="#private-keys" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Generate RSA and elliptic-curve private keys.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl genrsa -out private.key 2048&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generate RSA private key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl genrsa -out private.key 4096&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generate larger RSA key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl genrsa -aes256 -out private.key 2048&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generate encrypted RSA key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl rsa -in encrypted.key -out plain.key&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove key passphrase&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl ecparam -name prime256v1 -genkey -noout -out ec.key&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generate EC private key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;chmod 600 private.key&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Restrict private key permissions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="certificate-signing-requests"&gt;Certificate Signing Requests &lt;a class="headline-link" href="#certificate-signing-requests" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Create CSRs for certificate authorities.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl req -new -newkey rsa:2048 -noenc -keyout domain.key -out domain.csr&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generate key and CSR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl req -new -key domain.key -out domain.csr&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create CSR from existing key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl req -new -newkey rsa:2048 -noenc -keyout domain.key -out domain.csr -subj &amp;quot;/CN=example.com&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Non-interactive CSR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl req -new -key domain.key -out domain.csr -addext &amp;quot;subjectAltName=DNS:example.com,DNS:www.example.com&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CSR with SAN names&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl req -in domain.csr -noout -text&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Inspect CSR contents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl req -in domain.csr -noout -subject&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print CSR subject&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="self-signed-certificates"&gt;Self-Signed Certificates &lt;a class="headline-link" href="#self-signed-certificates" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Create local and internal certificates.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-use-openssl/"&gt;&lt;code&gt;openssl req -x509 -newkey rsa:2048 -noenc -keyout selfsigned.key -out selfsigned.crt -days 365&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Generate self-signed certificate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl req -x509 -new -key domain.key -out domain.crt -days 365&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Self-sign with existing key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl req -x509 -newkey rsa:2048 -noenc -keyout localhost.key -out localhost.crt -days 365 -subj &amp;quot;/CN=localhost&amp;quot; -addext &amp;quot;subjectAltName=DNS:localhost,IP:127.0.0.1&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Localhost certificate with SAN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl x509 -in selfsigned.crt -noout -text&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Inspect certificate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl x509 -in selfsigned.crt -noout -dates&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show validity dates&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="certificate-inspection"&gt;Certificate Inspection &lt;a class="headline-link" href="#certificate-inspection" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Read certificate fields without opening a browser.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl x509 -in cert.crt -noout -text&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show full certificate details&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl x509 -in cert.crt -noout -subject&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show subject&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl x509 -in cert.crt -noout -issuer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show issuer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl x509 -in cert.crt -noout -dates&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show notBefore and notAfter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl x509 -in cert.crt -noout -serial&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show serial number&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl x509 -in cert.crt -noout -fingerprint -sha256&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show SHA-256 fingerprint&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="match-key-csr-and-certificate"&gt;Match Key, CSR, and Certificate &lt;a class="headline-link" href="#match-key-csr-and-certificate" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Confirm that files belong together before deployment.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl rsa -in domain.key -noout -modulus | openssl md5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hash RSA key modulus&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl x509 -in domain.crt -noout -modulus | openssl md5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hash certificate modulus&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl req -in domain.csr -noout -modulus | openssl md5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hash CSR modulus&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl pkey -in private.key -pubout -outform pem&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Extract public key from private key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl x509 -in domain.crt -pubkey -noout&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Extract public key from certificate&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="format-conversion"&gt;Format Conversion &lt;a class="headline-link" href="#format-conversion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Convert certificates between common encodings and bundles.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl x509 -in cert.pem -outform der -out cert.der&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Convert PEM certificate to DER&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl x509 -in cert.der -inform der -out cert.pem&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Convert DER certificate to PEM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl pkcs12 -export -out bundle.pfx -inkey domain.key -in domain.crt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create PKCS#12 bundle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl pkcs12 -in bundle.pfx -out bundle.pem -noenc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Extract PEM from PKCS#12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl rsa -in private.key -outform der -out private.der&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Convert RSA key to DER&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="tls-connection-testing"&gt;TLS Connection Testing &lt;a class="headline-link" href="#tls-connection-testing" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Debug live TLS services with s_client.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-use-openssl/"&gt;&lt;code&gt;openssl s_client -connect example.com:443 -servername example.com&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Test HTTPS with SNI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl s_client -connect mail.example.com:993 -servername mail.example.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Test IMAPS TLS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl s_client -starttls smtp -connect mail.example.com:587 -servername mail.example.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Test SMTP STARTTLS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;echo | openssl s_client -connect example.com:443 -servername example.com 2&amp;gt;/dev/null | openssl x509 -noout -dates&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show remote certificate dates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl s_client -connect example.com:443 -tls1_3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Force TLS 1.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl s_client -connect example.com:443 -showcerts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show certificate chain&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="random-data-and-hashes"&gt;Random Data and Hashes &lt;a class="headline-link" href="#random-data-and-hashes" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Generate tokens and verify file digests.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl rand -base64 32&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generate base64 token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl rand -hex 32&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generate hex token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl dgst -sha256 file.iso&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SHA-256 file hash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl dgst -sha512 file.iso&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SHA-512 file hash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openssl dgst -sha256 -binary file.iso | openssl base64&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Base64-encoded binary digest&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="related-guides"&gt;Related Guides &lt;a class="headline-link" href="#related-guides" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Use these references for certificate workflows.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Guide&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-use-openssl/"&gt;OpenSSL Command Guide&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Full OpenSSL tutorial with examples&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/creating-a-self-signed-ssl-certificate/"&gt;Create a Self-Signed SSL Certificate&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Step-by-step self-signed certificate guide&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/what-is-an-ssl-certificate/"&gt;What is an SSL Certificate?&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;SSL/TLS certificate concepts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-use-dig-command-to-query-dns-in-linux/"&gt;dig Command&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Check DNS before certificate testing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;</content:encoded></item><item><title>How to Use OpenSSL: Generate Keys, Certificates, and Test TLS</title><link>https://linuxize.com/post/how-to-use-openssl/</link><pubDate>Sat, 06 Jun 2026 09:25:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/how-to-use-openssl/</guid><category>ssl</category><category>security</category><description>Practical OpenSSL guide for Linux: generate private keys, create CSRs and self-signed certificates, convert formats, inspect certificates, and test TLS connections.</description><content:encoded>&lt;p&gt;Sooner or later, working with web servers, mail servers, or APIs means dealing with TLS certificates. You may need a private key for a new service, a certificate signing request to send to a certificate authority, a quick self-signed certificate for a test environment, or a way to confirm what a remote server is actually presenting. OpenSSL covers all of this from a single command-line tool.&lt;/p&gt;
&lt;p&gt;OpenSSL is the de facto toolkit for the TLS and SSL protocols on Linux. It ships with almost every distribution and exposes dozens of subcommands for key generation, certificate handling, encoding conversions, and connection testing.&lt;/p&gt;
&lt;p&gt;This guide explains how to use &lt;code&gt;openssl&lt;/code&gt; to generate keys, create and inspect certificates, convert between formats, and test live TLS connections.&lt;/p&gt;
&lt;h2 id="syntax"&gt;Syntax &lt;a class="headline-link" href="#syntax" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;OpenSSL works through subcommands, each with its own options:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="txt"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;txt&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl command [ command_options ] [ arguments ]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For example, &lt;code&gt;openssl genrsa&lt;/code&gt; generates an RSA key, while &lt;code&gt;openssl x509&lt;/code&gt; works with X.509 certificates. You can list every available subcommand with:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl help&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Check which version is installed before you start, since option defaults change between major releases:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;OpenSSL 3.5.5 27 Jan 2026&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;OpenSSL 3.x is the current series on modern distributions. The examples below work on the 3.x line.&lt;/p&gt;
&lt;h2 id="generate-a-private-key"&gt;Generate a Private Key &lt;a class="headline-link" href="#generate-a-private-key" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Most certificate work starts with a private key. To create a 2048-bit RSA key, run:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl genrsa -out private.key &lt;span class="m"&gt;2048&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This writes an unencrypted key to &lt;code&gt;private.key&lt;/code&gt;. A 2048-bit key is the practical minimum today; use 4096 bits when you want a wider safety margin and do not mind the small performance cost.&lt;/p&gt;
&lt;p&gt;To protect the key with a passphrase, add a cipher option such as &lt;code&gt;-aes256&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl genrsa -aes256 -out private.key &lt;span class="m"&gt;2048&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;OpenSSL prompts you for the passphrase and encrypts the key on disk. The service that uses the key will then ask for that passphrase on startup.&lt;/p&gt;
&lt;p&gt;Elliptic-curve keys are smaller and faster than RSA at equivalent security. To generate one on the widely supported &lt;code&gt;prime256v1&lt;/code&gt; curve:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl ecparam -name prime256v1 -genkey -noout -out ec-private.key&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="note callout callout-warning"&gt;
&lt;div class="callout-header"&gt;&lt;svg role="img" aria-hidden="true" class="callout-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"&gt;
&lt;path d="M10 20C4.477 20 0 15.523 0 10S4.477 0 10 0s10 4.477 10 10-4.477 10-10 10zm0-2c4.418 0 8-3.582 8-8s-3.582-8-8-8-8 3.582-8 8 3.582 8 8 8zm-.5-5h1c.276 0 .5.224.5.5v1c0 .276-.224.5-.5.5h-1c-.276 0-.5-.224-.5-.5v-1c0-.276.224-.5.5-.5zm0-8h1c.276 0 .5.224.5.5V8l-.5 3-1 .5L9 8V5.5c0-.276.224-.5.5-.5z"&gt;&lt;/path&gt;
&lt;/svg&gt;
&lt;span class="callout-title"&gt;Warning&lt;/span&gt;&lt;/div&gt;
&lt;div class="callout-body"&gt;A private key is a secret. Keep its permissions tight (&lt;code&gt;chmod 600 private.key&lt;/code&gt;), never commit it to version control, and add key files to your &lt;code&gt;.gitignore&lt;/code&gt;. Anyone who has the key can impersonate your service.&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="create-a-certificate-signing-request"&gt;Create a Certificate Signing Request &lt;a class="headline-link" href="#create-a-certificate-signing-request" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A certificate signing request (CSR) bundles your public key and identifying details so a certificate authority can issue a signed certificate. Generate a key and CSR together:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl req -new -newkey rsa:2048 -noenc -keyout domain.key -out domain.csr&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;OpenSSL prompts for the subject fields. The Common Name (CN) should match the primary domain you are securing, for example &lt;code&gt;example.com&lt;/code&gt;. The &lt;code&gt;-noenc&lt;/code&gt; option leaves the key unencrypted so a service can read it without a passphrase prompt.&lt;/p&gt;
&lt;p&gt;To skip the interactive prompts, pass the subject inline and add Subject Alternative Name (SAN) entries for each hostname the certificate should cover:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl req -new -newkey rsa:2048 -noenc &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -keyout domain.key -out domain.csr &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -subj &lt;span class="s2"&gt;&amp;#34;/C=US/ST=California/L=San Francisco/O=Example Inc/CN=example.com&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -addext &lt;span class="s2"&gt;&amp;#34;subjectAltName=DNS:example.com,DNS:www.example.com&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Inspect the finished CSR to confirm the details before sending it off:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl req -in domain.csr -noout -text&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The output lists the subject, public key, and any requested extensions.&lt;/p&gt;
&lt;h2 id="create-a-self-signed-certificate"&gt;Create a Self-Signed Certificate &lt;a class="headline-link" href="#create-a-self-signed-certificate" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For local development, internal tools, or testing, a self-signed certificate avoids the need for a CA. Generate a key and certificate valid for one year in a single command:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl req -x509 -newkey rsa:2048 -noenc &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -keyout selfsigned.key -out selfsigned.crt &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -days &lt;span class="m"&gt;365&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -subj &lt;span class="s2"&gt;&amp;#34;/CN=localhost&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -addext &lt;span class="s2"&gt;&amp;#34;subjectAltName=DNS:localhost,IP:127.0.0.1&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;-x509&lt;/code&gt; option tells &lt;code&gt;req&lt;/code&gt; to output a certificate instead of a request, and &lt;code&gt;-days 365&lt;/code&gt; sets the validity period. Browsers will warn that the certificate is not trusted, which is expected for self-signed certificates. For a fuller walkthrough, see our guide on &lt;a href="https://linuxize.com/post/creating-a-self-signed-ssl-certificate/"&gt;creating a self-signed SSL certificate&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;When you need a browser-trusted certificate for a public site, use a CA such as Let&amp;rsquo;s Encrypt instead.&lt;/p&gt;
&lt;h2 id="inspect-a-certificate"&gt;Inspect a Certificate &lt;a class="headline-link" href="#inspect-a-certificate" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To read the contents of a certificate in human-readable form:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl x509 -in selfsigned.crt -noout -text&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This prints the issuer, subject, validity dates, public key, and extensions. When you only need specific fields, target them directly. To see just the validity window:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl x509 -in selfsigned.crt -noout -dates&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;notBefore=Jan 1 08:00:00 2026 GMT
notAfter=Jan 1 08:00:00 2027 GMT&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To print the subject and issuer:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl x509 -in selfsigned.crt -noout -subject -issuer&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For a self-signed certificate, the subject and issuer are the same.&lt;/p&gt;
&lt;h2 id="verify-key-certificate-and-csr-match"&gt;Verify Key, Certificate, and CSR Match &lt;a class="headline-link" href="#verify-key-certificate-and-csr-match" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When deploying a certificate, the private key, certificate, and CSR must all share the same public key. A mismatch is a common cause of services that refuse to start. Compare their modulus hashes:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl rsa -in domain.key -noout -modulus &lt;span class="p"&gt;|&lt;/span&gt; openssl md5
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl x509 -in domain.crt -noout -modulus &lt;span class="p"&gt;|&lt;/span&gt; openssl md5
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl req -in domain.csr -noout -modulus &lt;span class="p"&gt;|&lt;/span&gt; openssl md5&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;All three commands must print the same hash. If one differs, the files do not belong together.&lt;/p&gt;
&lt;h2 id="convert-certificate-formats"&gt;Convert Certificate Formats &lt;a class="headline-link" href="#convert-certificate-formats" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Different platforms expect different encodings. OpenSSL converts between them.&lt;/p&gt;
&lt;p&gt;Convert a PEM certificate to DER (binary), often required by Java or Windows tools:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl x509 -in cert.pem -outform der -out cert.der&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Convert DER back to PEM:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl x509 -in cert.der -inform der -outform pem -out cert.pem&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Bundle a certificate and private key into a PKCS#12 file (&lt;code&gt;.pfx&lt;/code&gt; or &lt;code&gt;.p12&lt;/code&gt;), the format many Windows services and load balancers expect:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl pkcs12 -export -out bundle.pfx -inkey domain.key -in domain.crt&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;OpenSSL asks for an export password that protects the bundle.&lt;/p&gt;
&lt;h2 id="test-a-tls-connection"&gt;Test a TLS Connection &lt;a class="headline-link" href="#test-a-tls-connection" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;s_client&lt;/code&gt; subcommand opens a TLS connection to a server and reports what it negotiated. This is the fastest way to debug a certificate that does not seem to work:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl s_client -connect example.com:443&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The output shows the certificate chain, the negotiated protocol and cipher, and the verification result. Press &lt;code&gt;Ctrl+C&lt;/code&gt; or type &lt;code&gt;Q&lt;/code&gt; to close the session.&lt;/p&gt;
&lt;p&gt;When a server hosts several names on one IP, send the hostname with Server Name Indication so you get the right certificate:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl s_client -connect example.com:443 -servername example.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To check when a live certificate expires without scrolling through the full chain, pipe the output into &lt;code&gt;x509&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; openssl s_client -connect example.com:443 -servername example.com 2&amp;gt;/dev/null &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;|&lt;/span&gt; openssl x509 -noout -dates&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;echo&lt;/code&gt; closes the connection immediately so the command returns instead of waiting for input. This is handy for verifying renewals after a certificate is reissued. If a hostname is not resolving as expected during testing, the &lt;a href="https://linuxize.com/post/how-to-use-dig-command-to-query-dns-in-linux/"&gt;dig command&lt;/a&gt;
helps confirm the DNS records first.&lt;/p&gt;
&lt;h2 id="generate-random-data-and-hashes"&gt;Generate Random Data and Hashes &lt;a class="headline-link" href="#generate-random-data-and-hashes" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;OpenSSL also doubles as a general crypto utility. Generate a random, URL-safe token:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl rand -base64 &lt;span class="m"&gt;32&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Hash a file with SHA-256:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl dgst -sha256 file.iso&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;These small helpers are useful for generating secrets or verifying downloads when no dedicated tool is at hand.&lt;/p&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For a printable quick reference, see the &lt;a href="https://linuxize.com/cheatsheet/openssl/"&gt;OpenSSL cheatsheet&lt;/a&gt;
.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Show version&lt;/td&gt;
&lt;td&gt;&lt;code&gt;openssl version&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generate RSA key&lt;/td&gt;
&lt;td&gt;&lt;code&gt;openssl genrsa -out private.key 2048&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generate EC key&lt;/td&gt;
&lt;td&gt;&lt;code&gt;openssl ecparam -name prime256v1 -genkey -noout -out ec.key&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Create CSR&lt;/td&gt;
&lt;td&gt;&lt;code&gt;openssl req -new -newkey rsa:2048 -noenc -keyout d.key -out d.csr&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Self-signed cert&lt;/td&gt;
&lt;td&gt;&lt;code&gt;openssl req -x509 -newkey rsa:2048 -noenc -keyout d.key -out d.crt -days 365&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inspect cert&lt;/td&gt;
&lt;td&gt;&lt;code&gt;openssl x509 -in cert.crt -noout -text&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check expiry dates&lt;/td&gt;
&lt;td&gt;&lt;code&gt;openssl x509 -in cert.crt -noout -dates&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PEM to DER&lt;/td&gt;
&lt;td&gt;&lt;code&gt;openssl x509 -in cert.pem -outform der -out cert.der&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Export PKCS#12&lt;/td&gt;
&lt;td&gt;&lt;code&gt;openssl pkcs12 -export -out b.pfx -inkey d.key -in d.crt&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test TLS&lt;/td&gt;
&lt;td&gt;&lt;code&gt;openssl s_client -connect host:443 -servername host&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;unable to load Private Key&lt;/strong&gt;&lt;br&gt;
The key file is encrypted and OpenSSL expected an unencrypted one, or the file is corrupt. Add the passphrase when prompted, or regenerate the key with &lt;code&gt;-noenc&lt;/code&gt; if the service cannot supply a passphrase.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;verify error:num=18:self signed certificate&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;s_client&lt;/code&gt; is reporting that the chain ends in a self-signed certificate. This is normal for self-signed certificates and for testing. For a public site, install the full chain issued by your CA.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;verify error:num=20:unable to get local issuer certificate&lt;/strong&gt;&lt;br&gt;
The server did not send the intermediate certificate. Concatenate your certificate and the CA intermediate into one file and serve that combined file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key and certificate modulus do not match&lt;/strong&gt;&lt;br&gt;
The certificate was not issued for this key. Re-check that you generated the CSR from the same key the CA signed, then compare modulus hashes as shown above.&lt;/p&gt;
&lt;h2 id="faq"&gt;FAQ &lt;a class="headline-link" href="#faq" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;What is the difference between a CSR and a certificate?&lt;/strong&gt;&lt;br&gt;
A CSR contains your public key and identity details and is sent to a certificate authority. The CA returns a signed certificate. The CSR is the request; the certificate is the issued result.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Should I use RSA or elliptic-curve keys?&lt;/strong&gt;&lt;br&gt;
Elliptic-curve keys (such as &lt;code&gt;prime256v1&lt;/code&gt;) are smaller and faster at equivalent security and are well supported. RSA 2048 remains a safe, widely compatible default. Either is fine for most servers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How do I remove a passphrase from a key?&lt;/strong&gt;&lt;br&gt;
Run &lt;code&gt;openssl rsa -in encrypted.key -out plain.key&lt;/code&gt;. OpenSSL prompts for the current passphrase and writes an unencrypted copy. Protect that file carefully, since it is no longer passphrase-protected.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why does my self-signed certificate trigger browser warnings?&lt;/strong&gt;&lt;br&gt;
Browsers only trust certificates signed by a recognized authority. Self-signed certificates are not in that trust store, so the warning is expected. Use Let&amp;rsquo;s Encrypt for public sites.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;OpenSSL handles the full lifecycle of TLS material: keys, requests, certificates, format conversions, and live connection testing. When you move from generating certificates to serving them, use a trusted CA and keep renewal on a schedule so certificates do not expire unnoticed.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/how-to-use-openssl/featured_hu_96a70bf090c1adfa.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>npm Cheatsheet</title><link>https://linuxize.com/cheatsheet/npm/</link><pubDate>Fri, 05 Jun 2026 10:20:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/cheatsheet/npm/</guid><description>Quick reference for npm commands for project setup, dependency management, scripts, updates, and package inspection</description><content:encoded>&lt;div class="card"&gt;
&lt;h3 id="project-setup"&gt;Project Setup &lt;a class="headline-link" href="#project-setup" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Create and inspect project metadata.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/npm-command/"&gt;&lt;code&gt;npm init&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Create package.json interactively&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm init -y&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create package.json with defaults&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm pkg get name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print package name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm pkg set scripts.start=&amp;quot;node index.js&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add or update a script&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm pkg delete scripts.old&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove a package.json field&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="install-dependencies"&gt;Install Dependencies &lt;a class="headline-link" href="#install-dependencies" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Add project, development, and global packages.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm install&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install project dependencies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/npm-command/"&gt;&lt;code&gt;npm install package&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Install and save dependency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm install -D package&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install development dependency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm install -g package&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install global CLI package&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm install package@1.2.3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install a specific version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm install package@latest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install latest published version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm install --no-save package&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install without saving&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="clean-installs"&gt;Clean Installs &lt;a class="headline-link" href="#clean-installs" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Reproduce dependency trees from lock files.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm ci&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Clean install from package-lock.json&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm ci --omit=dev&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install production dependencies only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm install --omit=dev&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Skip development dependencies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm install --package-lock-only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Update lock file only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm install --ignore-scripts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install without lifecycle scripts&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="versions-and-updates"&gt;Versions and Updates &lt;a class="headline-link" href="#versions-and-updates" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Check outdated packages and update dependency ranges.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm outdated&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show packages with newer versions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm update&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Update within package.json ranges&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm update package&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Update one package&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm view package version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show latest package version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm view package versions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List published versions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm version patch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bump package patch version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm version minor&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bump package minor version&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="remove-and-clean"&gt;Remove and Clean &lt;a class="headline-link" href="#remove-and-clean" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Uninstall packages and clear generated files.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm uninstall package&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove dependency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm uninstall -D package&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove development dependency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm uninstall -g package&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove global package&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm prune&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove extraneous packages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm dedupe&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reduce duplicate dependencies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm cache verify&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Verify npm cache&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm cache clean --force&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Clear npm cache&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="scripts-and-execution"&gt;Scripts and Execution &lt;a class="headline-link" href="#scripts-and-execution" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Run package scripts and one-off tools.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm run&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List available scripts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/npm-command/"&gt;&lt;code&gt;npm run script&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Run a named script&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm start&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run the start script&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm test&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run the test script&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm run build -- --watch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pass args to script&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npx package&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run package binary once&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm create vite@latest app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run create package&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="inspect-packages"&gt;Inspect Packages &lt;a class="headline-link" href="#inspect-packages" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;List dependencies and package metadata.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List direct dependencies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm list --all&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List full dependency tree&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm list -g --depth=0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List global packages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm explain package&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Explain why package is installed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm root&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show local node_modules path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm root -g&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show global node_modules path&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="configuration"&gt;Configuration &lt;a class="headline-link" href="#configuration" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;View and change npm settings.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm config list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show current config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm config get prefix&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show global install prefix&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm config set prefix ~/.npm-global&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set global install prefix&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm config get registry&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show registry URL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm config set registry url&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set registry URL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm whoami&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show logged-in npm user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm login&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Log in to npm registry&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="security-and-audit"&gt;Security and Audit &lt;a class="headline-link" href="#security-and-audit" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Check dependency risks and funding metadata.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm audit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Scan dependencies for advisories&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm audit fix&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Apply compatible security fixes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm audit --omit=dev&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Audit production dependencies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm fund&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show funding links&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm doctor&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check npm environment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm ping&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Test registry connection&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="related-guides"&gt;Related Guides &lt;a class="headline-link" href="#related-guides" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Use these references for Node.js and package workflows.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Guide&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/npm-command/"&gt;&lt;code&gt;npm Command&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Full npm tutorial with examples&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-install-node-js-on-ubuntu-26-04/"&gt;&lt;code&gt;Install Node.js on Ubuntu 26.04&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Install Node.js and npm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-install-yarn-on-ubuntu-20-04/"&gt;&lt;code&gt;Install Yarn on Ubuntu 20.04&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Alternative package manager&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;</content:encoded></item><item><title>npm Command: Install and Manage Node.js Packages</title><link>https://linuxize.com/post/npm-command/</link><pubDate>Fri, 05 Jun 2026 09:30:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/npm-command/</guid><category>nodejs</category><category>linux commands</category><description>This npm command guide covers project setup, local and global package installs, npm ci, updates, dependency removal, scripts, and one-off package runs.</description><content:encoded>&lt;p&gt;When you start or clone a Node.js project, one of the first things you usually do is install its packages. &lt;code&gt;npm&lt;/code&gt; is the default package manager that ships with Node.js, and it is the command you use to add, update, remove, and run project dependencies. It reads and writes the &lt;code&gt;package.json&lt;/code&gt; file that records what your project needs.&lt;/p&gt;
&lt;p&gt;This guide walks through the &lt;code&gt;npm&lt;/code&gt; commands you will use most often: starting a project, installing local and global packages, pinning versions, updating and removing dependencies, and running project scripts.&lt;/p&gt;
&lt;h2 id="syntax"&gt;Syntax &lt;a class="headline-link" href="#syntax" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The general form of the command is:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="txt"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;txt&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm &amp;lt;command&amp;gt; [args] [options]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;npm&lt;/code&gt; ships with Node.js, so installing Node.js installs &lt;code&gt;npm&lt;/code&gt; too. If you have not set up Node.js yet, see our guide on &lt;a href="https://linuxize.com/post/how-to-install-node-js-on-ubuntu-26-04/"&gt;installing Node.js on Ubuntu 26.04&lt;/a&gt;
. Before we run package commands, confirm both tools are available:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;node --version
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm --version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;v24.16.0
11.13.0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If &lt;code&gt;npm&lt;/code&gt; reports a version, you are ready to go.&lt;/p&gt;
&lt;h2 id="initialize-a-project"&gt;Initialize a Project &lt;a class="headline-link" href="#initialize-a-project" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Every npm project centers on a &lt;code&gt;package.json&lt;/code&gt; file. This file lists the project dependencies, scripts, package name, version, and other metadata. Create one in the current directory with:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm init&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;npm&lt;/code&gt; asks a series of questions (name, version, entry point, and so on) and writes your answers to &lt;code&gt;package.json&lt;/code&gt;. To accept all the defaults without prompts, add the &lt;code&gt;-y&lt;/code&gt; flag:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm init -y&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This is the usual starting point for a new project. Once &lt;code&gt;package.json&lt;/code&gt; exists, later &lt;code&gt;npm install&lt;/code&gt; commands have a file where they can record dependencies.&lt;/p&gt;
&lt;h2 id="install-packages"&gt;Install Packages &lt;a class="headline-link" href="#install-packages" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To add a package to your project, run &lt;code&gt;npm install&lt;/code&gt; with the package name:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm install express&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This downloads &lt;code&gt;express&lt;/code&gt; into a &lt;code&gt;node_modules&lt;/code&gt; directory and records it under &lt;code&gt;dependencies&lt;/code&gt; in &lt;code&gt;package.json&lt;/code&gt;. npm also creates or updates &lt;code&gt;package-lock.json&lt;/code&gt;, which records the exact versions that were installed.&lt;/p&gt;
&lt;p&gt;For tools you only need while developing, such as test runners or linters, save them as development dependencies with &lt;code&gt;-D&lt;/code&gt; (short for &lt;code&gt;--save-dev&lt;/code&gt;):&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm install -D eslint&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;These land under &lt;code&gt;devDependencies&lt;/code&gt; and are skipped when a project is installed for production with &lt;code&gt;npm install --omit=dev&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;When you clone an existing project, you do not install each package by hand. Run &lt;code&gt;install&lt;/code&gt; with no arguments from the project directory:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm install&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;npm&lt;/code&gt; reads &lt;code&gt;package.json&lt;/code&gt; and the lock file, installs the missing packages, and updates &lt;code&gt;package-lock.json&lt;/code&gt; when dependency ranges need to be resolved. This is the command you usually run during local development.&lt;/p&gt;
&lt;p&gt;For CI jobs, clean deploys, and other repeatable installs, use &lt;code&gt;npm ci&lt;/code&gt; instead:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm ci&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;npm ci&lt;/code&gt; requires an existing &lt;code&gt;package-lock.json&lt;/code&gt;, removes any current &lt;code&gt;node_modules&lt;/code&gt; directory, and installs the exact dependency tree from the lock file without changing &lt;code&gt;package.json&lt;/code&gt; or &lt;code&gt;package-lock.json&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="install-global-packages"&gt;Install Global Packages &lt;a class="headline-link" href="#install-global-packages" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Some packages are command-line tools you may want available everywhere instead of tied to one project. Install them globally with &lt;code&gt;-g&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo npm install -g http-server&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The tool is then on your &lt;code&gt;PATH&lt;/code&gt; and can be run from any directory. Reserve global installs for command-line utilities. Project dependencies should stay local so each project controls its own versions.&lt;/p&gt;
&lt;div class="note callout callout-tip"&gt;
&lt;div class="callout-header"&gt;&lt;svg role="img" aria-hidden="true" class="callout-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50"&gt;
&lt;path d="M25 6c-1.645 0-3 1.355-3 3v1.406c-1.945.457-3.645 1.36-4.953 2.676C15.094 15.055 14 17.848 14 21.062v7.801c0 1.836-1.004 4.164-2.04 5.89l-1.792 2.692a1.01 1.01 0 0 0-.05 1.028c.175.324.515.527.882.527h9c0 2.746 2.254 5 5 5s5-2.254 5-5h9c.367 0 .707-.203.883-.527a1.01 1.01 0 0 0-.051-1.028l-1.785-2.68-.004-.003C36.996 33.016 36 30.836 36 29v-7.8c0-5.368-3.195-9.524-8-10.759V9c0-1.645-1.355-3-3-3zm0 2c.555 0 1 .445 1 1v1.113c-.223-.02-.441-.043-.668-.05A1.064 1.064 0 0 0 25 10c-.11 0-.215.02-.316.059-.235.004-.457.027-.684.043V9c0-.555.445-1 1-1zM3.48 9.477C1.25 13.102 0 17.418 0 22s1.25 8.898 3.48 12.523l1.708-1.046C3.151 30.168 2 26.219 2 22s1.152-8.168 3.188-11.477zm43.04 0l-1.708 1.046C46.849 13.832 48 17.781 48 22s-1.152 8.168-3.188 11.477l1.708 1.046C48.75 30.898 50 26.582 50 22s-1.25-8.898-3.48-12.523zM25 12c5.512 0 9 3.668 9 9.2V29c0 2.512 1.203 4.918 2.328 6.797.012.012.02.027.027.039L37.13 37H12.87l.774-1.164c.007-.012.015-.027.027-.04C14.809 33.903 16 31.376 16 28.864v-7.8c0-2.766.914-5.004 2.469-6.57C20.019 12.925 22.239 12 25 12zm-17.184.14C5.996 15.083 5 18.356 5 22c0 3.672 1.129 7.047 2.809 9.848l1.714-1.032C8.008 28.286 7 25.262 7 22c0-3.29.871-6.148 2.516-8.809zm34.368 0l-1.7 1.051C42.13 15.851 43 18.711 43 22c0 3.262-1.008 6.285-2.527 8.816l1.718 1.032C43.871 29.047 45 25.672 45 22c0-3.645-.996-6.918-2.816-9.86zM22 39h6a3 3 0 0 1-6 0z"/&gt;
&lt;/svg&gt;
&lt;span class="callout-title"&gt;Tip&lt;/span&gt;&lt;/div&gt;
&lt;div class="callout-body"&gt;Using &lt;code&gt;sudo&lt;/code&gt; for global installs works but can create permission headaches. A cleaner approach is to manage Node.js with a version manager such as nvm, which installs global packages under your home directory and removes the need for &lt;code&gt;sudo&lt;/code&gt;.&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="install-a-specific-version"&gt;Install a Specific Version &lt;a class="headline-link" href="#install-a-specific-version" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When a project needs a specific package release, append &lt;code&gt;@&lt;/code&gt; and the version number:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm install express@4.18.2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You can also install by tag, such as &lt;code&gt;npm install express@latest&lt;/code&gt; for the newest release or &lt;code&gt;@next&lt;/code&gt; for a prerelease line. Pinning a specific version is useful when you need to match an application requirement or avoid a breaking change in a newer release.&lt;/p&gt;
&lt;h2 id="update-and-inspect-packages"&gt;Update and Inspect Packages &lt;a class="headline-link" href="#update-and-inspect-packages" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before updating packages, check what is currently installed and what newer versions are available:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm outdated&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Package Current Wanted Latest Location
express 4.18.0 4.18.2 4.19.2 node_modules/express&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;Wanted&lt;/code&gt; column is the newest version allowed by the range in &lt;code&gt;package.json&lt;/code&gt;, while &lt;code&gt;Latest&lt;/code&gt; is the newest published. Update packages to the wanted versions with:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm update&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If you want to move to a new major version beyond your declared range, install that version explicitly as shown above.&lt;/p&gt;
&lt;h2 id="remove-packages"&gt;Remove Packages &lt;a class="headline-link" href="#remove-packages" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When a dependency is no longer needed, uninstall it and remove it from &lt;code&gt;package.json&lt;/code&gt; with:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm uninstall express&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;npm&lt;/code&gt; removes the package from &lt;code&gt;node_modules&lt;/code&gt; and updates both &lt;code&gt;package.json&lt;/code&gt; and the lock file. For a global package, add &lt;code&gt;-g&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo npm uninstall -g http-server&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="run-scripts"&gt;Run Scripts &lt;a class="headline-link" href="#run-scripts" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;scripts&lt;/code&gt; section of &lt;code&gt;package.json&lt;/code&gt; defines named commands for common project tasks. A typical file might include:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="json"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-amber-100 text-amber-700 dark:bg-amber-900 dark:text-amber-300"&gt;json&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;scripts&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;start&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;node index.js&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;test&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;jest&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Run a script with &lt;code&gt;npm run&lt;/code&gt; followed by its name:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm run start&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;start&lt;/code&gt; and &lt;code&gt;test&lt;/code&gt; names are special and can be run without &lt;code&gt;run&lt;/code&gt;, so &lt;code&gt;npm start&lt;/code&gt; and &lt;code&gt;npm test&lt;/code&gt; both work. Scripts are how most projects expose their build, test, lint, and development commands.&lt;/p&gt;
&lt;h2 id="list-installed-packages"&gt;List Installed Packages &lt;a class="headline-link" href="#list-installed-packages" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When you need to inspect what is installed in the current project, use:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm list&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;By default, recent npm versions show the top-level project and its direct dependencies. If you need to inspect the full nested tree, add &lt;code&gt;--all&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm list --all&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Add &lt;code&gt;-g&lt;/code&gt; to list globally installed packages instead.&lt;/p&gt;
&lt;h2 id="run-a-package-without-installing-it"&gt;Run a Package Without Installing It &lt;a class="headline-link" href="#run-a-package-without-installing-it" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;npx&lt;/code&gt; command, bundled with &lt;code&gt;npm&lt;/code&gt;, runs a package binary without a permanent global install. Use it when you need a one-off tool but do not want to add it to the project:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npx prettier@latest --check .&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;npx&lt;/code&gt; downloads the package if needed, runs it, and does not leave a global install behind. For project scaffolding, many packages also support the &lt;code&gt;npm create&lt;/code&gt; form. For example, to start a Vite React project, run:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm create vite@latest my-app -- --template react&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For a printable quick reference, see the &lt;a href="https://linuxize.com/cheatsheet/npm/"&gt;npm cheatsheet&lt;/a&gt;
.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Create package.json&lt;/td&gt;
&lt;td&gt;&lt;code&gt;npm init -y&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Install a package&lt;/td&gt;
&lt;td&gt;&lt;code&gt;npm install &amp;lt;pkg&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Install as dev dependency&lt;/td&gt;
&lt;td&gt;&lt;code&gt;npm install -D &amp;lt;pkg&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Install all dependencies&lt;/td&gt;
&lt;td&gt;&lt;code&gt;npm install&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clean install from lock file&lt;/td&gt;
&lt;td&gt;&lt;code&gt;npm ci&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Install globally&lt;/td&gt;
&lt;td&gt;&lt;code&gt;npm install -g &amp;lt;pkg&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Install a specific version&lt;/td&gt;
&lt;td&gt;&lt;code&gt;npm install &amp;lt;pkg&amp;gt;@1.2.3&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Show outdated packages&lt;/td&gt;
&lt;td&gt;&lt;code&gt;npm outdated&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Update packages&lt;/td&gt;
&lt;td&gt;&lt;code&gt;npm update&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remove a package&lt;/td&gt;
&lt;td&gt;&lt;code&gt;npm uninstall &amp;lt;pkg&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run a script&lt;/td&gt;
&lt;td&gt;&lt;code&gt;npm run &amp;lt;script&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List installed packages&lt;/td&gt;
&lt;td&gt;&lt;code&gt;npm list&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List full dependency tree&lt;/td&gt;
&lt;td&gt;&lt;code&gt;npm list --all&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run without installing&lt;/td&gt;
&lt;td&gt;&lt;code&gt;npx &amp;lt;pkg&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;EACCES: permission denied during a global install&lt;/strong&gt;&lt;br&gt;
A global install is trying to write to a system directory your user cannot modify. Either prefix the command with &lt;code&gt;sudo&lt;/code&gt;, or, better, manage Node.js with nvm so global packages install under your home directory without root permissions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A script exits with an npm error&lt;/strong&gt;&lt;br&gt;
The script ran but one of the commands inside it exited with an error. Scroll up to the actual command output above the npm error block; the underlying tool, not npm, is usually reporting the failure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Conflicting peer dependency errors on install&lt;/strong&gt;&lt;br&gt;
A package expects a different version of a shared dependency than what is installed. Resolve the version mismatch where possible, or, as a temporary measure, retry with &lt;code&gt;npm install --legacy-peer-deps&lt;/code&gt; while you sort out the conflict.&lt;/p&gt;
&lt;h2 id="faq"&gt;FAQ &lt;a class="headline-link" href="#faq" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;What is the difference between dependencies and devDependencies?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;dependencies&lt;/code&gt; are packages your application needs to run. &lt;code&gt;devDependencies&lt;/code&gt; are needed only during development, such as test frameworks and linters, and are installed with &lt;code&gt;-D&lt;/code&gt;. Production installs can skip them with &lt;code&gt;--omit=dev&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What does package-lock.json do?&lt;/strong&gt;&lt;br&gt;
It records the exact version of every installed package, including nested dependencies. Commit it to version control, then use &lt;code&gt;npm ci&lt;/code&gt; when you need an exact lock-file install in CI or deployment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What is the difference between npm and npx?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;npm&lt;/code&gt; installs and manages packages, while &lt;code&gt;npx&lt;/code&gt; runs a package&amp;rsquo;s executable, downloading it temporarily if it is not installed. Use &lt;code&gt;npx&lt;/code&gt; for one-off commands and scaffolding tools.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How do I install a package without saving it to package.json?&lt;/strong&gt;&lt;br&gt;
Add the &lt;code&gt;--no-save&lt;/code&gt; flag, for example &lt;code&gt;npm install --no-save &amp;lt;pkg&amp;gt;&lt;/code&gt;. The package is installed into &lt;code&gt;node_modules&lt;/code&gt; but the change is not recorded in &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A small set of commands, &lt;code&gt;npm install&lt;/code&gt;, &lt;code&gt;npm ci&lt;/code&gt;, &lt;code&gt;npm update&lt;/code&gt;, &lt;code&gt;npm uninstall&lt;/code&gt;, and &lt;code&gt;npm run&lt;/code&gt;, covers nearly all package work in a Node.js project. Keep &lt;code&gt;package-lock.json&lt;/code&gt; committed, and use &lt;code&gt;npm ci&lt;/code&gt; whenever repeatable installs matter. For a package manager that uses a shared content-addressable store and built-in workspace filtering, see the &lt;a href="https://linuxize.com/post/pnpm-command/"&gt;pnpm command guide&lt;/a&gt;
.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/npm-command/featured_hu_75a7c579e9d840cf.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>Fix "sudo: command not found" on Linux</title><link>https://linuxize.com/post/fix-sudo-command-not-found/</link><pubDate>Thu, 04 Jun 2026 09:35:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/fix-sudo-command-not-found/</guid><category>sudo</category><category>linux commands</category><description>How to fix the 'sudo: command not found' error on Linux by installing the sudo package, adding your user to the right group, and recovering from a broken PATH.</description><content:encoded>&lt;p&gt;You log in to a fresh server, try to install a package, and the shell answers with &lt;code&gt;sudo: command not found&lt;/code&gt;. It is a confusing message, because &lt;code&gt;sudo&lt;/code&gt; feels like a built-in part of Linux. In reality &lt;code&gt;sudo&lt;/code&gt; is a regular package that some minimal images and container base layers skip, and it is also one of the first commands to disappear when your shell environment is broken.&lt;/p&gt;
&lt;p&gt;This guide explains the three reasons the error shows up: &lt;code&gt;sudo&lt;/code&gt; is not installed, your user is not allowed to run it, or your &lt;code&gt;PATH&lt;/code&gt; no longer points to &lt;code&gt;/usr/bin&lt;/code&gt;. Each case has a clean fix.&lt;/p&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What to check&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;What to do&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Is sudo installed?&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ls -l /usr/bin/sudo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install the &lt;code&gt;sudo&lt;/code&gt; package as root if the file is missing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Is your user allowed to use sudo?&lt;/td&gt;
&lt;td&gt;&lt;code&gt;groups username&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add the user to &lt;code&gt;sudo&lt;/code&gt; on Debian/Ubuntu or &lt;code&gt;wheel&lt;/code&gt; on Fedora/RHEL/Arch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Is PATH broken?&lt;/td&gt;
&lt;td&gt;&lt;code&gt;echo $PATH&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Restore &lt;code&gt;/usr/bin&lt;/code&gt;, &lt;code&gt;/usr/sbin&lt;/code&gt;, &lt;code&gt;/bin&lt;/code&gt;, and &lt;code&gt;/sbin&lt;/code&gt; in your shell startup file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Does sudo work by absolute path?&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/usr/bin/sudo whoami&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fix the shell &lt;code&gt;PATH&lt;/code&gt; if this works but &lt;code&gt;sudo whoami&lt;/code&gt; fails&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="confirm-the-real-cause"&gt;Confirm the Real Cause &lt;a class="headline-link" href="#confirm-the-real-cause" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before installing anything, check whether the &lt;code&gt;sudo&lt;/code&gt; binary exists on the system:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ls -l /usr/bin/sudo&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;-rwsr-xr-x 1 root root 277936 Jan 17 09:42 /usr/bin/sudo&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If the file is there with the SUID bit (&lt;code&gt;s&lt;/code&gt; in the owner permissions), &lt;code&gt;sudo&lt;/code&gt; is installed and the error is environmental. If the file is missing, you need to install the package as the root user. Pick the section below that matches your situation.&lt;/p&gt;
&lt;h2 id="install-sudo-on-debian-ubuntu-and-derivatives"&gt;Install sudo on Debian, Ubuntu, and Derivatives &lt;a class="headline-link" href="#install-sudo-on-debian-ubuntu-and-derivatives" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Minimal Debian installs and many Docker base images leave &lt;code&gt;sudo&lt;/code&gt; out. Switch to the root account first, because without &lt;code&gt;sudo&lt;/code&gt; you cannot escalate privileges from a normal user:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;su -&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Enter the root password, then install the package with &lt;code&gt;apt&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;apt update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;apt install sudo&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If &lt;code&gt;su&lt;/code&gt; itself is rejected because root has no password (the default on Ubuntu desktop installs), boot into recovery mode or use a live USB to set a root password before retrying.&lt;/p&gt;
&lt;h2 id="install-sudo-on-fedora-rhel-and-derivatives"&gt;Install sudo on Fedora, RHEL, and Derivatives &lt;a class="headline-link" href="#install-sudo-on-fedora-rhel-and-derivatives" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;On Fedora, RHEL, Rocky Linux, and AlmaLinux, switch to root and install with &lt;code&gt;dnf&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;su -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dnf install sudo&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On older Red Hat-family systems you may see &lt;code&gt;yum&lt;/code&gt; referenced in tutorials; &lt;code&gt;dnf&lt;/code&gt; is the current package manager and the right choice on every supported release.&lt;/p&gt;
&lt;h2 id="install-sudo-on-arch-linux"&gt;Install sudo on Arch Linux &lt;a class="headline-link" href="#install-sudo-on-arch-linux" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Arch ships a minimal base, and &lt;code&gt;sudo&lt;/code&gt; is a separate package:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;su -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pacman -S sudo&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="install-sudo-on-alpine-linux"&gt;Install sudo on Alpine Linux &lt;a class="headline-link" href="#install-sudo-on-alpine-linux" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Alpine, which is common inside containers, uses &lt;code&gt;apk&lt;/code&gt;. Two options exist: the full &lt;code&gt;sudo&lt;/code&gt; package, or the lighter &lt;code&gt;doas&lt;/code&gt; replacement.&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;su -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;apk add sudo&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If you only need privilege escalation inside a container and want to keep the image small, &lt;code&gt;apk add doas&lt;/code&gt; followed by a one-line &lt;code&gt;/etc/doas.conf&lt;/code&gt; is often enough.&lt;/p&gt;
&lt;h2 id="add-your-user-to-the-sudo-group"&gt;Add Your User to the sudo Group &lt;a class="headline-link" href="#add-your-user-to-the-sudo-group" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Installing the package is only half the job. By default, only users that belong to a specific administrative group can run &lt;code&gt;sudo&lt;/code&gt;. The group name varies by distribution.&lt;/p&gt;
&lt;p&gt;On Debian and Ubuntu, the group is &lt;code&gt;sudo&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;usermod -aG sudo sara&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On Fedora, RHEL, and Arch, the group is &lt;code&gt;wheel&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;usermod -aG wheel sara&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Group changes only take effect on a new login session. Log out and back in, then verify membership:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;groups sara&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;sara : sara sudo&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The presence of &lt;code&gt;sudo&lt;/code&gt; (or &lt;code&gt;wheel&lt;/code&gt;) in the list confirms the user can now run privileged commands.&lt;/p&gt;
&lt;h2 id="fix-a-broken-path"&gt;Fix a Broken PATH &lt;a class="headline-link" href="#fix-a-broken-path" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If &lt;code&gt;/usr/bin/sudo&lt;/code&gt; exists but the shell still reports &amp;ldquo;command not found&amp;rdquo;, the problem is your &lt;a href="https://linuxize.com/post/how-to-add-directory-to-path-in-linux/"&gt;&lt;code&gt;PATH&lt;/code&gt;&lt;/a&gt;
. A typo in &lt;code&gt;~/.bashrc&lt;/code&gt; or &lt;code&gt;~/.zshrc&lt;/code&gt; can wipe out the standard directories. Check the current value:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;/home/sara/bin:/home/sara/.local/bin&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When &lt;code&gt;/usr/bin&lt;/code&gt; and &lt;code&gt;/usr/local/bin&lt;/code&gt; are missing, the shell cannot find any of the system binaries. Restore a sane &lt;code&gt;PATH&lt;/code&gt; for the current session by calling &lt;code&gt;sudo&lt;/code&gt; with its absolute path:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/usr/bin/sudo apt update&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;That works as a one-off, but the durable fix is to repair the shell config. Open the file that broke the variable:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nano ~/.bashrc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;A safe baseline looks like this:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;~/.bashrc&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/bin:&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The key detail is keeping &lt;code&gt;$PATH&lt;/code&gt; itself in any line that extends it. A line like &lt;code&gt;export PATH=$HOME/bin&lt;/code&gt; (without &lt;code&gt;:$PATH&lt;/code&gt;) overwrites everything else and is the most common cause of this failure.&lt;/p&gt;
&lt;p&gt;Source the file and try again:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ~/.bashrc
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo whoami&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;root&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A &lt;code&gt;root&lt;/code&gt; response confirms &lt;code&gt;sudo&lt;/code&gt; is back on the path and working.&lt;/p&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;sara is not in the sudoers file. This incident will be reported.&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
The user exists but is not authorized. Switch to root and either add the user to the admin group (as shown above) or edit &lt;code&gt;/etc/sudoers&lt;/code&gt; with &lt;code&gt;visudo&lt;/code&gt; and add a line such as &lt;code&gt;sara ALL=(ALL:ALL) ALL&lt;/code&gt;. Never edit &lt;code&gt;/etc/sudoers&lt;/code&gt; directly; &lt;code&gt;visudo&lt;/code&gt; validates syntax and prevents lockout.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No root password and no sudo&lt;/strong&gt;&lt;br&gt;
On Ubuntu, root is disabled by default. Boot into the GRUB recovery menu, drop to a root shell, and run &lt;code&gt;passwd root&lt;/code&gt; to set a password. Then install &lt;code&gt;sudo&lt;/code&gt; and add your user to the group as shown above.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;sudo: /etc/sudo.conf is owned by uid 1000, should be 0&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
File ownership in &lt;code&gt;/etc&lt;/code&gt; was changed by mistake. As root, restore it with &lt;code&gt;chown root:root /etc/sudo.conf /etc/sudoers&lt;/code&gt; and &lt;code&gt;chmod 440 /etc/sudoers&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="faq"&gt;FAQ &lt;a class="headline-link" href="#faq" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Why does sudo work in one shell but not another?&lt;/strong&gt;&lt;br&gt;
The two shells likely load different startup files. Compare &lt;code&gt;echo $PATH&lt;/code&gt; in each, and check &lt;code&gt;~/.bashrc&lt;/code&gt;, &lt;code&gt;~/.bash_profile&lt;/code&gt;, &lt;code&gt;~/.profile&lt;/code&gt;, and &lt;code&gt;~/.zshrc&lt;/code&gt; for conflicting &lt;code&gt;PATH&lt;/code&gt; assignments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Does adding a user to the sudo group take effect right away?&lt;/strong&gt;&lt;br&gt;
No. Group membership is read at login. Log out and back in, or start a new login shell with &lt;code&gt;su - sara&lt;/code&gt; to pick up the change.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Is doas a real replacement for sudo?&lt;/strong&gt;&lt;br&gt;
For single-user systems and most container images, yes. &lt;code&gt;doas&lt;/code&gt; is smaller and configured through a much shorter file. Multi-user servers with detailed access rules are still better served by &lt;code&gt;sudo&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &amp;ldquo;command not found&amp;rdquo; message points to one of three problems: a missing package, a missing group membership, or a broken &lt;code&gt;PATH&lt;/code&gt;. Walking through them in that order resolves the error on almost every Linux system.&lt;/p&gt;
&lt;p&gt;For follow-up reading, see our guides on &lt;a href="https://linuxize.com/post/how-to-create-a-sudo-user-on-ubuntu/"&gt;creating a sudo user&lt;/a&gt;
and the &lt;a href="https://linuxize.com/post/sudo-command-in-linux/"&gt;sudo command reference&lt;/a&gt;
.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/fix-sudo-command-not-found/featured_hu_3f612fff86459112.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>Podman Cheatsheet</title><link>https://linuxize.com/cheatsheet/podman/</link><pubDate>Wed, 03 Jun 2026 14:15:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/cheatsheet/podman/</guid><description>Quick reference for Podman commands and rootless container workflows</description><content:encoded>&lt;div class="card"&gt;
&lt;h3 id="container-lifecycle"&gt;Container Lifecycle &lt;a class="headline-link" href="#container-lifecycle" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Create, start, stop, and remove containers.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman run image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create and start a container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman run -d image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run container in background&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman run -it image sh&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run with an interactive shell&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman run --name web image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run with a custom name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman run -p 8080:80 image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Map host port to container port&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman run -v /host:/container image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bind mount a host path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman start container&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start a stopped container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman stop container&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Gracefully stop a container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman restart container&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Restart a container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman rm container&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove a stopped container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman rm -f container&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Force remove a running container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman kill container&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Kill a container with SIGKILL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman pause container&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pause a running container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman unpause container&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Resume a paused container&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="container-inspection"&gt;Container Inspection &lt;a class="headline-link" href="#container-inspection" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;List containers, inspect state, and watch resource use.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman ps&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List running containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman ps -a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List all containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman ps -q&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show only container IDs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman inspect container&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show detailed JSON info&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman inspect --format '{{.State.Status}}' container&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print one field&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman top container&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List processes in a container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman stats&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show live resource usage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman stats --no-stream&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print one usage snapshot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman port container&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show port mappings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman diff container&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show filesystem changes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="logs"&gt;Logs &lt;a class="headline-link" href="#logs" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Read and follow container logs.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman logs container&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;View container logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman logs -f container&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Follow live logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman logs --tail 50 container&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the last 50 lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman logs -t container&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add timestamps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman logs --since 10m container&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show recent logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman logs --until 2026-06-03T12:00:00 container&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stop at a time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;journalctl --user -u name.service&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;View logs for a user service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo journalctl -u name.service&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;View logs for a system service&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="images-and-builds"&gt;Images and Builds &lt;a class="headline-link" href="#images-and-builds" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Pull, build, tag, and manage images. Podman accepts Dockerfiles and Containerfiles.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman pull image:tag&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pull image from a registry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman pull docker.io/library/nginx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pull with a full image name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman push image:tag&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Push image to a registry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-build-docker-images-with-dockerfile/"&gt;&lt;code&gt;podman build -t name .&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Build image from Dockerfile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman build -f Containerfile .&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build with a custom file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman images&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List local images&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman rmi image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove an image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman tag source target:tag&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tag an image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman save image &amp;gt; image.tar&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Save image to tar archive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman load &amp;lt; image.tar&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Load image from tar archive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman history image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show image layer history&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="exec-and-copy"&gt;Exec and Copy &lt;a class="headline-link" href="#exec-and-copy" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Run commands inside containers and copy files.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman exec -it container sh&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Open shell in container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman exec -it container bash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Open Bash if installed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman exec container command&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run command in container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman exec -u root container command&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run as a specific user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman cp ./file container:/path/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Copy file into container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman cp container:/path/file ./&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Copy file from container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman attach container&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Attach to main process&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman wait container&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Wait for container to stop&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="volumes"&gt;Volumes &lt;a class="headline-link" href="#volumes" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Manage persistent container data.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman volume create data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a named volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman volume ls&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List volumes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman volume inspect data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show volume details&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman volume rm data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove a volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman volume prune&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove unused volumes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman run -v data:/data image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mount named volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman run -v /host:/data image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bind mount host directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman run -v /host:/data:Z image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bind mount with SELinux relabeling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman run --tmpfs /tmp image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mount tmpfs in a container&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="networks"&gt;Networks &lt;a class="headline-link" href="#networks" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Create networks and connect containers.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/docker-networking/"&gt;&lt;code&gt;podman network create net&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Create a network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman network ls&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List networks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman network inspect net&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show network details&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman network rm net&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove a network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman network connect net container&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Connect container to network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman network disconnect net container&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Disconnect from network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman run --network net image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run container on network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman run --network host image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use host networking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman run --network none image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Disable networking&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="pods"&gt;Pods &lt;a class="headline-link" href="#pods" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Run groups of containers that share a network namespace.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman pod create --name app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a pod&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman pod create --name app -p 8080:80&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create pod with port mapping&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman run --pod app image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run container in pod&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman pod ps&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List pods&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman ps --pod&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show containers with pod info&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman pod inspect app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show pod details&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman pod stop app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stop all containers in pod&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman pod start app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start pod containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman pod rm app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove a stopped pod&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman pod rm -f app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Force remove a pod&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="rootless-and-systemd"&gt;Rootless and systemd &lt;a class="headline-link" href="#rootless-and-systemd" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Inspect rootless mode and manage Podman with systemd.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-install-podman-on-ubuntu/"&gt;&lt;code&gt;podman info&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Show Podman host configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman info --format '{{.Host.Security.Rootless}}'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check if rootless&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman system migrate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Apply user namespace changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;systemctl --user enable --now podman.socket&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start user Podman socket&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl enable --now podman.socket&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start system Podman socket&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo loginctl enable-linger $USER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Keep user services after logout&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;systemctl --user daemon-reload&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reload user units&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;systemctl --user status name.service&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check user service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl status name.service&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check system service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman auto-update&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Update containers with auto-update labels&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="compose"&gt;Compose &lt;a class="headline-link" href="#compose" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Run Compose projects with Podman.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman compose up&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start Compose project&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman compose up -d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start in background&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman compose down&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stop and remove services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman compose ps&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List Compose services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman compose logs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;View Compose logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman compose exec service sh&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Shell into a service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman-compose up -d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use podman-compose directly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Point Docker Compose at Podman&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker compose up -d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run Docker Compose against Podman socket&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="system-and-cleanup"&gt;System and Cleanup &lt;a class="headline-link" href="#system-and-cleanup" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Check disk usage and remove unused data.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman system df&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show storage usage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman system prune&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove unused data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman system prune --volumes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Also remove unused volumes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman image prune&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove dangling images&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman image prune -a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove unused images&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman container prune&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove stopped containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman volume prune&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove unused volumes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman network prune&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove unused networks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman system reset&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove all Podman storage&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="registry-and-login"&gt;Registry and Login &lt;a class="headline-link" href="#registry-and-login" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Authenticate and work with container registries.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman login docker.io&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Log in to Docker Hub&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman login registry.example.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Log in to private registry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman logout docker.io&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Log out from registry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman search term&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search configured registries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman pull user/image:tag&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pull image from registry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman push user/image:tag&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Push image to registry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman tag image registry/user/image:tag&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tag image for registry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman manifest create name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create manifest list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman manifest push name destination&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Push manifest list&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="docker-compatibility"&gt;Docker Compatibility &lt;a class="headline-link" href="#docker-compatibility" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Use Podman with Docker-style commands and tooling.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-install-podman-on-ubuntu/"&gt;&lt;code&gt;sudo apt install podman-docker&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Install Docker-compatible wrapper&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker ps&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run Podman through Docker wrapper&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;alias docker=podman&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add shell alias&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman --remote ps&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use remote Podman client&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman system service --time=0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start API service manually&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;podman info --format '{{.Host.RemoteSocket.Path}}'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show remote socket path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/podman-vs-docker/"&gt;&lt;code&gt;podman run -d -p 8080:80 nginx&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Docker-like run command&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;</content:encoded></item><item><title>Podman vs Docker: Differences and Migration Guide</title><link>https://linuxize.com/post/podman-vs-docker/</link><pubDate>Wed, 03 Jun 2026 12:40:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/podman-vs-docker/</guid><category>podman</category><category>docker</category><category>devops</category><description>This guide compares Podman and Docker across architecture, rootless containers, systemd integration, Compose workflows, image builds, and migration planning.</description><content:encoded>&lt;p&gt;For most of the last decade, &amp;ldquo;containers on Linux&amp;rdquo; meant Docker. The CLI, the image format, the workflow, and the muscle memory all came from one tool. Podman entered the picture as a drop-in alternative built around a different architecture: no daemon, rootless by default, and tighter integration with systemd.&lt;/p&gt;
&lt;p&gt;The two tools build and run OCI containers from the same images, expose nearly identical commands, and target the same workloads. The differences start to matter when you look at how containers are started, who owns them, how they run at boot, and what your existing tooling expects. This guide walks through how Podman and Docker compare in practice, and what a realistic migration looks like for a server already running Docker.&lt;/p&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For a printable quick reference, see the &lt;a href="https://linuxize.com/cheatsheet/podman/"&gt;Podman cheatsheet&lt;/a&gt;
.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Docker&lt;/th&gt;
&lt;th&gt;Podman&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Architecture&lt;/td&gt;
&lt;td&gt;Long-lived &lt;code&gt;dockerd&lt;/code&gt; daemon&lt;/td&gt;
&lt;td&gt;Daemonless, runs containers directly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Default privileges&lt;/td&gt;
&lt;td&gt;Root, with rootless as opt-in&lt;/td&gt;
&lt;td&gt;Rootless by default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;systemd integration&lt;/td&gt;
&lt;td&gt;Manual unit or restart policy&lt;/td&gt;
&lt;td&gt;First-class via Quadlet &lt;code&gt;.container&lt;/code&gt; files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pods&lt;/td&gt;
&lt;td&gt;Not available&lt;/td&gt;
&lt;td&gt;Native, Kubernetes-style pods&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image build backend&lt;/td&gt;
&lt;td&gt;Docker builder and BuildKit&lt;/td&gt;
&lt;td&gt;Buildah&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compose&lt;/td&gt;
&lt;td&gt;Docker Compose&lt;/td&gt;
&lt;td&gt;&lt;code&gt;podman compose&lt;/code&gt;, &lt;code&gt;podman-compose&lt;/code&gt;, or Compose over the Podman socket&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Default network backend&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bridge&lt;/code&gt; (&lt;code&gt;docker0&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;netavark&lt;/code&gt; (rootless uses &lt;code&gt;pasta&lt;/code&gt; or &lt;code&gt;slirp4netns&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image and CLI format&lt;/td&gt;
&lt;td&gt;OCI images, &lt;code&gt;docker&lt;/code&gt; CLI&lt;/td&gt;
&lt;td&gt;OCI images, &lt;code&gt;podman&lt;/code&gt; CLI plus &lt;code&gt;docker&lt;/code&gt; alias&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="same-image-format-same-cli-surface"&gt;Same Image Format, Same CLI Surface &lt;a class="headline-link" href="#same-image-format-same-cli-surface" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Both Podman and Docker work with OCI-compliant images and containers. Anything you pull from Docker Hub, quay.io, or a private registry runs the same way in either tool. The high-level commands also match:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman run -d -p 8080:80 --name web nginx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman ps
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman stop web
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman rm web&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Swap &lt;code&gt;podman&lt;/code&gt; for &lt;code&gt;docker&lt;/code&gt; in those lines and the result is the same. Podman ships a &lt;code&gt;docker&lt;/code&gt; alias as part of its package on most distributions, so existing scripts that call &lt;code&gt;docker ...&lt;/code&gt; continue to work without changes:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install podman-docker
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker ps&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For everyday commands, Podman is intentionally close to Docker. If your workflow is mostly &lt;code&gt;run&lt;/code&gt;, &lt;code&gt;ps&lt;/code&gt;, &lt;code&gt;logs&lt;/code&gt;, &lt;code&gt;exec&lt;/code&gt;, and &lt;code&gt;build&lt;/code&gt;, the commands will feel familiar. The larger differences sit underneath.&lt;/p&gt;
&lt;h2 id="daemon-vs-daemonless"&gt;Daemon vs Daemonless &lt;a class="headline-link" href="#daemon-vs-daemonless" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Docker runs a long-lived background daemon (&lt;code&gt;dockerd&lt;/code&gt;) that manages container state on the host. The &lt;code&gt;docker&lt;/code&gt; CLI is a client that talks to that daemon over a Unix socket. If the daemon is unavailable, you cannot create, stop, inspect, or attach to containers through the Docker CLI. Existing containers can keep running when Docker is configured with live restore, but the daemon is still the control plane for normal operations.&lt;/p&gt;
&lt;p&gt;Podman has no daemon. When you run &lt;code&gt;podman run&lt;/code&gt;, the CLI spawns the container directly using &lt;code&gt;runc&lt;/code&gt; or &lt;code&gt;crun&lt;/code&gt; as the OCI runtime, and exits. The container&amp;rsquo;s parent is your shell or whatever supervisor you choose, not a privileged background process.&lt;/p&gt;
&lt;p&gt;This has a few practical consequences:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There is no long-running container daemon to restart for ordinary CLI use. Updating Podman does not stop containers that are already running.&lt;/li&gt;
&lt;li&gt;Each container can be owned by a regular user account. The container&amp;rsquo;s processes show up in that user&amp;rsquo;s process tree, not under a root daemon.&lt;/li&gt;
&lt;li&gt;Tools that expect a Docker socket need an explicit Podman socket service if you want API compatibility.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For the Podman socket, you enable a user or system service:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl --user &lt;span class="nb"&gt;enable&lt;/span&gt; --now podman.socket&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The socket exposes a Docker-compatible REST API at &lt;code&gt;$XDG_RUNTIME_DIR/podman/podman.sock&lt;/code&gt;, which lets tools like &lt;code&gt;docker-compose&lt;/code&gt; or CI runners talk to Podman as if it were Docker.&lt;/p&gt;
&lt;h2 id="rootless-containers"&gt;Rootless Containers &lt;a class="headline-link" href="#rootless-containers" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Both tools support rootless containers, but the defaults differ. Docker historically runs as root, with rootless mode available as an opt-in setup that requires extra packages and a per-user installation. Podman is designed around rootless use. A regular user can pull images, build, and run containers without &lt;code&gt;sudo&lt;/code&gt; as soon as the user namespace prerequisites are in place.&lt;/p&gt;
&lt;p&gt;To run a container as a regular user with Podman:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman run -d -p 8080:80 --name web nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The container&amp;rsquo;s processes run as a UID inside a user namespace mapped to the invoking user on the host. From the host&amp;rsquo;s perspective, the container is owned by your user, not root, which limits the blast radius of a container escape and removes the need for the user to be in a &lt;code&gt;docker&lt;/code&gt; group (which is effectively equivalent to giving root).&lt;/p&gt;
&lt;p&gt;For most developer workstations and many production scenarios, rootless Podman is enough. Workloads that need privileged ports below 1024 or specific kernel features still benefit from a rootful install, but the default for new users is unprivileged.&lt;/p&gt;
&lt;h2 id="systemd-integration-and-quadlet"&gt;systemd Integration and Quadlet &lt;a class="headline-link" href="#systemd-integration-and-quadlet" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Docker treats containers as something separate from system services. To run a containerized app at boot, you usually rely on &lt;code&gt;docker-compose&lt;/code&gt; with a restart policy, a separate process supervisor, or a hand-written systemd unit that calls &lt;code&gt;docker run&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Podman integrates with systemd as a first-class option. Containers can be generated into systemd unit files, and modern Podman supports Quadlet, a declarative format that turns a small &lt;code&gt;.container&lt;/code&gt; file into a fully managed systemd service.&lt;/p&gt;
&lt;p&gt;A minimal Quadlet file at &lt;code&gt;~/.config/containers/systemd/web.container&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="ini"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;~/.config/containers/systemd/web.container&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-slate-200 text-slate-700 dark:bg-slate-600 dark:text-slate-300"&gt;ini&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Unit]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Nginx Web&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Container]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;docker.io/library/nginx:latest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;PublishPort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;8080:80&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Install]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;WantedBy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;default.target&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;After reloading the user daemon, the container becomes a normal systemd unit:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl --user daemon-reload
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl --user start web
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl --user status web&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You get journald logs, restart policies, dependencies on other units, and timers, all without writing a wrapper script. This is one of the strongest practical reasons to pick Podman on a Linux server.&lt;/p&gt;
&lt;h2 id="compose-workflows"&gt;Compose Workflows &lt;a class="headline-link" href="#compose-workflows" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Docker Compose is a major part of how teams use Docker. Multi-container apps live in a &lt;code&gt;docker-compose.yml&lt;/code&gt; file and run with a single command. Podman supports the same workflow in a few ways.&lt;/p&gt;
&lt;p&gt;The most direct option on current Podman releases is the Compose provider exposed through &lt;code&gt;podman compose&lt;/code&gt;. Depending on your distribution, that command may call &lt;code&gt;docker-compose&lt;/code&gt;, Docker Compose v2, or &lt;code&gt;podman-compose&lt;/code&gt; behind the scenes:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman compose up -d&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Another option is &lt;code&gt;podman-compose&lt;/code&gt;, a Python implementation of the Compose spec that runs against the Podman CLI:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman-compose up -d&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The most compatible path for existing Compose projects is often to enable the Podman socket and run the regular Docker Compose binary against it. This is useful when a project depends on Compose features that &lt;code&gt;podman-compose&lt;/code&gt; does not fully match:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl --user &lt;span class="nb"&gt;enable&lt;/span&gt; --now podman.socket
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;DOCKER_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;unix://&lt;span class="nv"&gt;$XDG_RUNTIME_DIR&lt;/span&gt;/podman/podman.sock
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker compose up -d&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For new projects, Quadlet often replaces Compose entirely on Podman. Each service becomes a &lt;code&gt;.container&lt;/code&gt; file, networks become &lt;code&gt;.network&lt;/code&gt; files, and systemd takes over orchestration.&lt;/p&gt;
&lt;h2 id="pods"&gt;Pods &lt;a class="headline-link" href="#pods" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Podman&amp;rsquo;s name comes from the feature that Docker does not have: pods. A pod is a group of containers that share a network namespace and (optionally) other namespaces, similar to a Kubernetes pod. You create a pod and add containers to it:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman pod create --name app -p 8080:80
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman run -d --pod app --name web nginx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman run -d --pod app --name cache redis&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Both containers share &lt;code&gt;localhost&lt;/code&gt; and the host port mapping, so &lt;code&gt;web&lt;/code&gt; can reach Redis at &lt;code&gt;127.0.0.1:6379&lt;/code&gt; without any custom network setup. This is useful when you are testing services that expect to share &lt;code&gt;localhost&lt;/code&gt;, and it also matches the way Kubernetes groups containers inside a pod.&lt;/p&gt;
&lt;p&gt;Docker does not have a direct equivalent. Multi-container patterns in Docker rely on user-defined &lt;a href="https://linuxize.com/post/docker-networking/"&gt;networks&lt;/a&gt;
or Compose stacks.&lt;/p&gt;
&lt;h2 id="networking-differences"&gt;Networking Differences &lt;a class="headline-link" href="#networking-differences" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Both tools provide bridge networks, host networking, and DNS resolution between containers in the same user-defined network. The default network drivers differ.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Docker&lt;/strong&gt; uses its own &lt;code&gt;bridge&lt;/code&gt; driver by default, with &lt;code&gt;docker0&lt;/code&gt; on the host.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Podman&lt;/strong&gt; uses &lt;code&gt;netavark&lt;/code&gt; on current releases, with legacy &lt;code&gt;CNI&lt;/code&gt; still present on older systems. Rootless containers use &lt;code&gt;pasta&lt;/code&gt; or &lt;code&gt;slirp4netns&lt;/code&gt; for userspace networking.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For straightforward workloads (publish ports, talk to upstream services, attach to a user network), the experience is the same. The differences show up when you need advanced DNS behavior, IPv6 inside containers, or specific MAC/IP assignments, where the two stacks diverge.&lt;/p&gt;
&lt;h2 id="building-images"&gt;Building Images &lt;a class="headline-link" href="#building-images" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Both tools build images from a Dockerfile (Podman calls it a Containerfile, but accepts both). The build commands match:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman build -t myapp:1.0 .&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Podman delegates builds to &lt;code&gt;buildah&lt;/code&gt; under the hood, while Docker has its own builder (and BuildKit for advanced features). For day-to-day builds the difference is invisible. For advanced cases (multi-platform builds, cache mounts, secret mounts), BuildKit has more features today, though Buildah has been catching up steadily.&lt;/p&gt;
&lt;p&gt;If you build a lot of images and use BuildKit-specific syntax (&lt;code&gt;# syntax=docker/dockerfile:1.4&lt;/code&gt;), test those Dockerfiles with Podman before assuming a clean migration.&lt;/p&gt;
&lt;h2 id="performance-and-resource-use"&gt;Performance and Resource Use &lt;a class="headline-link" href="#performance-and-resource-use" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For application workloads, performance is dominated by the workload itself, not the runtime. Both tools use the same kernel features (cgroups, namespaces, overlay filesystems) and run containers as regular Linux processes.&lt;/p&gt;
&lt;p&gt;Two areas where you may notice a difference:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Startup overhead&lt;/strong&gt; is similar; both spawn a runtime and a container process. Podman has no daemon to wait on, but the per-container cost is comparable.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Memory footprint&lt;/strong&gt; for the runtime itself favors Podman on a host with many idle containers, because there is no long-running daemon keeping connections and state in memory.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For most users, performance is not the deciding factor between the two tools.&lt;/p&gt;
&lt;h2 id="a-practical-migration-path"&gt;A Practical Migration Path &lt;a class="headline-link" href="#a-practical-migration-path" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A realistic Docker-to-Podman migration on an existing server does not need to be all-or-nothing. A common path:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install Podman alongside Docker. They can coexist on the same host because each tool manages its own state directory.&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install podman&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Pull and run one non-critical container as your user with Podman:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman run -d -p 9000:80 --name &lt;span class="nb"&gt;test&lt;/span&gt; nginx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl http://localhost:9000&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Move one Compose stack to either &lt;code&gt;podman compose&lt;/code&gt;, &lt;code&gt;podman-compose&lt;/code&gt;, or the Podman socket with Docker Compose. Confirm the app behaves the same.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Convert the most stable services to Quadlet units so systemd takes over restart, logging, and boot ordering. This is usually where the operational win shows up.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once every workload runs cleanly under Podman, stop and disable the Docker service, then remove the package.&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl disable --now docker
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt remove docker-ce docker-ce-cli containerd.io&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Plan for storage migration. Docker and Podman do not share the same image store. The simplest path is to export your important images with &lt;code&gt;docker save&lt;/code&gt; and import them with &lt;code&gt;podman load&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker save myapp:1.0 &lt;span class="p"&gt;|&lt;/span&gt; podman load&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For data volumes, treat them like any other host directory: bind-mount the same path from both runtimes during the transition, then point only Podman at it once you cut over.&lt;/p&gt;
&lt;h2 id="when-to-stay-on-docker"&gt;When to Stay on Docker &lt;a class="headline-link" href="#when-to-stay-on-docker" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Docker is still the right tool when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Your CI, hosting platform, or orchestrator assumes Docker (Docker Desktop, certain managed runners, some Kubernetes ingress workflows).&lt;/li&gt;
&lt;li&gt;Your team relies on advanced BuildKit features that Buildah does not match in your workflow.&lt;/li&gt;
&lt;li&gt;You use Docker Swarm or third-party tooling that talks only to the Docker daemon.&lt;/li&gt;
&lt;li&gt;You want a single supported product across Linux, macOS, and Windows, with a consistent GUI.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="when-to-switch-to-podman"&gt;When to Switch to Podman &lt;a class="headline-link" href="#when-to-switch-to-podman" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Podman is the better fit when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You run containers on a Linux server and want them managed as systemd units.&lt;/li&gt;
&lt;li&gt;You want rootless containers by default, with no &lt;code&gt;docker&lt;/code&gt; group equivalent.&lt;/li&gt;
&lt;li&gt;You target Kubernetes in production and want local pods that map to the same concepts.&lt;/li&gt;
&lt;li&gt;You prefer a daemonless architecture for upgrades, restarts, and security boundaries.&lt;/li&gt;
&lt;li&gt;You are on a distribution where Podman is the default container engine, such as Fedora, RHEL, or CentOS Stream.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Podman and Docker overlap heavily on the CLI surface and image format, so adopting Podman is rarely a rewrite. The main reasons to choose Podman are the daemonless architecture, rootless defaults, and clean systemd lifecycle. On a new Linux server, Podman is often the simpler choice for long-running services. On an existing Docker host, move one workload at a time and use Quadlet when you are ready for systemd to manage the container. If you have not installed Podman yet, the &lt;a href="https://linuxize.com/post/how-to-install-podman-on-ubuntu/"&gt;Podman on Ubuntu guide&lt;/a&gt;
is the next step.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/podman-vs-docker/featured_hu_bc6955ea2db5628b.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>pip vs apt: Installing Python Packages on Linux</title><link>https://linuxize.com/post/pip-vs-apt/</link><pubDate>Tue, 02 Jun 2026 14:15:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/pip-vs-apt/</guid><category>python</category><category>ubuntu</category><description>This guide explains when to use pip or apt for Python packages on Ubuntu and Debian, why sudo pip install is risky, and where virtual environments fit.</description><content:encoded>&lt;p&gt;When you need a Python package on Ubuntu or Debian, the right install command is not always obvious. Some packages come from the distribution through &lt;code&gt;apt&lt;/code&gt;, while others come from the Python Package Index through &lt;code&gt;pip&lt;/code&gt;. They solve different problems, and mixing them in the wrong place can break system tools or leave projects with conflicting dependencies.&lt;/p&gt;
&lt;p&gt;This guide explains when to use &lt;code&gt;apt&lt;/code&gt;, when to use &lt;code&gt;pip&lt;/code&gt;, and why Python virtual environments are the safest default for application dependencies.&lt;/p&gt;
&lt;h2 id="quick-decision-guide"&gt;Quick Decision Guide &lt;a class="headline-link" href="#quick-decision-guide" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Installing a system CLI tool maintained by Ubuntu or Debian&lt;/td&gt;
&lt;td&gt;&lt;code&gt;apt&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Installing Python libraries for a project&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip&lt;/code&gt; inside a virtual environment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Installing a package that is not in the apt repositories&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip&lt;/code&gt; inside a virtual environment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Installing a newer package version than the distribution provides&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip&lt;/code&gt; inside a virtual environment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Installing a Python CLI tool for your user account&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pipx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Installing a dependency required by another apt package&lt;/td&gt;
&lt;td&gt;&lt;code&gt;apt&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="what-each-tool-does"&gt;What Each Tool Does &lt;a class="headline-link" href="#what-each-tool-does" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;apt&lt;/code&gt; installs packages from the distribution repositories. Python packages distributed through &lt;code&gt;apt&lt;/code&gt;, such as &lt;code&gt;python3-requests&lt;/code&gt; or &lt;code&gt;python3-numpy&lt;/code&gt;, are tested against the Python version and system libraries in that Ubuntu or Debian release. They install system-wide and integrate with the distribution dependency resolver.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pip&lt;/code&gt; installs packages from the Python Package Index (PyPI). PyPI has more packages and newer versions than most distribution repositories. &lt;code&gt;pip&lt;/code&gt; does not coordinate with &lt;code&gt;apt&lt;/code&gt;, so it does not know which Python packages the operating system installed or depends on.&lt;/p&gt;
&lt;h2 id="when-to-use-apt"&gt;When to Use apt &lt;a class="headline-link" href="#when-to-use-apt" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use &lt;code&gt;apt&lt;/code&gt; for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;System tools that happen to be written in Python, such as &lt;code&gt;cloud-init&lt;/code&gt;, &lt;code&gt;ansible&lt;/code&gt;, or &lt;code&gt;certbot&lt;/code&gt;. These packages are maintained as system components and expect the system Python.&lt;/li&gt;
&lt;li&gt;Python packages required by other &lt;code&gt;apt&lt;/code&gt; packages. If you install a Python-based CLI tool with &lt;code&gt;apt&lt;/code&gt; and it lists &lt;code&gt;python3-yaml&lt;/code&gt; as a dependency, &lt;code&gt;apt&lt;/code&gt; handles that automatically.&lt;/li&gt;
&lt;li&gt;Servers or containers where you want reproducible, distribution-tested versions and do not need the latest upstream release.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Install a Python package from the Ubuntu or Debian repositories with &lt;code&gt;apt&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install python3-requests&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The package name usually includes the &lt;code&gt;python3-&lt;/code&gt; prefix. After installation, the package is available to the system Python interpreter and to other packages installed through &lt;code&gt;apt&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="when-to-use-pip"&gt;When to Use pip &lt;a class="headline-link" href="#when-to-use-pip" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use &lt;code&gt;pip&lt;/code&gt; for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Application dependencies inside a virtual environment.&lt;/li&gt;
&lt;li&gt;Packages not available in the distribution repositories.&lt;/li&gt;
&lt;li&gt;Packages where you need a specific or newer version than &lt;code&gt;apt&lt;/code&gt; provides.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Install a package with &lt;code&gt;pip&lt;/code&gt; inside a virtual environment:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pip install requests&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;In this case the PyPI package name is &lt;code&gt;requests&lt;/code&gt;, while the equivalent apt package is named &lt;code&gt;python3-requests&lt;/code&gt;. That naming difference is normal.&lt;/p&gt;
&lt;h2 id="why-sudo-pip-install-is-risky"&gt;Why sudo pip install Is Risky &lt;a class="headline-link" href="#why-sudo-pip-install-is-risky" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Running &lt;code&gt;sudo pip install&lt;/code&gt; can place packages on the system Python import path, alongside or ahead of packages managed by &lt;code&gt;apt&lt;/code&gt;. This creates two problems:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;pip&lt;/code&gt; can replace, downgrade, or shadow packages that system tools depend on.&lt;/li&gt;
&lt;li&gt;On Ubuntu 23.04 and later, and Debian 12 and later, &lt;code&gt;pip&lt;/code&gt; refuses to install into the system Python by default and raises an &lt;code&gt;externally-managed-environment&lt;/code&gt; error. This restriction is intentional.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The error message looks like:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;error: externally-managed-environment
× This environment is externally managed
╰─&amp;gt; To install Python packages system-wide, try apt install
python3-xyz, where xyz is the package you are trying to
install.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The right response to this error is to use a virtual environment, not to override the restriction with &lt;code&gt;--break-system-packages&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="use-virtual-environments-for-projects"&gt;Use Virtual Environments for Projects &lt;a class="headline-link" href="#use-virtual-environments-for-projects" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A virtual environment is an isolated Python installation with its own &lt;code&gt;site-packages&lt;/code&gt;. Installing packages inside it does not affect the system Python or any other environment:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python3 -m venv myenv
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;source&lt;/span&gt; myenv/bin/activate
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pip install requests flask gunicorn&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On Ubuntu and Debian, install venv support first if the &lt;code&gt;python3 -m venv&lt;/code&gt; command is not available:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install python3-venv&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;After &lt;code&gt;activate&lt;/code&gt;, &lt;code&gt;pip&lt;/code&gt; and &lt;code&gt;python&lt;/code&gt; point to the virtual environment. Packages install there instead of system-wide. Deactivate with:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;deactivate&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Virtual environments are the standard practice for any Python application or project. Create one per project, add it to &lt;code&gt;.gitignore&lt;/code&gt;, and install dependencies from a &lt;code&gt;requirements.txt&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pip install -r requirements.txt&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For a fuller walkthrough, see our guide to &lt;a href="https://linuxize.com/post/python-virtual-environments/"&gt;creating Python virtual environments&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="use-pipx-for-user-cli-tools"&gt;Use pipx for User CLI Tools &lt;a class="headline-link" href="#use-pipx-for-user-cli-tools" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you want a Python command-line tool available in your user account, use &lt;code&gt;pipx&lt;/code&gt; instead of installing it into the system Python or a shared user &lt;code&gt;site-packages&lt;/code&gt; directory. &lt;code&gt;pipx&lt;/code&gt; creates a small private virtual environment for each tool and exposes the command on your &lt;code&gt;PATH&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Install &lt;code&gt;pipx&lt;/code&gt; and then install a tool:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install pipx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pipx install httpie&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This is a good fit for tools such as &lt;code&gt;httpie&lt;/code&gt;, &lt;code&gt;black&lt;/code&gt;, &lt;code&gt;ruff&lt;/code&gt;, and &lt;code&gt;poetry&lt;/code&gt;, where you want the command available everywhere but do not want the package mixed into a project environment.&lt;/p&gt;
&lt;p&gt;You may still see older guides recommend &lt;code&gt;pip install --user&lt;/code&gt;. That installs packages under &lt;code&gt;~/.local/&lt;/code&gt;, but all user-level packages share the same location and can conflict with each other. On externally managed Python installations, &lt;code&gt;pip install --user&lt;/code&gt; can also be rejected for the same reason as a system install. Use &lt;code&gt;pipx&lt;/code&gt; for standalone CLI tools and a virtual environment for project libraries.&lt;/p&gt;
&lt;h2 id="where-to-install-pip-itself"&gt;Where to Install pip Itself &lt;a class="headline-link" href="#where-to-install-pip-itself" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;On Ubuntu and Debian, the distribution package for pip is &lt;code&gt;python3-pip&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install python3-pip&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This gives you the &lt;code&gt;pip&lt;/code&gt; command, but it does not mean you should install packages into the system Python. Treat it as the installer you use inside virtual environments. For detailed setup instructions, see &lt;a href="https://linuxize.com/post/how-to-install-pip-on-ubuntu/"&gt;How to Install Python Pip on Ubuntu&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use &lt;code&gt;apt&lt;/code&gt; for Python packages that belong to the operating system, &lt;code&gt;pip&lt;/code&gt; inside a virtual environment for project dependencies, and &lt;code&gt;pipx&lt;/code&gt; for standalone Python CLI tools. If you see the &lt;code&gt;externally-managed-environment&lt;/code&gt; error, create a virtual environment rather than overriding it with &lt;code&gt;--break-system-packages&lt;/code&gt;.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/pip-vs-apt/featured_hu_d1b2123c45649213.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>dpkg Cheatsheet</title><link>https://linuxize.com/cheatsheet/dpkg/</link><pubDate>Mon, 01 Jun 2026 13:40:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/cheatsheet/dpkg/</guid><description>Quick reference for installing, removing, querying, and inspecting Debian packages with dpkg</description><content:encoded>&lt;div class="card"&gt;
&lt;h3 id="install-and-remove"&gt;Install and Remove &lt;a class="headline-link" href="#install-and-remove" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Work with installed packages and local .deb files.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/dpkg-command-in-linux/"&gt;&lt;code&gt;sudo dpkg -i package.deb&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Install a local .deb file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo dpkg -i *.deb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install multiple .deb files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo dpkg -r package&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove package, keep config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo dpkg -P package&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Purge package and config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo dpkg --unpack package.deb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unpack without configuring&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="query-installed-packages"&gt;Query Installed Packages &lt;a class="headline-link" href="#query-installed-packages" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;List packages and inspect installed package metadata.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg -l&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List installed packages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg -l 'nginx*'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Filter packages by pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg -s package&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show package status&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg --status package&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Same as &lt;code&gt;dpkg -s&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg-query -W package&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show package and version&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="package-files"&gt;Package Files &lt;a class="headline-link" href="#package-files" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Find installed files and file ownership.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg -L package&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List files installed by package&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg -S /path/to/file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Find package owning a file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg -S '*/bin/curl'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search ownership by pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg -L package | less&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Browse long file lists&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg -V package&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Verify installed package files&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="inspect-deb-files"&gt;Inspect deb Files &lt;a class="headline-link" href="#inspect-deb-files" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Read package archives before installing them.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg --info package.deb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show .deb metadata&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg -I package.deb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Same as &lt;code&gt;dpkg --info&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg --contents package.deb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List files in .deb&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg -c package.deb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Same as &lt;code&gt;dpkg --contents&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg-deb -f package.deb Version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print package version field&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="extract-deb-files"&gt;Extract deb Files &lt;a class="headline-link" href="#extract-deb-files" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Unpack archive contents without installing.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg -x package.deb /tmp/extract&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Extract package files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg -X package.deb /tmp/extract&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Extract with details&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg -e package.deb /tmp/control&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Extract control files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg-deb --ctrl-tarfile package.deb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print control tar archive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg-deb --fsys-tarfile package.deb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print filesystem tar archive&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="package-selections"&gt;Package Selections &lt;a class="headline-link" href="#package-selections" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Export and restore package selection states.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg --get-selections&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print package selections&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg --get-selections &amp;gt; packages.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Save selections&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo dpkg --set-selections &amp;lt; packages.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Restore selections&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg --get-selections 'nginx*'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Filter selections&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo apt-get dselect-upgrade&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Apply restored selections&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="repair-and-configure"&gt;Repair and Configure &lt;a class="headline-link" href="#repair-and-configure" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Resume interrupted installs and fix package state.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo dpkg --configure -a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Configure pending packages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo dpkg --configure package&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Configure one package&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo dpkg -C&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Audit broken packages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo apt install -f&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fix missing dependencies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo dpkg-reconfigure package&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Re-run package setup&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="related-guides"&gt;Related Guides &lt;a class="headline-link" href="#related-guides" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Use these references for package management workflows.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Guide&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/dpkg-command-in-linux/"&gt;&lt;code&gt;dpkg Command in Linux&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Full dpkg tutorial with examples&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-install-deb-packages-on-ubuntu/"&gt;&lt;code&gt;Install deb Files on Ubuntu&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Install local .deb files with apt and dpkg&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/cheatsheet/apt/"&gt;&lt;code&gt;apt Cheatsheet&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Higher-level package management commands&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;</content:encoded></item><item><title>dpkg Command in Linux: Install and Manage Debian Packages</title><link>https://linuxize.com/post/dpkg-command-in-linux/</link><pubDate>Mon, 01 Jun 2026 10:20:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/dpkg-command-in-linux/</guid><category>linux commands</category><category>ubuntu</category><description>Use dpkg to install, remove, query, and inspect Debian packages on Ubuntu, Debian, and other Debian-based distributions.</description><content:encoded>&lt;p&gt;When you install software on Ubuntu or Debian with &lt;a href="https://linuxize.com/post/how-to-use-apt-command/"&gt;&lt;code&gt;apt&lt;/code&gt;&lt;/a&gt;
, the package manager quietly calls &lt;code&gt;dpkg&lt;/code&gt; in the background to do the actual work. &lt;code&gt;dpkg&lt;/code&gt; is the low-level tool that unpacks and installs &lt;code&gt;.deb&lt;/code&gt; files, maintains the package database, and handles removal and purging. Knowing how to use it directly is useful when you have a local &lt;code&gt;.deb&lt;/code&gt; file that is not in a repository, when you need to query what files a package installed, or when you want to find which package owns a specific file on your system.&lt;/p&gt;
&lt;h2 id="syntax"&gt;Syntax &lt;a class="headline-link" href="#syntax" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="txt"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;txt&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dpkg [OPTIONS] COMMAND [ARGUMENTS]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Install, remove, purge, and configure operations require &lt;code&gt;sudo&lt;/code&gt; because they modify system directories and the package database. Query commands such as &lt;code&gt;dpkg -l&lt;/code&gt;, &lt;code&gt;dpkg -L&lt;/code&gt;, &lt;code&gt;dpkg -S&lt;/code&gt;, and &lt;code&gt;dpkg -s&lt;/code&gt; usually run without elevated privileges.&lt;/p&gt;
&lt;h2 id="install-a-package"&gt;Install a Package &lt;a class="headline-link" href="#install-a-package" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To install a local &lt;code&gt;.deb&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dpkg -i package.deb&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;dpkg&lt;/code&gt; unpacks the archive, runs the maintainer scripts, and registers the package in its database. If the package has unmet dependencies, the install fails with a dependency error. Fix it by running:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install -f&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;apt install -f&lt;/code&gt; (fix broken) resolves and installs any missing dependencies, then completes the pending dpkg installation.&lt;/p&gt;
&lt;p&gt;For most local &lt;code&gt;.deb&lt;/code&gt; installs, &lt;a href="https://linuxize.com/post/how-to-install-deb-packages-on-ubuntu/"&gt;&lt;code&gt;apt install ./package.deb&lt;/code&gt;&lt;/a&gt;
is easier because it can resolve dependencies in the same step. Use &lt;code&gt;dpkg -i&lt;/code&gt; when you specifically need the lower-level package operation.&lt;/p&gt;
&lt;h2 id="remove-a-package"&gt;Remove a Package &lt;a class="headline-link" href="#remove-a-package" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To uninstall a package while keeping its configuration files:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dpkg -r package-name&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To uninstall a package and delete its configuration files at the same time (a full purge):&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dpkg -P package-name&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Use &lt;code&gt;-P&lt;/code&gt; when you want a clean removal with no leftover config, or when you plan to reinstall with a fresh configuration.&lt;/p&gt;
&lt;h2 id="list-installed-packages"&gt;List Installed Packages &lt;a class="headline-link" href="#list-installed-packages" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To list every installed package with its version and status:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dpkg -l&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The output is wide. Each package line starts with three status columns: desired action, current package status, and error state. The normal installed state appears as &lt;code&gt;ii&lt;/code&gt;, followed by the package name, version, architecture, and description:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name Version Architecture Description
+++-==============-=============-============-=================================
ii curl 8.5.0 amd64 command line tool for transferring data
ii nginx 1.26.0 amd64 small, powerful, scalable web/proxy server&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The first &lt;code&gt;i&lt;/code&gt; means the package is selected for installation, and the second &lt;code&gt;i&lt;/code&gt; means it is installed and configured. Filter by name to narrow the output:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dpkg -l &lt;span class="s1"&gt;&amp;#39;nginx*&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;ii nginx 1.26.0 amd64 small, powerful, scalable web/proxy server
ii nginx-common 1.26.0 all small, powerful, scalable web/proxy server (common files)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="list-files-installed-by-a-package"&gt;List Files Installed by a Package &lt;a class="headline-link" href="#list-files-installed-by-a-package" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To see every file a package placed on your system:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dpkg -L nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;/.
/etc
/etc/nginx
/etc/nginx/nginx.conf
/etc/nginx/sites-available
/etc/nginx/sites-enabled
/usr/sbin
/usr/sbin/nginx
...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is useful when you need to find where a package installs its binary, config files, or documentation.&lt;/p&gt;
&lt;h2 id="find-which-package-owns-a-file"&gt;Find Which Package Owns a File &lt;a class="headline-link" href="#find-which-package-owns-a-file" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To identify which package installed a particular file:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dpkg -S /usr/sbin/nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;nginx: /usr/sbin/nginx&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can also search by pattern:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dpkg -S &lt;span class="s1"&gt;&amp;#39;*/bin/curl&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;curl: /usr/bin/curl&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="show-package-details"&gt;Show Package Details &lt;a class="headline-link" href="#show-package-details" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To print the full metadata for an installed package including version, dependencies, and description:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dpkg -s nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Package: nginx
Status: install ok installed
Priority: optional
Section: web
Installed-Size: 47
Maintainer: Ubuntu Developers &amp;lt;ubuntu-devel-discuss@lists.ubuntu.com&amp;gt;
Architecture: amd64
Version: 1.26.0-1ubuntu1
...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="inspect-a-deb-file-without-installing"&gt;Inspect a .deb File Without Installing &lt;a class="headline-link" href="#inspect-a-deb-file-without-installing" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To read the metadata of a &lt;code&gt;.deb&lt;/code&gt; file before installing it:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dpkg --info package.deb&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt; new Debian package, version 2.0.
size 1234567 bytes: control archive=2048 bytes.
...
Package: myapp
Version: 2.1.0
Architecture: amd64
...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To list the files the package would install without actually installing it:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dpkg --contents package.deb&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="extract-a-package-without-installing"&gt;Extract a Package Without Installing &lt;a class="headline-link" href="#extract-a-package-without-installing" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To unpack the contents of a &lt;code&gt;.deb&lt;/code&gt; file into a directory for inspection:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dpkg -x package.deb /tmp/extracted&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The directory structure mirrors where files would be installed on the real system.&lt;/p&gt;
&lt;h2 id="export-and-restore-package-selections"&gt;Export and Restore Package Selections &lt;a class="headline-link" href="#export-and-restore-package-selections" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To export a list of all installed packages (useful for replicating a system):&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dpkg --get-selections &amp;gt; installed-packages.txt&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To restore those selections on another machine:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dpkg --set-selections &amp;lt; installed-packages.txt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt-get dselect-upgrade&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="reconfigure-an-installed-package"&gt;Reconfigure an Installed Package &lt;a class="headline-link" href="#reconfigure-an-installed-package" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Some packages run interactive setup at install time. To run that setup again:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dpkg-reconfigure package-name&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This is commonly used to reconfigure &lt;code&gt;tzdata&lt;/code&gt; for timezone changes, &lt;code&gt;keyboard-configuration&lt;/code&gt;, or &lt;code&gt;locales&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="fix-broken-package-state"&gt;Fix Broken Package State &lt;a class="headline-link" href="#fix-broken-package-state" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If a system update or manual operation leaves packages in a half-installed or unconfigured state, run:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dpkg --configure -a&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This processes any pending package configuration steps. Follow it with &lt;code&gt;sudo apt install -f&lt;/code&gt; if dependency issues remain.&lt;/p&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;dpkg reports dependency problems&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;dpkg&lt;/code&gt; installs the package file you give it, but it does not download missing dependencies. Run &lt;code&gt;sudo apt install -f&lt;/code&gt; to install the required packages and finish the pending configuration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A package operation was interrupted&lt;/strong&gt;&lt;br&gt;
If an update or install stopped before configuration finished, run &lt;code&gt;sudo dpkg --configure -a&lt;/code&gt;. This resumes pending package configuration scripts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;dpkg cannot find a package&lt;/strong&gt;&lt;br&gt;
Use the installed package name, not the &lt;code&gt;.deb&lt;/code&gt; filename. Run &lt;code&gt;dpkg -l 'pattern*'&lt;/code&gt; to find matching installed packages, then use that package name with commands such as &lt;code&gt;dpkg -L&lt;/code&gt;, &lt;code&gt;dpkg -s&lt;/code&gt;, or &lt;code&gt;sudo dpkg -r&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For a printable quick reference, see the &lt;a href="https://linuxize.com/cheatsheet/dpkg/"&gt;dpkg cheatsheet&lt;/a&gt;
.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg -i pkg.deb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install a local .deb file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg -r package&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove a package, keep config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg -P package&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Purge a package and its config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg -l&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List all installed packages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg -l 'pattern*'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Filter installed packages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg -L package&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List files installed by a package&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg -S /path/to/file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Find which package owns a file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg -s package&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show package status and details&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg --info pkg.deb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Inspect a .deb file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg --contents pkg.deb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List files in a .deb&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg -x pkg.deb /dir&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Extract a .deb without installing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg --get-selections&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Export installed package list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg-reconfigure package&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Re-run package configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dpkg --configure -a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fix incomplete installations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;dpkg&lt;/code&gt; is the foundation of package management on Debian-based systems. For everyday installs and updates from repositories, &lt;code&gt;apt&lt;/code&gt; is the better tool because it resolves dependencies automatically. Reach for &lt;code&gt;dpkg&lt;/code&gt; directly when you need to install a &lt;code&gt;.deb&lt;/code&gt; file from disk, audit what a package installed, or diagnose a broken package state.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/dpkg-command-in-linux/featured_hu_869824d772a9010a.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>reboot Cheatsheet</title><link>https://linuxize.com/cheatsheet/reboot/</link><pubDate>Sun, 31 May 2026 13:15:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/cheatsheet/reboot/</guid><description>Quick reference for rebooting and scheduling restarts on Linux</description><content:encoded>&lt;div class="card"&gt;
&lt;h3 id="immediate-reboot"&gt;Immediate Reboot &lt;a class="headline-link" href="#immediate-reboot" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/reboot-linux-using-command-line/"&gt;&lt;code&gt;sudo reboot&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Reboot immediately&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl reboot&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reboot via systemd&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo shutdown -r now&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reboot immediately via shutdown&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="systemctl-options"&gt;systemctl Options &lt;a class="headline-link" href="#systemctl-options" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl reboot --force&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Skip graceful shutdown if unresponsive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl --no-wall reboot&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reboot without broadcasting a wall message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl --message=&amp;quot;reason&amp;quot; reboot&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Log a reason in the system journal&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="schedule-with-shutdown--r"&gt;Schedule with shutdown -r &lt;a class="headline-link" href="#schedule-with-shutdown--r" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo shutdown -r +5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reboot in 5 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo shutdown -r 10:00&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reboot at 10:00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo shutdown -r +10 &amp;quot;message&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reboot in 10 minutes with a wall message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo shutdown -r&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reboot after 1 minute (default)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="cancel-and-status"&gt;Cancel and Status &lt;a class="headline-link" href="#cancel-and-status" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo shutdown -c&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cancel a scheduled reboot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;last reboot&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show reboot history&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uptime -s&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show current boot time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;journalctl --list-boots&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List all boot sessions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;</content:encoded></item><item><title>How to Pass Arguments to a Bash Script</title><link>https://linuxize.com/post/bash-script-arguments/</link><pubDate>Sat, 30 May 2026 15:40:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/bash-script-arguments/</guid><category>bash</category><description>This guide explains how to pass arguments to Bash scripts, read positional parameters, loop over $@, set defaults, validate input, and parse flags with getopts.</description><content:encoded>&lt;p&gt;Arguments let a Bash script behave differently each time you run it. Instead of hard-coding a file path, username, environment name, or port number inside the script, you pass those values on the command line and read them from inside the script.&lt;/p&gt;
&lt;p&gt;This guide explains how to pass arguments to Bash scripts, read positional parameters such as &lt;code&gt;$1&lt;/code&gt; and &lt;code&gt;$2&lt;/code&gt;, work with all arguments through &lt;code&gt;&amp;quot;$@&amp;quot;&lt;/code&gt;, set defaults, validate required input, and parse named flags with &lt;code&gt;getopts&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For a printable quick reference, see the &lt;a href="https://linuxize.com/cheatsheet/bash/"&gt;Bash cheatsheet&lt;/a&gt;
.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Syntax&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bash script.sh arg1 arg2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run a script with two arguments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Script name or path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$1&lt;/code&gt;, &lt;code&gt;$2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;First and second positional arguments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;${10}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tenth positional argument&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$#&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Number of arguments passed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;quot;$@&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All arguments as separate quoted words&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;quot;${1:-default}&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use a default value when the first argument is missing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;shift&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove the first positional argument and move the rest down&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;getopts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Parse short named options such as &lt;code&gt;-p 3000 -v&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="positional-parameters"&gt;Positional Parameters &lt;a class="headline-link" href="#positional-parameters" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When you run a script, Bash assigns each argument to a numbered variable:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;~/greet.sh&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/usr/bin/env bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Hello, %s!\n&amp;#39;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Argument count: %s\n&amp;#39;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$#&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Run the script with one argument:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;bash greet.sh World&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Hello, World!
Argument count: 1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The special variables are:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The script name or path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$1&lt;/code&gt;, &lt;code&gt;$2&lt;/code&gt;, &amp;hellip;&lt;/td&gt;
&lt;td&gt;First, second, &amp;hellip; argument&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;${10}&lt;/code&gt;, &lt;code&gt;${11}&lt;/code&gt;, &amp;hellip;&lt;/td&gt;
&lt;td&gt;Tenth argument and beyond&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$#&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Number of arguments passed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$@&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All arguments as separate words&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All arguments as a single string&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For more detail about each special parameter, see the &lt;a href="https://linuxize.com/post/bash-positional-parameters/"&gt;Bash positional parameters&lt;/a&gt;
guide.&lt;/p&gt;
&lt;h2 id="access-all-arguments"&gt;Access All Arguments &lt;a class="headline-link" href="#access-all-arguments" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use &lt;code&gt;$@&lt;/code&gt; to iterate over every argument regardless of how many were passed:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;~/list-args.sh&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/usr/bin/env bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; arg in &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Argument: %s\n&amp;#39;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$arg&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;bash list-args.sh alpha beta &lt;span class="s2"&gt;&amp;#34;hello world&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Argument: alpha
Argument: beta
Argument: hello world&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Always quote &lt;code&gt;&amp;quot;$@&amp;quot;&lt;/code&gt; so arguments with spaces stay as single values. Unquoted &lt;code&gt;$@&lt;/code&gt; allows word splitting, which turns &lt;code&gt;&amp;quot;hello world&amp;quot;&lt;/code&gt; into two separate words.&lt;/p&gt;
&lt;h2 id="set-default-values"&gt;Set Default Values &lt;a class="headline-link" href="#set-default-values" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When an argument is optional, assign a default with &lt;code&gt;${N:-default}&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;~/deploy.sh&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/usr/bin/env bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;production&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;BRANCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;2&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Deploying %s to %s\n&amp;#39;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$BRANCH&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$ENV&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If the caller passes no arguments, &lt;code&gt;ENV&lt;/code&gt; becomes &lt;code&gt;production&lt;/code&gt; and &lt;code&gt;BRANCH&lt;/code&gt; becomes &lt;code&gt;main&lt;/code&gt;. If they pass one argument, &lt;code&gt;ENV&lt;/code&gt; uses that value and &lt;code&gt;BRANCH&lt;/code&gt; falls back to &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="validate-required-arguments"&gt;Validate Required Arguments &lt;a class="headline-link" href="#validate-required-arguments" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Exit early with a usage message when a required argument is missing:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;~/backup.sh&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/usr/bin/env bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$#&lt;/span&gt; -lt &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Usage: &lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt; SOURCE DESTINATION&amp;#34;&lt;/span&gt; &amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;SOURCE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;DEST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Ready to back up %s to %s\n&amp;#39;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$SOURCE&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$DEST&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;$# -lt 2&lt;/code&gt; checks whether fewer than two arguments were provided. The error message goes to stderr with &lt;code&gt;&amp;gt;&amp;amp;2&lt;/code&gt;, and the script exits with code &lt;code&gt;1&lt;/code&gt; to signal failure. After validation succeeds, you can safely use &lt;code&gt;$SOURCE&lt;/code&gt; and &lt;code&gt;$DEST&lt;/code&gt; in the real backup command, such as &lt;code&gt;rsync&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="shift-through-arguments"&gt;Shift Through Arguments &lt;a class="headline-link" href="#shift-through-arguments" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;shift&lt;/code&gt; removes the first argument and renumbers the rest. It is useful when a script accepts a variable number of items to process:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;~/process.sh&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/usr/bin/env bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Script: %s\n&amp;#39;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$#&lt;/span&gt; -gt &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Processing: %s\n&amp;#39;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;shift&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Each &lt;code&gt;shift&lt;/code&gt; call moves &lt;code&gt;$2&lt;/code&gt; into &lt;code&gt;$1&lt;/code&gt;, &lt;code&gt;$3&lt;/code&gt; into &lt;code&gt;$2&lt;/code&gt;, and so on, and decrements &lt;code&gt;$#&lt;/code&gt; by one. The loop continues until no arguments remain.&lt;/p&gt;
&lt;h2 id="named-flags-with-getopts"&gt;Named Flags with getopts &lt;a class="headline-link" href="#named-flags-with-getopts" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For scripts with optional flags, &lt;code&gt;getopts&lt;/code&gt; parses named options in the style of standard Unix commands:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;~/server.sh&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/usr/bin/env bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;8080&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;VERBOSE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;usage&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Usage: &lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt; [-p PORT] [-v]&amp;#34;&lt;/span&gt; &amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nb"&gt;getopts&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;:p:v&amp;#34;&lt;/span&gt; opt&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nv"&gt;$opt&lt;/span&gt; in
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; p&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$OPTARG&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; v&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;VERBOSE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="se"&gt;\?&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; usage&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; :&lt;span class="o"&gt;)&lt;/span&gt; usage&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;esac&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;shift&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;OPTIND &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Starting server on port %s (verbose: %s)\n&amp;#39;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$PORT&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$VERBOSE&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The option string &lt;code&gt;&amp;quot;:p:v&amp;quot;&lt;/code&gt; means &lt;code&gt;-p&lt;/code&gt; takes a value, &lt;code&gt;-v&lt;/code&gt; is a flag with no value, and the leading colon lets the script handle errors itself. &lt;code&gt;$OPTARG&lt;/code&gt; holds the value for options that require one. &lt;code&gt;shift $((OPTIND - 1))&lt;/code&gt; removes all parsed flags so &lt;code&gt;$1&lt;/code&gt;, &lt;code&gt;$2&lt;/code&gt;, and the rest refer to any remaining positional arguments after the flags.&lt;/p&gt;
&lt;p&gt;Run it:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;bash server.sh -p &lt;span class="m"&gt;3000&lt;/span&gt; -v&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Starting server on port 3000 (verbose: 1)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For a deeper look at option strings, &lt;code&gt;OPTARG&lt;/code&gt;, &lt;code&gt;OPTIND&lt;/code&gt;, and error handling, read the &lt;a href="https://linuxize.com/post/bash-getopts/"&gt;Bash getopts&lt;/a&gt;
guide.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Bash positional parameters handle the straightforward case of ordered arguments, while &lt;code&gt;getopts&lt;/code&gt; adds named flag support for more complex scripts. Start with positional parameters, validate the count early, set defaults for optional arguments, and use &lt;code&gt;getopts&lt;/code&gt; when your script grows a meaningful set of options. For more on script execution and permissions, see the guide on &lt;a href="https://linuxize.com/post/run-bash-script/"&gt;running Bash scripts&lt;/a&gt;
.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/bash-script-arguments/featured_hu_ebf1c6123869e3b2.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>iptables Command in Linux: Manage Firewall Rules</title><link>https://linuxize.com/post/iptables-command-in-linux/</link><pubDate>Fri, 29 May 2026 09:30:00 +0100</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/iptables-command-in-linux/</guid><category>firewall</category><category>linux commands</category><description>Manage Linux firewall rules with iptables. This guide covers tables and chains, listing rules, allowing and blocking ports and IP addresses, setting default policies, and saving rules across reboots.</description><content:encoded>&lt;p&gt;When a packet arrives at a Linux machine, the kernel decides what to do with it based on a set of firewall rules. Those rules live in the kernel&amp;rsquo;s Netfilter framework, and for more than two decades the standard way to edit them from user space has been the &lt;code&gt;iptables&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;iptables&lt;/code&gt; controls packet filtering, network address translation, and packet mangling. Higher-level front ends such as &lt;a href="https://linuxize.com/post/how-to-setup-a-firewall-with-ufw-on-ubuntu-24-04/"&gt;&lt;code&gt;ufw&lt;/code&gt;&lt;/a&gt;
and &lt;code&gt;firewalld&lt;/code&gt; manage the same Netfilter firewall stack through simpler interfaces, although many modern systems use &lt;code&gt;nftables&lt;/code&gt; underneath. Understanding the underlying command is still valuable, especially when you inherit a server that was set up by someone else. This guide walks through the concepts and the commands you need to read, edit, and persist firewall rules.&lt;/p&gt;
&lt;h2 id="tables-and-chains"&gt;Tables and Chains &lt;a class="headline-link" href="#tables-and-chains" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before you write a rule, you need to know where it goes. &lt;code&gt;iptables&lt;/code&gt; is organized into tables, and each table contains chains.&lt;/p&gt;
&lt;p&gt;The three tables you will use most often are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;filter&lt;/code&gt; - the default table, used for allowing and blocking traffic&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nat&lt;/code&gt; - used for network address translation, such as port forwarding and masquerading&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mangle&lt;/code&gt; - used to alter packet headers, for example to set QoS marks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each table has a set of built-in chains that correspond to moments in the life of a packet. In the &lt;code&gt;filter&lt;/code&gt; table:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;INPUT&lt;/code&gt; - packets destined for the local machine&lt;/li&gt;
&lt;li&gt;&lt;code&gt;OUTPUT&lt;/code&gt; - packets originating from the local machine&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FORWARD&lt;/code&gt; - packets routed through the machine&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A rule says: for packets that enter this chain and match these criteria, take this action. The action is called a target and is usually &lt;code&gt;ACCEPT&lt;/code&gt;, &lt;code&gt;DROP&lt;/code&gt;, &lt;code&gt;REJECT&lt;/code&gt;, or the name of another chain.&lt;/p&gt;
&lt;h2 id="iptables-syntax"&gt;iptables Syntax &lt;a class="headline-link" href="#iptables-syntax" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The general form of the command is:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="txt"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;txt&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;iptables [-t TABLE] COMMAND CHAIN [MATCH] [-j TARGET]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If &lt;code&gt;-t&lt;/code&gt; is omitted, &lt;code&gt;iptables&lt;/code&gt; uses the &lt;code&gt;filter&lt;/code&gt; table. Common commands include &lt;code&gt;-A&lt;/code&gt; (append a rule), &lt;code&gt;-I&lt;/code&gt; (insert), &lt;code&gt;-D&lt;/code&gt; (delete), &lt;code&gt;-L&lt;/code&gt; (list), &lt;code&gt;-F&lt;/code&gt; (flush), and &lt;code&gt;-P&lt;/code&gt; (set default policy).&lt;/p&gt;
&lt;p&gt;All commands that change the firewall require root privileges. Run them with &lt;code&gt;sudo&lt;/code&gt; or as root.&lt;/p&gt;
&lt;div class="note callout callout-warning"&gt;
&lt;div class="callout-header"&gt;&lt;svg role="img" aria-hidden="true" class="callout-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"&gt;
&lt;path d="M10 20C4.477 20 0 15.523 0 10S4.477 0 10 0s10 4.477 10 10-4.477 10-10 10zm0-2c4.418 0 8-3.582 8-8s-3.582-8-8-8-8 3.582-8 8 3.582 8 8 8zm-.5-5h1c.276 0 .5.224.5.5v1c0 .276-.224.5-.5.5h-1c-.276 0-.5-.224-.5-.5v-1c0-.276.224-.5.5-.5zm0-8h1c.276 0 .5.224.5.5V8l-.5 3-1 .5L9 8V5.5c0-.276.224-.5.5-.5z"&gt;&lt;/path&gt;
&lt;/svg&gt;
&lt;span class="callout-title"&gt;Warning&lt;/span&gt;&lt;/div&gt;
&lt;div class="callout-body"&gt;It is easy to lock yourself out of a remote server with a single wrong rule. Before you apply a restrictive ruleset over SSH, either test on a local machine first or use &lt;code&gt;iptables-apply&lt;/code&gt;, which rolls back automatically if you lose access.&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="list-rules"&gt;List Rules &lt;a class="headline-link" href="#list-rules" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To print every rule in the &lt;code&gt;filter&lt;/code&gt; table, use the &lt;code&gt;-L&lt;/code&gt; option:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo iptables -L&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The default output shows service names, resolves IP addresses, and hides packet and byte counters. For real work, add &lt;code&gt;-n&lt;/code&gt; to keep numeric output and &lt;code&gt;-v&lt;/code&gt; to show counters and interface information:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo iptables -L -n -v&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
1234 98K ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22
0 0 DROP tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:23
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To list a single chain, append its name:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo iptables -L INPUT -n -v&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To number the rules so you can reference them by index when deleting, add &lt;code&gt;--line-numbers&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo iptables -L INPUT -n -v --line-numbers&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="add-and-remove-rules"&gt;Add and Remove Rules &lt;a class="headline-link" href="#add-and-remove-rules" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;New rules are added to the end of a chain with &lt;code&gt;-A&lt;/code&gt; (append) or at a specific position with &lt;code&gt;-I&lt;/code&gt; (insert). The difference matters because &lt;code&gt;iptables&lt;/code&gt; evaluates rules top to bottom and stops at the first match.&lt;/p&gt;
&lt;p&gt;To allow incoming SSH connections:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo iptables -A INPUT -p tcp --dport &lt;span class="m"&gt;22&lt;/span&gt; -j ACCEPT&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To allow HTTP and HTTPS:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo iptables -A INPUT -p tcp --dport &lt;span class="m"&gt;80&lt;/span&gt; -j ACCEPT
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo iptables -A INPUT -p tcp --dport &lt;span class="m"&gt;443&lt;/span&gt; -j ACCEPT&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To insert a rule as the first one in the chain, use &lt;code&gt;-I CHAIN 1&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo iptables -I INPUT &lt;span class="m"&gt;1&lt;/span&gt; -p tcp --dport &lt;span class="m"&gt;22&lt;/span&gt; -j ACCEPT&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This is important when you are working over SSH. If a broad &lt;code&gt;DROP&lt;/code&gt; rule already appears earlier in the chain, appending the accept rule after it would not help because the packet would be dropped first.&lt;/p&gt;
&lt;p&gt;To delete a rule, either repeat the exact specification with &lt;code&gt;-D&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo iptables -D INPUT -p tcp --dport &lt;span class="m"&gt;23&lt;/span&gt; -j DROP&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Or delete by line number, which is easier when the rule has many options:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo iptables -D INPUT &lt;span class="m"&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="allow-and-block-specific-ips"&gt;Allow and Block Specific IPs &lt;a class="headline-link" href="#allow-and-block-specific-ips" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To block all traffic from a single IP address:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo iptables -A INPUT -s 203.0.113.10 -j DROP&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To block a range using CIDR notation:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo iptables -A INPUT -s 203.0.113.0/24 -j DROP&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To allow SSH only from a trusted subnet:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo iptables -A INPUT -p tcp -s 192.168.1.0/24 --dport &lt;span class="m"&gt;22&lt;/span&gt; -j ACCEPT
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo iptables -A INPUT -p tcp --dport &lt;span class="m"&gt;22&lt;/span&gt; -j DROP&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The first rule accepts SSH from the local subnet. The second drops SSH from everywhere else. Order matters: if you reversed the two lines, every SSH attempt would be dropped before the accept rule had a chance to match.&lt;/p&gt;
&lt;h2 id="allow-established-connections"&gt;Allow Established Connections &lt;a class="headline-link" href="#allow-established-connections" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Most firewall setups include a rule that accepts traffic belonging to an already established connection. This lets return traffic through without needing a matching rule for each outbound request:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Put this rule near the top of the &lt;code&gt;INPUT&lt;/code&gt; chain so it matches early. Without it, the default &lt;code&gt;DROP&lt;/code&gt; policy breaks outbound connections that expect responses.&lt;/p&gt;
&lt;h2 id="set-a-default-policy"&gt;Set a Default Policy &lt;a class="headline-link" href="#set-a-default-policy" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Each built-in chain has a default policy that applies when no rule matches. The &lt;code&gt;-P&lt;/code&gt; option changes it:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo iptables -P INPUT DROP
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo iptables -P FORWARD DROP
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo iptables -P OUTPUT ACCEPT&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Switching &lt;code&gt;INPUT&lt;/code&gt; to &lt;code&gt;DROP&lt;/code&gt; is the foundation of a deny-by-default firewall: nothing gets in unless an explicit rule allows it. Before you flip the policy, make sure you have already added the rules that allow SSH, established connections, and anything else you need.&lt;/p&gt;
&lt;h2 id="flush-rules"&gt;Flush Rules &lt;a class="headline-link" href="#flush-rules" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To remove every rule from every chain in the current table:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo iptables -F&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To flush a specific chain only:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo iptables -F INPUT&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Flushing does not reset the default policies. If you have set &lt;code&gt;INPUT&lt;/code&gt; to &lt;code&gt;DROP&lt;/code&gt;, flushing will leave it at &lt;code&gt;DROP&lt;/code&gt; with no rules, which blocks all inbound traffic. Reset the policy to &lt;code&gt;ACCEPT&lt;/code&gt; first if that is not what you want:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo iptables -P INPUT ACCEPT
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo iptables -F&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="save-and-restore-rules"&gt;Save and Restore Rules &lt;a class="headline-link" href="#save-and-restore-rules" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Rules added with &lt;code&gt;iptables&lt;/code&gt; live in kernel memory only. They disappear on reboot unless you save them.&lt;/p&gt;
&lt;p&gt;On Ubuntu, Debian, and Derivatives, the &lt;code&gt;iptables-persistent&lt;/code&gt; package saves rules to &lt;code&gt;/etc/iptables/rules.v4&lt;/code&gt; and reloads them at boot:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install iptables-persistent&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The installer asks whether to save the current rules. To update the saved copy later:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo netfilter-persistent save&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On Fedora, RHEL, and Derivatives, the equivalent service is &lt;code&gt;iptables-services&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dnf install iptables-services
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; --now iptables
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo service iptables save&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Independent of the distribution, you can dump and restore rules manually with &lt;code&gt;iptables-save&lt;/code&gt; and &lt;code&gt;iptables-restore&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo iptables-save -f /etc/iptables/rules.v4
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo iptables-restore /etc/iptables/rules.v4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This is also the recommended way to edit a large ruleset: save to a file, edit the file, then restore it atomically.&lt;/p&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Rules disappear after a reboot&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;iptables&lt;/code&gt; rules are not persistent by default. Install &lt;code&gt;iptables-persistent&lt;/code&gt; on Debian-based systems or &lt;code&gt;iptables-services&lt;/code&gt; on RHEL-based ones, and save the ruleset.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SSH stops working after setting a DROP policy&lt;/strong&gt;&lt;br&gt;
You switched &lt;code&gt;INPUT&lt;/code&gt; to &lt;code&gt;DROP&lt;/code&gt; without an ACCEPT rule for port 22, or the ACCEPT rule is positioned after a more general DROP rule. Connect through the console, add the rule with &lt;code&gt;-I INPUT 1&lt;/code&gt;, and save.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A rule looks correct but does not match&lt;/strong&gt;&lt;br&gt;
Check the order. &lt;code&gt;iptables&lt;/code&gt; walks the chain top to bottom and stops at the first match, so an earlier accept or drop may be catching the packet first. Use &lt;code&gt;iptables -L INPUT -n -v --line-numbers&lt;/code&gt; to inspect the order.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Changes are silently ignored&lt;/strong&gt;&lt;br&gt;
You may be editing the wrong table. A rule in &lt;code&gt;filter&lt;/code&gt; does not affect NAT, and vice versa. Pass &lt;code&gt;-t TABLE&lt;/code&gt; explicitly when you are not working in &lt;code&gt;filter&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;iptables: command not found&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
On some modern distributions, only &lt;code&gt;nftables&lt;/code&gt; is installed by default. Install &lt;code&gt;iptables&lt;/code&gt; with your package manager, or use &lt;code&gt;nft&lt;/code&gt; directly.&lt;/p&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For a printable quick reference, see the &lt;a href="https://linuxize.com/cheatsheet/iptables/"&gt;iptables cheatsheet&lt;/a&gt;
.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;List rules (verbose, numeric)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;iptables -L -n -v --line-numbers&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Allow port&lt;/td&gt;
&lt;td&gt;&lt;code&gt;iptables -A INPUT -p tcp --dport PORT -j ACCEPT&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block IP&lt;/td&gt;
&lt;td&gt;&lt;code&gt;iptables -A INPUT -s IP -j DROP&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Allow established connections&lt;/td&gt;
&lt;td&gt;&lt;code&gt;iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Insert rule at top&lt;/td&gt;
&lt;td&gt;&lt;code&gt;iptables -I INPUT 1 -p tcp --dport 22 -j ACCEPT&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete rule by number&lt;/td&gt;
&lt;td&gt;&lt;code&gt;iptables -D INPUT N&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Set default policy&lt;/td&gt;
&lt;td&gt;&lt;code&gt;iptables -P INPUT DROP&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flush all rules&lt;/td&gt;
&lt;td&gt;&lt;code&gt;iptables -F&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Save rules&lt;/td&gt;
&lt;td&gt;&lt;code&gt;iptables-save -f /etc/iptables/rules.v4&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restore rules&lt;/td&gt;
&lt;td&gt;&lt;code&gt;iptables-restore /etc/iptables/rules.v4&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="faq"&gt;FAQ &lt;a class="headline-link" href="#faq" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Is iptables still relevant in 2026?&lt;/strong&gt;&lt;br&gt;
It is still installed and widely used, but it is being replaced by &lt;code&gt;nftables&lt;/code&gt;, which offers a cleaner syntax and better performance. On recent distributions, the &lt;code&gt;iptables&lt;/code&gt; command is often a compatibility front end that writes &lt;code&gt;nftables&lt;/code&gt; rules underneath.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Should I use iptables, ufw, or firewalld?&lt;/strong&gt;&lt;br&gt;
If you are managing rules by hand on Debian or Ubuntu, &lt;a href="https://linuxize.com/post/how-to-setup-a-firewall-with-ufw-on-ubuntu-24-04/"&gt;ufw&lt;/a&gt;
is simpler and covers most cases. On Fedora and RHEL, &lt;code&gt;firewalld&lt;/code&gt; is the default. Reach for raw &lt;code&gt;iptables&lt;/code&gt; when you need fine-grained control that the front ends do not expose, or when you are troubleshooting an existing ruleset.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What is the difference between DROP and REJECT?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;DROP&lt;/code&gt; silently discards the packet; the sender sees a timeout. &lt;code&gt;REJECT&lt;/code&gt; sends an ICMP error back (or a TCP reset for TCP), so the sender gets immediate feedback. &lt;code&gt;DROP&lt;/code&gt; is often preferred on public interfaces because it does not confirm that the port exists.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How do I block a country or a list of IPs?&lt;/strong&gt;&lt;br&gt;
For a handful of addresses, add one &lt;code&gt;-s&lt;/code&gt; rule per entry. For larger lists, use the &lt;code&gt;ipset&lt;/code&gt; tool to manage the addresses and reference the set from a single &lt;code&gt;iptables&lt;/code&gt; rule.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Does iptables handle IPv6?&lt;/strong&gt;&lt;br&gt;
No. Use &lt;code&gt;ip6tables&lt;/code&gt; for IPv6 rules. It has the same syntax and the same tables and chains, but operates on a separate rule set.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;iptables&lt;/code&gt; is a low-level but reliable way to read and shape the Linux firewall. Once you have a working ruleset, save it with &lt;code&gt;iptables-save&lt;/code&gt; and commit the file so the next person to touch the server has a clear starting point.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/iptables-command-in-linux/featured_hu_c7e35177340aebc3.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>Network Bandwidth Monitoring Tools: iftop, nload, bmon, and vnstat</title><link>https://linuxize.com/post/network-bandwidth-monitoring-tools/</link><pubDate>Thu, 28 May 2026 09:30:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/network-bandwidth-monitoring-tools/</guid><category>networking</category><category>monitoring</category><description>Compare nload, iftop, bmon, and vnstat for checking Linux network bandwidth in real time and tracking historical traffic usage.</description><content:encoded>&lt;p&gt;When a server feels slow or a connection is saturated, the first question is usually &amp;ldquo;what is using the bandwidth?&amp;rdquo; The answer depends on what you need to see: the total rate on an interface, the individual connections moving the most data, a graph across several interfaces, or how much you have transferred this month. No single tool does all of this well, so it helps to know which one fits each question.&lt;/p&gt;
&lt;p&gt;This guide compares four command-line bandwidth monitors: nload, iftop, bmon, and vnstat. Each one is small, available in the default repositories, and built for a different angle on network usage.&lt;/p&gt;
&lt;h2 id="nload-per-interface-throughput"&gt;nload: Per-Interface Throughput &lt;a class="headline-link" href="#nload-per-interface-throughput" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;nload is the simplest of the four. It shows the incoming and outgoing rate on a single interface in real time, with a small ASCII graph and running statistics. It answers the question &amp;ldquo;how fast is this interface moving data right now?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Install it on Ubuntu, Debian, and Derivatives:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install nload&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On Fedora, RHEL, and Derivatives, install it with &lt;code&gt;dnf&lt;/code&gt;. On RHEL-compatible systems, enable EPEL first if the package is not available:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dnf install nload&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Run it with no arguments to monitor the default interface, or name an interface directly:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nload eth0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The display splits into incoming and outgoing sections, each showing the current rate along with the average, minimum, maximum, and total transferred:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Device eth0 [192.168.1.50] (1/1):
Incoming:
Curr: 4.21 MBit/s
Avg: 2.88 MBit/s
Max: 9.10 MBit/s
Ttl: 1.42 GByte
Outgoing:
Curr: 512.00 kBit/s
Avg: 320.10 kBit/s&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When more than one interface is present, the left and right arrow keys cycle between them. Press &lt;code&gt;q&lt;/code&gt; to quit. nload does not need root privileges, which makes it the quickest way to glance at raw throughput.&lt;/p&gt;
&lt;h2 id="iftop-bandwidth-by-connection"&gt;iftop: Bandwidth by Connection &lt;a class="headline-link" href="#iftop-bandwidth-by-connection" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;nload tells you how much traffic is flowing, but not where it is going. iftop fills that gap. It works like &lt;code&gt;top&lt;/code&gt; for the network, listing active connections sorted by bandwidth so you can see which hosts and ports are responsible for the load.&lt;/p&gt;
&lt;p&gt;Install it on Ubuntu, Debian, and Derivatives:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install iftop&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On Fedora, RHEL, and Derivatives, use &lt;code&gt;dnf&lt;/code&gt;. On RHEL-compatible systems, enable EPEL first if the package is not available:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dnf install iftop&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;iftop needs to capture packets, so it must run as root. Point it at an interface:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo iftop -i eth0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Each row shows a pair of hosts and the bandwidth between them, averaged over the last 2, 10, and 40 seconds:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt; =&amp;gt; 192.168.1.50 4.10Mb 3.85Mb 3.20Mb
192.0.2.14 &amp;lt;= 256Kb 210Kb 180Kb&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;By default iftop resolves IP addresses to hostnames and port numbers to service names, which can be slow and can clutter the view. Three flags make it more useful on a busy server: &lt;code&gt;-n&lt;/code&gt; skips DNS hostname lookups, &lt;code&gt;-N&lt;/code&gt; shows ports as numbers instead of service names, and &lt;code&gt;-P&lt;/code&gt; displays the port column so you can tell which service is responsible:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo iftop -i eth0 -nNP&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Press &lt;code&gt;q&lt;/code&gt; to quit. When you need to find the connection saturating a link, iftop is the tool to reach for. To then identify the local process behind a port, pair it with the &lt;a href="https://linuxize.com/post/ss-command-in-linux/"&gt;ss command&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="bmon-multiple-interfaces-at-a-glance"&gt;bmon: Multiple Interfaces at a Glance &lt;a class="headline-link" href="#bmon-multiple-interfaces-at-a-glance" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;bmon (bandwidth monitor) is the right choice when you want to watch several interfaces at once. It lists every interface with its current receive and transmit rates, and draws a live graph for the one you select, which is handy on routers, gateways, and virtualization hosts with many NICs and bridges.&lt;/p&gt;
&lt;p&gt;Install it on Ubuntu, Debian, and Derivatives:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install bmon&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On Fedora, RHEL, and Derivatives, use &lt;code&gt;dnf&lt;/code&gt;. On RHEL-compatible systems, enable EPEL first if the package is not available:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dnf install bmon&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Launch it with no arguments:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;bmon&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The top pane lists all interfaces with their RX and TX rates. Use the arrow keys to highlight an interface, and the lower pane updates its graph to match. Press &lt;code&gt;d&lt;/code&gt; to toggle detailed counters such as errors and dropped packets, and &lt;code&gt;q&lt;/code&gt; to quit. bmon reads kernel counters and does not require root for basic monitoring.&lt;/p&gt;
&lt;h2 id="vnstat-historical-usage-over-time"&gt;vnstat: Historical Usage Over Time &lt;a class="headline-link" href="#vnstat-historical-usage-over-time" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The first three tools show what is happening now and forget it the moment you quit. vnstat is different: it runs as a background service, records traffic per interface into a database, and reports usage by hour, day, and month. It answers &amp;ldquo;how much data have I used this month?&amp;rdquo; rather than &amp;ldquo;what is the rate right now?&amp;rdquo;, which makes it the tool for usage caps and capacity planning.&lt;/p&gt;
&lt;p&gt;Install it and enable the service on Ubuntu, Debian, and Derivatives:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install vnstat
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; --now vnstat&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On Fedora, RHEL, and Derivatives, install the package first. On RHEL-compatible systems, enable EPEL first if the package is not available:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dnf install vnstat
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; --now vnstat&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;vnstat needs time to collect data, because it samples the interface counters periodically rather than capturing packets. After it has been running for a while, view a summary:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vnstat&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt; eth0 since 2026-01-01
rx: 42.18 GiB tx: 8.04 GiB total: 50.22 GiB
monthly
rx | tx | total
------------------------+-------------+------------
2026-01 42.18 GiB | 8.04 GiB | 50.22 GiB&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Several flags change the reporting period: &lt;code&gt;vnstat -h&lt;/code&gt; for hourly, &lt;code&gt;vnstat -d&lt;/code&gt; for daily, and &lt;code&gt;vnstat -m&lt;/code&gt; for monthly. For a live rate display similar to the other tools, use &lt;code&gt;vnstat -l&lt;/code&gt;, which shows the current throughput until you press &lt;code&gt;Ctrl+C&lt;/code&gt;. Because the daemon keeps logging, the history survives reboots.&lt;/p&gt;
&lt;h2 id="which-tool-to-use"&gt;Which Tool to Use &lt;a class="headline-link" href="#which-tool-to-use" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The four tools overlap a little but each has a clear best use:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Shows&lt;/th&gt;
&lt;th&gt;Needs root&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;nload&lt;/td&gt;
&lt;td&gt;Per-interface in/out rate&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;A quick glance at total throughput&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;iftop&lt;/td&gt;
&lt;td&gt;Per-connection bandwidth&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Finding which host or port is using the link&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;bmon&lt;/td&gt;
&lt;td&gt;All interfaces plus graphs&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Hosts with many interfaces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vnstat&lt;/td&gt;
&lt;td&gt;Historical hourly/daily/monthly totals&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Tracking usage over time and quotas&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;In practice, many administrators keep vnstat running for history and reach for iftop when they need to investigate a live spike.&lt;/p&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;command not found&lt;/code&gt; after installing&lt;/strong&gt;&lt;br&gt;
Confirm the package installed with &lt;code&gt;apt list --installed | grep &amp;lt;tool&amp;gt;&lt;/code&gt; or &lt;code&gt;dnf list installed &amp;lt;tool&amp;gt;&lt;/code&gt;. On Ubuntu, the package may be in the &lt;code&gt;universe&lt;/code&gt; repository; on RHEL-compatible systems, you may need to enable EPEL first.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;iftop shows no traffic&lt;/strong&gt;&lt;br&gt;
iftop must run as root and be pointed at the active interface. List interfaces with &lt;code&gt;ip link&lt;/code&gt; and pass the correct one with &lt;code&gt;-i&lt;/code&gt;. Traffic on the loopback interface will not appear unless you select &lt;code&gt;lo&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;vnstat reports &amp;ldquo;Not enough data available yet&amp;rdquo;&lt;/strong&gt;&lt;br&gt;
The daemon has not collected a full sampling interval. Confirm the service is running with &lt;code&gt;systemctl status vnstat&lt;/code&gt; and wait a few minutes for the first data points.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Rates look wrong on a virtual interface&lt;/strong&gt;&lt;br&gt;
Bridges, bonds, and VLAN interfaces report aggregate counters. Monitor the underlying physical interface as well to see where the traffic actually enters the host.&lt;/p&gt;
&lt;h2 id="faq"&gt;FAQ &lt;a class="headline-link" href="#faq" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Which tool shows bandwidth per process?&lt;/strong&gt;&lt;br&gt;
None of these four map traffic to a process directly. iftop shows it per connection; to tie a connection to a process, use &lt;code&gt;ss -tp&lt;/code&gt; or &lt;code&gt;nethogs&lt;/code&gt;, which groups bandwidth by process.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Do these tools work over SSH?&lt;/strong&gt;&lt;br&gt;
Yes. All four are terminal applications and run fine in an SSH session, which is exactly how they are used on headless servers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Does vnstat slow down the network?&lt;/strong&gt;&lt;br&gt;
No. vnstat reads interface counters periodically rather than inspecting packets, so its overhead is negligible even on busy links.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Can I monitor a specific interface only?&lt;/strong&gt;&lt;br&gt;
Yes. nload, iftop, and vnstat all accept an interface name (for example &lt;code&gt;eth0&lt;/code&gt;), and bmon lets you select one from its list.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Pick the tool that matches the question: nload for a fast look at throughput, iftop to find the connection eating the link, bmon for many interfaces, and vnstat for usage history. When you need to move from bandwidth numbers to sockets and interface details, pair them with the lower-level &lt;a href="https://linuxize.com/post/ss-command-in-linux/"&gt;ss&lt;/a&gt;
and &lt;a href="https://linuxize.com/post/linux-ip-command/"&gt;ip&lt;/a&gt;
commands.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/network-bandwidth-monitoring-tools/featured_hu_7577fc99bffa884d.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>Bash set Command: set -e, set -x, and set -u Explained</title><link>https://linuxize.com/post/bash-set-command/</link><pubDate>Wed, 27 May 2026 15:53:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/bash-set-command/</guid><category>bash</category><description>How the bash set builtin changes shell behavior, with practical examples of set -e to exit on errors, set -u to catch unset variables, set -x to trace execution, and pipefail.</description><content:encoded>&lt;p&gt;By default, a bash script keeps running even after a command fails. A typo in a variable name expands to an empty string, a failed &lt;code&gt;cd&lt;/code&gt; is ignored, and the script marches on as if nothing happened, often doing real damage by the time it stops. The &lt;code&gt;set&lt;/code&gt; builtin changes that. With a few flags, you can make bash stop on the first error, treat unset variables as mistakes, and print each command as it runs.&lt;/p&gt;
&lt;p&gt;This guide explains the &lt;code&gt;set&lt;/code&gt; command and the three options you will use most: &lt;code&gt;set -e&lt;/code&gt;, &lt;code&gt;set -u&lt;/code&gt;, and &lt;code&gt;set -x&lt;/code&gt;, plus &lt;code&gt;pipefail&lt;/code&gt; and how to combine them into a strict mode.&lt;/p&gt;
&lt;h2 id="what-the-set-command-does"&gt;What the set Command Does &lt;a class="headline-link" href="#what-the-set-command-does" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;set&lt;/code&gt; is a shell builtin that turns shell options on or off and sets the positional parameters. For options, it takes this form:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="txt"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;txt&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;set [options]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Each option has a short form and a long form. A leading dash turns an option on, and a leading plus turns it off, which is the reverse of what most people expect:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;set -e&lt;/code&gt; enables an option (here, exit on error).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;set +e&lt;/code&gt; disables it.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;set -o errexit&lt;/code&gt; is the long form of &lt;code&gt;set -e&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;set +o errexit&lt;/code&gt; is the long form of &lt;code&gt;set +e&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You place these near the top of a script so they apply to everything that follows, or around a specific block when you only want them in part of the script.&lt;/p&gt;
&lt;h2 id="set--e-exit-on-error"&gt;set -e: Exit on Error &lt;a class="headline-link" href="#set--e-exit-on-error" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;set -e&lt;/code&gt; (also written &lt;code&gt;set -o errexit&lt;/code&gt;) tells bash to exit immediately if any command returns a non-zero status. It stops a script from continuing past a failure.&lt;/p&gt;
&lt;p&gt;Consider a script that changes into a directory and then writes a log message:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;cleanup.sh&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; -e
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /var/cache/myapp
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Cleaning application cache\n&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Without &lt;code&gt;set -e&lt;/code&gt;, if the &lt;code&gt;cd&lt;/code&gt; fails because the directory does not exist, the script would continue and run the next command in whatever directory it started in. With &lt;code&gt;set -e&lt;/code&gt;, the failed &lt;code&gt;cd&lt;/code&gt; stops the script before the log message or any later cache command runs.&lt;/p&gt;
&lt;p&gt;There are important exceptions where &lt;code&gt;set -e&lt;/code&gt; does not trigger an exit, and they trip people up. A command does not cause an exit when it is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;part of an &lt;code&gt;if&lt;/code&gt;, &lt;code&gt;while&lt;/code&gt;, or &lt;code&gt;until&lt;/code&gt; test,&lt;/li&gt;
&lt;li&gt;joined with &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; or &lt;code&gt;||&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;preceded by &lt;code&gt;!&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is by design, so that you can test a command&amp;rsquo;s exit status. If you need a specific command&amp;rsquo;s failure to be tolerated, append &lt;code&gt;|| true&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;grep &lt;span class="s2"&gt;&amp;#34;pattern&amp;#34;&lt;/span&gt; file.txt &lt;span class="o"&gt;||&lt;/span&gt; true&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="set--u-treat-unset-variables-as-errors"&gt;set -u: Treat Unset Variables as Errors &lt;a class="headline-link" href="#set--u-treat-unset-variables-as-errors" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;set -u&lt;/code&gt; (or &lt;code&gt;set -o nounset&lt;/code&gt;) makes bash exit when you reference a variable that has not been set. This catches one of the most common scripting bugs: a typo in a variable name that silently expands to nothing.&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;deploy.sh&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; -u
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;target_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/srv/www&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Target cache directory: %s\n&amp;#39;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;tagret_dir&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/cache&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The variable name is misspelled as &lt;code&gt;tagret_dir&lt;/code&gt;. Without &lt;code&gt;set -u&lt;/code&gt;, &lt;code&gt;${tagret_dir}&lt;/code&gt; expands to an empty string, and the command prints &lt;code&gt;/cache&lt;/code&gt; instead of &lt;code&gt;/srv/www/cache&lt;/code&gt;. With &lt;code&gt;set -u&lt;/code&gt;, bash stops with an error instead:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;deploy.sh: line 5: tagret_dir: unbound variable&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When a variable is legitimately optional, provide a default with &lt;code&gt;${VAR:-default}&lt;/code&gt; so the reference is always defined:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Deploying to &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ENVIRONMENT&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;staging&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="set--x-trace-each-command"&gt;set -x: Trace Each Command &lt;a class="headline-link" href="#set--x-trace-each-command" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;set -x&lt;/code&gt; (or &lt;code&gt;set -o xtrace&lt;/code&gt;) prints every command to standard error before it runs, with the expanded values of any variables. It is the fastest way to see exactly what a script is doing without adding &lt;code&gt;echo&lt;/code&gt; statements everywhere.&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;greet.sh&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; -x
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Linuxize&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Hello, &lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Running the script shows each command, prefixed with a &lt;code&gt;+&lt;/code&gt;, alongside its normal output:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;+ name=Linuxize
+ echo &amp;#39;Hello, Linuxize&amp;#39;
Hello, Linuxize&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The trace shows the variable already expanded, so you see the real value bash used. The prefix comes from the &lt;code&gt;PS4&lt;/code&gt; variable. Set it to include the script name and line number, which is invaluable in longer scripts:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;PS4&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;+ ${BASH_SOURCE}:${LINENO}: &amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Because tracing is noisy, it is common to enable it only around the section you are debugging with &lt;code&gt;set -x&lt;/code&gt; and then turn it off again with &lt;code&gt;set +x&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="set--o-pipefail-catch-failures-in-a-pipeline"&gt;set -o pipefail: Catch Failures in a Pipeline &lt;a class="headline-link" href="#set--o-pipefail-catch-failures-in-a-pipeline" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;By default, a pipeline returns the exit status of its last command, so a failure earlier in the pipe is hidden. In the pipeline below, &lt;code&gt;wc&lt;/code&gt; succeeds even though &lt;code&gt;grep&lt;/code&gt; failed because the file does not exist:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;grep &lt;span class="s2"&gt;&amp;#34;ERROR&amp;#34;&lt;/span&gt; missing.log &lt;span class="p"&gt;|&lt;/span&gt; wc -l&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Without &lt;code&gt;pipefail&lt;/code&gt;, the pipeline exits with the status from &lt;code&gt;wc&lt;/code&gt;, which is &lt;code&gt;0&lt;/code&gt;. &lt;code&gt;set -o pipefail&lt;/code&gt; changes this so the pipeline returns the status of the rightmost command that failed, or &lt;code&gt;0&lt;/code&gt; if every command succeeded. Combined with &lt;code&gt;set -e&lt;/code&gt;, it makes a failed first stage actually stop the script.&lt;/p&gt;
&lt;h2 id="combine-them-into-a-strict-mode"&gt;Combine Them into a Strict Mode &lt;a class="headline-link" href="#combine-them-into-a-strict-mode" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;These options are most useful together. The common combination at the top of a careful script is:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;script.sh&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; -euo pipefail&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This enables exit-on-error, unset-variable checking, and pipeline failure detection in one line. Add &lt;code&gt;x&lt;/code&gt; as &lt;code&gt;set -euxo pipefail&lt;/code&gt; while debugging, then remove it. This pattern is the foundation of what is often called &amp;ldquo;bash strict mode&amp;rdquo;, which our &lt;a href="https://linuxize.com/post/bash-strict-mode/"&gt;bash strict mode&lt;/a&gt;
guide covers in more depth, including the trade-offs of &lt;code&gt;set -e&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="enable-and-disable-options-for-a-block"&gt;Enable and Disable Options for a Block &lt;a class="headline-link" href="#enable-and-disable-options-for-a-block" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You do not have to apply an option to the whole script. Turn one on for a block and off afterward. This is common with tracing:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; -x
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;deploy_application
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sync_assets
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; +x&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Only the two commands between &lt;code&gt;set -x&lt;/code&gt; and &lt;code&gt;set +x&lt;/code&gt; are traced. The same pattern works with &lt;code&gt;set -e&lt;/code&gt; when you have a section where you want to handle errors manually.&lt;/p&gt;
&lt;h2 id="view-the-current-options"&gt;View the Current Options &lt;a class="headline-link" href="#view-the-current-options" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Running &lt;code&gt;set -o&lt;/code&gt; with no flag prints every option and whether it is on or off. The full list has more than two dozen entries; the four options discussed here are:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; -o&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;errexit off
nounset off
pipefail off
xtrace off&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is a quick way to confirm which options an interactive shell or a sourced script has enabled.&lt;/p&gt;
&lt;h2 id="set-positional-parameters"&gt;Set Positional Parameters &lt;a class="headline-link" href="#set-positional-parameters" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;set&lt;/code&gt; command has a second job unrelated to options: when given arguments after &lt;code&gt;--&lt;/code&gt;, it replaces the positional parameters &lt;code&gt;$1&lt;/code&gt;, &lt;code&gt;$2&lt;/code&gt;, and so on. This is useful when you want to reset the arguments a script or function reads:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; -- one two three
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;two&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;--&lt;/code&gt; marks the end of options so that arguments starting with a dash are not mistaken for flags.&lt;/p&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For a printable quick reference, see the &lt;a href="https://linuxize.com/cheatsheet/bash/"&gt;Bash cheatsheet&lt;/a&gt;
.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Long form&lt;/th&gt;
&lt;th&gt;Effect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;set -e&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;set -o errexit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exit immediately on a command failure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;set -u&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;set -o nounset&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Error when an unset variable is used&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;set -x&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;set -o xtrace&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print each command before running it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;set -o pipefail&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;set -o pipefail&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fail a pipeline if any command in it fails&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;set +e&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;set +o errexit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Disable an option (plus instead of dash)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;set -o&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;List all options and their state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;set -- a b c&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Set the positional parameters&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="faq"&gt;FAQ &lt;a class="headline-link" href="#faq" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;What is the difference between set -e and set -o errexit?&lt;/strong&gt;&lt;br&gt;
They are the same thing. &lt;code&gt;set -e&lt;/code&gt; is the short form and &lt;code&gt;set -o errexit&lt;/code&gt; is the long, more readable form. Use whichever you prefer; long forms are common in shared scripts because they are self-documenting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why does my script still continue after a command fails with set -e?&lt;/strong&gt;&lt;br&gt;
The failed command is probably part of an &lt;code&gt;if&lt;/code&gt; test, joined with &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; or &lt;code&gt;||&lt;/code&gt;, or preceded by &lt;code&gt;!&lt;/code&gt;. In those positions bash deliberately ignores the failure so you can test exit status. A command substitution or a function call can also mask the failure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What does the plus sign do, as in set +x?&lt;/strong&gt;&lt;br&gt;
A plus disables an option that a dash would enable. &lt;code&gt;set -x&lt;/code&gt; turns tracing on and &lt;code&gt;set +x&lt;/code&gt; turns it off, so you can scope an option to part of a script.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Should I always use set -euo pipefail?&lt;/strong&gt;&lt;br&gt;
It is a good default for new scripts and catches many bugs early, but &lt;code&gt;set -e&lt;/code&gt; in particular has surprising edge cases. For complex scripts, understand how each option behaves and handle the exceptions explicitly rather than assuming the script is fully protected.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;set&lt;/code&gt; builtin turns bash from a forgiving shell into a strict one: &lt;code&gt;set -e&lt;/code&gt; stops on errors, &lt;code&gt;set -u&lt;/code&gt; catches typos in variable names, and &lt;code&gt;set -x&lt;/code&gt; shows you exactly what ran. Combine them as &lt;code&gt;set -euo pipefail&lt;/code&gt; at the top of a script, scope tracing to the block you are debugging, and your scripts will fail loudly and early instead of quietly doing the wrong thing. For more on writing reliable scripts, see our guides on &lt;a href="https://linuxize.com/post/bash-best-practices/"&gt;bash best practices&lt;/a&gt;
and &lt;a href="https://linuxize.com/post/bash-functions/"&gt;bash functions&lt;/a&gt;
.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/bash-set-command/featured_hu_d052d54007380a15.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>ripgrep Cheatsheet</title><link>https://linuxize.com/cheatsheet/ripgrep/</link><pubDate>Tue, 26 May 2026 10:20:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/cheatsheet/ripgrep/</guid><description>Quick reference for recursive text searches with the ripgrep command</description><content:encoded>&lt;div class="card"&gt;
&lt;h3 id="basic-search"&gt;Basic Search &lt;a class="headline-link" href="#basic-search" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Search files and directories for matching text.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/ripgrep-in-linux/"&gt;&lt;code&gt;rg &amp;quot;pattern&amp;quot;&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Search recursively from the current directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg &amp;quot;pattern&amp;quot; file.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search a single file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg &amp;quot;pattern&amp;quot; dir/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search a specific directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg &amp;quot;pattern&amp;quot; file1 file2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search specific files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg --version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the installed ripgrep version&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="file-type-filters"&gt;File Type Filters &lt;a class="headline-link" href="#file-type-filters" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Limit searches to known file types.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg -t py &amp;quot;pattern&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search only Python files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg -t js &amp;quot;pattern&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search only JavaScript files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg -t markdown &amp;quot;pattern&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search only Markdown files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg -T js &amp;quot;pattern&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exclude JavaScript files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg --type-list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show available file type names&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="glob-filters"&gt;Glob Filters &lt;a class="headline-link" href="#glob-filters" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Include or exclude paths with glob patterns.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg -g '*.log' &amp;quot;error&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search only &lt;code&gt;.log&lt;/code&gt; files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg -g '*.conf' &amp;quot;listen&amp;quot; /etc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search matching config files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg -g '!*.min.js' &amp;quot;console.log&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exclude minified JavaScript files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg -g '!node_modules/' &amp;quot;TODO&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exclude a directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg -g '*.md' -g '!README.md' &amp;quot;pattern&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Combine include and exclude globs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="case-and-literal-search"&gt;Case and Literal Search &lt;a class="headline-link" href="#case-and-literal-search" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Control case matching and regex handling.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg -i &amp;quot;warning&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Case-insensitive search&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg -S &amp;quot;warning&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Smart case search&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg -s &amp;quot;Warning&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Force case-sensitive search&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg -F &amp;quot;price[0]&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search for a fixed string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg -w &amp;quot;id&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Match whole words only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="counts-and-file-lists"&gt;Counts and File Lists &lt;a class="headline-link" href="#counts-and-file-lists" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Summarize matches or print filenames.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg -c &amp;quot;error&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Count matching lines per file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg --count-matches &amp;quot;error&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Count individual matches per file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg -l &amp;quot;error&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List files with matches&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg --files-without-match &amp;quot;error&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List files without matches&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg --stats &amp;quot;error&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print search statistics&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="context-output"&gt;Context Output &lt;a class="headline-link" href="#context-output" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Show lines around each match.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg -C 3 &amp;quot;panic&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show 3 lines before and after&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg -A 2 &amp;quot;error&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show 2 lines after&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg -B 2 &amp;quot;error&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show 2 lines before&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg -n &amp;quot;error&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show line numbers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg -N &amp;quot;error&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hide line numbers&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="patterns-and-regex"&gt;Patterns and Regex &lt;a class="headline-link" href="#patterns-and-regex" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Search with multiple patterns and regex features.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg -e &amp;quot;error&amp;quot; -e &amp;quot;warning&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Match either pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg -e &amp;quot;--force&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search for a pattern starting with &lt;code&gt;-&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg 'error|warning'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use regex alternation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg '^server'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Match lines starting with &lt;code&gt;server&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg 'listen$'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Match lines ending with &lt;code&gt;listen&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="hidden-and-ignored-files"&gt;Hidden and Ignored Files &lt;a class="headline-link" href="#hidden-and-ignored-files" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Search paths that &lt;code&gt;rg&lt;/code&gt; skips by default.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg --hidden &amp;quot;api_key&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Include hidden files and directories&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg --no-ignore &amp;quot;TODO&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ignore &lt;code&gt;.gitignore&lt;/code&gt;, &lt;code&gt;.ignore&lt;/code&gt;, and &lt;code&gt;.rgignore&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg --hidden --no-ignore &amp;quot;password&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search hidden and ignored files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg -u &amp;quot;pattern&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reduce ignore filtering by one level&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg -uuu &amp;quot;pattern&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search almost everything, including binary files&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="replacement-preview"&gt;Replacement Preview &lt;a class="headline-link" href="#replacement-preview" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Preview changed output without editing files.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg &amp;quot;old&amp;quot; -r &amp;quot;new&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Preview replacing &lt;code&gt;old&lt;/code&gt; with &lt;code&gt;new&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg '(foo)(bar)' -r '$2$1'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reorder capture groups in output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg -o 'v[0-9]+\.[0-9]+\.[0-9]+'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print only matched version strings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rg -o -r '$1' 'version = &amp;quot;([^&amp;quot;]+)&amp;quot;'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Extract a captured value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-use-sed-to-find-and-replace-string-in-files/"&gt;&lt;code&gt;rg &amp;quot;foo&amp;quot; -r &amp;quot;bar&amp;quot;&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Preview before using &lt;code&gt;sed&lt;/code&gt; or an editor&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Quick checks for common &lt;code&gt;rg&lt;/code&gt; issues.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Issue&lt;/th&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;File is missing from results&lt;/td&gt;
&lt;td&gt;Run &lt;code&gt;rg --debug &amp;quot;pattern&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hidden files are skipped&lt;/td&gt;
&lt;td&gt;Add &lt;code&gt;--hidden&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ignored files are skipped&lt;/td&gt;
&lt;td&gt;Add &lt;code&gt;--no-ignore&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Glob does not work&lt;/td&gt;
&lt;td&gt;Quote it, for example &lt;code&gt;-g '*.conf'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pattern starts with &lt;code&gt;-&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;rg -e &amp;quot;--flag&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="common-options"&gt;Common Options &lt;a class="headline-link" href="#common-options" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Useful flags to remember.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-t TYPE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search only a file type&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-T TYPE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exclude a file type&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-g GLOB&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Include or exclude paths by glob&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-i&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ignore case&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-S&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Smart case&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-F&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fixed string search&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-w&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Whole-word match&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-l&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List matching files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--files-without-match&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List non-matching files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-C N&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show context lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-r TEXT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Preview replacement output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--hidden&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Include hidden files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--no-ignore&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ignore ignore-file rules&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="related-guides"&gt;Related Guides &lt;a class="headline-link" href="#related-guides" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Use these guides for full command workflows.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Guide&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/ripgrep-in-linux/"&gt;ripgrep Command in Linux&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Full &lt;code&gt;rg&lt;/code&gt; tutorial with practical examples&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-use-grep-command-to-search-files-in-linux/"&gt;grep Command in Linux&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Standard text search with GNU &lt;code&gt;grep&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/grep-exclude/"&gt;Grep Exclude&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Exclude files, directories, and patterns with &lt;code&gt;grep&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-find-files-in-linux-using-the-command-line/"&gt;find Files in Linux&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Locate files by metadata and path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-use-sed-to-find-and-replace-string-in-files/"&gt;sed Find and Replace&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Replace text after previewing matches&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;</content:encoded></item><item><title>ripgrep Command in Linux: Fast Recursive Search</title><link>https://linuxize.com/post/ripgrep-in-linux/</link><pubDate>Tue, 26 May 2026 09:30:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/ripgrep-in-linux/</guid><category>grep</category><category>linux commands</category><description>The ripgrep command searches directories recursively while respecting ignore files. This guide explains rg syntax, file type filters, context lines, replacement previews, and config defaults.</description><content:encoded>&lt;p&gt;Searching through a large codebase or a directory full of log files with &lt;code&gt;grep&lt;/code&gt; often means adding &lt;code&gt;-r&lt;/code&gt; for recursion, &lt;code&gt;-n&lt;/code&gt; for line numbers, and &lt;code&gt;--include&lt;/code&gt; or &lt;code&gt;--exclude&lt;/code&gt; rules to avoid unrelated files. In a Git repository, you may also need extra excludes for build directories, vendor files, and binary output.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ripgrep&lt;/code&gt; (&lt;code&gt;rg&lt;/code&gt;) handles the common case with better defaults. It searches directories recursively, respects &lt;code&gt;.gitignore&lt;/code&gt;, &lt;code&gt;.ignore&lt;/code&gt;, and &lt;code&gt;.rgignore&lt;/code&gt; rules, skips hidden and binary files during recursive searches, and shows friendly interactive output. It is also much faster than traditional recursive &lt;code&gt;grep&lt;/code&gt; on many large file trees because it uses Rust&amp;rsquo;s regex engine and parallel directory traversal.&lt;/p&gt;
&lt;p&gt;This guide explains how to use the &lt;code&gt;ripgrep&lt;/code&gt; command in Linux, from basic searches to file type filters, context lines, replacement previews, and config defaults.&lt;/p&gt;
&lt;h2 id="installing-ripgrep"&gt;Installing ripgrep &lt;a class="headline-link" href="#installing-ripgrep" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;On Ubuntu, Debian, and Derivatives, install the &lt;code&gt;ripgrep&lt;/code&gt; package with &lt;code&gt;apt&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install ripgrep&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On Fedora, RHEL, and Derivatives, use &lt;code&gt;dnf&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dnf install ripgrep&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On Arch Linux, install it from the official repositories:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo pacman -S ripgrep&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Verify the installation with:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg --version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The output shows the installed version:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;ripgrep 15.1.0 (rev af60c2de9d)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Your distribution may ship a different version, but the examples in this guide use common options available in current &lt;code&gt;ripgrep&lt;/code&gt; releases.&lt;/p&gt;
&lt;h2 id="syntax"&gt;Syntax &lt;a class="headline-link" href="#syntax" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The basic syntax for the &lt;code&gt;rg&lt;/code&gt; command is:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="txt"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;txt&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg [OPTIONS] PATTERN [PATH ...]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;PATTERN&lt;/code&gt; argument is the text or regular expression to search for. If you omit &lt;code&gt;PATH&lt;/code&gt;, &lt;code&gt;rg&lt;/code&gt; searches recursively from the current directory.&lt;/p&gt;
&lt;p&gt;For example, &lt;code&gt;rg &amp;quot;error&amp;quot;&lt;/code&gt; searches the current directory tree, while &lt;code&gt;rg &amp;quot;error&amp;quot; logs/&lt;/code&gt; limits the search to the &lt;code&gt;logs&lt;/code&gt; directory.&lt;/p&gt;
&lt;h2 id="basic-search"&gt;Basic Search &lt;a class="headline-link" href="#basic-search" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Search for a pattern in all files under the current directory:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg &lt;span class="s2"&gt;&amp;#34;error&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Example output:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;logs/app.log:42:error: connection refused
logs/app.log:78:error: timeout after 30s
src/main.py:114:raise ValueError(&amp;#34;error parsing config&amp;#34;)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The output shows the filename, line number, and matching line. In an interactive terminal, &lt;code&gt;rg&lt;/code&gt; also uses color by default, so you do not need the usual recursive &lt;code&gt;grep&lt;/code&gt; flags for the common code-search workflow.&lt;/p&gt;
&lt;p&gt;To search in a specific file, pass the filename after the pattern:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg &lt;span class="s2"&gt;&amp;#34;error&amp;#34;&lt;/span&gt; logs/app.log&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To search in a specific directory, pass the directory path:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg &lt;span class="s2"&gt;&amp;#34;error&amp;#34;&lt;/span&gt; logs/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="filtering-by-file-type"&gt;Filtering by File Type &lt;a class="headline-link" href="#filtering-by-file-type" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use &lt;code&gt;-t&lt;/code&gt; to restrict the search to a known file type:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg -t py &lt;span class="s2"&gt;&amp;#34;import os&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This searches only Python files. &lt;code&gt;ripgrep&lt;/code&gt; includes built-in definitions for many file types. To see the full list, run:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg --type-list&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To exclude a file type, use &lt;code&gt;-T&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg -T js &lt;span class="s2"&gt;&amp;#34;TODO&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You can also use glob patterns with &lt;code&gt;-g&lt;/code&gt; to match or exclude specific filenames. Quote glob patterns so the shell does not expand them before &lt;code&gt;rg&lt;/code&gt; receives them:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg -g &lt;span class="s1"&gt;&amp;#39;*.log&amp;#39;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;error&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Use a leading &lt;code&gt;!&lt;/code&gt; to exclude matching paths:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg -g &lt;span class="s1"&gt;&amp;#39;!*.min.js&amp;#39;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;console.log&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;!&lt;/code&gt; prefix belongs to &lt;code&gt;rg&lt;/code&gt;, so quoting is especially useful in shells where &lt;code&gt;!&lt;/code&gt; can trigger history expansion.&lt;/p&gt;
&lt;h2 id="case-insensitive-search"&gt;Case-Insensitive Search &lt;a class="headline-link" href="#case-insensitive-search" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Add &lt;code&gt;-i&lt;/code&gt; to ignore letter case:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg -i &lt;span class="s2"&gt;&amp;#34;warning&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This matches &lt;code&gt;warning&lt;/code&gt;, &lt;code&gt;Warning&lt;/code&gt;, &lt;code&gt;WARNING&lt;/code&gt;, and other case variants.&lt;/p&gt;
&lt;p&gt;For a more flexible default, use &lt;code&gt;-S&lt;/code&gt; or &lt;code&gt;--smart-case&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg -S &lt;span class="s2"&gt;&amp;#34;warning&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;With smart case, an all-lowercase pattern is case-insensitive, but a pattern with any uppercase letter is case-sensitive. For example, &lt;code&gt;rg -S &amp;quot;warning&amp;quot;&lt;/code&gt; matches &lt;code&gt;Warning&lt;/code&gt;, while &lt;code&gt;rg -S &amp;quot;Warning&amp;quot;&lt;/code&gt; searches for that exact capitalization.&lt;/p&gt;
&lt;h2 id="fixed-string-search"&gt;Fixed String Search &lt;a class="headline-link" href="#fixed-string-search" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;By default, &lt;code&gt;rg&lt;/code&gt; treats the pattern as a regular expression. Use &lt;code&gt;-F&lt;/code&gt; to search for a literal string instead, which is useful when the pattern contains regex characters:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg -F &lt;span class="s2"&gt;&amp;#34;price[0]&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Without &lt;code&gt;-F&lt;/code&gt;, &lt;code&gt;[0]&lt;/code&gt; would be interpreted as a character class. With &lt;code&gt;-F&lt;/code&gt;, &lt;code&gt;rg&lt;/code&gt; searches for the literal text &lt;code&gt;price[0]&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="counting-and-listing-matches"&gt;Counting and Listing Matches &lt;a class="headline-link" href="#counting-and-listing-matches" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To count matching lines per file instead of printing the matching lines, use &lt;code&gt;-c&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg -c &lt;span class="s2"&gt;&amp;#34;error&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Example output:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;logs/app.log:14
logs/nginx/access.log:3&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The count is the number of matching lines, not the total number of matching words or strings.&lt;/p&gt;
&lt;p&gt;To print only filenames that contain at least one match, use &lt;code&gt;-l&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg -l &lt;span class="s2"&gt;&amp;#34;error&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To print only filenames that contain no matches, use &lt;code&gt;--files-without-match&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg --files-without-match &lt;span class="s2"&gt;&amp;#34;error&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This is different from combining &lt;code&gt;-v&lt;/code&gt; with &lt;code&gt;-l&lt;/code&gt;. The &lt;code&gt;--files-without-match&lt;/code&gt; option checks whether a file has zero matching lines.&lt;/p&gt;
&lt;h2 id="context-lines"&gt;Context Lines &lt;a class="headline-link" href="#context-lines" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When a matching line alone is not enough to understand the surrounding code, add context with &lt;code&gt;-C&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg -C &lt;span class="m"&gt;3&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;panic&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This prints three lines before and three lines after each match.&lt;/p&gt;
&lt;p&gt;Use &lt;code&gt;-A&lt;/code&gt; for lines after the match only:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg -A &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;def connect&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Use &lt;code&gt;-B&lt;/code&gt; for lines before the match only:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg -B &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;def connect&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Context output is useful when you want to inspect the code around a function, error message, or configuration value without opening each file.&lt;/p&gt;
&lt;h2 id="multiple-patterns"&gt;Multiple Patterns &lt;a class="headline-link" href="#multiple-patterns" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use &lt;code&gt;-e&lt;/code&gt; to search for more than one pattern in a single pass:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg -e &lt;span class="s2"&gt;&amp;#34;error&amp;#34;&lt;/span&gt; -e &lt;span class="s2"&gt;&amp;#34;warning&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This matches any line containing either word. It is equivalent to the regex &lt;code&gt;error|warning&lt;/code&gt;, but repeated &lt;code&gt;-e&lt;/code&gt; options are easier to read when patterns become longer.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;-e&lt;/code&gt; option is also useful when a pattern begins with a dash:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg -e &lt;span class="s2"&gt;&amp;#34;--force&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Without &lt;code&gt;-e&lt;/code&gt;, &lt;code&gt;rg&lt;/code&gt; would try to interpret &lt;code&gt;--force&lt;/code&gt; as an option.&lt;/p&gt;
&lt;h2 id="whole-word-match"&gt;Whole-Word Match &lt;a class="headline-link" href="#whole-word-match" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;-w&lt;/code&gt; flag restricts matches to whole words. This is useful when you want to find a variable name without matching it inside a longer name:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg -w &lt;span class="s2"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This matches &lt;code&gt;id&lt;/code&gt;, but not &lt;code&gt;uid&lt;/code&gt; or &lt;code&gt;invalid&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="invert-match"&gt;Invert Match &lt;a class="headline-link" href="#invert-match" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;-v&lt;/code&gt; flag prints lines that do not match the pattern:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg -v &lt;span class="s2"&gt;&amp;#34;^#&amp;#34;&lt;/span&gt; config.txt&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This shows all lines in &lt;code&gt;config.txt&lt;/code&gt; that do not begin with &lt;code&gt;#&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you want filenames that do not contain a pattern at all, use &lt;code&gt;--files-without-match&lt;/code&gt; instead:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg --files-without-match &lt;span class="s2"&gt;&amp;#34;version&amp;#34;&lt;/span&gt; -g &lt;span class="s1"&gt;&amp;#39;*.json&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="searching-hidden-files-and-ignoring-gitignore"&gt;Searching Hidden Files and Ignoring .gitignore &lt;a class="headline-link" href="#searching-hidden-files-and-ignoring-gitignore" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;By default, recursive &lt;code&gt;rg&lt;/code&gt; searches skip hidden files and directories, and respect &lt;code&gt;.gitignore&lt;/code&gt;, &lt;code&gt;.ignore&lt;/code&gt;, and &lt;code&gt;.rgignore&lt;/code&gt; files. This is usually what you want in a source tree.&lt;/p&gt;
&lt;p&gt;To include hidden files and directories, use &lt;code&gt;--hidden&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg --hidden &lt;span class="s2"&gt;&amp;#34;api_key&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To ignore &lt;code&gt;.gitignore&lt;/code&gt;, &lt;code&gt;.ignore&lt;/code&gt;, and &lt;code&gt;.rgignore&lt;/code&gt; rules, use &lt;code&gt;--no-ignore&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg --no-ignore &lt;span class="s2"&gt;&amp;#34;TODO&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;--no-ignore&lt;/code&gt; option does not include hidden files by itself. To search hidden files and ignored files in the same search, combine both options:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg --hidden --no-ignore &lt;span class="s2"&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="note callout callout-warning"&gt;
&lt;div class="callout-header"&gt;&lt;svg role="img" aria-hidden="true" class="callout-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"&gt;
&lt;path d="M10 20C4.477 20 0 15.523 0 10S4.477 0 10 0s10 4.477 10 10-4.477 10-10 10zm0-2c4.418 0 8-3.582 8-8s-3.582-8-8-8-8 3.582-8 8 3.582 8 8 8zm-.5-5h1c.276 0 .5.224.5.5v1c0 .276-.224.5-.5.5h-1c-.276 0-.5-.224-.5-.5v-1c0-.276.224-.5.5-.5zm0-8h1c.276 0 .5.224.5.5V8l-.5 3-1 .5L9 8V5.5c0-.276.224-.5.5-.5z"&gt;&lt;/path&gt;
&lt;/svg&gt;
&lt;span class="callout-title"&gt;Warning&lt;/span&gt;&lt;/div&gt;
&lt;div class="callout-body"&gt;Searching with &lt;code&gt;--hidden --no-ignore&lt;/code&gt; can include build artifacts, dependency directories, cache folders, and other large trees. It is much slower and can produce noisy results, so use it only when you have a specific reason to search outside the normal project files.&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="replacing-output"&gt;Replacing Output &lt;a class="headline-link" href="#replacing-output" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;-r&lt;/code&gt; option rewrites matching text in the output to show what a replacement would look like. It does not modify any files:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg &lt;span class="s2"&gt;&amp;#34;foo&amp;#34;&lt;/span&gt; -r &lt;span class="s2"&gt;&amp;#34;bar&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Example output:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;src/config.py:5:bar = get_setting(&amp;#34;bar&amp;#34;)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The output shows the line as it would look after replacing &lt;code&gt;foo&lt;/code&gt; with &lt;code&gt;bar&lt;/code&gt;. This is useful for previewing a project-wide rename before using &lt;a href="https://linuxize.com/post/how-to-use-sed-to-find-and-replace-string-in-files/"&gt;&lt;code&gt;sed&lt;/code&gt;&lt;/a&gt;
or a text editor&amp;rsquo;s find-and-replace feature.&lt;/p&gt;
&lt;p&gt;For capture groups in replacements, wrap the command in single quotes so the shell does not expand &lt;code&gt;$1&lt;/code&gt;, &lt;code&gt;$name&lt;/code&gt;, or similar replacement references before &lt;code&gt;rg&lt;/code&gt; runs.&lt;/p&gt;
&lt;h2 id="showing-only-the-match"&gt;Showing Only the Match &lt;a class="headline-link" href="#showing-only-the-match" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;By default, &lt;code&gt;rg&lt;/code&gt; prints the full line containing the match. Use &lt;code&gt;-o&lt;/code&gt; to print only the matched text:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rg -o &lt;span class="s1"&gt;&amp;#39;v[0-9]+\.[0-9]+\.[0-9]+&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Example output:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;package.json:4:v1.4.2
package-lock.json:8:v1.4.2&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is useful when you want to extract values from files rather than inspect matching lines in context.&lt;/p&gt;
&lt;h2 id="ripgrep-configuration-file"&gt;ripgrep Configuration File &lt;a class="headline-link" href="#ripgrep-configuration-file" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;ripgrep&lt;/code&gt; can read default options from a configuration file, but it does not automatically look in &lt;code&gt;~/.config/ripgrep/&lt;/code&gt; or any other fixed path. You must point &lt;code&gt;RIPGREP_CONFIG_PATH&lt;/code&gt; to the file you want &lt;code&gt;rg&lt;/code&gt; to read.&lt;/p&gt;
&lt;p&gt;For example, create a config file named &lt;code&gt;~/.ripgreprc&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="txt"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;~/.ripgreprc&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;txt&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;--smart-case
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;--hidden
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;--glob=!.git/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Then export the environment variable:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;RIPGREP_CONFIG_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.ripgreprc&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Add that &lt;code&gt;export&lt;/code&gt; line to your shell startup file, such as &lt;code&gt;~/.bashrc&lt;/code&gt; or &lt;code&gt;~/.zshrc&lt;/code&gt;, if you want the setting to apply in new terminal sessions.&lt;/p&gt;
&lt;p&gt;Each config file line is passed to &lt;code&gt;rg&lt;/code&gt; as one command-line argument. For options with values, use either &lt;code&gt;--option=value&lt;/code&gt; on one line or put the option and its value on separate lines.&lt;/p&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;rg does not find a file you expected&lt;/strong&gt;&lt;br&gt;
The file may be hidden, ignored by &lt;code&gt;.gitignore&lt;/code&gt;, ignored by &lt;code&gt;.ignore&lt;/code&gt; or &lt;code&gt;.rgignore&lt;/code&gt;, or detected as binary. Start with &lt;code&gt;rg --debug &amp;quot;pattern&amp;quot;&lt;/code&gt; to see why paths were skipped, then add &lt;code&gt;--hidden&lt;/code&gt;, &lt;code&gt;--no-ignore&lt;/code&gt;, or both only when needed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A glob pattern does not work as expected&lt;/strong&gt;&lt;br&gt;
Quote glob patterns, such as &lt;code&gt;rg -g '*.conf' &amp;quot;server&amp;quot;&lt;/code&gt; and &lt;code&gt;rg -g '!*.min.js' &amp;quot;console.log&amp;quot;&lt;/code&gt;. Without quotes, your shell may expand &lt;code&gt;*&lt;/code&gt; before &lt;code&gt;rg&lt;/code&gt; sees the pattern.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A pattern starting with - is treated as an option&lt;/strong&gt;&lt;br&gt;
Use &lt;code&gt;-e&lt;/code&gt; before the pattern, for example &lt;code&gt;rg -e &amp;quot;--force&amp;quot;&lt;/code&gt;. You can also use &lt;code&gt;--&lt;/code&gt; to stop option parsing, as in &lt;code&gt;rg -- &amp;quot;--force&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="options-reference"&gt;Options Reference &lt;a class="headline-link" href="#options-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-t TYPE&lt;/code&gt; - Search only files of the given type.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-T TYPE&lt;/code&gt; - Exclude files of the given type.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-g GLOB&lt;/code&gt; - Include or exclude files by glob. A &lt;code&gt;!&lt;/code&gt; prefix excludes paths.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-i&lt;/code&gt; - Search case-insensitively.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-F&lt;/code&gt; - Treat the pattern as a fixed string, not a regex.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-c&lt;/code&gt; - Count matching lines per file.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-l&lt;/code&gt; - List only filenames with matches.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--files-without-match&lt;/code&gt; - List only filenames without matches.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-C N&lt;/code&gt; - Show N lines of context around each match.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-A N&lt;/code&gt; - Show N lines after each match.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-B N&lt;/code&gt; - Show N lines before each match.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-e PATTERN&lt;/code&gt; - Add a search pattern. Repeat it to search for multiple patterns.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-w&lt;/code&gt; - Match whole words only.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-v&lt;/code&gt; - Invert the match and print non-matching lines.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-o&lt;/code&gt; - Print only the matched text, not the full line.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-r REPLACEMENT&lt;/code&gt; - Show output with matches replaced. This is a preview only.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-n&lt;/code&gt; - Show line numbers. This is enabled by default in interactive terminal output.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--hidden&lt;/code&gt; - Search hidden files and directories.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--no-ignore&lt;/code&gt; - Do not respect &lt;code&gt;.gitignore&lt;/code&gt;, &lt;code&gt;.ignore&lt;/code&gt;, and &lt;code&gt;.rgignore&lt;/code&gt; rules.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-S&lt;/code&gt;, &lt;code&gt;--smart-case&lt;/code&gt; - Search case-insensitively when the pattern is lowercase, and case-sensitively when it contains uppercase.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--stats&lt;/code&gt; - Print a summary of the search at the end.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-M N&lt;/code&gt; - Omit lines longer than N bytes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For a printable quick reference, see the &lt;a href="https://linuxize.com/cheatsheet/ripgrep/"&gt;ripgrep cheatsheet&lt;/a&gt;
.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Search recursively from the current directory&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rg &amp;quot;pattern&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search in a specific file&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rg &amp;quot;pattern&amp;quot; file.txt&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search in a specific directory&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rg &amp;quot;pattern&amp;quot; logs/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search only Python files&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rg -t py &amp;quot;pattern&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exclude JavaScript files&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rg -T js &amp;quot;pattern&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search by glob&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rg -g '*.log' &amp;quot;pattern&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exclude by glob&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rg -g '!*.min.js' &amp;quot;pattern&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search case-insensitively&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rg -i &amp;quot;pattern&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use smart case&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rg -S &amp;quot;pattern&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search for a fixed string&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rg -F &amp;quot;price[0]&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Count matching lines per file&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rg -c &amp;quot;pattern&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List matching filenames&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rg -l &amp;quot;pattern&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List filenames without matches&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rg --files-without-match &amp;quot;pattern&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Show three lines of context&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rg -C 3 &amp;quot;pattern&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search multiple patterns&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rg -e &amp;quot;foo&amp;quot; -e &amp;quot;bar&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Match a whole word&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rg -w &amp;quot;id&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Invert line matches&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rg -v &amp;quot;pattern&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Include hidden files&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rg --hidden &amp;quot;pattern&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ignore ignore files&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rg --no-ignore &amp;quot;pattern&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Include hidden and ignored files&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rg --hidden --no-ignore &amp;quot;pattern&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Preview replacement output&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rg &amp;quot;old&amp;quot; -r &amp;quot;new&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Print only matched text&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rg -o &amp;quot;pattern&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;ripgrep&lt;/code&gt; covers the same ground as &lt;a href="https://linuxize.com/post/how-to-use-grep-command-to-search-files-in-linux/"&gt;&lt;code&gt;grep&lt;/code&gt;&lt;/a&gt;
, but its recursive search, file filtering, and ignore-file support make it a better default for many code and log searches. For path-based file finding, pair it with &lt;a href="https://linuxize.com/post/how-to-find-files-in-linux-using-the-command-line/"&gt;&lt;code&gt;find&lt;/code&gt;&lt;/a&gt;
, or use &lt;code&gt;rg -l&lt;/code&gt; when you need a list of files that contain a specific pattern.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/ripgrep-in-linux/featured_hu_95063e1cb004cfc3.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>whois Command in Linux: Query Domain Registration Info</title><link>https://linuxize.com/post/whois-command-in-linux/</link><pubDate>Mon, 25 May 2026 11:30:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/whois-command-in-linux/</guid><category>dns</category><category>linux commands</category><description>The whois command looks up domain registration, registrar, name server, expiry, IP allocation, and AS number details from Linux.</description><content:encoded>&lt;p&gt;When you need to know who owns a domain, when it expires, which registrar handles it, or which organization holds a particular IP block, the &lt;code&gt;whois&lt;/code&gt; command is the fastest route. It queries the registry databases that record this information and returns a plain-text response you can scan in a terminal. The output format varies by registry, but the questions you can answer are consistent: registrar, name servers, registration and expiry dates, and contact info (where privacy rules allow).&lt;/p&gt;
&lt;p&gt;This guide explains how to use &lt;code&gt;whois&lt;/code&gt; in Linux to look up domains, IP addresses, and AS numbers, how to target a specific server, and how to parse the output for the fields you actually care about.&lt;/p&gt;
&lt;h2 id="whois-syntax"&gt;whois Syntax &lt;a class="headline-link" href="#whois-syntax" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The general form is:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="txt"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;txt&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;whois [OPTIONS] OBJECT&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;OBJECT&lt;/code&gt; is the domain, IP address, or AS number you want information about. With no options, &lt;code&gt;whois&lt;/code&gt; picks the right registry automatically based on the type of query.&lt;/p&gt;
&lt;h2 id="install-whois"&gt;Install whois &lt;a class="headline-link" href="#install-whois" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;whois&lt;/code&gt; is not always installed by default. On Ubuntu, Debian, and Derivatives:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install whois&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On Fedora, RHEL, and Derivatives:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dnf install whois&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Confirm it is in place:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;whois --version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Version 5.6.6.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The Debian-family &lt;code&gt;whois&lt;/code&gt; is an actively maintained client with built-in routing logic that knows which registry to ask for each TLD.&lt;/p&gt;
&lt;h2 id="look-up-a-domain"&gt;Look Up a Domain &lt;a class="headline-link" href="#look-up-a-domain" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The most common use is checking a domain:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;whois example.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt; Domain Name: EXAMPLE.COM
Registry Domain ID: 2336799_DOMAIN_COM-VRSN
Registrar WHOIS Server: whois.iana.org
Registrar URL: http://res-dom.iana.org
Updated Date: 2026-01-16T18:26:50Z
Creation Date: 1995-08-14T04:00:00Z
Registry Expiry Date: 2026-08-13T04:00:00Z
Registrar: RESERVED-Internet Assigned Numbers Authority
Registrar IANA ID: 376
Name Server: ELLIOTT.NS.CLOUDFLARE.COM
Name Server: HERA.NS.CLOUDFLARE.COM
DNSSEC: signedDelegation
...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The fields that matter most for everyday questions are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Registrar&lt;/code&gt;, the company managing the registration.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Creation Date&lt;/code&gt; and &lt;code&gt;Registry Expiry Date&lt;/code&gt;, which tell you how old the domain is and when it needs renewing.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Name Server&lt;/code&gt;, which lists the DNS servers authoritative for the domain.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DNSSEC&lt;/code&gt;, which shows whether the domain is cryptographically signed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For ccTLDs (&lt;code&gt;.de&lt;/code&gt;, &lt;code&gt;.uk&lt;/code&gt;, &lt;code&gt;.jp&lt;/code&gt;), the format differs because each country runs its own registry. The information is similar; the field names and order change.&lt;/p&gt;
&lt;h2 id="look-up-an-ip-address"&gt;Look Up an IP Address &lt;a class="headline-link" href="#look-up-an-ip-address" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;whois&lt;/code&gt; on an IP returns the network allocation, not the domain:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;whois 93.184.216.34&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;inetnum: 93.184.216.0 - 93.184.216.255
netname: EDGECAST-NETBLK-03
descr: NETBLK-03-EU-93-184-216-0-24
country: EU
admin-c: DS7892-RIPE
tech-c: DS7892-RIPE
status: ASSIGNED PA
...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This kind of query is the right tool for &amp;ldquo;who owns this IP that has been hitting my server&amp;rdquo; investigations. The output names the network block, maintainer, and abuse contact details when the registry publishes them.&lt;/p&gt;
&lt;h2 id="look-up-an-as-number"&gt;Look Up an AS Number &lt;a class="headline-link" href="#look-up-an-as-number" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Pass an autonomous system number with the &lt;code&gt;AS&lt;/code&gt; prefix:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;whois AS15169&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;ASNumber: 15169
ASName: GOOGLE
ASHandle: AS15169
RegDate: 2000-03-30
Updated: 2012-02-24
Ref: https://rdap.arin.net/registry/autnum/15169&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;AS lookups are useful when you trace a route with &lt;code&gt;mtr&lt;/code&gt; or &lt;code&gt;traceroute&lt;/code&gt; and want to know which network each hop belongs to.&lt;/p&gt;
&lt;h2 id="pick-a-specific-whois-server"&gt;Pick a Specific WHOIS Server &lt;a class="headline-link" href="#pick-a-specific-whois-server" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The default routing finds the right server for most TLDs, but you can force a query against a specific server with &lt;code&gt;-h&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;whois -h whois.arin.net 8.8.8.8&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The flag is the right tool for two situations: when the default routing picks the wrong upstream (rare but happens for some legacy TLDs), and when you want to compare answers between regional registries (ARIN, RIPE, APNIC, AFRINIC, LACNIC).&lt;/p&gt;
&lt;h2 id="limit-the-recursion"&gt;Limit the Recursion &lt;a class="headline-link" href="#limit-the-recursion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Most modern &lt;code&gt;whois&lt;/code&gt; clients follow a referral chain: query IANA, follow the pointer to the TLD registry, follow the pointer to the registrar, and return the most specific answer. To stop registry-to-registrar recursion, pass &lt;code&gt;--no-recursion&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;whois --no-recursion example.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The flag is most useful when you specifically want the registry data and not the registrar&amp;rsquo;s slightly different format.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;-H&lt;/code&gt; option has a different purpose. It hides legal disclaimers from the output, which can make short lookups easier to read:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;whois -H example.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="filter-the-output"&gt;Filter the Output &lt;a class="headline-link" href="#filter-the-output" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Real &lt;code&gt;whois&lt;/code&gt; responses are dozens of lines long with legal disclaimers and template text. To extract one field, pipe through &lt;code&gt;grep&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;whois example.com &lt;span class="p"&gt;|&lt;/span&gt; grep -E &lt;span class="s2"&gt;&amp;#34;Registrar:|Expiry Date:&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt; Registry Expiry Date: 2026-08-13T04:00:00Z
Registrar: RESERVED-Internet Assigned Numbers Authority&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For a name-server list:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;whois example.com &lt;span class="p"&gt;|&lt;/span&gt; awk &lt;span class="s1"&gt;&amp;#39;/Name Server:/ {print $NF}&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;ELLIOTT.NS.CLOUDFLARE.COM
HERA.NS.CLOUDFLARE.COM&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;These short patterns work for monitoring scripts that watch for domain expirations or DNSSEC status changes.&lt;/p&gt;
&lt;h2 id="check-domain-availability"&gt;Check Domain Availability &lt;a class="headline-link" href="#check-domain-availability" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If the domain is not registered, the response says so explicitly. The exact wording depends on the registry:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;whois never-existed-domain-xyzzy.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;No match for domain &amp;#34;NEVER-EXISTED-DOMAIN-XYZZY.COM&amp;#34;.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Some registries (notably &lt;code&gt;.io&lt;/code&gt;, &lt;code&gt;.co&lt;/code&gt;, and several ccTLDs) return an empty or near-empty response for unregistered domains. Two heuristics that work in scripts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For &lt;code&gt;.com&lt;/code&gt;/&lt;code&gt;.net&lt;/code&gt;/&lt;code&gt;.org&lt;/code&gt;, grep for &lt;code&gt;No match for&lt;/code&gt; or &lt;code&gt;Domain Name:&lt;/code&gt; in the output.&lt;/li&gt;
&lt;li&gt;For ccTLDs, grep for &lt;code&gt;Domain not found&lt;/code&gt; or check whether the registration fields exist.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="rate-limits-and-etiquette"&gt;Rate Limits and Etiquette &lt;a class="headline-link" href="#rate-limits-and-etiquette" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Registries rate-limit &lt;code&gt;whois&lt;/code&gt; queries. Hammering them with a script is the fastest way to get blocked. If you query many domains, add a &lt;code&gt;sleep&lt;/code&gt; between calls and cache the result locally. For bulk lookups, use the registry&amp;rsquo;s RDAP service directly or pay for a commercial WHOIS API.&lt;/p&gt;
&lt;p&gt;A simple polite pattern:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; -r domain&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; whois &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$domain&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; sleep &lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt; &amp;lt; domains.txt&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Two seconds between queries is a sane starting point; raise it if you see throttling responses.&lt;/p&gt;
&lt;h2 id="privacy-and-redacted-output"&gt;Privacy and Redacted Output &lt;a class="headline-link" href="#privacy-and-redacted-output" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Since GDPR took effect, most TLDs redact personal contact information for individual registrants. The response usually contains placeholders like &lt;code&gt;REDACTED FOR PRIVACY&lt;/code&gt; or &lt;code&gt;Data Protected, Not Disclosed&lt;/code&gt;. For organizations and legal entities, the contact information often stays visible.&lt;/p&gt;
&lt;p&gt;This is not a defect in &lt;code&gt;whois&lt;/code&gt;; the underlying registry data is simply less detailed than it used to be. For account-takeover prevention and abuse handling, focus on the registrar field and the abuse contact email, which remain published.&lt;/p&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Look up a domain&lt;/td&gt;
&lt;td&gt;&lt;code&gt;whois example.com&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Look up an IP address&lt;/td&gt;
&lt;td&gt;&lt;code&gt;whois 93.184.216.34&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Look up an AS number&lt;/td&gt;
&lt;td&gt;&lt;code&gt;whois AS15169&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Query a specific server&lt;/td&gt;
&lt;td&gt;&lt;code&gt;whois -h whois.arin.net 8.8.8.8&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stop registry-to-registrar recursion&lt;/td&gt;
&lt;td&gt;&lt;code&gt;whois --no-recursion example.com&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hide legal disclaimers&lt;/td&gt;
&lt;td&gt;&lt;code&gt;whois -H example.com&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extract registrar and expiry fields&lt;/td&gt;
&lt;td&gt;&lt;code&gt;whois example.com | grep -E &amp;ldquo;Registrar:|Expiry Date:&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List name servers&lt;/td&gt;
&lt;td&gt;&lt;code&gt;whois example.com | awk &amp;lsquo;/Name Server:/ {print $NF}&amp;rsquo;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;whois: command not found&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Install the package: &lt;code&gt;sudo apt install whois&lt;/code&gt; on Ubuntu, Debian, and Derivatives, or &lt;code&gt;sudo dnf install whois&lt;/code&gt; on Fedora, RHEL, and Derivatives. The package is small and adds no significant dependencies.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Output says &amp;ldquo;fgets: Connection reset by peer&amp;rdquo;&lt;/strong&gt;&lt;br&gt;
The registry rate-limited or blocked your IP. Wait a few minutes and retry, slow your script down, or query through a different network.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Response is in a different language or alphabet&lt;/strong&gt;&lt;br&gt;
Some ccTLD registries return data in the local language. Look for the English section (usually further down), or pipe through &lt;code&gt;iconv&lt;/code&gt; if the encoding makes the response unreadable in your terminal.&lt;/p&gt;
&lt;h2 id="faq"&gt;FAQ &lt;a class="headline-link" href="#faq" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;What is the difference between WHOIS and RDAP?&lt;/strong&gt;&lt;br&gt;
RDAP (Registration Data Access Protocol) is the modern replacement for WHOIS. It returns structured JSON instead of free-text and supports authentication and access controls. Most registries now serve both, and RDAP is usually the better choice for scripts that need predictable fields.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why does the data for the same domain look different between two &lt;code&gt;whois&lt;/code&gt; runs?&lt;/strong&gt;&lt;br&gt;
Different clients and servers can follow the referral chain differently. One response may come from the registry, while another may include data from the registrar&amp;rsquo;s WHOIS server. Use &lt;code&gt;--no-recursion&lt;/code&gt; when you want to stop at the registry answer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Can I run my own WHOIS server?&lt;/strong&gt;&lt;br&gt;
Yes, but only registrars and registries have authoritative data. Self-hosted WHOIS servers are useful for internal directories (IP allocation in a large network), not for public domain lookups.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;whois&lt;/code&gt; is the answer to &amp;ldquo;who owns this&amp;rdquo;, whether the &amp;ldquo;this&amp;rdquo; is a domain, an IP, or an AS number. The output is plain text, the flags are short, and a handful of &lt;code&gt;grep&lt;/code&gt;/&lt;code&gt;awk&lt;/code&gt; patterns turn it into a script-friendly data source. For bulk work, slow the queries down and respect the rate limits the registries publish.&lt;/p&gt;
&lt;p&gt;For related reading, see our guides on &lt;a href="https://linuxize.com/post/how-to-use-dig-command-to-query-dns-in-linux/"&gt;the dig command&lt;/a&gt;
and &lt;a href="https://linuxize.com/post/nslookup-command-in-linux/"&gt;the nslookup command&lt;/a&gt;
.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/whois-command-in-linux/featured_hu_e63e30c7626fc6a5.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>How to Install and Use Zsh on Ubuntu</title><link>https://linuxize.com/post/how-to-install-and-use-zsh-on-ubuntu/</link><pubDate>Sat, 23 May 2026 10:20:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/how-to-install-and-use-zsh-on-ubuntu/</guid><category>ubuntu</category><description>Install Zsh on Ubuntu, set it as the default shell, configure ~/.zshrc, and add Oh My Zsh themes and plugins for daily terminal work.</description><content:encoded>&lt;p&gt;Bash has been the default login shell on Ubuntu for years, and it works fine for running scripts and one-off commands. When we spend most of the day inside a terminal, though, small details start to matter: smarter tab completion, spell correction on commands, recursive globs, and rich theming. Zsh adds those features without breaking the Bash scripts we already rely on.&lt;/p&gt;
&lt;p&gt;This guide explains how to install Zsh on Ubuntu, set it as the default shell, configure &lt;code&gt;~/.zshrc&lt;/code&gt;, and bolt on the Oh My Zsh framework for plugins and themes.&lt;/p&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Install Zsh&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt install zsh&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check version&lt;/td&gt;
&lt;td&gt;&lt;code&gt;zsh --version&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Set as default shell&lt;/td&gt;
&lt;td&gt;&lt;code&gt;chsh -s $(which zsh)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reload config&lt;/td&gt;
&lt;td&gt;&lt;code&gt;source ~/.zshrc&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List shells&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cat /etc/shells&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Switch back to Bash&lt;/td&gt;
&lt;td&gt;&lt;code&gt;chsh -s /bin/bash&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Install Oh My Zsh&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sh -c &amp;quot;$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites &lt;a class="headline-link" href="#prerequisites" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You need an Ubuntu system with a user account that has &lt;code&gt;sudo&lt;/code&gt; privileges. The Zsh package comes from the default Ubuntu repositories, while the Oh My Zsh installer needs &lt;code&gt;curl&lt;/code&gt; and &lt;code&gt;git&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Install those helper packages if they are missing:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install curl git&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="install-zsh"&gt;Install Zsh &lt;a class="headline-link" href="#install-zsh" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Zsh is available in the default Ubuntu repositories. Refresh the package index and install it:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install zsh&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Verify the installation by checking the Zsh version:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;zsh --version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The output looks similar to:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;zsh 5.9 (x86_64-ubuntu-linux-gnu)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="run-zsh-for-the-first-time"&gt;Run Zsh for the First Time &lt;a class="headline-link" href="#run-zsh-for-the-first-time" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Start Zsh without changing your default shell yet:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;zsh&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The first launch opens the &lt;code&gt;zsh-newuser-install&lt;/code&gt; configuration wizard. The relevant choices are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;0&lt;/code&gt; exits the wizard and creates an empty &lt;code&gt;~/.zshrc&lt;/code&gt; so Zsh stops asking on every start.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1&lt;/code&gt; continues to the interactive menu, where we can pick defaults for history, completion, key bindings, and a few other behaviors.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;2&lt;/code&gt; populates &lt;code&gt;~/.zshrc&lt;/code&gt; with the system-wide recommended defaults.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;q&lt;/code&gt; quits without creating a config file at all, which means the wizard runs again next time.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pick option &lt;code&gt;0&lt;/code&gt; or &lt;code&gt;q&lt;/code&gt; if you plan to install Oh My Zsh next, since it ships its own template and will overwrite the file anyway. Otherwise, run the interactive setup to generate a sensible starting point.&lt;/p&gt;
&lt;h2 id="set-zsh-as-the-default-shell"&gt;Set Zsh as the Default Shell &lt;a class="headline-link" href="#set-zsh-as-the-default-shell" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once you are happy with the way Zsh behaves, change the default login shell for your user:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;chsh -s &lt;span class="k"&gt;$(&lt;/span&gt;which zsh&lt;span class="k"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Log out and log back in so the new shell takes effect. Confirm the change with:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$SHELL&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The output should be &lt;code&gt;/usr/bin/zsh&lt;/code&gt; or &lt;code&gt;/bin/zsh&lt;/code&gt;, depending on your Ubuntu release and shell path layout.&lt;/p&gt;
&lt;p&gt;If you prefer to keep Bash as the login shell and only run Zsh on demand, skip this step and start Zsh manually with &lt;code&gt;zsh&lt;/code&gt; whenever you need it.&lt;/p&gt;
&lt;h2 id="edit-zshrc"&gt;Edit ~/.zshrc &lt;a class="headline-link" href="#edit-zshrc" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;~/.zshrc&lt;/code&gt; file runs every time an interactive Zsh session starts. Open it with your editor:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nano ~/.zshrc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;A minimal useful starting point looks like this:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;~/.zshrc&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# History&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;HISTFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;~/.zsh_history
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;HISTSIZE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;10000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;SAVEHIST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;10000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;setopt SHARE_HISTORY
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;setopt HIST_IGNORE_DUPS
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Completion&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;autoload -Uz compinit
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;compinit
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Aliases&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;ll&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ls -lah&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;gs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;git status&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Prompt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;PROMPT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;%n@%m %~ %# &amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Reload the file in the current session without opening a new terminal:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ~/.zshrc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For more on aliases, see &lt;a href="https://linuxize.com/post/how-to-create-bash-aliases/"&gt;how to create Bash aliases&lt;/a&gt;
. The same syntax works in Zsh.&lt;/p&gt;
&lt;h2 id="install-oh-my-zsh"&gt;Install Oh My Zsh &lt;a class="headline-link" href="#install-oh-my-zsh" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Oh My Zsh is a popular framework that ships a large catalog of plugins and themes. The official installer is the most common setup path:&lt;/p&gt;
&lt;div class="note callout callout-warning"&gt;
&lt;div class="callout-header"&gt;&lt;svg role="img" aria-hidden="true" class="callout-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"&gt;
&lt;path d="M10 20C4.477 20 0 15.523 0 10S4.477 0 10 0s10 4.477 10 10-4.477 10-10 10zm0-2c4.418 0 8-3.582 8-8s-3.582-8-8-8-8 3.582-8 8 3.582 8 8 8zm-.5-5h1c.276 0 .5.224.5.5v1c0 .276-.224.5-.5.5h-1c-.276 0-.5-.224-.5-.5v-1c0-.276.224-.5.5-.5zm0-8h1c.276 0 .5.224.5.5V8l-.5 3-1 .5L9 8V5.5c0-.276.224-.5.5-.5z"&gt;&lt;/path&gt;
&lt;/svg&gt;
&lt;span class="callout-title"&gt;Warning&lt;/span&gt;&lt;/div&gt;
&lt;div class="callout-body"&gt;Piping a remote install script into &lt;code&gt;sh&lt;/code&gt; runs code from the network as your user. If you are setting up a shared or production machine, download the installer first, review it, and then run it locally.&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;To inspect the script before running it, use:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh -o install-oh-my-zsh.sh
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;less install-oh-my-zsh.sh
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sh install-oh-my-zsh.sh&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For a quick personal workstation setup, you can run the installer directly:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sh -c &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The installer clones Oh My Zsh into &lt;code&gt;~/.oh-my-zsh&lt;/code&gt;, backs up the existing &lt;code&gt;~/.zshrc&lt;/code&gt; to &lt;code&gt;~/.zshrc.pre-oh-my-zsh&lt;/code&gt;, and writes a new &lt;code&gt;~/.zshrc&lt;/code&gt; with sensible defaults.&lt;/p&gt;
&lt;h2 id="pick-a-theme"&gt;Pick a Theme &lt;a class="headline-link" href="#pick-a-theme" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Open &lt;code&gt;~/.zshrc&lt;/code&gt; and find the &lt;code&gt;ZSH_THEME&lt;/code&gt; line. The default value is &lt;code&gt;robbyrussell&lt;/code&gt;. Try a different theme by setting:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;ZSH_THEME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;agnoster&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;agnoster&lt;/code&gt; theme uses powerline-style segments and requires a Nerd Font for the special glyphs. If you want to sample themes without installing extra fonts first, use &lt;code&gt;ZSH_THEME=&amp;quot;random&amp;quot;&lt;/code&gt; to cycle themes on every login until you find one you like.&lt;/p&gt;
&lt;p&gt;Reload the configuration to apply the new theme:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ~/.zshrc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="enable-plugins"&gt;Enable Plugins &lt;a class="headline-link" href="#enable-plugins" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Plugins extend Zsh with completion definitions, shortcuts, and visual aids. Edit the &lt;code&gt;plugins=()&lt;/code&gt; line in &lt;code&gt;~/.zshrc&lt;/code&gt; and add the plugins you want:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;plugins&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;git docker kubectl sudo zsh-autosuggestions zsh-syntax-highlighting&lt;span class="o"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;git&lt;/code&gt;, &lt;code&gt;docker&lt;/code&gt;, &lt;code&gt;kubectl&lt;/code&gt;, and &lt;code&gt;sudo&lt;/code&gt; ship with Oh My Zsh. &lt;code&gt;zsh-autosuggestions&lt;/code&gt; and &lt;code&gt;zsh-syntax-highlighting&lt;/code&gt; are external plugins. Install them with:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git clone https://github.com/zsh-users/zsh-autosuggestions &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ZSH_CUSTOM&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="p"&gt;~/.oh-my-zsh/custom&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;/plugins/zsh-autosuggestions
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git clone https://github.com/zsh-users/zsh-syntax-highlighting.git &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ZSH_CUSTOM&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="p"&gt;~/.oh-my-zsh/custom&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;/plugins/zsh-syntax-highlighting&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Reload &lt;code&gt;~/.zshrc&lt;/code&gt; to activate them:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ~/.zshrc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;zsh-autosuggestions&lt;/code&gt; shows a faded completion based on your history as you type. Press the right arrow key to accept it. &lt;code&gt;zsh-syntax-highlighting&lt;/code&gt; colors valid commands green and unknown commands red, which catches typos before you press Enter.&lt;/p&gt;
&lt;h2 id="useful-built-in-features"&gt;Useful Built-in Features &lt;a class="headline-link" href="#useful-built-in-features" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Zsh ships several features that are not available in Bash without extra setup:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Recursive glob patterns: &lt;code&gt;ls **/*.log&lt;/code&gt; lists every &lt;code&gt;.log&lt;/code&gt; file under the current directory.&lt;/li&gt;
&lt;li&gt;Globbing qualifiers: &lt;code&gt;ls *(.)&lt;/code&gt; lists only regular files; &lt;code&gt;ls *(/)&lt;/code&gt; lists only directories.&lt;/li&gt;
&lt;li&gt;Spell correction: &lt;code&gt;setopt CORRECT&lt;/code&gt; prompts you to fix mistyped commands.&lt;/li&gt;
&lt;li&gt;Sharing history across sessions in real time with &lt;code&gt;setopt SHARE_HISTORY&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;chsh&lt;/code&gt; reports &amp;ldquo;non-standard shell&amp;rdquo;&lt;/strong&gt;&lt;br&gt;
Make sure &lt;code&gt;/usr/bin/zsh&lt;/code&gt; is listed in &lt;code&gt;/etc/shells&lt;/code&gt;. If it is missing, add it with &lt;code&gt;echo $(which zsh) | sudo tee -a /etc/shells&lt;/code&gt; and run &lt;code&gt;chsh&lt;/code&gt; again.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Zsh starts but the prompt is broken on a remote SSH session&lt;/strong&gt;&lt;br&gt;
The theme probably depends on a Nerd Font that the local terminal does not have. Switch to a simpler theme such as &lt;code&gt;robbyrussell&lt;/code&gt; or install a Nerd Font in the terminal application.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Plugins are listed but nothing happens&lt;/strong&gt;&lt;br&gt;
Check the &lt;code&gt;ZSH_CUSTOM&lt;/code&gt; path matches where you cloned the plugin. Run &lt;code&gt;ls ~/.oh-my-zsh/custom/plugins&lt;/code&gt; to confirm the plugin directory exists, then reload &lt;code&gt;~/.zshrc&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;From here we can keep tweaking &lt;code&gt;~/.zshrc&lt;/code&gt; to match the way we work: add aliases for repetitive commands, pin a theme that reads well in the terminal we use most, and pull in plugins as new tools enter the workflow. The alias syntax matches Bash, so &lt;a href="https://linuxize.com/post/how-to-create-bash-aliases/"&gt;existing alias workflows&lt;/a&gt;
carry over without changes.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/how-to-install-and-use-zsh-on-ubuntu/featured_hu_c569f100b91e9f6.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>Bash Arithmetic: Integer and Floating-Point Math</title><link>https://linuxize.com/post/bash-arithmetic/</link><pubDate>Fri, 22 May 2026 14:41:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/bash-arithmetic/</guid><category>bash</category><category>linux commands</category><description>How to do arithmetic in Bash with the (( )) operator, $(( )) expansion, the let builtin, and bc or awk for floating-point math the shell cannot do natively.</description><content:encoded>&lt;p&gt;Bash treats everything as a string by default, which catches many people out the first time they write &lt;code&gt;total=$count+1&lt;/code&gt; and end up with the literal text &lt;code&gt;5+1&lt;/code&gt; instead of &lt;code&gt;6&lt;/code&gt;. The shell has dedicated arithmetic forms that evaluate numeric expressions properly, but they only handle integers; for floating-point math you reach for &lt;code&gt;bc&lt;/code&gt; or &lt;code&gt;awk&lt;/code&gt;. Once you know which form to use where, the math is short and predictable.&lt;/p&gt;
&lt;p&gt;This guide explains how Bash arithmetic works, the differences between &lt;code&gt;(( ))&lt;/code&gt;, &lt;code&gt;$(( ))&lt;/code&gt;, and &lt;code&gt;let&lt;/code&gt;, and how to combine them with external tools when you need decimals.&lt;/p&gt;
&lt;h2 id="why-plain-assignment-does-not-add"&gt;Why Plain Assignment Does Not Add &lt;a class="headline-link" href="#why-plain-assignment-does-not-add" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A first attempt at incrementing a variable usually looks like this:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;total&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$count&lt;/span&gt;+1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$total&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;5+1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The shell sees &lt;code&gt;$count&lt;/code&gt; as the string &lt;code&gt;5&lt;/code&gt;, joins it with &lt;code&gt;+1&lt;/code&gt;, and assigns the result. To force numeric evaluation, use one of the arithmetic forms below.&lt;/p&gt;
&lt;h2 id="the---compound-command"&gt;The (( )) Compound Command &lt;a class="headline-link" href="#the---compound-command" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Wrap an expression in double parentheses to evaluate it as integer math:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;((&lt;/span&gt; &lt;span class="nv"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; count + &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$count&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;6&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Inside &lt;code&gt;(( ))&lt;/code&gt;, variable names work without &lt;code&gt;$&lt;/code&gt;, the C-style operators are available (&lt;code&gt;+&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;, &lt;code&gt;*&lt;/code&gt;, &lt;code&gt;/&lt;/code&gt;, &lt;code&gt;%&lt;/code&gt;, &lt;code&gt;**&lt;/code&gt;, &lt;code&gt;++&lt;/code&gt;, &lt;code&gt;--&lt;/code&gt;, &lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;amp;&lt;/code&gt;, &lt;code&gt;|&lt;/code&gt;, &lt;code&gt;^&lt;/code&gt;, &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;, &lt;code&gt;||&lt;/code&gt;), and the exit status of the whole expression is non-zero when the result is &lt;code&gt;0&lt;/code&gt;, which means you can use arithmetic in &lt;code&gt;if&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt; count &amp;gt; &lt;span class="m"&gt;5&lt;/span&gt; &lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;count is above 5&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;count is above 5&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The shortcut increment and decrement operators read cleanly in loops:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt; &lt;span class="nv"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 0&lt;span class="p"&gt;;&lt;/span&gt; i &amp;lt; 3&lt;span class="p"&gt;;&lt;/span&gt; i++ &lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;i=&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;i=0
i=1
i=2&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;(( ))&lt;/code&gt; is the right form when you assign back to a variable or use the result for control flow. It does not print anything to standard output.&lt;/p&gt;
&lt;h2 id="the---expansion"&gt;The $(( )) Expansion &lt;a class="headline-link" href="#the---expansion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When you want the value of an expression rather than the side effect, use the &lt;code&gt;$(( ))&lt;/code&gt; arithmetic expansion. The result is substituted into the surrounding command like any other expansion:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Next: &lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt; count &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Next: 6&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;$(( ))&lt;/code&gt; is the right form for inline math inside &lt;code&gt;echo&lt;/code&gt;, assignments to other variables, command arguments, and &lt;code&gt;printf&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;files&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;42&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Average: &lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt; files &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="m"&gt;7&lt;/span&gt; &lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="s2"&gt; per day&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Average: 6 per day&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice that the division truncates: &lt;code&gt;42 / 7&lt;/code&gt; happens to be exact, but &lt;code&gt;43 / 7&lt;/code&gt; would yield &lt;code&gt;6&lt;/code&gt;, not &lt;code&gt;6.14&lt;/code&gt;. Bash arithmetic is integer-only; the decimal part is silently discarded.&lt;/p&gt;
&lt;h2 id="the-let-builtin"&gt;The let Builtin &lt;a class="headline-link" href="#the-let-builtin" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;let&lt;/code&gt; is the older builtin for arithmetic. It evaluates each argument as an expression:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;let&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;count = 5 + 1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$count&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;6&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;let&lt;/code&gt; is mostly historical at this point. &lt;code&gt;(( ))&lt;/code&gt; does the same work without the quoting trap (&lt;code&gt;let count=*&lt;/code&gt; would expand the &lt;code&gt;*&lt;/code&gt; as a glob unless quoted) and reads more cleanly. New scripts should prefer &lt;code&gt;(( ))&lt;/code&gt;; &lt;code&gt;let&lt;/code&gt; is worth recognizing in code you inherit.&lt;/p&gt;
&lt;h2 id="operators-you-will-actually-use"&gt;Operators You Will Actually Use &lt;a class="headline-link" href="#operators-you-will-actually-use" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Bash supports a wide list of arithmetic operators. These are the ones that come up in everyday scripts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;+&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;, &lt;code&gt;*&lt;/code&gt;, &lt;code&gt;/&lt;/code&gt;, &lt;code&gt;%&lt;/code&gt; - Standard arithmetic and remainder.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;**&lt;/code&gt; - Exponentiation.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;++&lt;/code&gt;, &lt;code&gt;--&lt;/code&gt; - Increment and decrement, prefix or postfix.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;+=&lt;/code&gt;, &lt;code&gt;-=&lt;/code&gt;, &lt;code&gt;*=&lt;/code&gt;, &lt;code&gt;/=&lt;/code&gt;, &lt;code&gt;%=&lt;/code&gt; - Compound assignment.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;==&lt;/code&gt;, &lt;code&gt;!=&lt;/code&gt;, &lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;lt;=&lt;/code&gt;, &lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;gt;=&lt;/code&gt; - Numeric comparison (inside &lt;code&gt;(( ))&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;, &lt;code&gt;||&lt;/code&gt;, &lt;code&gt;!&lt;/code&gt; - Logical AND, OR, NOT.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;amp;&lt;/code&gt;, &lt;code&gt;|&lt;/code&gt;, &lt;code&gt;^&lt;/code&gt;, &lt;code&gt;~&lt;/code&gt; - Bitwise shifts and operations.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A small example that uses several:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1024&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt; size &amp;gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; size % &lt;span class="nv"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Power of two? &lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;size &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;size &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="s2"&gt; == 0 ? 1 : 0 ))&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Power of two? 1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The ternary operator (&lt;code&gt;? :&lt;/code&gt;) works inside arithmetic contexts, which keeps short branches readable without nesting &lt;code&gt;if&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="number-bases"&gt;Number Bases &lt;a class="headline-link" href="#number-bases" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Bash arithmetic supports several integer bases. Prefix a number to declare its base:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;0xNN&lt;/code&gt; for hexadecimal.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0NN&lt;/code&gt; for octal.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BASE#NNN&lt;/code&gt; for any base from 2 to 64.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Examples:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;xff &lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt; &lt;span class="m"&gt;0755&lt;/span&gt; &lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt; &lt;span class="m"&gt;2#1010&lt;/span&gt; &lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;255
493
10&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Watch out for leading zeros: &lt;code&gt;08&lt;/code&gt; is interpreted as octal and rejected because &lt;code&gt;8&lt;/code&gt; is not a valid octal digit. If user input may include leading zeros (HTTP status codes parsed from a string, timestamps), strip them before arithmetic with &lt;code&gt;${var#0}&lt;/code&gt; or use the explicit &lt;code&gt;10#&lt;/code&gt; base prefix:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;08&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt; &lt;span class="m"&gt;10#&lt;/span&gt;&lt;span class="nv"&gt;$value&lt;/span&gt; &lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;8&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="floating-point-math-with-bc"&gt;Floating-Point Math with bc &lt;a class="headline-link" href="#floating-point-math-with-bc" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Bash itself cannot do decimal math, but it is happy to call out to a tool that can. &lt;code&gt;bc&lt;/code&gt; is the arbitrary-precision calculator that ships with most distributions:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;scale=2; 43 / 7&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; bc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;6.14&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;scale=2&lt;/code&gt; directive sets two decimal places of precision. Without it, &lt;code&gt;bc&lt;/code&gt; truncates the division the same way Bash does. Use a higher scale for currency or scientific work:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;scale=10; 4*a(1)&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; bc -l&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;3.1415926532&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;-l&lt;/code&gt; flag loads the math library, which gives you &lt;code&gt;s&lt;/code&gt; (sine), &lt;code&gt;c&lt;/code&gt; (cosine), &lt;code&gt;a&lt;/code&gt; (arctangent), &lt;code&gt;l&lt;/code&gt; (natural log), &lt;code&gt;e&lt;/code&gt; (exponential), and &lt;code&gt;sqrt&lt;/code&gt;. The expression above computes pi from &lt;code&gt;4 * arctan(1)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To capture the result into a variable:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;pi&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;scale=4; 4*a(1)&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; bc -l&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;pi = &lt;/span&gt;&lt;span class="nv"&gt;$pi&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;pi = 3.1415&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;bc&lt;/code&gt; is the right tool when you need genuine decimals and do not want a heavyweight dependency. For one-off interactive calculations, run &lt;code&gt;bc -l&lt;/code&gt; and type expressions at its prompt.&lt;/p&gt;
&lt;h2 id="floating-point-math-with-awk"&gt;Floating-Point Math with awk &lt;a class="headline-link" href="#floating-point-math-with-awk" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;awk&lt;/code&gt; is the other obvious choice and is often faster because it does not start a separate calculator process. Use &lt;code&gt;awk&lt;/code&gt; for inline expressions inside scripts:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;rate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;awk &lt;span class="s1"&gt;&amp;#39;BEGIN { printf &amp;#34;%.2f\n&amp;#34;, 43 / 7 }&amp;#39;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;rate = &lt;/span&gt;&lt;span class="nv"&gt;$rate&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;rate = 6.14&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;printf&lt;/code&gt; inside &lt;code&gt;awk&lt;/code&gt; works the same way as the C function, with &lt;code&gt;%.2f&lt;/code&gt; controlling the number of decimal places. For a percentage:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;pct&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;awk -v &lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;23&lt;/span&gt; -v &lt;span class="nv"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;89&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;BEGIN { printf &amp;#34;%.1f%%\n&amp;#34;, (a / b) * 100 }&amp;#39;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;pct = &lt;/span&gt;&lt;span class="nv"&gt;$pct&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;pct = 25.8%&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Pass values into &lt;code&gt;awk&lt;/code&gt; with &lt;code&gt;-v name=value&lt;/code&gt; rather than building the expression by string concatenation; the &lt;code&gt;-v&lt;/code&gt; form avoids quoting headaches and shell-injection issues when the inputs come from user data.&lt;/p&gt;
&lt;h2 id="increment-counters-in-a-loop"&gt;Increment Counters in a Loop &lt;a class="headline-link" href="#increment-counters-in-a-loop" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Counter loops are the bread and butter of arithmetic in scripts, and they lean on the &lt;a href="https://linuxize.com/post/bash-increment-decrement-variable/"&gt;increment and decrement operators&lt;/a&gt;
. Two equivalent forms work; pick the one that reads best:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; -r line&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;((&lt;/span&gt; count++ &lt;span class="o"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt; &amp;lt; input.txt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Lines: &lt;/span&gt;&lt;span class="nv"&gt;$count&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; -r line&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt; count &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="k"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt; &amp;lt; input.txt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Lines: &lt;/span&gt;&lt;span class="nv"&gt;$count&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;(( count++ ))&lt;/code&gt; form is shorter and is what most authors prefer. Both produce the same result and run at the same speed.&lt;/p&gt;
&lt;p&gt;If the script uses &lt;code&gt;set -e&lt;/code&gt;, avoid &lt;code&gt;(( count++ ))&lt;/code&gt; when &lt;code&gt;count&lt;/code&gt; may start at zero. The arithmetic command returns a failure status when the expression evaluates to &lt;code&gt;0&lt;/code&gt;, so the first increment can stop the script. Use pre-increment or assignment instead:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;((&lt;/span&gt; ++count &lt;span class="o"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;((&lt;/span&gt; &lt;span class="nv"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;syntax error in expression&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
A variable that should hold a number contains non-numeric characters (often a leading or trailing space from &lt;code&gt;read&lt;/code&gt;, or a stray letter). Inside &lt;code&gt;(( ))&lt;/code&gt; and &lt;code&gt;$(( ))&lt;/code&gt;, Bash reports a syntax error on the offending token. Trim the input with &lt;code&gt;${var//[[:space:]]/}&lt;/code&gt; or validate it before the arithmetic line.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;division by 0&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
A divisor evaluated to zero, often because the variable was empty and Bash treated it as &lt;code&gt;0&lt;/code&gt;. Quote the expansion in your check (&lt;code&gt;if [ -n &amp;quot;$divisor&amp;quot; ]&lt;/code&gt;) before the divide, and either skip the operation or substitute a default with &lt;code&gt;${divisor:-1}&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Result is &lt;code&gt;0&lt;/code&gt; when it should be a fraction&lt;/strong&gt;&lt;br&gt;
Bash arithmetic is integer-only. Switch to &lt;code&gt;bc&lt;/code&gt; with an explicit &lt;code&gt;scale=&lt;/code&gt; or to &lt;code&gt;awk&lt;/code&gt; with &lt;code&gt;%.Nf&lt;/code&gt; formatting for decimal output.&lt;/p&gt;
&lt;h2 id="faq"&gt;FAQ &lt;a class="headline-link" href="#faq" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;When should I use &lt;code&gt;(( ))&lt;/code&gt; versus &lt;code&gt;$(( ))&lt;/code&gt;?&lt;/strong&gt;&lt;br&gt;
Use &lt;code&gt;(( ))&lt;/code&gt; for assignments and control flow (&lt;code&gt;if&lt;/code&gt;, &lt;code&gt;while&lt;/code&gt;, &lt;code&gt;for&lt;/code&gt;). Use &lt;code&gt;$(( ))&lt;/code&gt; when you want the value substituted into a surrounding command. They share the same expression syntax.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Can I do floating-point math without an external tool?&lt;/strong&gt;&lt;br&gt;
Not in Bash. &lt;code&gt;ksh93&lt;/code&gt; and &lt;code&gt;zsh&lt;/code&gt; support floating-point in their arithmetic contexts; Bash does not. For Bash, route decimals through &lt;code&gt;bc&lt;/code&gt; or &lt;code&gt;awk&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Is there a performance difference between &lt;code&gt;bc&lt;/code&gt; and &lt;code&gt;awk&lt;/code&gt;?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;awk&lt;/code&gt; is usually faster because it runs entirely inside a single process and does not communicate over a pipe. For one-off calculations the difference is negligible; in a tight loop, prefer &lt;code&gt;awk&lt;/code&gt; or precompute the values with a single &lt;code&gt;awk&lt;/code&gt; call instead of running &lt;code&gt;bc&lt;/code&gt; per iteration.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Reach for &lt;code&gt;(( ))&lt;/code&gt; and &lt;code&gt;$(( ))&lt;/code&gt; whenever a script needs counters, conditions, or quick integer math, and switch to &lt;code&gt;bc&lt;/code&gt; or &lt;code&gt;awk&lt;/code&gt; the moment decimals enter the picture. Pair that habit with &lt;a href="https://linuxize.com/post/bash-strict-mode/"&gt;Bash strict mode&lt;/a&gt;
and the patterns in our &lt;a href="https://linuxize.com/post/bash-best-practices/"&gt;Bash best practices&lt;/a&gt;
guide so numeric code keeps working even when the inputs do not.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/bash-arithmetic/featured_hu_37a9db326c6daf78.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>Docker Networking: Connect Containers</title><link>https://linuxize.com/post/docker-networking/</link><pubDate>Thu, 21 May 2026 12:20:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/docker-networking/</guid><category>docker</category><category>devops</category><description>Docker networking controls how containers communicate. This guide covers bridge, host, and none networks, port publishing, container DNS, isolation, and Compose networking.</description><content:encoded>&lt;p&gt;Once you move past single-container tests, the next problem is communication. A web application needs to reach its database, a reverse proxy needs to forward traffic to an app server, and none of those internal services should be exposed to the public internet by accident.&lt;/p&gt;
&lt;p&gt;Docker networking handles that traffic. The defaults work for quick tests, but production-like setups are easier to manage when you understand networks, drivers, DNS names, and published ports. This guide explains how Docker networking works and walks through practical examples for connecting and isolating containers.&lt;/p&gt;
&lt;h2 id="how-docker-networking-works"&gt;How Docker Networking Works &lt;a class="headline-link" href="#how-docker-networking-works" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When the Docker daemon starts, it creates three networks automatically: &lt;code&gt;bridge&lt;/code&gt;, &lt;code&gt;host&lt;/code&gt;, and &lt;code&gt;none&lt;/code&gt;. You can see them with &lt;code&gt;docker network ls&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker network ls&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;NETWORK ID NAME DRIVER SCOPE
b84a2c1f9d8e bridge bridge local
c1f3a7b2d4e5 host host local
e7d6a8c2b1f4 none null local&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Each network uses a driver that decides how containers on that network communicate:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;bridge&lt;/code&gt; - Creates an isolated virtual network on one Docker host.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;host&lt;/code&gt; - Removes network namespace isolation and uses the host network directly.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;none&lt;/code&gt; - Starts the container with only a loopback interface.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;bridge&lt;/code&gt; driver is the default and works for most single-host applications. For anything beyond a quick test, create a user-defined bridge network instead of relying on the default &lt;code&gt;bridge&lt;/code&gt; network. User-defined networks give containers automatic DNS resolution by name and keep unrelated services separated.&lt;/p&gt;
&lt;h2 id="the-default-bridge-network"&gt;The Default Bridge Network &lt;a class="headline-link" href="#the-default-bridge-network" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When you start a container without specifying a network, Docker attaches it to the default &lt;code&gt;bridge&lt;/code&gt; network. Containers on this network can reach each other by IP address, but not by container name, which makes configuration brittle.&lt;/p&gt;
&lt;p&gt;Start two containers on the default network:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run -d --name default_web1 nginx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run -d --name default_web2 nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Inspect the default bridge to see both containers and their IP addresses:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker network inspect bridge&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The output includes a &lt;code&gt;Containers&lt;/code&gt; section listing each container with its assigned IP address. Containers on the default bridge can communicate with those addresses, but a name such as &lt;code&gt;default_web2&lt;/code&gt; does not resolve automatically from &lt;code&gt;default_web1&lt;/code&gt;. That is the main reason to avoid the default bridge for application stacks.&lt;/p&gt;
&lt;h2 id="creating-a-user-defined-bridge-network"&gt;Creating a User-Defined Bridge Network &lt;a class="headline-link" href="#creating-a-user-defined-bridge-network" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;User-defined bridge networks solve the name resolution problem and let you group related containers together. Create one with &lt;code&gt;docker network create&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker network create app_net&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;a1b2c3d4e5f67890123456789abcdef0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now run containers on that network by passing &lt;code&gt;--network&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run -d --name net_web --network app_net nginx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run -d --name net_client --network app_net alpine sleep &lt;span class="m"&gt;3600&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;From inside &lt;code&gt;net_client&lt;/code&gt;, you can reach &lt;code&gt;net_web&lt;/code&gt; by container name:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; -it net_client ping -c &lt;span class="m"&gt;2&lt;/span&gt; net_web&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;PING net_web (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.098 ms
64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.074 ms&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The embedded Docker DNS server resolves the container name to the correct IP address. If you add or remove containers, the DNS records update automatically, so you do not have to track container IP addresses yourself.&lt;/p&gt;
&lt;h2 id="publishing-ports-to-the-host"&gt;Publishing Ports to the Host &lt;a class="headline-link" href="#publishing-ports-to-the-host" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Containers on a bridge network are reachable from other containers on the same network, but not from outside the Docker host unless you publish a port. Use &lt;code&gt;-p host_port:container_port&lt;/code&gt; with &lt;code&gt;docker run&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run -d --name port_web -p 8080:80 nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Nginx listens on port 80 inside the container, and Docker forwards traffic from port 8080 on the host to it. A browser pointed at &lt;code&gt;http://localhost:8080&lt;/code&gt; reaches the container.&lt;/p&gt;
&lt;p&gt;You can publish multiple ports by repeating the flag:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run -d --name port_app -p 8081:80 -p 8443:443 nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;By default, Docker publishes the port on all host interfaces. To bind only to a specific host interface, prefix the host port with the address:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run -d --name loopback_web -p 127.0.0.1:8082:80 nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This binds the port to the loopback interface only. The container is reachable from the host itself, but not from other machines on the network.&lt;/p&gt;
&lt;h2 id="connecting-a-running-container-to-a-network"&gt;Connecting a Running Container to a Network &lt;a class="headline-link" href="#connecting-a-running-container-to-a-network" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A container is not locked to the network it was started on. You can attach a running container to an additional network with &lt;code&gt;docker network connect&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker network connect app_net default_web1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;After connecting, &lt;code&gt;default_web1&lt;/code&gt; has an IP address on both its original network and &lt;code&gt;app_net&lt;/code&gt;. Containers on either network can reach it through the network they share. This is useful when a service needs to talk to more than one group of containers, for example a shared logger or monitoring agent.&lt;/p&gt;
&lt;p&gt;To remove a container from a network, use &lt;code&gt;docker network disconnect&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker network disconnect app_net default_web1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="isolating-containers-with-separate-networks"&gt;Isolating Containers with Separate Networks &lt;a class="headline-link" href="#isolating-containers-with-separate-networks" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Multiple user-defined networks act as separate network segments. Containers on different networks cannot reach each other unless one of them is explicitly connected to both. This makes it simple to isolate a frontend tier from a database tier.&lt;/p&gt;
&lt;p&gt;Create two networks:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker network create frontend_net
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker network create backend_net&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Start a database on &lt;code&gt;backend_net&lt;/code&gt;, an application container on both networks, and a web server on &lt;code&gt;frontend_net&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run -d --name db_net --network backend_net -e &lt;span class="nv"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;localpass postgres:16-alpine
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run -d --name app_net_demo --network backend_net alpine sleep &lt;span class="m"&gt;3600&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker network connect frontend_net app_net_demo
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run -d --name web_net --network frontend_net -p 8083:80 nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;web_net&lt;/code&gt; container can reach &lt;code&gt;app_net_demo&lt;/code&gt; over &lt;code&gt;frontend_net&lt;/code&gt;, and &lt;code&gt;app_net_demo&lt;/code&gt; can reach &lt;code&gt;db_net&lt;/code&gt; over &lt;code&gt;backend_net&lt;/code&gt;. The &lt;code&gt;web_net&lt;/code&gt; container cannot reach &lt;code&gt;db_net&lt;/code&gt; directly because they do not share a network. If the web container is compromised, it cannot open a connection to the database without going through the application layer.&lt;/p&gt;
&lt;div class="note callout callout-warning"&gt;
&lt;div class="callout-header"&gt;&lt;svg role="img" aria-hidden="true" class="callout-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"&gt;
&lt;path d="M10 20C4.477 20 0 15.523 0 10S4.477 0 10 0s10 4.477 10 10-4.477 10-10 10zm0-2c4.418 0 8-3.582 8-8s-3.582-8-8-8-8 3.582-8 8 3.582 8 8 8zm-.5-5h1c.276 0 .5.224.5.5v1c0 .276-.224.5-.5.5h-1c-.276 0-.5-.224-.5-.5v-1c0-.276.224-.5.5-.5zm0-8h1c.276 0 .5.224.5.5V8l-.5 3-1 .5L9 8V5.5c0-.276.224-.5.5-.5z"&gt;&lt;/path&gt;
&lt;/svg&gt;
&lt;span class="callout-title"&gt;Warning&lt;/span&gt;&lt;/div&gt;
&lt;div class="callout-body"&gt;The PostgreSQL password above is only a local demo value. Use strong secrets for real services, and do not store production credentials in shell history, Compose files, or source control.&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="the-host-network-driver"&gt;The host Network Driver &lt;a class="headline-link" href="#the-host-network-driver" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;host&lt;/code&gt; driver skips network namespace isolation and attaches the container directly to the host network. The container shares the host network interfaces, so any port it listens on is immediately available on the host:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run --rm -d --name host_nginx --network host nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Nginx is now reachable on port 80 of the host with no port publishing. In host mode, &lt;code&gt;-p&lt;/code&gt;, &lt;code&gt;--publish&lt;/code&gt;, and &lt;code&gt;--publish-all&lt;/code&gt; are ignored because there is no separate container network namespace to map from.&lt;/p&gt;
&lt;p&gt;Use host networking only when you specifically need it, such as for very high port counts or when the application must bind directly to host interfaces. The trade-off is weaker network isolation and possible port conflicts with services already running on the host.&lt;/p&gt;
&lt;p&gt;Host networking is native to Docker Engine on Linux. Docker Desktop supports host networking in version 4.34 and later, but it must be enabled in Docker Desktop settings.&lt;/p&gt;
&lt;h2 id="the-none-network-driver"&gt;The none Network Driver &lt;a class="headline-link" href="#the-none-network-driver" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;none&lt;/code&gt; driver disables external networking for the container. The container gets a loopback interface and nothing else:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run --rm --network none alpine ip addr&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This is useful for batch jobs that process local files and should not reach the network.&lt;/p&gt;
&lt;h2 id="inspecting-networks"&gt;Inspecting Networks &lt;a class="headline-link" href="#inspecting-networks" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;docker network inspect&lt;/code&gt; prints the full configuration of a network in JSON:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker network inspect app_net&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The output includes the subnet, gateway, driver options, and a list of every container attached to the network with its IPv4 and IPv6 addresses. When a container cannot reach another container, this is usually the first place to look.&lt;/p&gt;
&lt;p&gt;For a quick overview across all bridge networks, use a filter:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker network ls --filter &lt;span class="nv"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bridge&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You can also inspect a container to see its network attachments:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker inspect net_web&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Look for the &lt;code&gt;NetworkSettings.Networks&lt;/code&gt; section in the output.&lt;/p&gt;
&lt;h2 id="removing-networks"&gt;Removing Networks &lt;a class="headline-link" href="#removing-networks" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Unused networks remain on the host until you remove them. Delete a single network with &lt;code&gt;docker network rm&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker network create temp_net
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker network rm temp_net&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The removal command fails if any containers are still attached. Stop or disconnect those containers first, then retry.&lt;/p&gt;
&lt;p&gt;To clean up every user-defined network that is not in use, run:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker network prune&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Docker asks for confirmation and then removes idle user-defined networks. The built-in &lt;code&gt;bridge&lt;/code&gt;, &lt;code&gt;host&lt;/code&gt;, and &lt;code&gt;none&lt;/code&gt; networks are not removed.&lt;/p&gt;
&lt;h2 id="docker-compose-networking"&gt;Docker Compose Networking &lt;a class="headline-link" href="#docker-compose-networking" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When you run applications with &lt;a href="https://linuxize.com/post/docker-compose/"&gt;Docker Compose&lt;/a&gt;
, Compose creates a dedicated default network for each project and attaches every service to it. Services reach each other by service name without extra configuration:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="yaml"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;docker-compose.yml&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-orange-100 text-orange-700 dark:bg-orange-900 dark:text-orange-300"&gt;yaml&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;web&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;nginx&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s2"&gt;&amp;#34;8080:80&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;postgres:16-alpine&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${POSTGRES_PASSWORD}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Define the password in a local &lt;code&gt;.env&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="txt"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;.env&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;txt&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;POSTGRES_PASSWORD=change-this-password&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;From the &lt;code&gt;web&lt;/code&gt; service, &lt;code&gt;db&lt;/code&gt; resolves to the database container IP address. You can also declare multiple named networks under a top-level &lt;code&gt;networks&lt;/code&gt; key and attach services selectively, which mirrors the frontend and backend split shown earlier.&lt;/p&gt;
&lt;div class="note callout callout-warning"&gt;
&lt;div class="callout-header"&gt;&lt;svg role="img" aria-hidden="true" class="callout-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"&gt;
&lt;path d="M10 20C4.477 20 0 15.523 0 10S4.477 0 10 0s10 4.477 10 10-4.477 10-10 10zm0-2c4.418 0 8-3.582 8-8s-3.582-8-8-8-8 3.582-8 8 3.582 8 8 8zm-.5-5h1c.276 0 .5.224.5.5v1c0 .276-.224.5-.5.5h-1c-.276 0-.5-.224-.5-.5v-1c0-.276.224-.5.5-.5zm0-8h1c.276 0 .5.224.5.5V8l-.5 3-1 .5L9 8V5.5c0-.276.224-.5.5-.5z"&gt;&lt;/path&gt;
&lt;/svg&gt;
&lt;span class="callout-title"&gt;Warning&lt;/span&gt;&lt;/div&gt;
&lt;div class="callout-body"&gt;Do not commit &lt;code&gt;.env&lt;/code&gt; files or real passwords to version control. Add &lt;code&gt;.env&lt;/code&gt; to &lt;code&gt;.gitignore&lt;/code&gt;, and use secret management from your platform for production credentials.&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For a printable quick reference, see the &lt;a href="https://linuxize.com/cheatsheet/docker/"&gt;Docker cheatsheet&lt;/a&gt;
.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker network ls&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List Docker networks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker network create app_net&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a user-defined bridge network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker run --network app_net image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start a container on a network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker network connect app_net container&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Attach a running container to a network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker network disconnect app_net container&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove a container from a network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker network inspect app_net&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show network configuration and attached containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker run -p 8080:80 image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Publish container port 80 on host port 8080&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker run -p 127.0.0.1:8080:80 image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Publish a port only on the host loopback interface&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker run --network host image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run a container on the host network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker run --network none image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run a container with no external network access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker network rm app_net&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove an unused network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker network prune&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove all unused user-defined networks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Containers cannot reach each other by name&lt;/strong&gt;&lt;br&gt;
Name-based DNS works on user-defined networks. If you started the containers on the default &lt;code&gt;bridge&lt;/code&gt; network, recreate them on a user-defined network, or attach them with &lt;code&gt;docker network connect&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Port is already allocated when publishing&lt;/strong&gt;&lt;br&gt;
Another process on the host is already using that port. Pick a different host port, or find and stop the conflicting process with &lt;a href="https://linuxize.com/post/lsof-command-in-linux/"&gt;&lt;code&gt;lsof -i :PORT&lt;/code&gt;&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cannot remove a network&lt;/strong&gt;&lt;br&gt;
The network still has containers attached. List them with &lt;code&gt;docker network inspect NAME&lt;/code&gt;, disconnect or stop them, and try again.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Service is unreachable from the host after publishing a port&lt;/strong&gt;&lt;br&gt;
Check that the service inside the container is listening on &lt;code&gt;0.0.0.0&lt;/code&gt;, not only on &lt;code&gt;127.0.0.1&lt;/code&gt;. A service bound to the container loopback interface is not reachable through the published port.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The -p option does not work with host networking&lt;/strong&gt;&lt;br&gt;
Published ports are ignored when a container uses &lt;code&gt;--network host&lt;/code&gt;. The container is already using the host network directly, so it must listen on the desired host port itself.&lt;/p&gt;
&lt;h2 id="faq"&gt;FAQ &lt;a class="headline-link" href="#faq" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;What is the difference between a bridge and a host network?&lt;/strong&gt;&lt;br&gt;
A bridge network creates an isolated virtual network for containers and requires port publishing to expose services on the host. A host network attaches the container directly to the host network, with no separate container network namespace and no port publishing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Do containers on the same user-defined bridge network need published ports to talk to each other?&lt;/strong&gt;&lt;br&gt;
No. Port publishing controls access from the host or external network. Containers on the same user-defined network reach each other directly on the container port.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Can two containers share the same name on different networks?&lt;/strong&gt;&lt;br&gt;
No. Container names are unique per Docker daemon, regardless of how many networks they are attached to. Network-scoped aliases, set with &lt;code&gt;--network-alias&lt;/code&gt;, can overlap across networks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why does ping not work from some containers?&lt;/strong&gt;&lt;br&gt;
Some minimal images do not include &lt;code&gt;ping&lt;/code&gt;. Alpine includes it by default, which makes it useful for quick network tests. Other images may require installing the relevant package, or you can use a temporary troubleshooting image.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Docker networking becomes easier to manage when each application gets its own user-defined network and only public-facing services publish ports. For more Docker basics, see the &lt;a href="https://linuxize.com/post/docker-run-command/"&gt;docker run&lt;/a&gt;
and &lt;a href="https://linuxize.com/post/how-to-connect-to-docker-container/"&gt;docker exec&lt;/a&gt;
guides.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/docker-networking/featured_hu_f288799ed6e59cde.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>ss Cheatsheet</title><link>https://linuxize.com/cheatsheet/ss/</link><pubDate>Thu, 21 May 2026 10:00:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/cheatsheet/ss/</guid><description>Quick reference for listing sockets, listening ports, connection states, and process owners with the ss command</description><content:encoded>&lt;div class="card"&gt;
&lt;h3 id="basic-syntax"&gt;Basic Syntax &lt;a class="headline-link" href="#basic-syntax" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Core &lt;code&gt;ss&lt;/code&gt; command forms and output controls.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ss&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show non-listening sockets with an established connection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ss -a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show all sockets, listening and non-listening&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ss -n&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show numeric addresses and ports, no name resolution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ss -p&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the process that owns each socket&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ss -s&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show a summary of socket counts by type and state&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="filter-by-protocol"&gt;Filter by Protocol &lt;a class="headline-link" href="#filter-by-protocol" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Restrict output to a single socket family.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ss -t&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;TCP sockets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ss -u&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;UDP sockets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ss -x&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unix domain sockets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ss -ta&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All TCP sockets, including listening&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ss -4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;IPv4 sockets only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ss -6&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;IPv6 sockets only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="listening-ports"&gt;Listening Ports &lt;a class="headline-link" href="#listening-ports" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Find services that are accepting connections.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ss -l&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show listening sockets only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ss -tl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Listening TCP sockets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ss -ul&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Listening UDP sockets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ss -tulpn&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Listening TCP/UDP with process and numeric output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo ss -tlpn 'sport = :80'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Find the process listening on TCP port 80&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="connection-state-filters"&gt;Connection State Filters &lt;a class="headline-link" href="#connection-state-filters" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Narrow output to a specific TCP state.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ss -tn state ESTABLISHED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Established TCP connections&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ss -tn state listening&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Listening TCP sockets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ss -tn state TIME-WAIT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Connections in &lt;code&gt;TIME-WAIT&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ss -tn state CLOSE-WAIT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Connections in &lt;code&gt;CLOSE-WAIT&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ss -tn state ESTABLISHED | tail -n +2 | wc -l&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Count established TCP connections&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="address-and-port-filters"&gt;Address and Port Filters &lt;a class="headline-link" href="#address-and-port-filters" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Match sockets by source or destination.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ss -tnp 'dport = :443'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Filter by destination port&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ss -tnp 'sport = :22'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Filter by source port&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ss -tn dst 192.168.1.5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Filter by remote address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ss -tn src 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Filter by local address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo ss -tlpn sport = :8080&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Find the process listening on port 8080&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="process-and-statistics"&gt;Process and Statistics &lt;a class="headline-link" href="#process-and-statistics" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Tie sockets to processes and read summary counts.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo ss -tp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;TCP sockets with process name and PID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo ss -tulpn&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Listening sockets with owning processes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ss -s&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Total sockets by transport and state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ss -tn&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;TCP sockets with numeric addresses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ss -tn dst 203.0.113.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All connections to a remote host&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="netstat-to-ss-translation"&gt;netstat to ss Translation &lt;a class="headline-link" href="#netstat-to-ss-translation" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Map old &lt;code&gt;netstat&lt;/code&gt; commands to their &lt;code&gt;ss&lt;/code&gt; equivalents.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;netstat Command&lt;/th&gt;
&lt;th&gt;ss Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;netstat -tuln&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ss -tuln&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -tulnp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo ss -tulpn&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;netstat -at&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ss -ta&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;netstat -ant | grep ESTABLISHED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ss -tn state ESTABLISHED&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;netstat -s&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ss -s&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Common &lt;code&gt;ss&lt;/code&gt; issues and quick fixes.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Issue&lt;/th&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-p&lt;/code&gt; shows no process&lt;/td&gt;
&lt;td&gt;Run with &lt;code&gt;sudo&lt;/code&gt; to see sockets owned by other users&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Filters return nothing&lt;/td&gt;
&lt;td&gt;Quote the expression and verify &lt;code&gt;sport&lt;/code&gt; versus &lt;code&gt;dport&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service names hide ports&lt;/td&gt;
&lt;td&gt;Add &lt;code&gt;-n&lt;/code&gt; to keep numeric ports&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output too broad&lt;/td&gt;
&lt;td&gt;Start with &lt;code&gt;-t&lt;/code&gt;, &lt;code&gt;-u&lt;/code&gt;, or a &lt;code&gt;state&lt;/code&gt; filter, then narrow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Port match too broad&lt;/td&gt;
&lt;td&gt;Use a built-in filter, such as &lt;code&gt;ss -tlpn 'sport = :80'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="related-guides"&gt;Related Guides &lt;a class="headline-link" href="#related-guides" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Use these guides for full walkthroughs and related tools.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Guide&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/ss-command-in-linux/"&gt;&lt;code&gt;ss&lt;/code&gt; Command in Linux&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Full &lt;code&gt;ss&lt;/code&gt; guide with examples&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/netstat-command-in-linux/"&gt;&lt;code&gt;netstat&lt;/code&gt; Command in Linux&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;The legacy tool &lt;code&gt;ss&lt;/code&gt; replaces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/linux-ip-command/"&gt;&lt;code&gt;ip&lt;/code&gt; Command in Linux&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Modern routes and interface management&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/check-listening-ports-linux/"&gt;How to Check Listening Ports in Linux&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Compare &lt;code&gt;ss&lt;/code&gt;, &lt;code&gt;netstat&lt;/code&gt;, and &lt;code&gt;lsof&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/lsof-command-in-linux/"&gt;&lt;code&gt;lsof&lt;/code&gt; Command in Linux&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Tie sockets and files back to processes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;</content:encoded></item><item><title>How to Install Nginx on Debian 13</title><link>https://linuxize.com/post/how-to-install-nginx-on-debian-13/</link><pubDate>Wed, 20 May 2026 09:30:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/how-to-install-nginx-on-debian-13/</guid><category>nginx</category><category>debian</category><description>Step-by-step instructions for installing and configuring Nginx on Debian 13 Trixie using apt, including UFW rules, the systemd service, and your first server block.</description><content:encoded>&lt;p&gt;Nginx is a fast, lightweight web server that serves static content, terminates TLS, and proxies requests to application servers behind it. Debian 13 Trixie ships Nginx in its main apt repository, which makes the install a single command on a fresh system.&lt;/p&gt;
&lt;p&gt;This guide explains how to install Nginx on Debian 13, open the firewall, manage the service with systemd, and create a basic server block for your first site.&lt;/p&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites &lt;a class="headline-link" href="#prerequisites" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You will need a Debian 13 server with a sudo user. UFW should be installed and active so the install is firewalled by default.&lt;/p&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Install Nginx&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt install nginx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Allow HTTP and HTTPS in UFW&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo ufw allow 'Nginx Full'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Start the service&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl start nginx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enable on boot&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl enable nginx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reload after config change&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl reload nginx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test configuration&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo nginx -t&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Show service status&lt;/td&gt;
&lt;td&gt;&lt;code&gt;systemctl status nginx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Site config directory&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/etc/nginx/sites-available/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enabled-site symlinks&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/etc/nginx/sites-enabled/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Default document root&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/var/www/html/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="step-1-update-the-package-index"&gt;Step 1: Update the Package Index &lt;a class="headline-link" href="#step-1-update-the-package-index" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Refresh apt so it sees the latest package metadata before the install:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt update&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The output lists the repositories that were checked. A clean run finishes without errors.&lt;/p&gt;
&lt;h2 id="step-2-install-nginx"&gt;Step 2: Install Nginx &lt;a class="headline-link" href="#step-2-install-nginx" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Install the &lt;code&gt;nginx&lt;/code&gt; package:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The default install pulls in the &lt;code&gt;nginx-common&lt;/code&gt; package, the systemd unit, and the standard set of modules. apt also creates the &lt;code&gt;www-data&lt;/code&gt; user that owns the document root and runs the worker processes.&lt;/p&gt;
&lt;p&gt;Confirm the version:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nginx -v&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;nginx version: nginx/1.26.3&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The exact version may differ as Debian publishes point updates, but 1.26.x is the version that ships with Debian 13 Trixie.&lt;/p&gt;
&lt;h2 id="step-3-allow-http-and-https-in-ufw"&gt;Step 3: Allow HTTP and HTTPS in UFW &lt;a class="headline-link" href="#step-3-allow-http-and-https-in-ufw" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The Nginx package registers application profiles with UFW. Allow both HTTP and HTTPS in one rule:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo ufw allow &lt;span class="s1"&gt;&amp;#39;Nginx Full&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Verify the rule is in place:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo ufw status&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Status: active
To Action From
-- ------ ----
Nginx Full ALLOW Anywhere
OpenSSH ALLOW Anywhere&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you only need HTTP for now, replace the rule with &lt;code&gt;Nginx HTTP&lt;/code&gt;. Switch to &lt;code&gt;Nginx Full&lt;/code&gt; later when you add TLS.&lt;/p&gt;
&lt;h2 id="step-4-manage-the-nginx-service-with-systemd"&gt;Step 4: Manage the Nginx Service with systemd &lt;a class="headline-link" href="#step-4-manage-the-nginx-service-with-systemd" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The Nginx package starts the service at install time and enables it for boot. Confirm both:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl is-active nginx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl is-enabled nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;active
enabled&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If the service is not running, start it:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl start nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The most common service operations are restart, reload, and status:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl restart nginx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl reload nginx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl status nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;reload&lt;/code&gt; re-reads the configuration without dropping connections and is the right command after editing a server block. &lt;code&gt;restart&lt;/code&gt; stops and starts the service, which is only needed when the binary itself changes.&lt;/p&gt;
&lt;h2 id="step-5-verify-the-default-page"&gt;Step 5: Verify the Default Page &lt;a class="headline-link" href="#step-5-verify-the-default-page" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Open a browser and visit the server&amp;rsquo;s IP, or use &lt;code&gt;curl&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -I http://localhost&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;HTTP/1.1 200 OK
Server: nginx/1.26.3
Content-Type: text/html&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A &lt;code&gt;200 OK&lt;/code&gt; response confirms Nginx is serving the default page. The default document root is &lt;code&gt;/var/www/html/&lt;/code&gt; and contains an &lt;code&gt;index.nginx-debian.html&lt;/code&gt; welcome page.&lt;/p&gt;
&lt;h2 id="step-6-create-your-first-server-block"&gt;Step 6: Create Your First Server Block &lt;a class="headline-link" href="#step-6-create-your-first-server-block" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Server blocks are how Nginx serves multiple sites from a single host. Create the document root for the new site:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo mkdir -p /var/www/example.com/html
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo chown -R &lt;span class="nv"&gt;$USER&lt;/span&gt;:&lt;span class="nv"&gt;$USER&lt;/span&gt; /var/www/example.com/html
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;h1&amp;gt;Hello from example.com&amp;lt;/h1&amp;gt;&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; sudo tee /var/www/example.com/html/index.html&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Create a server block file:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nano /etc/nginx/sites-available/example.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Paste the following configuration and adjust the domain name:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="nginx"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;/etc/nginx/sites-available/example.com&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300"&gt;nginx&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="s"&gt;[::]:80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;example.com&lt;/span&gt; &lt;span class="s"&gt;www.example.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;root&lt;/span&gt; &lt;span class="s"&gt;/var/www/example.com/html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;index&lt;/span&gt; &lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;try_files&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt; &lt;span class="nv"&gt;$uri/&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Enable the site by creating a symlink in &lt;code&gt;sites-enabled&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Test the configuration before reloading:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nginx -t&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Reload Nginx so the new server block takes effect:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl reload nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Visit the domain to verify the site loads. If the domain does not yet point at the server, edit &lt;code&gt;/etc/hosts&lt;/code&gt; on a workstation or use &lt;code&gt;curl --resolve example.com:80:SERVER_IP http://example.com/&lt;/code&gt; to test.&lt;/p&gt;
&lt;h2 id="important-nginx-files-and-directories"&gt;Important Nginx Files and Directories &lt;a class="headline-link" href="#important-nginx-files-and-directories" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The default Debian layout splits configuration across several locations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/etc/nginx/nginx.conf&lt;/code&gt; is the main configuration file.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/etc/nginx/sites-available/&lt;/code&gt; holds the server block templates.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/etc/nginx/sites-enabled/&lt;/code&gt; holds symlinks to the active server blocks.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/etc/nginx/snippets/&lt;/code&gt; is for shared configuration snippets such as TLS hardening.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/var/log/nginx/access.log&lt;/code&gt; and &lt;code&gt;/var/log/nginx/error.log&lt;/code&gt; are the log files.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/var/www/html/&lt;/code&gt; is the default document root.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Default page does not load&lt;/strong&gt;&lt;br&gt;
Check that the service is running with &lt;code&gt;systemctl status nginx&lt;/code&gt;. If it is not, run &lt;code&gt;sudo nginx -t&lt;/code&gt; to spot configuration errors. UFW may also block port 80; confirm with &lt;code&gt;sudo ufw status&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;bind() to 0.0.0.0:80 failed (98: Address already in use)&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Another web server is bound to port 80. Apache is the usual culprit on Debian. Stop it with &lt;code&gt;sudo systemctl stop apache2&lt;/code&gt; and disable it on boot with &lt;code&gt;sudo systemctl disable apache2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Server block returns 404&lt;/strong&gt;&lt;br&gt;
The symlink in &lt;code&gt;sites-enabled&lt;/code&gt; is missing or points to the wrong file. List it with &lt;code&gt;ls -l /etc/nginx/sites-enabled/&lt;/code&gt;. Recreate the symlink and reload Nginx.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;nginx -t&lt;/code&gt; reports duplicate &lt;code&gt;default_server&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Two server blocks set &lt;code&gt;listen 80 default_server&lt;/code&gt;. Only one can be the default. Remove the keyword from the new server block and reload.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Nginx on Debian 13 is one apt command away. Stick with the Debian package for general-purpose use, and switch to the upstream nginx.org repository when you need a feature only the latest mainline release supports.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/how-to-install-nginx-on-debian-13/featured_hu_135bf6eed67cb562.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>How to Install Podman on Ubuntu: Rootless Container Alternative to Docker</title><link>https://linuxize.com/post/how-to-install-podman-on-ubuntu/</link><pubDate>Tue, 19 May 2026 09:40:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/how-to-install-podman-on-ubuntu/</guid><category>podman</category><category>ubuntu</category><category>linux commands</category><description>Step-by-step instructions for installing Podman on Ubuntu, running rootless containers, building images with Buildah, and switching from Docker.</description><content:encoded>&lt;p&gt;Podman is a daemonless container engine that runs OCI containers without a long-lived root process. Each container is started as a regular user-owned process, which removes the privileged daemon that Docker relies on and makes containers safer to run on shared servers. The command-line surface mirrors Docker almost exactly, so most Dockerfiles, image references, and &lt;code&gt;docker run&lt;/code&gt; patterns work with little to no change.&lt;/p&gt;
&lt;p&gt;This guide explains how to install Podman on Ubuntu, configure rootless mode, run your first container, and use Podman as a drop-in replacement for the Docker CLI.&lt;/p&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For a printable quick reference, see the &lt;a href="https://linuxize.com/cheatsheet/podman/"&gt;Podman cheatsheet&lt;/a&gt;
.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Install Podman&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt install podman&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run a container&lt;/td&gt;
&lt;td&gt;&lt;code&gt;podman run -d --name web -p 8080:80 nginx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List containers&lt;/td&gt;
&lt;td&gt;&lt;code&gt;podman ps -a&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build an image&lt;/td&gt;
&lt;td&gt;&lt;code&gt;podman build -t myimage .&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List images&lt;/td&gt;
&lt;td&gt;&lt;code&gt;podman images&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pull an image&lt;/td&gt;
&lt;td&gt;&lt;code&gt;podman pull alpine:latest&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stop and remove&lt;/td&gt;
&lt;td&gt;&lt;code&gt;podman rm -f web&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inspect rootless setup&lt;/td&gt;
&lt;td&gt;&lt;code&gt;podman info&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="install-podman"&gt;Install Podman &lt;a class="headline-link" href="#install-podman" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Podman has been part of the Ubuntu universe repository since 20.10, so on every supported release you can install it through &lt;code&gt;apt&lt;/code&gt; without adding a third-party PPA.&lt;/p&gt;
&lt;p&gt;Update the package index, then install:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install podman&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Confirm the version:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman --version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;podman version 5.7.0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Ubuntu 26.04 ships Podman 5.7 from the universe repository. Ubuntu 24.04 ships the 4.9 series, while Ubuntu 22.04 ships the 3.4 series, so the exact version depends on the Ubuntu release you are using.&lt;/p&gt;
&lt;h2 id="verify-the-rootless-setup"&gt;Verify the Rootless Setup &lt;a class="headline-link" href="#verify-the-rootless-setup" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Podman supports both root and rootless modes, and the rootless mode is the one most users want. Run &lt;code&gt;podman info&lt;/code&gt; as your regular user and look for &lt;code&gt;rootless: true&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman info &lt;span class="p"&gt;|&lt;/span&gt; head -20&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;host:
arch: amd64
cgroupManager: systemd
...
security:
rootless: true&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Two pieces have to be in place for rootless containers to work: subordinate UID/GID mappings, and a user namespace. Ubuntu sets both up automatically on first run, but you can confirm with:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cat /etc/subuid /etc/subgid&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;sara:100000:65536
sara:100000:65536&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Each user gets a range of 65,536 subordinate UIDs and GIDs that Podman uses to map container processes to non-overlapping host IDs. If the file is missing your user, add an entry with &lt;code&gt;sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 sara&lt;/code&gt; and run &lt;code&gt;podman system migrate&lt;/code&gt; to apply the change.&lt;/p&gt;
&lt;h2 id="run-your-first-container"&gt;Run Your First Container &lt;a class="headline-link" href="#run-your-first-container" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Pull and run an Nginx container in detached mode, exposing port &lt;code&gt;8080&lt;/code&gt; on the host:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman run -d --name web -p 8080:80 nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Resolved &amp;#34;nginx&amp;#34; as an alias (/etc/containers/registries.conf.d/shortnames.conf)
Trying to pull docker.io/library/nginx:latest...
Getting image source signatures
Copying blob sha256:...
Writing manifest to image destination
Storing signatures
2b9bd4e1f3...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Check that the container is running:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman ps&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2b9bd4e1f3a8 docker.io/library/nginx:latest nginx -g daemon o... 10 seconds ago Up 10 seconds 0.0.0.0:8080-&amp;gt;80/tcp web&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Hit the published port from the host:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -I http://localhost:8080&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;HTTP/1.1 200 OK
Server: nginx/1.27.4&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The response confirms that the user-mode network stack forwarded the request into the container correctly. Stop and remove the container with:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman rm -f web&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="configure-registries"&gt;Configure Registries &lt;a class="headline-link" href="#configure-registries" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;By default Podman searches a configured list of registries when you run &lt;code&gt;podman pull nginx&lt;/code&gt; (without a registry prefix). The list lives in &lt;code&gt;/etc/containers/registries.conf&lt;/code&gt;. To prefer Docker Hub, edit the file:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nano /etc/containers/registries.conf&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Set the unqualified-search registries:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="toml"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;/etc/containers/registries.conf&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-orange-100 text-orange-700 dark:bg-orange-900 dark:text-orange-300"&gt;toml&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;unqualified-search-registries&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;docker.io&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;quay.io&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Save and exit. From now on &lt;code&gt;podman pull alpine&lt;/code&gt; resolves to &lt;code&gt;docker.io/library/alpine:latest&lt;/code&gt;, which matches Docker&amp;rsquo;s default behavior. Save yourself confusion by always pulling with the full reference (&lt;code&gt;podman pull docker.io/library/alpine&lt;/code&gt;) in scripts and CI pipelines.&lt;/p&gt;
&lt;h2 id="use-podman-as-a-docker-replacement"&gt;Use Podman as a Docker Replacement &lt;a class="headline-link" href="#use-podman-as-a-docker-replacement" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you already have shell history, Makefiles, or scripts that call &lt;code&gt;docker&lt;/code&gt;, install the alias package and keep them working:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install podman-docker&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The package adds a &lt;code&gt;/usr/bin/docker&lt;/code&gt; symlink that points to &lt;code&gt;podman&lt;/code&gt;, plus a stub that suppresses the daemon-not-running warning. Test it:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run --rm hello-world&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Hello from Docker!&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Behind the scenes, &lt;code&gt;docker run&lt;/code&gt; invoked &lt;code&gt;podman run&lt;/code&gt;. Most everyday commands (&lt;code&gt;build&lt;/code&gt;, &lt;code&gt;pull&lt;/code&gt;, &lt;code&gt;push&lt;/code&gt;, &lt;code&gt;images&lt;/code&gt;, &lt;code&gt;ps&lt;/code&gt;, &lt;code&gt;logs&lt;/code&gt;, &lt;code&gt;exec&lt;/code&gt;) work identically. The differences show up around Docker Compose and the daemon socket; Podman 4 ships its own socket that Docker Compose can target by setting &lt;code&gt;DOCKER_HOST&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="build-an-image-with-a-dockerfile"&gt;Build an Image with a Dockerfile &lt;a class="headline-link" href="#build-an-image-with-a-dockerfile" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Podman builds OCI images from a standard Dockerfile, no changes required. Create a small Flask app to demonstrate:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkdir hello-podman &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; hello-podman
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nano app.py&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="py"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;app.py&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-blue-100 text-blue-700 dark:bg-blue-900 dark:text-blue-300"&gt;py&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vm"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Hello from Podman!&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;0.0.0.0&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Write the Dockerfile:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="dockerfile"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Dockerfile&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-sky-100 text-sky-700 dark:bg-sky-900 dark:text-sky-300"&gt;dockerfile&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-dockerfile" data-lang="dockerfile"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;python:3.12-slim&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;/app&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;RUN&lt;/span&gt; pip install --no-cache-dir flask&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;COPY&lt;/span&gt; app.py .&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;5000&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CMD&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;python&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;app.py&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Build the image:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman build -t hello-podman .&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;STEP 1/6: FROM python:3.12-slim
STEP 2/6: WORKDIR /app
...
Successfully tagged localhost/hello-podman:latest&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Run a container from the new image:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman run -d --name hello -p 5000:5000 hello-podman
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl http://localhost:5000&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Hello from Podman!&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The image and container both live in the user&amp;rsquo;s storage (&lt;code&gt;~/.local/share/containers&lt;/code&gt;), so no &lt;code&gt;sudo&lt;/code&gt; is involved at any step.&lt;/p&gt;
&lt;h2 id="run-pods-of-containers"&gt;Run Pods of Containers &lt;a class="headline-link" href="#run-pods-of-containers" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The name &amp;ldquo;Podman&amp;rdquo; comes from its support for Kubernetes-style pods: groups of containers that share a network namespace. Create an empty pod, then add containers to it:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman pod create --name webapp -p 8080:80
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman run -d --pod webapp --name redis redis:7
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman run -d --pod webapp --name nginx nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Inside the pod, &lt;code&gt;nginx&lt;/code&gt; can reach Redis at &lt;code&gt;localhost:6379&lt;/code&gt; because they share the network namespace. List pods with:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman pod ps&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS
8f4d7b8c0e1f webapp Running 20 seconds ago c2a8f9b1d3e4 3&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The count of three includes an &lt;code&gt;infra&lt;/code&gt; container that holds the shared namespaces.&lt;/p&gt;
&lt;h2 id="run-podman-as-a-systemd-service"&gt;Run Podman as a systemd Service &lt;a class="headline-link" href="#run-podman-as-a-systemd-service" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To start a container at boot in rootless mode, use a Quadlet container unit. Quadlet is Podman&amp;rsquo;s current systemd integration method and keeps the container definition in a small, readable file.&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkdir -p ~/.config/containers/systemd
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nano ~/.config/containers/systemd/web.container&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Add the container definition:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="ini"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;~/.config/containers/systemd/web.container&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-slate-200 text-slate-700 dark:bg-slate-600 dark:text-slate-300"&gt;ini&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Unit]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Rootless Nginx container&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Container]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;docker.io/library/nginx:latest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;ContainerName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;web&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;PublishPort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;8080:80&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Service]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Restart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;always&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Install]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;WantedBy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;default.target&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Reload the user systemd manager, then start the generated service:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl --user daemon-reload
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl --user start web.service&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Enable lingering so the user services keep running after logout:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo loginctl enable-linger sara&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;[Install]&lt;/code&gt; section tells the Podman systemd generator to attach the service to the user&amp;rsquo;s default target. The container now starts without root or a system-wide daemon.&lt;/p&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Error: short-name &amp;quot;nginx&amp;quot; did not resolve to an alias&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
The &lt;code&gt;unqualified-search-registries&lt;/code&gt; list is empty. Edit &lt;code&gt;/etc/containers/registries.conf&lt;/code&gt; and add at least &lt;code&gt;docker.io&lt;/code&gt;, or pull with the full reference (&lt;code&gt;podman pull docker.io/library/nginx&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;ERRO[0000] cannot find UID/GID for user&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
The current user has no entry in &lt;code&gt;/etc/subuid&lt;/code&gt; or &lt;code&gt;/etc/subgid&lt;/code&gt;. Run &lt;code&gt;sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER&lt;/code&gt;, then &lt;code&gt;podman system migrate&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ports under 1024 refuse to bind in rootless mode&lt;/strong&gt;&lt;br&gt;
The kernel restricts low ports for non-root users. Either publish to a high port (&lt;code&gt;-p 8080:80&lt;/code&gt;), or allow your user to bind low ports with &lt;code&gt;sudo sysctl net.ipv4.ip_unprivileged_port_start=80&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="faq"&gt;FAQ &lt;a class="headline-link" href="#faq" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Is Podman a drop-in Docker replacement?&lt;/strong&gt;&lt;br&gt;
For everyday &lt;code&gt;run&lt;/code&gt;, &lt;code&gt;build&lt;/code&gt;, &lt;code&gt;pull&lt;/code&gt;, &lt;code&gt;push&lt;/code&gt;, and &lt;code&gt;exec&lt;/code&gt; workflows, yes. Compose and the long-lived socket need extra setup. Most CI jobs only need the CLI and switch over without changes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Does Podman work with Docker Compose?&lt;/strong&gt;&lt;br&gt;
Yes. Start the Podman socket with &lt;code&gt;systemctl --user enable --now podman.socket&lt;/code&gt;, export &lt;code&gt;DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock&lt;/code&gt;, and run &lt;code&gt;docker compose up&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Are Podman images compatible with Docker?&lt;/strong&gt;&lt;br&gt;
Yes. Both produce OCI-compliant images, so an image built with &lt;code&gt;podman build&lt;/code&gt; runs unchanged under Docker, and vice versa.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Podman gives you the Docker workflow without the daemon and without root, which is often what you want on a multi-user server or a developer workstation. Install it from &lt;code&gt;apt&lt;/code&gt;, alias &lt;code&gt;docker&lt;/code&gt; if you have existing scripts, and the rest of the CLI feels familiar.&lt;/p&gt;
&lt;p&gt;For follow-up reading, see our guides on &lt;a href="https://linuxize.com/post/how-to-install-docker-on-ubuntu-26-04/"&gt;installing Docker on Ubuntu 26.04&lt;/a&gt;
and &lt;a href="https://linuxize.com/post/how-to-build-docker-images-with-dockerfile/"&gt;building Docker images with a Dockerfile&lt;/a&gt;
.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/how-to-install-podman-on-ubuntu/featured_hu_dc1b76c2fbf72420.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>Bash String Manipulation: Substring, Replace, Length, and More</title><link>https://linuxize.com/post/bash-string-manipulation/</link><pubDate>Mon, 18 May 2026 10:30:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/bash-string-manipulation/</guid><category>bash</category><description>Bash string manipulation using parameter expansion: extract substrings, replace text, strip prefixes and suffixes, change case, and check for substrings with practical examples.</description><content:encoded>&lt;p&gt;When you write Bash scripts, many small text tasks do not need &lt;code&gt;sed&lt;/code&gt;, &lt;code&gt;awk&lt;/code&gt;, or another external command. Parameter expansion can extract part of a string, replace text, strip a prefix, or change case directly inside &lt;code&gt;${}&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This guide covers the most useful string operations with practical examples for each one.&lt;/p&gt;
&lt;h2 id="string-length"&gt;String Length &lt;a class="headline-link" href="#string-length" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Get the number of characters in a variable using &lt;code&gt;${#variable}&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;backup-2026-04-14.tar.gz&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;filename&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;24&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This returns the character count. For strings that contain only ASCII characters, the character count and byte count are the same.&lt;/p&gt;
&lt;h2 id="extracting-a-substring"&gt;Extracting a Substring &lt;a class="headline-link" href="#extracting-a-substring" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use &lt;code&gt;${variable:offset:length}&lt;/code&gt; to extract part of a string:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;str&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Hello, World&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;7&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;5&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;World&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The offset is zero-based, so &lt;code&gt;7&lt;/code&gt; starts at the eighth character. &lt;code&gt;5&lt;/code&gt; is how many characters to return.&lt;/p&gt;
&lt;p&gt;Omit the length to extract from the offset to the end of the string:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;7&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;World&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Negative offsets count from the end of the string. Note the space before the minus sign; it is required to avoid being interpreted as the &lt;code&gt;:-&lt;/code&gt; default value syntax:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;str&lt;/span&gt;&lt;span class="p"&gt;: -5&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;World&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="replacing-a-substring"&gt;Replacing a Substring &lt;a class="headline-link" href="#replacing-a-substring" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use &lt;code&gt;${variable/pattern/replacement}&lt;/code&gt; to replace the first match of a pattern:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/var/log/app/error.log&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="p"&gt;/log/logs&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;/var/logs/app/error.log&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Only the first occurrence is replaced. To replace every occurrence, double the forward slash:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="p"&gt;//log/logs&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;/var/logs/app/error.logs&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Leave the replacement empty to delete a substring:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;v1.2.3&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;version&lt;/span&gt;&lt;span class="p"&gt;/v/&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;1.2.3&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The pattern supports glob characters: &lt;code&gt;*&lt;/code&gt; matches any sequence of characters, &lt;code&gt;?&lt;/code&gt; matches a single character, and &lt;code&gt;[...]&lt;/code&gt; matches a character class.&lt;/p&gt;
&lt;h2 id="removing-a-prefix"&gt;Removing a Prefix &lt;a class="headline-link" href="#removing-a-prefix" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use &lt;code&gt;#&lt;/code&gt; to remove a prefix from the beginning of a string. The shortest matching prefix is removed:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;photo.backup.tar.gz&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;file&lt;/span&gt;&lt;span class="p"&gt;#*.&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;backup.tar.gz&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Use &lt;code&gt;##&lt;/code&gt; to remove the longest possible matching prefix:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;file&lt;/span&gt;&lt;span class="p"&gt;##*.&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;gz&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A common use is stripping a directory path to get just the filename:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;filepath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/home/user/documents/report.pdf&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;##*/&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;report.pdf&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="removing-a-suffix"&gt;Removing a Suffix &lt;a class="headline-link" href="#removing-a-suffix" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use &lt;code&gt;%&lt;/code&gt; to strip a suffix from the end of a string. The shortest match is removed:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;photo.backup.tar.gz&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;file&lt;/span&gt;&lt;span class="p"&gt;%.*&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;photo.backup.tar&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Use &lt;code&gt;%%&lt;/code&gt; for the longest match:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;file&lt;/span&gt;&lt;span class="p"&gt;%%.*&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;photo&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Combining prefix and suffix removal is a clean way to extract a bare filename from a path:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;filepath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/home/user/documents/report.pdf&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;base&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;##*/&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;base&lt;/span&gt;&lt;span class="p"&gt;%.*&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;report&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="changing-case"&gt;Changing Case &lt;a class="headline-link" href="#changing-case" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Convert a string to uppercase with &lt;code&gt;^^&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;greeting&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hello, world&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;greeting&lt;/span&gt;&lt;span class="p"&gt;^^&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;HELLO, WORLD&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Convert to lowercase with &lt;code&gt;,,&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Hello World&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;hello world&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To capitalize only the first character, use a single &lt;code&gt;^&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;word&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;linux&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;word&lt;/span&gt;&lt;span class="p"&gt;^&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Linux&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To lowercase only the first character, use a single &lt;code&gt;,&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;word&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;LINUX&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;word&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;lINUX&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Case conversion requires Bash 4.0 or newer. The default shell on older macOS systems ships with Bash 3.2 and does not support these operators. As a portable alternative, use &lt;code&gt;tr&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$greeting&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; tr &lt;span class="s1"&gt;&amp;#39;[:lower:]&amp;#39;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;[:upper:]&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="checking-if-a-string-contains-a-substring"&gt;Checking if a String Contains a Substring &lt;a class="headline-link" href="#checking-if-a-string-contains-a-substring" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use a glob pattern inside &lt;code&gt;[[ ]]&lt;/code&gt; to test whether a string contains another string:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;str&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Running on Linux kernel 6.8&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; *&lt;span class="s2"&gt;&amp;#34;Linux&amp;#34;&lt;/span&gt;* &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Linux detected&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Linux detected&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;*&lt;/code&gt; wildcards match anything before and after the search pattern. The comparison is case-sensitive. To check case-insensitively, convert both sides to the same case first using &lt;code&gt;,,&lt;/code&gt; before comparing.&lt;/p&gt;
&lt;p&gt;For a more detailed walkthrough of substring detection, including case-insensitive matching and edge cases, see &lt;a href="https://linuxize.com/post/how-to-check-if-string-contains-substring-in-bash/"&gt;How to Check if a String Contains a Substring in Bash&lt;/a&gt;
. For more on &lt;code&gt;[[ ]]&lt;/code&gt; conditionals and string comparison operators, see the guide on &lt;a href="https://linuxize.com/post/bash-comparison-operators/"&gt;Bash comparison operators&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For a printable quick reference, see the &lt;a href="https://linuxize.com/cheatsheet/bash/"&gt;Bash cheatsheet&lt;/a&gt;
.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Syntax&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;String length&lt;/td&gt;
&lt;td&gt;&lt;code&gt;${#var}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Substring from offset&lt;/td&gt;
&lt;td&gt;&lt;code&gt;${var:offset}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Substring with length&lt;/td&gt;
&lt;td&gt;&lt;code&gt;${var:offset:length}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Replace first match&lt;/td&gt;
&lt;td&gt;&lt;code&gt;${var/pattern/replacement}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Replace all matches&lt;/td&gt;
&lt;td&gt;&lt;code&gt;${var//pattern/replacement}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete substring&lt;/td&gt;
&lt;td&gt;&lt;code&gt;${var/pattern/}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remove shortest prefix&lt;/td&gt;
&lt;td&gt;&lt;code&gt;${var#pattern}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remove longest prefix&lt;/td&gt;
&lt;td&gt;&lt;code&gt;${var##pattern}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remove shortest suffix&lt;/td&gt;
&lt;td&gt;&lt;code&gt;${var%pattern}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remove longest suffix&lt;/td&gt;
&lt;td&gt;&lt;code&gt;${var%%pattern}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Uppercase all&lt;/td&gt;
&lt;td&gt;&lt;code&gt;${var^^}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lowercase all&lt;/td&gt;
&lt;td&gt;&lt;code&gt;${var,,}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Uppercase first char&lt;/td&gt;
&lt;td&gt;&lt;code&gt;${var^}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lowercase first char&lt;/td&gt;
&lt;td&gt;&lt;code&gt;${var,}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Bash parameter expansion handles most day-to-day string operations without extra commands, which keeps scripts shorter and faster. For more on working with strings, see the guides on &lt;a href="https://linuxize.com/post/bash-concatenate-strings/"&gt;Bash string concatenation&lt;/a&gt;
and &lt;a href="https://linuxize.com/post/bash-split-string/"&gt;splitting strings by delimiter&lt;/a&gt;
.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/bash-string-manipulation/featured_hu_a2a2d6314de9566c.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>nmcli Cheatsheet</title><link>https://linuxize.com/cheatsheet/nmcli/</link><pubDate>Sun, 17 May 2026 10:40:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/cheatsheet/nmcli/</guid><description>Quick reference for using nmcli to manage NetworkManager devices, connection profiles, Wi-Fi, static IP addresses, DNS, and troubleshooting</description><content:encoded>&lt;div class="card"&gt;
&lt;h3 id="basic-syntax"&gt;Basic Syntax &lt;a class="headline-link" href="#basic-syntax" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Use this structure for most &lt;code&gt;nmcli&lt;/code&gt; commands.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmcli [OPTIONS] OBJECT COMMAND&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;General command syntax&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmcli OBJECT help&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show help for an object&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmcli --version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show nmcli version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmcli -t ...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use terse output for scripts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmcli -f FIELD1,FIELD2 ...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Select output fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmcli -g FIELD ...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print field values only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="general-status"&gt;General Status &lt;a class="headline-link" href="#general-status" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Check NetworkManager state and hostname settings.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmcli general status&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show overall NetworkManager status&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmcli networking connectivity&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check internet connectivity state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmcli general hostname&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show system hostname&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli general hostname server01&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set system hostname&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmcli general permissions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show NetworkManager permissions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmcli general logging&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show logging level and domains&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="devices"&gt;Devices &lt;a class="headline-link" href="#devices" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Inspect and control network interfaces.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmcli device status&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List devices and connection state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmcli device show eth0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show detailed device properties&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmcli -f GENERAL,IP4 device show eth0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show selected device sections&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli device connect eth0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Connect a device&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli device disconnect eth0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Disconnect a device&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmcli device reapply eth0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reapply active profile settings&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="connection-profiles"&gt;Connection Profiles &lt;a class="headline-link" href="#connection-profiles" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Manage saved NetworkManager profiles.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmcli connection show&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List all profiles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmcli connection show --active&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List active profiles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmcli connection show &amp;quot;home-wifi&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show one profile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli connection up &amp;quot;home-wifi&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Activate a profile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli connection down &amp;quot;home-wifi&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deactivate a profile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli connection delete &amp;quot;home-wifi&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete a saved profile&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="wi-fi"&gt;Wi-Fi &lt;a class="headline-link" href="#wi-fi" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Scan networks and connect to wireless profiles.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmcli radio wifi&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show Wi-Fi radio state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli radio wifi on&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Turn Wi-Fi on&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli radio wifi off&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Turn Wi-Fi off&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmcli device wifi list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Scan visible Wi-Fi networks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli device wifi connect &amp;quot;SSID&amp;quot; password &amp;quot;PASSWORD&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Connect with password&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli --ask device wifi connect &amp;quot;SSID&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prompt for Wi-Fi password&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli device wifi connect &amp;quot;SSID&amp;quot; password &amp;quot;PASSWORD&amp;quot; hidden yes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Connect to hidden network&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="static-ipv4"&gt;Static IPv4 &lt;a class="headline-link" href="#static-ipv4" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Set or remove manual IPv4 settings on a profile.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli connection modify &amp;quot;Wired connection 1&amp;quot; ipv4.method manual&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use manual IPv4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli connection modify &amp;quot;Wired connection 1&amp;quot; ipv4.addresses 192.168.1.50/24&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set static address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli connection modify &amp;quot;Wired connection 1&amp;quot; ipv4.gateway 192.168.1.1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set gateway&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli connection modify &amp;quot;Wired connection 1&amp;quot; ipv4.dns &amp;quot;1.1.1.1 9.9.9.9&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set DNS servers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli connection up &amp;quot;Wired connection 1&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Apply profile changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmcli -g IP4.ADDRESS,IP4.GATEWAY connection show &amp;quot;Wired connection 1&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check IPv4 settings&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="dhcp-and-dns"&gt;DHCP and DNS &lt;a class="headline-link" href="#dhcp-and-dns" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Return to DHCP or adjust DNS servers.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli connection modify &amp;quot;Wired connection 1&amp;quot; ipv4.method auto&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use DHCP for IPv4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli connection modify &amp;quot;Wired connection 1&amp;quot; ipv4.addresses &amp;quot;&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Clear static address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli connection modify &amp;quot;Wired connection 1&amp;quot; ipv4.gateway &amp;quot;&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Clear static gateway&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli connection modify &amp;quot;Wired connection 1&amp;quot; ipv4.dns &amp;quot;&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Clear static DNS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli connection modify &amp;quot;Wired connection 1&amp;quot; +ipv4.dns 8.8.8.8&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Append DNS server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli connection modify &amp;quot;Wired connection 1&amp;quot; -ipv4.dns 8.8.8.8&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove DNS server&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="add-profiles"&gt;Add Profiles &lt;a class="headline-link" href="#add-profiles" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Create new wired and wireless profiles.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli connection add type ethernet ifname eth0 con-name &amp;quot;wired-dhcp&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add wired DHCP profile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli connection add type ethernet ifname eth0 con-name &amp;quot;wired-static&amp;quot; ip4 192.168.1.50/24 gw4 192.168.1.1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add wired static profile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli connection add type wifi ifname wlan0 con-name &amp;quot;office-wifi&amp;quot; ssid &amp;quot;Office&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add Wi-Fi profile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli connection modify &amp;quot;office-wifi&amp;quot; wifi-sec.key-mgmt wpa-psk&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set WPA-PSK security&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli connection modify &amp;quot;office-wifi&amp;quot; wifi-sec.psk &amp;quot;PASSWORD&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set Wi-Fi password&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli connection up &amp;quot;wired-static&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Activate new profile&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="scripting-output"&gt;Scripting Output &lt;a class="headline-link" href="#scripting-output" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Use stable output formats in scripts.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmcli -t connection show --active&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Terse active profile list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmcli -t -f NAME,DEVICE connection show --active&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Active names and devices&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmcli -g GENERAL.STATE device show eth0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print device state value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmcli -g IP4.ADDRESS device show eth0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print IPv4 address values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmcli -f DEVICE,TYPE,STATE device status&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Select device status fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmcli -m multiline connection show &amp;quot;home-wifi&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Multiline profile details&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="monitor-and-reload"&gt;Monitor and Reload &lt;a class="headline-link" href="#monitor-and-reload" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Watch changes and reload profile files.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmcli monitor&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Watch NetworkManager events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmcli device monitor eth0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Watch one device&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli connection reload&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reload profile files from disk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli general reload&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reload NetworkManager configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli general reload dns-rc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rewrite DNS resolver configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;journalctl -u NetworkManager -n 100&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show recent NetworkManager logs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Fast checks for common NetworkManager issues.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Issue&lt;/th&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;NetworkManager is not running&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl start NetworkManager&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wi-Fi list is empty&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli radio wifi on&lt;/code&gt; and &lt;code&gt;rfkill list&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wrong profile is active&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nmcli connection show --active&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Static IP did not apply&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli connection up &amp;quot;PROFILE&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DNS is not working&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nmcli -g IP4.DNS device show eth0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Permission denied&lt;/td&gt;
&lt;td&gt;Run the command with &lt;code&gt;sudo&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="related-guides"&gt;Related Guides &lt;a class="headline-link" href="#related-guides" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Use these articles for detailed networking workflows.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Guide&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/nmcli-command-in-linux/"&gt;&lt;code&gt;nmcli Command in Linux&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Complete &lt;code&gt;nmcli&lt;/code&gt; command guide&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/linux-ip-command/"&gt;&lt;code&gt;Linux ip Command with Examples&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Interface, address, and route management&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-configure-static-ip-address-on-ubuntu-20-04/"&gt;&lt;code&gt;How to Configure Static IP Address on Ubuntu&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Netplan and desktop static IP setup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-use-dig-command-to-query-dns-in-linux/"&gt;&lt;code&gt;How to Use the dig Command&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;DNS lookup and troubleshooting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/ssh-command-in-linux/"&gt;&lt;code&gt;SSH Command in Linux&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Remote shell access and options&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;</content:encoded></item><item><title>nmcli Command in Linux: NetworkManager CLI Reference</title><link>https://linuxize.com/post/nmcli-command-in-linux/</link><pubDate>Sun, 17 May 2026 09:15:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/nmcli-command-in-linux/</guid><category>networking</category><category>linux commands</category><description>The nmcli command manages NetworkManager from the terminal. This guide covers device status, Wi-Fi connections, static IP configuration, DNS settings, and scripting-friendly output.</description><content:encoded>&lt;p&gt;When a Linux system needs a network change from a terminal, &lt;code&gt;nmcli&lt;/code&gt; is usually the cleanest way to make it. It controls NetworkManager, the service that handles wired, wireless, VPN, and other connection profiles on many Linux desktops and servers.&lt;/p&gt;
&lt;p&gt;This guide explains how to inspect device status, list and edit connections, join Wi-Fi networks, assign a static IP address, and get script-friendly output with &lt;code&gt;nmcli&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="before-you-begin"&gt;Before You Begin &lt;a class="headline-link" href="#before-you-begin" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before changing network settings, confirm that NetworkManager is installed and running:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl status NetworkManager&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On distributions that use a different network stack, install NetworkManager from the package manager and enable the service before continuing. Most Ubuntu, Fedora, and Arch desktops have it preinstalled.&lt;/p&gt;
&lt;p&gt;If you are connected over &lt;a href="https://linuxize.com/post/ssh-command-in-linux/"&gt;SSH&lt;/a&gt;
, be careful with commands that disconnect an interface, change an address, or bring a profile down. A wrong gateway, IP address, or device name can drop the remote session.&lt;/p&gt;
&lt;h2 id="nmcli-syntax"&gt;nmcli Syntax &lt;a class="headline-link" href="#nmcli-syntax" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The general &lt;code&gt;nmcli&lt;/code&gt; syntax is:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="txt"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;txt&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nmcli [OPTIONS] OBJECT { COMMAND | help }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;OBJECT&lt;/code&gt; is the part of NetworkManager you want to inspect or change. The most common objects are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;general&lt;/code&gt; - show overall NetworkManager status and hostname settings&lt;/li&gt;
&lt;li&gt;&lt;code&gt;networking&lt;/code&gt; - enable, disable, or check networking&lt;/li&gt;
&lt;li&gt;&lt;code&gt;radio&lt;/code&gt; - control Wi-Fi and WWAN radio switches&lt;/li&gt;
&lt;li&gt;&lt;code&gt;device&lt;/code&gt; - show and manage network interfaces&lt;/li&gt;
&lt;li&gt;&lt;code&gt;connection&lt;/code&gt; - show and manage saved connection profiles&lt;/li&gt;
&lt;li&gt;&lt;code&gt;monitor&lt;/code&gt; - watch NetworkManager events in real time&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can abbreviate most objects, for example &lt;code&gt;nmcli con show&lt;/code&gt; instead of &lt;code&gt;nmcli connection show&lt;/code&gt;, but the full form is clearer in scripts and documentation.&lt;/p&gt;
&lt;h2 id="show-general-status"&gt;Show General Status &lt;a class="headline-link" href="#show-general-status" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;general&lt;/code&gt; subcommand reports the overall NetworkManager state, the hostname, and whether networking is enabled:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nmcli general status&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;STATE CONNECTIVITY WIFI-HW WIFI WWAN-HW WWAN
connected full enabled enabled enabled enabled&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Read or change the system hostname through the same subcommand:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nmcli general hostname
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nmcli general hostname server01&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The second form writes the new hostname to &lt;code&gt;/etc/hostname&lt;/code&gt; and updates the kernel value without a reboot. For more hostname options, see the guide on &lt;a href="https://linuxize.com/post/how-to-change-hostname-in-linux/"&gt;changing the hostname in Linux&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="manage-devices"&gt;Manage Devices &lt;a class="headline-link" href="#manage-devices" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A device in NetworkManager is a physical or virtual interface, for example &lt;code&gt;eth0&lt;/code&gt;, &lt;code&gt;wlan0&lt;/code&gt;, or &lt;code&gt;wg0&lt;/code&gt;. List every known device with:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nmcli device status&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;DEVICE TYPE STATE CONNECTION
eth0 ethernet connected Wired connection 1
wlan0 wifi connected home-wifi
lo loopback unmanaged --&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Show detailed properties of a single device, including the MAC address, driver, and current IP configuration:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nmcli device show eth0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Bring an interface up or down without changing the underlying connection profile:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nmcli device connect eth0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nmcli device disconnect eth0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;disconnect&lt;/code&gt; stops the connection but keeps the profile available for the next &lt;code&gt;connect&lt;/code&gt; call. Do not disconnect the device that carries your current SSH session unless you have another access path.&lt;/p&gt;
&lt;h2 id="manage-connection-profiles"&gt;Manage Connection Profiles &lt;a class="headline-link" href="#manage-connection-profiles" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;NetworkManager stores each network as a connection profile under &lt;code&gt;/etc/NetworkManager/system-connections/&lt;/code&gt;. List every profile:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nmcli connection show&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;NAME UUID TYPE DEVICE
Wired connection 1 3f4e... ethernet eth0
home-wifi 7a92... wifi wlan0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Activate a saved profile by name:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nmcli connection up &lt;span class="s2"&gt;&amp;#34;home-wifi&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Deactivate it without deleting the profile:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nmcli connection down &lt;span class="s2"&gt;&amp;#34;home-wifi&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Delete a profile when it is no longer needed:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nmcli connection delete &lt;span class="s2"&gt;&amp;#34;home-wifi&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Deleting a profile removes the saved NetworkManager configuration. If you only want to stop using it for the moment, bring it down instead.&lt;/p&gt;
&lt;h2 id="connect-to-a-wi-fi-network"&gt;Connect to a Wi-Fi Network &lt;a class="headline-link" href="#connect-to-a-wi-fi-network" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Scan for available networks. The radio must be on for the scan to return results:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nmcli device wifi list&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Join an open or WPA2 network in one call. NetworkManager creates a new profile and stores the password in the system keyring:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nmcli device wifi connect &lt;span class="s2"&gt;&amp;#34;home-wifi&amp;#34;&lt;/span&gt; password &lt;span class="s2"&gt;&amp;#34;secret-passphrase&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Avoid putting real Wi-Fi passwords in shell history, shared scripts, or configuration repositories. For interactive use, run &lt;code&gt;sudo nmcli --ask device wifi connect &amp;quot;home-wifi&amp;quot;&lt;/code&gt; so &lt;code&gt;nmcli&lt;/code&gt; prompts for the password instead.&lt;/p&gt;
&lt;p&gt;For hidden networks, add the &lt;code&gt;hidden yes&lt;/code&gt; flag so NetworkManager probes the SSID directly:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nmcli device wifi connect &lt;span class="s2"&gt;&amp;#34;office-hidden&amp;#34;&lt;/span&gt; password &lt;span class="s2"&gt;&amp;#34;secret-passphrase&amp;#34;&lt;/span&gt; hidden yes&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Turn the radio on or off without removing saved profiles:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nmcli radio wifi off
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nmcli radio wifi on&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="configure-a-static-ip-address"&gt;Configure a Static IP Address &lt;a class="headline-link" href="#configure-a-static-ip-address" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Static addressing is one of the most common scripted changes on a server. The example below sets a static IPv4 address, gateway, and DNS servers on the existing &lt;code&gt;Wired connection 1&lt;/code&gt; profile:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nmcli connection modify &lt;span class="s2"&gt;&amp;#34;Wired connection 1&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ipv4.method manual &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ipv4.addresses 192.168.1.50/24 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ipv4.gateway 192.168.1.1 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ipv4.dns &lt;span class="s2"&gt;&amp;#34;1.1.1.1 9.9.9.9&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Apply the change by reactivating the profile:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nmcli connection up &lt;span class="s2"&gt;&amp;#34;Wired connection 1&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If you are changing a remote machine, confirm the address, prefix, gateway, and DNS values before running &lt;code&gt;connection up&lt;/code&gt;. A wrong value can make the host unreachable. On Ubuntu systems that use Netplan instead, see the guide on &lt;a href="https://linuxize.com/post/how-to-configure-static-ip-address-on-ubuntu-20-04/"&gt;configuring a static IP address on Ubuntu&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;Switch back to DHCP at any point with:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nmcli connection modify &lt;span class="s2"&gt;&amp;#34;Wired connection 1&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ipv4.method auto &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ipv4.addresses &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ipv4.gateway &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ipv4.dns &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nmcli connection up &lt;span class="s2"&gt;&amp;#34;Wired connection 1&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The empty values clear the manual entries that would otherwise stay on the profile.&lt;/p&gt;
&lt;h2 id="add-a-new-wired-connection"&gt;Add a New Wired Connection &lt;a class="headline-link" href="#add-a-new-wired-connection" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When the default profile is missing or has been deleted, create a fresh ethernet profile:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nmcli connection add &lt;span class="nb"&gt;type&lt;/span&gt; ethernet ifname eth0 con-name &lt;span class="s2"&gt;&amp;#34;wired-static&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ipv4.method manual &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ipv4.addresses 192.168.1.50/24 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ipv4.gateway 192.168.1.1 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ipv4.dns 1.1.1.1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nmcli connection up &lt;span class="s2"&gt;&amp;#34;wired-static&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This pattern works well in provisioning scripts when you first check whether the profile exists, or when you intentionally replace a known profile. If you only need to change an existing profile, use &lt;code&gt;connection modify&lt;/code&gt; instead of deleting and recreating it.&lt;/p&gt;
&lt;h2 id="set-dns-servers"&gt;Set DNS Servers &lt;a class="headline-link" href="#set-dns-servers" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can change DNS servers without changing the IP address method. For example, to set Cloudflare and Quad9 DNS on a wired profile:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nmcli connection modify &lt;span class="s2"&gt;&amp;#34;Wired connection 1&amp;#34;&lt;/span&gt; ipv4.dns &lt;span class="s2"&gt;&amp;#34;1.1.1.1 9.9.9.9&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nmcli connection up &lt;span class="s2"&gt;&amp;#34;Wired connection 1&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To append another DNS server instead of replacing the whole list, prefix the property with &lt;code&gt;+&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nmcli connection modify &lt;span class="s2"&gt;&amp;#34;Wired connection 1&amp;#34;&lt;/span&gt; +ipv4.dns 8.8.8.8&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;After changing DNS, verify name resolution with a lookup tool such as &lt;a href="https://linuxize.com/post/how-to-use-dig-command-to-query-dns-in-linux/"&gt;&lt;code&gt;dig&lt;/code&gt;&lt;/a&gt;
or &lt;a href="https://linuxize.com/post/nslookup-command-in-linux/"&gt;&lt;code&gt;nslookup&lt;/code&gt;&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="common-examples"&gt;Common Examples &lt;a class="headline-link" href="#common-examples" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Show only the active connections in a parseable format. The &lt;code&gt;-t&lt;/code&gt; flag enables terse output and &lt;code&gt;-f&lt;/code&gt; selects fields:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nmcli -t -f NAME,DEVICE connection show --active&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Watch interface events in real time, useful when a connection drops during a remote session:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nmcli monitor&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Reload all profile files from disk after editing them by hand:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nmcli connection reload&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For low-level interface and route inspection outside NetworkManager, pair &lt;code&gt;nmcli&lt;/code&gt; with the &lt;a href="https://linuxize.com/post/linux-ip-command/"&gt;&lt;code&gt;ip&lt;/code&gt; command&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For a printable quick reference, see the &lt;a href="https://linuxize.com/cheatsheet/nmcli/"&gt;nmcli cheatsheet&lt;/a&gt;
.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Show overall status&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nmcli general status&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Show syntax help&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nmcli OBJECT help&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List devices&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nmcli device status&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List connection profiles&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nmcli connection show&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Activate a profile&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli connection up &amp;lt;name&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deactivate a profile&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli connection down &amp;lt;name&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scan Wi-Fi networks&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nmcli device wifi list&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Join a Wi-Fi network&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli device wifi connect &amp;lt;ssid&amp;gt; password &amp;lt;pwd&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prompt for Wi-Fi password&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli --ask device wifi connect &amp;lt;ssid&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Set static IPv4&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli connection modify &amp;lt;name&amp;gt; ipv4.method manual ipv4.addresses &amp;lt;ip/cidr&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Set DNS servers&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli connection modify &amp;lt;name&amp;gt; ipv4.dns &amp;quot;&amp;lt;a&amp;gt; &amp;lt;b&amp;gt;&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Switch back to DHCP&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli connection modify &amp;lt;name&amp;gt; ipv4.method auto ipv4.addresses &amp;quot;&amp;quot; ipv4.gateway &amp;quot;&amp;quot; ipv4.dns &amp;quot;&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reload profile files&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo nmcli connection reload&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Watch events&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nmcli monitor&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Error: NetworkManager is not running&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Start the service with &lt;code&gt;sudo systemctl start NetworkManager&lt;/code&gt; and enable it at boot with &lt;code&gt;sudo systemctl enable NetworkManager&lt;/code&gt;. On a fresh server install, the package may not be present yet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Wi-Fi list is empty after &lt;code&gt;nmcli device wifi list&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
The radio is most likely off. Run &lt;code&gt;sudo nmcli radio wifi on&lt;/code&gt;, and check that no rfkill switch is blocking the device with &lt;code&gt;rfkill list&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A new static IP does not take effect&lt;/strong&gt;&lt;br&gt;
NetworkManager applies the change only after the connection is reactivated. Run &lt;code&gt;sudo nmcli connection up &amp;lt;name&amp;gt;&lt;/code&gt; after &lt;code&gt;connection modify&lt;/code&gt;. If a stale DHCP lease is still in place, run &lt;code&gt;sudo nmcli connection down &amp;lt;name&amp;gt;&lt;/code&gt; first.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Not authorized to control networking&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
The operation requires administrator privileges or a PolicyKit rule that allows the current user. Run the command with &lt;code&gt;sudo&lt;/code&gt;, or ask the system administrator to grant the needed NetworkManager permissions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A profile keeps reverting to DHCP after reboot&lt;/strong&gt;&lt;br&gt;
A second profile or a cloud-init configuration may be overwriting it. Check &lt;code&gt;/etc/NetworkManager/system-connections/&lt;/code&gt; and &lt;code&gt;/etc/netplan/&lt;/code&gt; for conflicting files.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;nmcli&lt;/code&gt; gives you a scriptable way to inspect NetworkManager state, activate profiles, join Wi-Fi networks, and make persistent IP and DNS changes. For deeper network troubleshooting, use it alongside &lt;code&gt;ip&lt;/code&gt;, &lt;code&gt;ss&lt;/code&gt;, &lt;code&gt;dig&lt;/code&gt;, and the system logs so you can see both the saved profile and the live kernel state.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/nmcli-command-in-linux/featured_hu_4548a0e35d8fbbde.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>timedatectl Command in Linux: Manage Time, Date, and Time Zone</title><link>https://linuxize.com/post/timedatectl-command-in-linux/</link><pubDate>Sat, 16 May 2026 09:20:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/timedatectl-command-in-linux/</guid><category>linux commands</category><description>Use timedatectl to check system time, change the time zone, control NTP synchronization, set the clock manually, and inspect RTC settings on Linux.</description><content:encoded>&lt;p&gt;Wrong system time can break TLS checks, make cron jobs run at the wrong hour, and leave logs with misleading timestamps. On systemd-based Linux systems, &lt;code&gt;timedatectl&lt;/code&gt; is the standard tool for checking and changing time settings.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;timedatectl&lt;/code&gt; is part of systemd and is available on most systemd-based distributions. It handles time zones, NTP synchronization, RTC settings, and manual time adjustments from the command line. This guide explains how to use it with practical examples.&lt;/p&gt;
&lt;h2 id="syntax"&gt;Syntax &lt;a class="headline-link" href="#syntax" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="txt"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;txt&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;timedatectl [OPTIONS] COMMAND&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;When called without any arguments, &lt;code&gt;timedatectl&lt;/code&gt; shows the current status, which is the same as running &lt;code&gt;timedatectl status&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="check-current-time-and-date"&gt;Check Current Time and Date &lt;a class="headline-link" href="#check-current-time-and-date" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Run &lt;code&gt;timedatectl&lt;/code&gt; with no arguments to see the full time status:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;timedatectl&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt; Local time: Tue 2026-04-14 12:00:00 CEST
Universal time: Tue 2026-04-14 10:00:00 UTC
RTC time: Tue 2026-04-14 10:00:00
Time zone: Europe/Berlin (CEST, +0200)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The output shows the local time in your configured time zone, the same moment in UTC, and the hardware clock (RTC) time. &lt;code&gt;System clock synchronized: yes&lt;/code&gt; means NTP has synced the clock recently. &lt;code&gt;NTP service: active&lt;/code&gt; means &lt;code&gt;systemd-timesyncd&lt;/code&gt; (or another NTP daemon) is running and managing synchronization.&lt;/p&gt;
&lt;p&gt;If &lt;code&gt;RTC in local TZ&lt;/code&gt; shows &lt;code&gt;yes&lt;/code&gt;, the hardware clock is set to local time rather than UTC. Most Linux systems keep the hardware clock in UTC, which avoids daylight saving time complications when rebooting.&lt;/p&gt;
&lt;h2 id="set-the-time-zone"&gt;Set the Time Zone &lt;a class="headline-link" href="#set-the-time-zone" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To set the system time zone, use &lt;code&gt;set-timezone&lt;/code&gt; followed by a time zone identifier:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo timedatectl set-timezone America/New_York&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;After running this, &lt;code&gt;timedatectl&lt;/code&gt; shows the new time zone immediately. The change persists across reboots.&lt;/p&gt;
&lt;p&gt;If you do not know the exact time zone identifier, use &lt;code&gt;list-timezones&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;timedatectl list-timezones&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The list is long, so pipe it through &lt;code&gt;grep&lt;/code&gt; to filter by region:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;timedatectl list-timezones &lt;span class="p"&gt;|&lt;/span&gt; grep America&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;America/Adak
America/Anchorage
America/Anguilla
America/Antigua
America/Araguaina
America/Argentina/Buenos_Aires
...
America/New_York
...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Time zone identifiers follow the &lt;code&gt;Region/City&lt;/code&gt; format, such as &lt;code&gt;Europe/Paris&lt;/code&gt;, &lt;code&gt;Asia/Tokyo&lt;/code&gt;, or &lt;code&gt;Australia/Sydney&lt;/code&gt;. For a step-by-step walkthrough, see the guide on &lt;a href="https://linuxize.com/post/how-to-set-or-change-timezone-in-linux/"&gt;how to set or change the time zone in Linux&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="enable-and-disable-ntp-synchronization"&gt;Enable and Disable NTP Synchronization &lt;a class="headline-link" href="#enable-and-disable-ntp-synchronization" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;NTP (Network Time Protocol) keeps the system clock accurate by periodically syncing it against internet time servers. &lt;code&gt;timedatectl&lt;/code&gt; can enable or disable NTP management:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo timedatectl set-ntp true&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo timedatectl set-ntp false&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;After enabling NTP, run &lt;code&gt;timedatectl&lt;/code&gt; to confirm &lt;code&gt;NTP service: active&lt;/code&gt; appears in the output.&lt;/p&gt;
&lt;p&gt;Turn off NTP before setting the time manually. If NTP is active, &lt;code&gt;timedatectl&lt;/code&gt; rejects direct clock changes.&lt;/p&gt;
&lt;h2 id="set-the-time-and-date-manually"&gt;Set the Time and Date Manually &lt;a class="headline-link" href="#set-the-time-and-date-manually" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To set the clock manually, you must first disable NTP synchronization:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo timedatectl set-ntp false&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Then set the time and date using the &lt;code&gt;set-time&lt;/code&gt; subcommand. You can provide a full timestamp:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo timedatectl set-time &lt;span class="s1"&gt;&amp;#39;2026-04-15 14:30:00&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To set only the time and keep the current date:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo timedatectl set-time &lt;span class="s1"&gt;&amp;#39;14:30:00&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To set only the date and keep the current time:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo timedatectl set-time &lt;span class="s1"&gt;&amp;#39;2026-04-15&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;After setting the time, you can re-enable NTP to let the system sync automatically afterward:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo timedatectl set-ntp true&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="set-the-hardware-clock-mode"&gt;Set the Hardware Clock Mode &lt;a class="headline-link" href="#set-the-hardware-clock-mode" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The RTC, or hardware clock, can store time in UTC or in local time. Linux systems should normally keep the RTC in UTC because it avoids daylight saving time issues and works better on servers.&lt;/p&gt;
&lt;p&gt;To make sure the hardware clock uses UTC, run:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo timedatectl set-local-rtc &lt;span class="m"&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If you dual-boot with an operating system that expects the hardware clock to use local time, you can switch the RTC to local time:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo timedatectl set-local-rtc &lt;span class="m"&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;After changing the RTC mode, run &lt;code&gt;timedatectl&lt;/code&gt; again and check the &lt;code&gt;RTC in local TZ&lt;/code&gt; line. Use local RTC only when another operating system on the same machine requires it.&lt;/p&gt;
&lt;h2 id="check-ntp-synchronization-status"&gt;Check NTP Synchronization Status &lt;a class="headline-link" href="#check-ntp-synchronization-status" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For more detail about the NTP sync, use &lt;code&gt;timesync-status&lt;/code&gt;. This subcommand is provided by &lt;code&gt;systemd-timesyncd&lt;/code&gt; and shows the current server, offset, and polling interval:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;timedatectl timesync-status&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt; Server: 185.125.190.56 (ntp.ubuntu.com)
Poll interval: 34min 8s (min: 32s; max 34min 8s)
Leap: normal
Version: 4
Stratum: 2
Reference: 17.253.52.253
Precision: 1us (-19)
Root distance: 19.455ms (max: 5s)
Offset: -2.408ms
Delay: 29.124ms
Jitter: 5.716ms
Packet count: 5&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;Offset&lt;/code&gt; is how far your system clock drifted before the last sync. A small offset, under a few hundred milliseconds, is normal. A large offset can point to network problems or a misconfigured time source.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;timesync-status&lt;/code&gt; only works when &lt;code&gt;systemd-timesyncd&lt;/code&gt; is handling NTP. If your system uses a different NTP daemon such as &lt;code&gt;chrony&lt;/code&gt; or &lt;code&gt;ntpd&lt;/code&gt;, check its own status commands instead.&lt;/p&gt;
&lt;h2 id="machine-parseable-output"&gt;Machine-Parseable Output &lt;a class="headline-link" href="#machine-parseable-output" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;show&lt;/code&gt; subcommand prints the same information as &lt;code&gt;status&lt;/code&gt; in a key-value format that is easier to parse in scripts:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;timedatectl show&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Timezone=Europe/Berlin
LocalRTC=no
CanNTP=yes
NTP=yes
NTPSynchronized=yes
TimeUSec=Tue 2026-04-14 10:00:00 UTC
RTCTimeUSec=Tue 2026-04-14 10:00:00 UTC&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Pass &lt;code&gt;--property&lt;/code&gt; to extract a single value:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;timedatectl show --property&lt;span class="o"&gt;=&lt;/span&gt;Timezone --value&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Europe/Berlin&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is useful in automation and configuration management scripts where you need to check the current time zone without parsing the human-readable &lt;code&gt;status&lt;/code&gt; output.&lt;/p&gt;
&lt;p&gt;For timesync details in key-value format, use &lt;code&gt;show-timesync&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;timedatectl show-timesync&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This gives scripts access to &lt;code&gt;systemd-timesyncd&lt;/code&gt; properties such as the current server name, poll interval, and root distance.&lt;/p&gt;
&lt;h2 id="timedatectl-options"&gt;timedatectl Options &lt;a class="headline-link" href="#timedatectl-options" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--no-ask-password&lt;/code&gt; - Do not prompt for authentication; use the invoking user&amp;rsquo;s credentials&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--adjust-system-clock&lt;/code&gt; - When setting the RTC, also adjust the system clock accordingly&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--no-pager&lt;/code&gt; - Print output without piping through a pager&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-p NAME&lt;/code&gt;, &lt;code&gt;--property=NAME&lt;/code&gt; - Show one named property from &lt;code&gt;show&lt;/code&gt; output&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-H ADDR&lt;/code&gt;, &lt;code&gt;--host=ADDR&lt;/code&gt; - Execute the command on a remote host over SSH&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-M MACHINE&lt;/code&gt;, &lt;code&gt;--machine=MACHINE&lt;/code&gt; - Execute the command inside a local container&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For a printable quick reference, see the &lt;a href="https://linuxize.com/cheatsheet/timedatectl/"&gt;timedatectl cheatsheet&lt;/a&gt;
.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;timedatectl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show current time, time zone, and NTP status&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;timedatectl show&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show status in key-value format (script-friendly)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;timedatectl list-timezones&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List all available time zone identifiers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo timedatectl set-timezone TZ&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set the time zone (e.g., &lt;code&gt;America/New_York&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo timedatectl set-ntp true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enable NTP synchronization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo timedatectl set-ntp false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Disable NTP synchronization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo timedatectl set-time 'YYYY-MM-DD HH:MM:SS'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set time and date manually (NTP must be off)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo timedatectl set-local-rtc 0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Store the hardware clock in UTC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;timedatectl timesync-status&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show NTP sync detail from systemd-timesyncd&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;timedatectl show-timesync&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show NTP sync detail in key-value format&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;timedatectl show --property=Timezone --value&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print the current time zone name only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Failed to set time: Automatic time synchronization is enabled&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
NTP is active and has locked the clock. Disable NTP first with &lt;code&gt;sudo timedatectl set-ntp false&lt;/code&gt;, then run your &lt;code&gt;set-time&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Failed to set time: Access denied&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
You need administrator privileges. Prepend &lt;code&gt;sudo&lt;/code&gt; to the command.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Failed to connect to bus: No such file or directory&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;timedatectl&lt;/code&gt; communicates with &lt;code&gt;systemd-logind&lt;/code&gt; over D-Bus. This error usually appears in minimal containers or chroot environments where systemd is not running. In those cases, set the time directly with the &lt;a href="https://linuxize.com/post/linux-date-command/"&gt;&lt;code&gt;date&lt;/code&gt;&lt;/a&gt;
command: &lt;code&gt;sudo date -s '2026-04-15 14:30:00'&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;timesync-status&lt;/code&gt; returns &lt;code&gt;Failed to query server: Unit dbus-org.freedesktop.timesync1.service not found&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;systemd-timesyncd&lt;/code&gt; is not installed or not running. If your system uses &lt;code&gt;chrony&lt;/code&gt; or &lt;code&gt;ntpd&lt;/code&gt; instead, check their status with &lt;code&gt;chronyc tracking&lt;/code&gt; or &lt;code&gt;ntpq -p&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="faq"&gt;FAQ &lt;a class="headline-link" href="#faq" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;What is the difference between &lt;code&gt;timedatectl&lt;/code&gt; and the &lt;code&gt;date&lt;/code&gt; command?&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://linuxize.com/post/linux-date-command/"&gt;&lt;code&gt;date&lt;/code&gt;&lt;/a&gt;
reads and formats timestamps, and it can set the system clock with &lt;code&gt;sudo date -s&lt;/code&gt;. &lt;code&gt;timedatectl&lt;/code&gt; is the systemd tool that also manages time zones, the RTC, and NTP synchronization. On systemd-based systems, use &lt;code&gt;timedatectl&lt;/code&gt; for time configuration and &lt;code&gt;date&lt;/code&gt; for formatting timestamps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Does &lt;code&gt;timedatectl&lt;/code&gt; work on non-systemd distributions?&lt;/strong&gt;&lt;br&gt;
No. &lt;code&gt;timedatectl&lt;/code&gt; is part of systemd and requires &lt;code&gt;systemd-logind&lt;/code&gt; and D-Bus to be running. On systems without systemd (such as Alpine Linux with OpenRC), use the &lt;code&gt;date&lt;/code&gt; command and edit &lt;code&gt;/etc/localtime&lt;/code&gt; or &lt;code&gt;/etc/timezone&lt;/code&gt; directly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How do I check which NTP servers the system is using?&lt;/strong&gt;&lt;br&gt;
Run &lt;code&gt;timedatectl timesync-status&lt;/code&gt; to see the current NTP server. To see the full list of configured servers, check &lt;code&gt;/etc/systemd/timesyncd.conf&lt;/code&gt;. The &lt;code&gt;NTP=&lt;/code&gt; and &lt;code&gt;FallbackNTP=&lt;/code&gt; lines list the configured time servers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How do I view historical time changes in system logs?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;timedatectl&lt;/code&gt; itself does not keep a history, but the system journal does. Search for time-related events with:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;journalctl -u systemd-timesyncd&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For more on reading system logs, see the &lt;a href="https://linuxize.com/post/journalctl-command-in-linux/"&gt;journalctl guide&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For day-to-day administration, &lt;code&gt;timedatectl&lt;/code&gt; is usually enough to check the current time settings, switch time zones, toggle NTP, and inspect RTC behavior. If you need distribution-specific time zone steps, see the guide on &lt;a href="https://linuxize.com/post/how-to-set-or-change-timezone-in-linux/"&gt;how to set or change the time zone in Linux&lt;/a&gt;
.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/timedatectl-command-in-linux/featured_hu_2b65c1d0d3240571.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>How to Check CPU Usage in Linux</title><link>https://linuxize.com/post/how-to-check-cpu-usage-in-linux/</link><pubDate>Fri, 15 May 2026 09:40:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/how-to-check-cpu-usage-in-linux/</guid><category>monitoring</category><category>linux commands</category><description>This guide explains how to check CPU usage in Linux with top, htop, mpstat, vmstat, sar, ps, uptime, and per-core load tools.</description><content:encoded>&lt;p&gt;A Linux server tells you what its CPUs are doing through several built-in tools, each with a different angle. &lt;code&gt;top&lt;/code&gt; and &lt;code&gt;htop&lt;/code&gt; show the live picture. &lt;code&gt;mpstat&lt;/code&gt;, &lt;code&gt;vmstat&lt;/code&gt;, and &lt;code&gt;sar&lt;/code&gt; from the &lt;code&gt;sysstat&lt;/code&gt; package report numeric samples that suit scripts and dashboards. &lt;code&gt;ps&lt;/code&gt; attributes usage to specific processes, and &lt;code&gt;uptime&lt;/code&gt; summarizes the load over the last fifteen minutes.&lt;/p&gt;
&lt;p&gt;This guide explains how to read CPU usage from each of those tools, which one to reach for in which situation, and how to interpret common output fields.&lt;/p&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;top&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Live snapshot, sort by CPU&lt;/td&gt;
&lt;td&gt;&lt;code&gt;top&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;htop&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Interactive, per-core view&lt;/td&gt;
&lt;td&gt;&lt;code&gt;htop&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mpstat&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Per-core sampling for scripts&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mpstat -P ALL 1 5&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;vmstat&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;System-wide CPU and memory&lt;/td&gt;
&lt;td&gt;&lt;code&gt;vmstat -y 1 5&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sar&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Current or saved CPU samples&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sar -u 1 5&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ps&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Per-process attribution&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ps -eo pid,user,%cpu,comm --sort=-%cpu | head&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uptime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Load average summary&lt;/td&gt;
&lt;td&gt;&lt;code&gt;uptime&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nproc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Logical core count&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nproc&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lscpu&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CPU topology details&lt;/td&gt;
&lt;td&gt;&lt;code&gt;lscpu&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="method-1-top"&gt;Method 1: top &lt;a class="headline-link" href="#method-1-top" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://linuxize.com/post/top-command-in-linux/"&gt;&lt;code&gt;top&lt;/code&gt; command&lt;/a&gt;
is available on most Linux systems without any extra packages. Run it without arguments to open the live view:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;top&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The header summarizes the CPU lines:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;%Cpu(s): 4.2 us, 1.3 sy, 0.0 ni, 93.8 id, 0.5 wa, 0.0 hi, 0.2 si, 0.0 st&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The fields tell you where CPU time goes. &lt;code&gt;us&lt;/code&gt; is user-space code, &lt;code&gt;sy&lt;/code&gt; is kernel code, &lt;code&gt;id&lt;/code&gt; is idle, &lt;code&gt;wa&lt;/code&gt; is time waiting for I/O, and &lt;code&gt;st&lt;/code&gt; is time stolen by the hypervisor on a virtual machine. A high &lt;code&gt;wa&lt;/code&gt; value points at slow disks rather than a CPU bottleneck.&lt;/p&gt;
&lt;p&gt;Press &lt;code&gt;1&lt;/code&gt; while &lt;code&gt;top&lt;/code&gt; is open to switch from the aggregate line to one row per logical CPU. Press &lt;code&gt;P&lt;/code&gt; to sort processes by CPU usage, &lt;code&gt;M&lt;/code&gt; to sort by memory, and &lt;code&gt;q&lt;/code&gt; to quit.&lt;/p&gt;
&lt;h2 id="method-2-htop"&gt;Method 2: htop &lt;a class="headline-link" href="#method-2-htop" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://linuxize.com/post/htop-command-in-linux/"&gt;htop&lt;/a&gt;
is an interactive replacement for &lt;code&gt;top&lt;/code&gt;. It is available in the default repositories of most Linux distributions, but it may not be installed by default.&lt;/p&gt;
&lt;p&gt;On Ubuntu, Debian, and Derivatives:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install htop&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On Fedora, RHEL, and Derivatives:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dnf install htop&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Run it the same way:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;htop&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;htop&lt;/code&gt; shows a colored bar per CPU at the top, a memory and swap bar, and a sortable process table below. Click a column header or use the function keys to sort. Search for a process with &lt;code&gt;F3&lt;/code&gt;, filter with &lt;code&gt;F4&lt;/code&gt;, and send a signal to a selected process with &lt;code&gt;F9&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="method-3-mpstat"&gt;Method 3: mpstat &lt;a class="headline-link" href="#method-3-mpstat" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;mpstat&lt;/code&gt; is part of the &lt;code&gt;sysstat&lt;/code&gt; package and is the right tool when you need numeric CPU samples per core. Install it once before using &lt;code&gt;mpstat&lt;/code&gt; or &lt;code&gt;sar&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;On Ubuntu, Debian, and Derivatives:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install sysstat&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On Fedora, RHEL, and Derivatives:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dnf install sysstat&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Sample all cores at one-second intervals five times:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mpstat -P ALL &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;03:14:00 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
03:14:01 PM all 3.27 0.00 0.75 0.00 0.00 0.00 0.00 0.00 0.00 95.98
03:14:01 PM 0 4.95 0.00 0.99 0.00 0.00 0.00 0.00 0.00 0.00 94.06
03:14:01 PM 1 1.00 0.00 1.00 0.00 0.00 0.00 0.00 0.00 0.00 98.00&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;all&lt;/code&gt; row is the aggregate. Each numbered row is a single logical CPU. Use this output when one core is pinned at 100 percent and the others are idle, which can happen when an application is single-threaded.&lt;/p&gt;
&lt;h2 id="method-4-vmstat"&gt;Method 4: vmstat &lt;a class="headline-link" href="#method-4-vmstat" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;vmstat&lt;/code&gt; is part of &lt;code&gt;procps&lt;/code&gt; and combines CPU usage with memory, swap, and I/O. The first report normally shows averages since boot, so use &lt;code&gt;-y&lt;/code&gt; when you want only interval samples. The columns at the right are the CPU summary:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vmstat -y &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 0 7430080 311288 1843264 0 0 8 18 148 263 3 1 96 0 0
0 0 0 7430080 311288 1843264 0 0 0 0 127 226 1 0 99 0 0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;r&lt;/code&gt; is the run queue length, which is how many processes are ready to use a CPU. A run queue consistently larger than the number of CPUs is a sign of CPU pressure. The &lt;code&gt;cpu&lt;/code&gt; columns mirror the meaning of the &lt;code&gt;top&lt;/code&gt; header.&lt;/p&gt;
&lt;h2 id="method-5-sar"&gt;Method 5: sar &lt;a class="headline-link" href="#method-5-sar" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;sar&lt;/code&gt;, also from &lt;code&gt;sysstat&lt;/code&gt;, can print current samples and read saved activity logs. On Ubuntu and Debian, enable history collection after installing &lt;code&gt;sysstat&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo sed -i &lt;span class="s1"&gt;&amp;#39;s/^ENABLED=&amp;#34;false&amp;#34;/ENABLED=&amp;#34;true&amp;#34;/&amp;#39;&lt;/span&gt; /etc/default/sysstat
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; --now sysstat&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The collector setup differs between distributions. If &lt;code&gt;sar&lt;/code&gt; shows no saved history, check your distribution&amp;rsquo;s &lt;code&gt;sysstat&lt;/code&gt; service or timer configuration.&lt;/p&gt;
&lt;p&gt;Show current CPU samples:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sar -u &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;03:15:01 PM CPU %user %nice %system %iowait %steal %idle
03:15:02 PM all 2.34 0.00 0.71 0.05 0.00 96.90
03:15:03 PM all 2.18 0.00 0.69 0.04 0.00 97.09&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Each row is a sample interval. Combine with &lt;code&gt;-f&lt;/code&gt; to read an older log, for example &lt;code&gt;sar -u -f /var/log/sysstat/sa10&lt;/code&gt; for the tenth of the month on systems that store logs in &lt;code&gt;/var/log/sysstat/&lt;/code&gt;. This is the right tool when you need to confirm that a slowdown reported yesterday lined up with a CPU spike.&lt;/p&gt;
&lt;h2 id="method-6-ps"&gt;Method 6: ps &lt;a class="headline-link" href="#method-6-ps" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://linuxize.com/post/ps-command-in-linux/"&gt;&lt;code&gt;ps&lt;/code&gt; command&lt;/a&gt;
lists every process and lets you sort by CPU percentage. The pattern most admins reach for is:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ps -eo pid,user,%cpu,%mem,comm --sort&lt;span class="o"&gt;=&lt;/span&gt;-%cpu &lt;span class="p"&gt;|&lt;/span&gt; head&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt; PID USER %CPU %MEM COMMAND
1437 dejan 23.5 4.2 firefox
2049 dejan 11.2 2.8 node
1102 root 4.7 0.9 systemd-journal&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The output is a snapshot at the moment &lt;code&gt;ps&lt;/code&gt; ran. The &lt;code&gt;%CPU&lt;/code&gt; value is the average CPU usage over the lifetime of the process, not an instantaneous reading. For a live view, repeat the command with &lt;code&gt;watch&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;watch -n &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ps -eo pid,user,%cpu,%mem,comm --sort=-%cpu | head&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;watch&lt;/code&gt; reruns the command every two seconds, which gives you a refreshing top-five view without leaving the shell.&lt;/p&gt;
&lt;h2 id="method-7-uptime-and-load-averages"&gt;Method 7: uptime and Load Averages &lt;a class="headline-link" href="#method-7-uptime-and-load-averages" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://linuxize.com/post/linux-uptime-command/"&gt;&lt;code&gt;uptime&lt;/code&gt; command&lt;/a&gt;
is the smallest of the lot. Without arguments it prints the system uptime and the load average:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uptime&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt; 15:04:23 up 3:17, 2 users, load average: 0.42, 0.35, 0.31&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The three numbers are the average number of processes that are runnable or in uninterruptible sleep over the last 1, 5, and 15 minutes. Compare them with &lt;code&gt;nproc&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nproc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;8&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A load average that stays below the core count is healthy. A load average well above the core count means processes are queueing for CPU time. The 15-minute value is the trend; the 1-minute value reacts to short spikes.&lt;/p&gt;
&lt;h2 id="method-8-capacity-context"&gt;Method 8: Capacity Context &lt;a class="headline-link" href="#method-8-capacity-context" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before you read CPU usage, know what the host has. &lt;code&gt;lscpu&lt;/code&gt; summarizes the CPU model, sockets, cores, and threads. For a deeper hardware view, see the guide on &lt;a href="https://linuxize.com/post/get-cpu-information-on-linux/"&gt;checking CPU information in Linux&lt;/a&gt;
.&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lscpu&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Address sizes: 46 bits physical, 48 bits virtual
Byte Order: Little Endian
CPU(s): 8
On-line CPU(s) list: 0-7
Vendor ID: GenuineIntel
Model name: Intel(R) Core(TM) i7-9700K CPU @ 3.60 GHz
Thread(s) per core: 1
Core(s) per socket: 8
Socket(s): 1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;Thread(s) per core: 1&lt;/code&gt; means hyper-threading is disabled or not present, so each logical CPU is a physical core. A high CPU usage on a chip with eight physical cores is different from the same number on a four-core chip with hyper-threading.&lt;/p&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;mpstat: command not found&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
The &lt;code&gt;sysstat&lt;/code&gt; package is not installed. Install it with &lt;code&gt;sudo apt install sysstat&lt;/code&gt; on Ubuntu, Debian, and Derivatives, or &lt;code&gt;sudo dnf install sysstat&lt;/code&gt; on Fedora, RHEL, and Derivatives.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;sar&lt;/code&gt; reports &lt;code&gt;Cannot open /var/log/sysstat/&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Sample collection is not enabled. Set &lt;code&gt;ENABLED=&amp;quot;true&amp;quot;&lt;/code&gt; in &lt;code&gt;/etc/default/sysstat&lt;/code&gt; and restart the service with &lt;code&gt;sudo systemctl restart sysstat&lt;/code&gt;. Wait at least one collection interval before rerunning &lt;code&gt;sar&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;top&lt;/code&gt; shows more than 100% CPU for one process&lt;/strong&gt;&lt;br&gt;
The default mode shows percentage relative to a single CPU, so a multi-threaded process can exceed 100 percent. Press &lt;code&gt;Shift+I&lt;/code&gt; to toggle Irix mode off, and the value normalizes across all CPUs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Load average looks high but no process consumes CPU&lt;/strong&gt;&lt;br&gt;
Load includes processes in &lt;code&gt;D&lt;/code&gt; state, which is uninterruptible sleep. Check &lt;code&gt;ps -eo state,pid,comm | awk '$1 ~ /D/'&lt;/code&gt; to find processes blocked on I/O. The cause is usually a slow or stalled disk.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Per-core view in &lt;code&gt;htop&lt;/code&gt; is empty&lt;/strong&gt;&lt;br&gt;
Old terminal sizes hide the CPU bar. Resize the window or set &lt;code&gt;Display&lt;/code&gt; options inside &lt;code&gt;htop&lt;/code&gt; with &lt;code&gt;F2&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="faq"&gt;FAQ &lt;a class="headline-link" href="#faq" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Which tool should I check first?&lt;/strong&gt;&lt;br&gt;
Run &lt;code&gt;top&lt;/code&gt; or &lt;code&gt;htop&lt;/code&gt; for a live picture, then drop to &lt;code&gt;mpstat&lt;/code&gt; and &lt;code&gt;sar&lt;/code&gt; when you need numbers or history. &lt;code&gt;ps&lt;/code&gt; answers the per-process question once you know there is a problem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What does &lt;code&gt;%steal&lt;/code&gt; mean?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;%steal&lt;/code&gt; is the time the hypervisor took from the guest VM to run something else. A consistent non-zero &lt;code&gt;%steal&lt;/code&gt; value on a VM means the host is over-committed and the guest cannot get the CPU it asked for.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How do I count CPUs from a script?&lt;/strong&gt;&lt;br&gt;
Use &lt;code&gt;nproc&lt;/code&gt; for the logical CPU count, or &lt;code&gt;nproc --all&lt;/code&gt; to include offline CPUs. Both return a single integer and are stable across distributions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Is high CPU always bad?&lt;/strong&gt;&lt;br&gt;
No. A batch job that finishes faster on a CPU running at full capacity is healthier than the same job spread thinly over a long period. CPU is bad when the load average exceeds the core count for sustained periods or when interactive workloads time out.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Pick the tool that matches the question. &lt;code&gt;top&lt;/code&gt; and &lt;code&gt;htop&lt;/code&gt; answer &amp;ldquo;what is happening right now&amp;rdquo;. &lt;code&gt;mpstat&lt;/code&gt; and &lt;code&gt;vmstat&lt;/code&gt; answer &amp;ldquo;is the load even across cores&amp;rdquo;. &lt;code&gt;sar&lt;/code&gt; answers &amp;ldquo;what happened last night&amp;rdquo;. Treat load averages as a trend, not a verdict, and always read CPU numbers in the context of the core count from &lt;code&gt;nproc&lt;/code&gt;.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/how-to-check-cpu-usage-in-linux/featured_hu_7fd4237466751cce.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>systemctl Command in Linux: Start, Stop, and Manage Services</title><link>https://linuxize.com/post/systemctl-command-in-linux/</link><pubDate>Thu, 14 May 2026 10:30:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/systemctl-command-in-linux/</guid><category>linux commands</category><description>Manage services on systemd-based Linux distributions with systemctl. This guide explains how to start, stop, restart, enable, and disable services, reload unit files, and inspect service status.</description><content:encoded>&lt;p&gt;Every modern Linux distribution runs a handful of services in the background at any given moment: an SSH daemon, a cron daemon, a web server, a database, a firewall. On systemd-based systems, the one tool you use to control all of them is &lt;code&gt;systemctl&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;systemctl&lt;/code&gt; is the command-line front end for systemd. It starts and stops services, enables them at boot, reloads unit files after an edit, and reports the status of each one. This guide walks through the commands you will reach for most often when managing services on a running system.&lt;/p&gt;
&lt;h2 id="systemctl-syntax"&gt;systemctl Syntax &lt;a class="headline-link" href="#systemctl-syntax" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The general syntax of the command is:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="txt"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;txt&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl [OPTIONS] COMMAND [UNIT...]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;A &lt;code&gt;UNIT&lt;/code&gt; is usually a service name such as &lt;code&gt;nginx.service&lt;/code&gt; or &lt;code&gt;cron.service&lt;/code&gt;. The &lt;code&gt;.service&lt;/code&gt; suffix is optional when the unit type is unambiguous, so &lt;code&gt;systemctl start nginx&lt;/code&gt; and &lt;code&gt;systemctl start nginx.service&lt;/code&gt; do the same thing.&lt;/p&gt;
&lt;p&gt;Most commands that change state (start, stop, enable, disable) require root privileges. Run them with &lt;code&gt;sudo&lt;/code&gt; or as root.&lt;/p&gt;
&lt;h2 id="check-the-status-of-a-service"&gt;Check the Status of a Service &lt;a class="headline-link" href="#check-the-status-of-a-service" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before you touch a service, check what it is doing. The &lt;code&gt;status&lt;/code&gt; command prints the current state, recent log lines, the PID of the main process, and the path of the unit file:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl status nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;● nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/lib/systemd/system/nginx.service; enabled; preset: enabled)
Active: active (running) since Mon 2026-04-13 09:12:04 CEST; 2h 3min ago
Docs: man:nginx(8)
Main PID: 1591 (nginx)
Tasks: 3 (limit: 4612)
Memory: 6.2M
CPU: 112ms
CGroup: /system.slice/nginx.service
├─1591 &amp;#34;nginx: master process /usr/sbin/nginx -g daemon on; master_process on;&amp;#34;
└─1592 &amp;#34;nginx: worker process&amp;#34;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The two lines to read first are &lt;code&gt;Loaded&lt;/code&gt; and &lt;code&gt;Active&lt;/code&gt;. &lt;code&gt;Loaded&lt;/code&gt; tells you whether the unit file was found and whether it is set to start at boot (&lt;code&gt;enabled&lt;/code&gt;, &lt;code&gt;disabled&lt;/code&gt;, &lt;code&gt;masked&lt;/code&gt;, or &lt;code&gt;static&lt;/code&gt;). &lt;code&gt;Active&lt;/code&gt; tells you what the service is doing right now (&lt;code&gt;active (running)&lt;/code&gt;, &lt;code&gt;inactive (dead)&lt;/code&gt;, &lt;code&gt;failed&lt;/code&gt;, and so on).&lt;/p&gt;
&lt;h2 id="start-and-stop-services"&gt;Start and Stop Services &lt;a class="headline-link" href="#start-and-stop-services" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To start a service that is not running:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl start nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The command returns silently when the service starts. Run &lt;code&gt;systemctl status nginx&lt;/code&gt; afterwards to confirm.&lt;/p&gt;
&lt;p&gt;To stop a running service:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl stop nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Stopping a service only affects the current session. If the service is enabled at boot, it will come back the next time the system starts.&lt;/p&gt;
&lt;h2 id="restart-and-reload"&gt;Restart and Reload &lt;a class="headline-link" href="#restart-and-reload" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A restart fully stops the service and starts it again, which terminates all active connections. This is the command you use after a binary upgrade or any change that the service cannot pick up on its own:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl restart nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;A reload asks the service to re-read its configuration without dropping active connections. Whether this works depends on the service itself. Web servers such as Nginx and Apache support it well; some services do not define a reload action.&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl reload nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If the unit file supports it, &lt;code&gt;reload-or-restart&lt;/code&gt; is a safer combination: it tries &lt;code&gt;reload&lt;/code&gt; first and falls back to a full restart if the service does not handle reload signals.&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl reload-or-restart nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="enable-and-disable-at-boot"&gt;Enable and Disable at Boot &lt;a class="headline-link" href="#enable-and-disable-at-boot" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Starting a service with &lt;code&gt;start&lt;/code&gt; does not persist across a reboot. To make a service launch automatically at boot, use &lt;code&gt;enable&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service → /lib/systemd/system/nginx.service.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The output hints at what is happening behind the scenes. Enabling a service creates a symlink in one of the &lt;code&gt;*.wants&lt;/code&gt; directories so that the appropriate boot target pulls the unit in.&lt;/p&gt;
&lt;p&gt;To disable a service so that it no longer starts at boot:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl disable nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;enable&lt;/code&gt; and &lt;code&gt;disable&lt;/code&gt; only change the boot behavior. To start or stop the service in the current session as well, add the &lt;code&gt;--now&lt;/code&gt; flag:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; --now nginx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl disable --now nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To check whether a service is currently enabled at boot:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl is-enabled nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="mask-and-unmask"&gt;Mask and Unmask &lt;a class="headline-link" href="#mask-and-unmask" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Disabling a service prevents it from starting at boot, but another service, socket, or admin can still start it manually. &lt;code&gt;mask&lt;/code&gt; is the stronger option. It links the unit file to &lt;code&gt;/dev/null&lt;/code&gt; so that every attempt to start it fails, whether from the command line, from another unit, or from a boot target.&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl mask apache2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To undo this:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl unmask apache2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Masking is useful when two services conflict over the same port (for example, Nginx and Apache both wanting port 80) and you want to make sure the one you are not using cannot come back.&lt;/p&gt;
&lt;h2 id="reload-systemd-after-unit-file-changes"&gt;Reload systemd After Unit File Changes &lt;a class="headline-link" href="#reload-systemd-after-unit-file-changes" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When you add a new unit file or edit an existing one, systemd does not pick up the changes automatically. Tell it to reload its configuration with:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl daemon-reload&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Run this command after:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Creating a new &lt;code&gt;.service&lt;/code&gt; file under &lt;code&gt;/etc/systemd/system/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Editing a unit file (either directly or via &lt;code&gt;systemctl edit&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Installing a package that ships a new unit file&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After &lt;code&gt;daemon-reload&lt;/code&gt;, restart the affected service if it was already running, so it starts under the new definition.&lt;/p&gt;
&lt;h2 id="list-services"&gt;List Services &lt;a class="headline-link" href="#list-services" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;systemctl&lt;/code&gt; can list every unit it knows about, which is useful when you want to see what is active, what is failing, or what is simply installed. Because the listing commands have several useful variations, they have their own dedicated guide: see &lt;a href="https://linuxize.com/post/systemctl-list/"&gt;listing Linux services with systemctl&lt;/a&gt;
for filtering by state, showing inactive units, and listing unit files.&lt;/p&gt;
&lt;p&gt;The quickest view is:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl --failed&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;It prints every service that has failed since boot, which is usually the first thing you want to know on a box you do not fully trust yet.&lt;/p&gt;
&lt;h2 id="view-service-logs"&gt;View Service Logs &lt;a class="headline-link" href="#view-service-logs" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;systemd stores service logs in the journal, which you query with &lt;code&gt;journalctl&lt;/code&gt;. To follow the log for a single service:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo journalctl -u nginx -f&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;-u&lt;/code&gt; flag filters by unit, and &lt;code&gt;-f&lt;/code&gt; follows new entries as they are written. For a full walkthrough of the journal, see the &lt;a href="https://linuxize.com/post/journalctl-command-in-linux/"&gt;journalctl command&lt;/a&gt;
guide.&lt;/p&gt;
&lt;h2 id="reboot-power-off-and-suspend"&gt;Reboot, Power Off, and Suspend &lt;a class="headline-link" href="#reboot-power-off-and-suspend" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;systemctl also controls system power state. These commands do not require a service name:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl reboot
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl poweroff
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl &lt;span class="nb"&gt;suspend&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl hibernate&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;They are equivalent to the older &lt;code&gt;reboot&lt;/code&gt;, &lt;code&gt;poweroff&lt;/code&gt;, and &lt;code&gt;halt&lt;/code&gt; commands, and they go through systemd so that services shut down cleanly.&lt;/p&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Unit nginx.service could not be found.&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
The service is not installed, or the unit file is in a location systemd does not scan. Install the package, or run &lt;code&gt;sudo systemctl daemon-reload&lt;/code&gt; if you just added a unit file manually.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Failed to start nginx.service: Unit is masked.&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
The unit has been masked. Run &lt;code&gt;sudo systemctl unmask nginx&lt;/code&gt; and try again.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Service keeps failing after a config change&lt;/strong&gt;&lt;br&gt;
Run &lt;code&gt;systemctl status SERVICE&lt;/code&gt; and &lt;code&gt;journalctl -u SERVICE -n 50&lt;/code&gt; to read the most recent log lines. Configuration syntax errors are the most common cause.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Changes to the unit file are ignored&lt;/strong&gt;&lt;br&gt;
You forgot &lt;code&gt;sudo systemctl daemon-reload&lt;/code&gt;. systemd caches unit files in memory and needs to be told to reread them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Permission denied&lt;/strong&gt;&lt;br&gt;
Most state-changing commands require root. Prefix the command with &lt;code&gt;sudo&lt;/code&gt;, or switch to the root user.&lt;/p&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For a printable quick reference, see the &lt;a href="https://linuxize.com/cheatsheet/systemctl/"&gt;systemctl cheatsheet&lt;/a&gt;
.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Show service status&lt;/td&gt;
&lt;td&gt;&lt;code&gt;systemctl status nginx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Start a service&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl start nginx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stop a service&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl stop nginx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restart a service&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl restart nginx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reload configuration&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl reload nginx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enable at boot&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl enable nginx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enable and start now&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl enable --now nginx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Disable at boot&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl disable nginx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check boot status&lt;/td&gt;
&lt;td&gt;&lt;code&gt;systemctl is-enabled nginx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reload unit files&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl daemon-reload&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Show failed units&lt;/td&gt;
&lt;td&gt;&lt;code&gt;systemctl --failed&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Follow service logs&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo journalctl -u nginx -f&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="faq"&gt;FAQ &lt;a class="headline-link" href="#faq" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;What is the difference between &lt;code&gt;enable&lt;/code&gt; and &lt;code&gt;start&lt;/code&gt;?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;start&lt;/code&gt; launches the service in the current session only. &lt;code&gt;enable&lt;/code&gt; creates the symlinks that make systemd start the service at every boot. The two are independent, which is why &lt;code&gt;--now&lt;/code&gt; exists to do both in one command.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What is the difference between &lt;code&gt;disable&lt;/code&gt; and &lt;code&gt;mask&lt;/code&gt;?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;disable&lt;/code&gt; removes the service from boot targets, but it can still be started manually or pulled in by another unit. &lt;code&gt;mask&lt;/code&gt; links the unit file to &lt;code&gt;/dev/null&lt;/code&gt; so every start attempt fails.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How do I know if my system uses systemd?&lt;/strong&gt;&lt;br&gt;
Run &lt;code&gt;ps -p 1 -o comm=&lt;/code&gt;. If the output is &lt;code&gt;systemd&lt;/code&gt;, your init system is systemd and &lt;code&gt;systemctl&lt;/code&gt; applies. Virtually every mainstream distribution has used systemd as the default since around 2015.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Does &lt;code&gt;reload&lt;/code&gt; work for every service?&lt;/strong&gt;&lt;br&gt;
No. It depends on whether the unit file defines an &lt;code&gt;ExecReload&lt;/code&gt; command and whether the service itself supports reloading. If you are unsure, use &lt;code&gt;reload-or-restart&lt;/code&gt;, which falls back to &lt;code&gt;restart&lt;/code&gt; when &lt;code&gt;reload&lt;/code&gt; is not available.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What happens to running connections when I &lt;code&gt;restart&lt;/code&gt; a service?&lt;/strong&gt;&lt;br&gt;
They are dropped. A full restart kills the process and starts a new one, so anything holding a connection will see it close. Use &lt;code&gt;reload&lt;/code&gt; or &lt;code&gt;reload-or-restart&lt;/code&gt; when the service supports it.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Most day-to-day work with &lt;code&gt;systemctl&lt;/code&gt; comes down to &lt;code&gt;status&lt;/code&gt;, &lt;code&gt;start&lt;/code&gt;, &lt;code&gt;stop&lt;/code&gt;, &lt;code&gt;restart&lt;/code&gt;, &lt;code&gt;enable&lt;/code&gt;, and &lt;code&gt;disable&lt;/code&gt;. Keep &lt;code&gt;daemon-reload&lt;/code&gt; in mind whenever you edit a unit file, and pair &lt;code&gt;systemctl&lt;/code&gt; with &lt;a href="https://linuxize.com/post/journalctl-command-in-linux/"&gt;journalctl&lt;/a&gt;
when something fails to start.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/systemctl-command-in-linux/featured_hu_53012176f0ed0bfa.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>Bash Best Practices: Writing Safer, Cleaner Scripts</title><link>https://linuxize.com/post/bash-best-practices/</link><pubDate>Wed, 13 May 2026 10:00:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/bash-best-practices/</guid><category>bash</category><category>linux commands</category><description>Practical Bash best practices for writing safer and more predictable scripts, covering strict mode, quoting, error handling, and everyday patterns.</description><content:encoded>&lt;p&gt;Bash scripts tend to start small and grow over time. A helper you wrote in five minutes to copy a few files can end up running in a cron job, a deployment pipeline, or a server startup script. When that happens, small oversights such as an unquoted variable or an ignored error become real outages.&lt;/p&gt;
&lt;p&gt;This guide covers practical Bash best practices that keep scripts predictable, safe to re-run, and easy for other people to read.&lt;/p&gt;
&lt;h2 id="start-with-a-clear-shebang"&gt;Start With a Clear Shebang &lt;a class="headline-link" href="#start-with-a-clear-shebang" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Every Bash script should begin with a proper shebang line. The portable choice for Bash is:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;~/script.sh&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;#!/usr/bin/env bash&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Using &lt;code&gt;/usr/bin/env bash&lt;/code&gt; lets the script find Bash through the &lt;code&gt;PATH&lt;/code&gt;, which matters on systems where Bash is installed outside &lt;code&gt;/bin&lt;/code&gt;, such as macOS with Homebrew. If you rely on features that exist only in Bash, do not use &lt;code&gt;#!/bin/sh&lt;/code&gt;. On many distributions &lt;code&gt;/bin/sh&lt;/code&gt; points to &lt;code&gt;dash&lt;/code&gt; or another minimal shell, and arrays, &lt;code&gt;[[ ]]&lt;/code&gt;, and &lt;code&gt;local&lt;/code&gt; will not behave the same way.&lt;/p&gt;
&lt;h2 id="enable-strict-mode"&gt;Enable Strict Mode &lt;a class="headline-link" href="#enable-strict-mode" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;By default, Bash ignores many errors. A command can fail, a variable can be unset, and the script will keep running as if nothing happened. Enabling strict mode at the top of the script changes that behavior.&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;~/script.sh&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/usr/bin/env bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; -euo pipefail&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Here is what each flag does:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-e&lt;/code&gt; - Exit immediately if any command returns a non-zero status.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-u&lt;/code&gt; - Treat unset variables as an error and exit.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-o pipefail&lt;/code&gt; - Make a pipeline fail if any command in it fails, not only the last one.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A common extra is &lt;code&gt;IFS=$'\n\t'&lt;/code&gt;, which prevents word splitting on spaces and avoids surprises when iterating over filenames that contain spaces.&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;~/script.sh&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/usr/bin/env bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; -euo pipefail
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;$&amp;#39;\n\t&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Strict mode is not magic. It will not catch logic errors, and in some scripts you will need to opt out locally with &lt;code&gt;|| true&lt;/code&gt; for commands that are allowed to fail. The point is to flip the default from &amp;ldquo;keep going on errors&amp;rdquo; to &amp;ldquo;stop on errors&amp;rdquo;, which is almost always the safer choice. For a closer look at each option on its own, see our &lt;a href="https://linuxize.com/post/bash-set-command/"&gt;bash set command&lt;/a&gt;
guide.&lt;/p&gt;
&lt;h2 id="always-quote-your-variables"&gt;Always Quote Your Variables &lt;a class="headline-link" href="#always-quote-your-variables" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Unquoted variables in Bash are expanded and then word-split. If a variable contains spaces, wildcards, or is empty, the result may be very different from what you expect.&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;My Report.txt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;%s&amp;gt;\n&amp;#39;&lt;/span&gt; &lt;span class="nv"&gt;$file&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Because &lt;code&gt;$file&lt;/code&gt; is not quoted, Bash passes two arguments to &lt;code&gt;printf&lt;/code&gt;: &lt;code&gt;My&lt;/code&gt; and &lt;code&gt;Report.txt&lt;/code&gt;. Quoting fixes it and keeps the filename as one value:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;%s&amp;gt;\n&amp;#39;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The same rule applies inside tests, loops, and command substitutions. When in doubt, quote. The only common case where you intentionally leave a variable unquoted is when you want word splitting, which should be rare and deliberate.&lt;/p&gt;
&lt;h2 id="prefer---over--"&gt;Prefer [[ ]] Over [ ] &lt;a class="headline-link" href="#prefer---over--" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Bash supports two test constructs: the older &lt;code&gt;[ ]&lt;/code&gt; (a synonym for the &lt;code&gt;test&lt;/code&gt; command) and the newer &lt;code&gt;[[ ]]&lt;/code&gt; keyword. For Bash scripts, prefer &lt;code&gt;[[ ]]&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; -f &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$env&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;production&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Loading production config&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;[[ ]]&lt;/code&gt; handles empty variables safely even without quotes, supports pattern matching with &lt;code&gt;==&lt;/code&gt; and regex with &lt;code&gt;=~&lt;/code&gt;, and allows logical operators such as &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; and &lt;code&gt;||&lt;/code&gt; inside the test. Only fall back to &lt;code&gt;[ ]&lt;/code&gt; when writing a portable POSIX script that must run under &lt;code&gt;sh&lt;/code&gt; or &lt;code&gt;dash&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="use-functions-for-repeated-logic"&gt;Use Functions for Repeated Logic &lt;a class="headline-link" href="#use-functions-for-repeated-logic" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once a script has more than a couple of steps, group related commands into functions. Functions make scripts easier to read, test, and refactor.&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;~/backup.sh&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;log&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;[%s] %s\n&amp;#39;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;date +&lt;span class="s1"&gt;&amp;#39;%F %T&amp;#39;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$*&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;backup_dir&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="nv"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="nv"&gt;dst&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; log &lt;span class="s2"&gt;&amp;#34;Backing up &lt;/span&gt;&lt;span class="nv"&gt;$src&lt;/span&gt;&lt;span class="s2"&gt; to &lt;/span&gt;&lt;span class="nv"&gt;$dst&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; rsync -a &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$src&lt;/span&gt;&lt;span class="s2"&gt;/&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$dst&lt;/span&gt;&lt;span class="s2"&gt;/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;backup_dir &lt;span class="s2"&gt;&amp;#34;/var/www&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/backup/www&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Declare variables inside functions with &lt;code&gt;local&lt;/code&gt; so they do not leak into the rest of the script. Pass input as positional parameters, and return output via &lt;code&gt;echo&lt;/code&gt; or &lt;code&gt;printf&lt;/code&gt; rather than global variables when possible.&lt;/p&gt;
&lt;p&gt;See &lt;a href="https://linuxize.com/post/bash-functions/"&gt;Bash functions&lt;/a&gt;
for more details on arguments, return values, and scoping.&lt;/p&gt;
&lt;h2 id="handle-errors-explicitly"&gt;Handle Errors Explicitly &lt;a class="headline-link" href="#handle-errors-explicitly" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Strict mode stops the script on errors, but it does not tell the user what went wrong. Adding a &lt;code&gt;trap&lt;/code&gt; on &lt;code&gt;ERR&lt;/code&gt; gives you a single place to log failures.&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;~/script.sh&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/usr/bin/env bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; -Eeuo pipefail
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;on_error&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="nv"&gt;exit_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="nv"&gt;line&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Error on line &lt;/span&gt;&lt;span class="nv"&gt;$line&lt;/span&gt;&lt;span class="s2"&gt; (exit code &lt;/span&gt;&lt;span class="nv"&gt;$exit_code&lt;/span&gt;&lt;span class="s2"&gt;)&amp;#34;&lt;/span&gt; &amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$exit_code&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;trap&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;on_error $LINENO&amp;#39;&lt;/span&gt; ERR&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;-E&lt;/code&gt; option makes the &lt;code&gt;ERR&lt;/code&gt; trap inherit into functions and subshells. Without it, a failure inside a helper function may exit the script without running your error handler.&lt;/p&gt;
&lt;p&gt;For cleanup tasks such as removing temporary files, use &lt;code&gt;trap&lt;/code&gt; on &lt;code&gt;EXIT&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;~/script.sh&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;tmp_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;mktemp -d&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;trap&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rm -rf &amp;#34;$tmp_dir&amp;#34;&amp;#39;&lt;/span&gt; EXIT&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;EXIT&lt;/code&gt; trap runs whether the script exits normally, hits an error under &lt;code&gt;set -e&lt;/code&gt;, or is interrupted with Ctrl+C. It is the safest way to guarantee cleanup.&lt;/p&gt;
&lt;h2 id="validate-input-early"&gt;Validate Input Early &lt;a class="headline-link" href="#validate-input-early" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Scripts that take arguments should fail fast when those arguments are missing or invalid. Check them before doing any work:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;~/deploy.sh&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$#&lt;/span&gt; -lt &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Usage: &lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt; &amp;lt;environment&amp;gt; &amp;lt;version&amp;gt;&amp;#34;&lt;/span&gt; &amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$env&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; in
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; staging&lt;span class="p"&gt;|&lt;/span&gt;production&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; *&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Unknown environment: &lt;/span&gt;&lt;span class="nv"&gt;$env&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;esac&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For scripts with several flags, use &lt;a href="https://linuxize.com/post/bash-getopts/"&gt;getopts&lt;/a&gt;
instead of parsing &lt;code&gt;$@&lt;/code&gt; manually. It handles short options, arguments, and error reporting in a consistent way.&lt;/p&gt;
&lt;h2 id="avoid-parsing-the-output-of-ls"&gt;Avoid Parsing the Output of ls &lt;a class="headline-link" href="#avoid-parsing-the-output-of-ls" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A common beginner mistake is looping over &lt;code&gt;ls&lt;/code&gt; output:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; f in &lt;span class="k"&gt;$(&lt;/span&gt;ls /var/log&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This breaks on filenames with spaces, newlines, or special characters. Use a glob or &lt;code&gt;find&lt;/code&gt; instead:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; f in /var/log/*&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For recursive traversal, use &lt;code&gt;find&lt;/code&gt; with &lt;code&gt;-print0&lt;/code&gt; and &lt;code&gt;xargs -0&lt;/code&gt;, or a &lt;code&gt;while read&lt;/code&gt; loop with &lt;code&gt;IFS=&lt;/code&gt; and &lt;code&gt;-d ''&lt;/code&gt; to handle unusual filenames correctly.&lt;/p&gt;
&lt;h2 id="keep-scripts-idempotent-when-possible"&gt;Keep Scripts Idempotent When Possible &lt;a class="headline-link" href="#keep-scripts-idempotent-when-possible" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A script you can safely re-run is much easier to work with than one that breaks on the second attempt. Aim for idempotent behavior: check before creating directories, use &lt;code&gt;mkdir -p&lt;/code&gt; instead of &lt;code&gt;mkdir&lt;/code&gt;, use &lt;code&gt;ln -sf&lt;/code&gt; instead of &lt;code&gt;ln -s&lt;/code&gt;, and guard commands with &lt;code&gt;if&lt;/code&gt; blocks when they would otherwise fail on re-run.&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkdir -p /opt/app/config&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The same pattern applies to package installation, user creation, and configuration edits. Tools like Ansible enforce idempotency by design; in Bash, you have to add those checks yourself.&lt;/p&gt;
&lt;h2 id="use-shellcheck"&gt;Use Shellcheck &lt;a class="headline-link" href="#use-shellcheck" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.shellcheck.net/" target="_blank" rel="noopener noreferrer"&gt;Shellcheck&lt;/a&gt;
is a static analyzer for shell scripts. It catches unquoted variables, unsafe &lt;code&gt;for&lt;/code&gt; loops, and many other common mistakes before you hit them in production.&lt;/p&gt;
&lt;p&gt;Install it from your package manager:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install shellcheck&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Then run it against your script:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;shellcheck script.sh&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Fix the warnings it raises, or document with an inline comment why a specific check is being ignored. Making shellcheck part of your editor or pre-commit hook removes an entire class of Bash bugs.&lt;/p&gt;
&lt;h2 id="prefer-readable-over-clever"&gt;Prefer Readable Over Clever &lt;a class="headline-link" href="#prefer-readable-over-clever" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Bash is full of shortcuts and obscure expansions. They can make scripts shorter, but they also make them harder to read six months later. Choose the clearer form, even when it is a few characters longer.&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; -z &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;anonymous&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;is almost always easier to maintain than the golfed equivalent:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;: &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:=anonymous&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Aim for scripts that a teammate can read once and understand, not ones that require a reference manual.&lt;/p&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For a printable quick reference, see the &lt;a href="https://linuxize.com/cheatsheet/bash/"&gt;Bash cheatsheet&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;Before committing a Bash script, confirm the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;#!/usr/bin/env bash&lt;/code&gt; shebang is present.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;set -euo pipefail&lt;/code&gt; is enabled.&lt;/li&gt;
&lt;li&gt;All variable expansions are quoted.&lt;/li&gt;
&lt;li&gt;Tests use &lt;code&gt;[[ ]]&lt;/code&gt; instead of &lt;code&gt;[ ]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Functions replace long repeated blocks, with &lt;code&gt;local&lt;/code&gt; variables.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;trap&lt;/code&gt; handles errors and cleanup.&lt;/li&gt;
&lt;li&gt;Input is validated before work begins.&lt;/li&gt;
&lt;li&gt;No parsing of &lt;code&gt;ls&lt;/code&gt; output.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;shellcheck&lt;/code&gt; reports no warnings, or they are explicitly ignored.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="faq"&gt;FAQ &lt;a class="headline-link" href="#faq" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Is &lt;code&gt;set -e&lt;/code&gt; enough on its own?&lt;/strong&gt;&lt;br&gt;
No. &lt;code&gt;set -e&lt;/code&gt; stops on many errors, but it misses failures in pipelines and ignores unset variables. Combine it with &lt;code&gt;-u&lt;/code&gt; and &lt;code&gt;-o pipefail&lt;/code&gt; to cover those cases.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Does strict mode break existing scripts?&lt;/strong&gt;&lt;br&gt;
It often surfaces bugs that were already there, such as unset variables and ignored errors. The fix is to quote variables, use default values like &lt;code&gt;${VAR:-}&lt;/code&gt;, and handle expected failures with &lt;code&gt;|| true&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Should I write scripts in Bash or switch to Python?&lt;/strong&gt;&lt;br&gt;
Bash is a good fit for short glue scripts that mostly call other commands. Once a script grows past a few hundred lines, has complex data structures, or needs proper error handling and testing, Python or another language is usually a better choice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Where can I learn more safe scripting patterns?&lt;/strong&gt;&lt;br&gt;
Keep the &lt;a href="https://www.shellcheck.net/wiki/" target="_blank" rel="noopener noreferrer"&gt;Shellcheck wiki&lt;/a&gt;
open while you write, and read through the official &lt;a href="https://www.gnu.org/software/bash/manual/bash.html" target="_blank" rel="noopener noreferrer"&gt;Bash manual&lt;/a&gt;
section on shell builtins. Both cover the reasoning behind each rule, not only the rule itself.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Strict mode, quoting, and a few well-placed traps cover most of the risk in Bash scripting. Make these practices your defaults, run &lt;code&gt;shellcheck&lt;/code&gt; on every change, and your scripts will behave the same way on your laptop, in CI, and on a production server.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/bash-best-practices/featured_hu_de6a5152d402e81c.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>How to Install LEMP Stack on Ubuntu 26.04</title><link>https://linuxize.com/post/how-to-install-lemp-stack-on-ubuntu-26-04/</link><pubDate>Tue, 12 May 2026 10:00:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/how-to-install-lemp-stack-on-ubuntu-26-04/</guid><category>nginx</category><category>mysql</category><category>php</category><category>ubuntu</category><description>Set up a LEMP stack on Ubuntu 26.04 with Nginx, MySQL 8.4, PHP 8.5, PHP-FPM, and a working PHP database test.</description><content:encoded>&lt;p&gt;When you need to host a PHP application or a CMS such as WordPress, Magento, or Laravel on a fresh Ubuntu server, the LEMP stack is one of the most common ways to get there. LEMP stands for Linux, Nginx (pronounced &amp;ldquo;engine-x&amp;rdquo;), MySQL, and PHP, and the four pieces fit together to serve dynamic PHP pages over HTTP.&lt;/p&gt;
&lt;p&gt;This guide explains how to install and configure a complete LEMP stack on Ubuntu 26.04. By the end you will have Nginx serving HTTP traffic, MySQL 8.4 running as the database, and PHP 8.5 processing dynamic pages through PHP-FPM.&lt;/p&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Update package index&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt update&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Install Nginx&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt install nginx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Install MySQL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt install mysql-server&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Install PHP-FPM&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt install php-fpm php-mysql&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Secure MySQL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo mysql_secure_installation&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restart PHP-FPM&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl restart php8.5-fpm&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reload Nginx&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl reload nginx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test Nginx config&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo nginx -t&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Allow HTTP/HTTPS&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo ufw allow 'Nginx Full'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PHP-FPM socket&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/run/php/php8.5-fpm.sock&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites &lt;a class="headline-link" href="#prerequisites" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before installing the stack, make sure you have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A server running Ubuntu 26.04 with a &lt;a href="https://linuxize.com/post/how-to-create-a-sudo-user-on-ubuntu/"&gt;user with sudo privileges&lt;/a&gt;
.&lt;/li&gt;
&lt;li&gt;The UFW firewall enabled. If you have not configured it yet, see &lt;a href="https://linuxize.com/post/ufw-command-in-linux/"&gt;ufw Command in Linux: Uncomplicated Firewall Reference&lt;/a&gt;
.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="step-1-install-nginx"&gt;Step 1: Install Nginx &lt;a class="headline-link" href="#step-1-install-nginx" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Nginx is in the default Ubuntu 26.04 repositories. Refresh the package index and install it:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Once the install finishes, the service starts automatically. Confirm it is running:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl status nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The output shows the service as &lt;code&gt;active (running)&lt;/code&gt;. If UFW is enabled, allow HTTP and HTTPS traffic:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo ufw allow &lt;span class="s1"&gt;&amp;#39;Nginx Full&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Open &lt;code&gt;http://your_server_ip&lt;/code&gt; in a browser. You should see the default Nginx welcome page, which confirms that Nginx is serving requests.&lt;/p&gt;
&lt;p&gt;For a deeper walkthrough of the install and the directory layout, see &lt;a href="https://linuxize.com/post/how-to-install-nginx-on-ubuntu-26-04/"&gt;How to Install Nginx on Ubuntu 26.04&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="step-2-install-mysql"&gt;Step 2: Install MySQL &lt;a class="headline-link" href="#step-2-install-mysql" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The database tier handles persistence. Install the MySQL server package:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install mysql-server&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Once the install completes, run the security script. It walks you through removing anonymous users, disabling remote root login, and dropping the test database:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo mysql_secure_installation&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;When prompted, answer &lt;code&gt;Y&lt;/code&gt; to each hardening question. The validate password component is optional and you can disable it if you plan to manage credentials yourself.&lt;/p&gt;
&lt;p&gt;To verify that the service is running:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl status mysql&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For details on creating users and granting privileges, see &lt;a href="https://linuxize.com/post/how-to-install-mysql-on-ubuntu-26-04/"&gt;How to Install MySQL on Ubuntu 26.04&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="step-3-install-php-and-php-fpm"&gt;Step 3: Install PHP and PHP-FPM &lt;a class="headline-link" href="#step-3-install-php-and-php-fpm" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Nginx does not embed a PHP interpreter. Instead, requests for PHP files are handed off to PHP-FPM (FastCGI Process Manager) over a Unix socket. Install PHP-FPM and the MySQL driver:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install php-fpm php-mysql&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On Ubuntu 26.04 this pulls in PHP 8.5. Confirm the version:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;php -v&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The output starts with &lt;code&gt;PHP 8.5.x&lt;/code&gt;. The PHP-FPM service starts automatically and listens on a Unix socket at &lt;code&gt;/run/php/php8.5-fpm.sock&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl status php8.5-fpm&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If you need extra extensions such as &lt;code&gt;php-curl&lt;/code&gt;, &lt;code&gt;php-gd&lt;/code&gt;, &lt;code&gt;php-mbstring&lt;/code&gt;, &lt;code&gt;php-xml&lt;/code&gt;, or &lt;code&gt;php-zip&lt;/code&gt;, install them now:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install php-curl php-gd php-mbstring php-xml php-zip&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For information on additional modules and switching PHP versions, see &lt;a href="https://linuxize.com/post/how-to-install-php-on-ubuntu-26-04/"&gt;How to Install PHP on Ubuntu 26.04&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="step-4-configure-nginx-to-serve-php"&gt;Step 4: Configure Nginx to Serve PHP &lt;a class="headline-link" href="#step-4-configure-nginx-to-serve-php" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Now that Nginx and PHP-FPM are running, you need a server block that passes &lt;code&gt;.php&lt;/code&gt; requests to PHP-FPM. Create a directory for the site and a small test file:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo mkdir -p /var/www/example.com
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo chown -R &lt;span class="nv"&gt;$USER&lt;/span&gt;:&lt;span class="nv"&gt;$USER&lt;/span&gt; /var/www/example.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Create a new server block configuration:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nano /etc/nginx/sites-available/example.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Paste the following content. Replace &lt;code&gt;example.com&lt;/code&gt; with your domain or your server IP if you do not have a domain yet:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="nginx"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300"&gt;nginx&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="s"&gt;[::]:80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;example.com&lt;/span&gt; &lt;span class="s"&gt;www.example.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;root&lt;/span&gt; &lt;span class="s"&gt;/var/www/example.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;index&lt;/span&gt; &lt;span class="s"&gt;index.php&lt;/span&gt; &lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;try_files&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt; &lt;span class="nv"&gt;$uri/&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt; &lt;span class="sr"&gt;\.php$&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;include&lt;/span&gt; &lt;span class="s"&gt;snippets/fastcgi-php.conf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;fastcgi_pass&lt;/span&gt; &lt;span class="s"&gt;unix:/run/php/php8.5-fpm.sock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt; &lt;span class="sr"&gt;/\.ht&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;deny&lt;/span&gt; &lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;try_files&lt;/code&gt; directive serves static files when they exist and falls back to a 404 otherwise. The &lt;code&gt;location ~ \.php$&lt;/code&gt; block matches any request ending in &lt;code&gt;.php&lt;/code&gt; and forwards it to PHP-FPM through the Unix socket. The final &lt;code&gt;location ~ /\.ht&lt;/code&gt; block blocks access to legacy &lt;code&gt;.htaccess&lt;/code&gt; files, which Nginx does not use.&lt;/p&gt;
&lt;p&gt;Enable the site by creating a symlink in &lt;code&gt;sites-enabled&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If the default server block is still enabled, remove its symlink so Nginx serves your new site for matching requests:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo unlink /etc/nginx/sites-enabled/default&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Test the configuration before reloading:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nginx -t&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The output should end with &lt;code&gt;syntax is ok&lt;/code&gt; and &lt;code&gt;test is successful&lt;/code&gt;. Reload Nginx to apply the change:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl reload nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="step-5-test-the-php-setup"&gt;Step 5: Test the PHP Setup &lt;a class="headline-link" href="#step-5-test-the-php-setup" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Create a simple PHP info file to confirm that Nginx hands PHP requests to PHP-FPM:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;?php phpinfo();&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; sudo tee /var/www/example.com/info.php&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Open &lt;code&gt;http://example.com/info.php&lt;/code&gt; (or &lt;code&gt;http://your_server_ip/info.php&lt;/code&gt;) in a browser. You should see the PHP information page that lists the PHP version, loaded extensions, and configuration directives. If the page shows the source code as plain text, Nginx is not passing the request to PHP-FPM and the &lt;code&gt;location ~ \.php$&lt;/code&gt; block needs to be reviewed.&lt;/p&gt;
&lt;p&gt;Once you confirm PHP is working, remove the info file. It exposes details about your environment that should not be publicly readable:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo rm /var/www/example.com/info.php&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="step-6-test-the-mysql-connection-from-php"&gt;Step 6: Test the MySQL Connection from PHP &lt;a class="headline-link" href="#step-6-test-the-mysql-connection-from-php" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To confirm that PHP can talk to MySQL, create a test database and user:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo mysql&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;In the MySQL prompt, run:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sql"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-sky-100 text-sky-700 dark:bg-sky-900 dark:text-sky-300"&gt;sql&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DATABASE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lemp_test&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;lemp_user&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IDENTIFIED&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;changeme&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;GRANT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ALL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;PRIVILEGES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lemp_test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;lemp_user&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;FLUSH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;PRIVILEGES&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;EXIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="note callout callout-warning"&gt;
&lt;div class="callout-header"&gt;&lt;svg role="img" aria-hidden="true" class="callout-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"&gt;
&lt;path d="M10 20C4.477 20 0 15.523 0 10S4.477 0 10 0s10 4.477 10 10-4.477 10-10 10zm0-2c4.418 0 8-3.582 8-8s-3.582-8-8-8-8 3.582-8 8 3.582 8 8 8zm-.5-5h1c.276 0 .5.224.5.5v1c0 .276-.224.5-.5.5h-1c-.276 0-.5-.224-.5-.5v-1c0-.276.224-.5.5-.5zm0-8h1c.276 0 .5.224.5.5V8l-.5 3-1 .5L9 8V5.5c0-.276.224-.5.5-.5z"&gt;&lt;/path&gt;
&lt;/svg&gt;
&lt;span class="callout-title"&gt;Warning&lt;/span&gt;&lt;/div&gt;
&lt;div class="callout-body"&gt;The password above is a placeholder. Replace &lt;code&gt;changeme&lt;/code&gt; with a strong value, and do not commit credentials to version control. Store secrets in environment variables or a &lt;code&gt;.env&lt;/code&gt; file outside the repository.&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Create a small connection test:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nano /var/www/example.com/db-test.php&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Add the following content:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="php"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-indigo-100 text-indigo-700 dark:bg-indigo-900 dark:text-indigo-300"&gt;php&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;?&lt;/span&gt;&lt;span class="nx"&gt;php&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$mysqli&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;mysqli&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;localhost&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;lemp_user&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;changeme&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;lemp_test&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$mysqli&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;connect_error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;die&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Connection failed: &amp;#34;&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$mysqli&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;connect_error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Connected to MySQL &amp;#34;&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$mysqli&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;server_info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$mysqli&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Open &lt;code&gt;http://example.com/db-test.php&lt;/code&gt;. The page prints &lt;code&gt;Connected to MySQL 8.4.x&lt;/code&gt; when the credentials and the PHP MySQL driver are working. Remove the file once you confirm the connection:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo rm /var/www/example.com/db-test.php&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Nginx shows the welcome page after editing the server block&lt;/strong&gt;&lt;br&gt;
Nginx still serves the default site. Remove the symlink at &lt;code&gt;/etc/nginx/sites-enabled/default&lt;/code&gt; or rename your server block so it loads first, then run &lt;code&gt;sudo nginx -t&lt;/code&gt; and &lt;code&gt;sudo systemctl reload nginx&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PHP files download instead of executing&lt;/strong&gt;&lt;br&gt;
Nginx is not forwarding &lt;code&gt;.php&lt;/code&gt; requests to PHP-FPM. Verify that the &lt;code&gt;location ~ \.php$&lt;/code&gt; block exists, that &lt;code&gt;fastcgi_pass&lt;/code&gt; points to &lt;code&gt;/run/php/php8.5-fpm.sock&lt;/code&gt;, and that PHP-FPM is running with &lt;code&gt;sudo systemctl status php8.5-fpm&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;502 Bad Gateway&lt;/strong&gt;&lt;br&gt;
PHP-FPM is not running, or Nginx is pointing at a wrong socket path. Run &lt;code&gt;ls -l /run/php/&lt;/code&gt; and confirm that the socket file matches the version in your config (for example, &lt;code&gt;php8.5-fpm.sock&lt;/code&gt;). Restart the service with &lt;code&gt;sudo systemctl restart php8.5-fpm&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Permission denied accessing the socket&lt;/strong&gt;&lt;br&gt;
The socket is owned by &lt;code&gt;www-data&lt;/code&gt; by default. Make sure Nginx runs as &lt;code&gt;www-data&lt;/code&gt; (the default on Ubuntu) and that no custom file permissions on &lt;code&gt;/run/php/&lt;/code&gt; block access.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MySQL connection refused from PHP&lt;/strong&gt;&lt;br&gt;
Confirm that the &lt;code&gt;php-mysql&lt;/code&gt; package is installed, that the user has been granted privileges on the target database, and that the password in the PHP code matches the one used in &lt;code&gt;CREATE USER&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="faq"&gt;FAQ &lt;a class="headline-link" href="#faq" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;What is the difference between LEMP and LAMP?&lt;/strong&gt;&lt;br&gt;
LEMP uses Nginx as the web server, while LAMP uses Apache. Nginx handles many concurrent connections with low memory and is a strong fit for static content and reverse proxying. Apache supports per-directory &lt;code&gt;.htaccess&lt;/code&gt; files and &lt;code&gt;mod_php&lt;/code&gt;, which simplifies shared hosting setups. For a step-by-step Apache install, see &lt;a href="https://linuxize.com/post/how-to-install-lamp-stack-on-ubuntu-26-04/"&gt;How to Install LAMP Stack on Ubuntu 26.04&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Can I use MariaDB instead of MySQL?&lt;/strong&gt;&lt;br&gt;
Yes. MariaDB is a drop-in replacement for MySQL and uses the same client tools, the same protocol, and the same SQL syntax for the cases covered in this guide.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Which PHP-FPM socket path do I use?&lt;/strong&gt;&lt;br&gt;
The path follows the PHP version. On Ubuntu 26.04 with the default PHP 8.5, the socket is &lt;code&gt;/run/php/php8.5-fpm.sock&lt;/code&gt;. If you install another version, list &lt;code&gt;/run/php/&lt;/code&gt; to find the right socket file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How do I add HTTPS to the site?&lt;/strong&gt;&lt;br&gt;
The recommended path is to install Certbot and request a Let&amp;rsquo;s Encrypt certificate for your domain. The Certbot Nginx plugin updates the server block automatically and sets up redirection from HTTP to HTTPS.&lt;/p&gt;
&lt;h2 id="next-steps"&gt;Next Steps &lt;a class="headline-link" href="#next-steps" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You now have a working LEMP stack on Ubuntu 26.04. From here you can deploy a PHP application, create additional &lt;a href="https://linuxize.com/post/how-to-set-up-nginx-server-blocks-on-ubuntu-22-04/"&gt;Nginx server blocks&lt;/a&gt;
for more sites, or front the stack with HTTPS using Let&amp;rsquo;s Encrypt.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/how-to-install-lemp-stack-on-ubuntu-26-04/featured_hu_9cfc14ae7ce48afe.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>How to Install LAMP Stack on Ubuntu 26.04</title><link>https://linuxize.com/post/how-to-install-lamp-stack-on-ubuntu-26-04/</link><pubDate>Tue, 12 May 2026 09:00:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/how-to-install-lamp-stack-on-ubuntu-26-04/</guid><category>apache</category><category>mysql</category><category>php</category><category>ubuntu</category><description>Install Apache, MySQL 8.4, and PHP 8.5 on Ubuntu 26.04, configure an Apache virtual host, and test PHP database access.</description><content:encoded>&lt;p&gt;When you spin up a new server to run a PHP application or a CMS such as WordPress, Joomla, or Drupal, the LAMP stack is one of the most familiar starting points. LAMP stands for Linux, Apache, MySQL, and PHP, and the four pieces work together to serve dynamic web pages over HTTP.&lt;/p&gt;
&lt;p&gt;This guide explains how to install and configure a complete LAMP stack on Ubuntu 26.04. By the end you will have Apache serving HTTP traffic, MySQL 8.4 running as the database, and PHP 8.5 processing dynamic pages through &lt;code&gt;mod_php&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Update package index&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt update&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Install Apache&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt install apache2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Install MySQL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt install mysql-server&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Install PHP with Apache&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt install php libapache2-mod-php php-mysql&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Secure MySQL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo mysql_secure_installation&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restart Apache&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl restart apache2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test Apache config&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apache2ctl configtest&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Allow HTTP/HTTPS&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo ufw allow 'Apache Full'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enable a site&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo a2ensite example.com&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Disable default site&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo a2dissite 000-default&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites &lt;a class="headline-link" href="#prerequisites" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before installing the stack, make sure you have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A server running Ubuntu 26.04 with a &lt;a href="https://linuxize.com/post/how-to-create-a-sudo-user-on-ubuntu/"&gt;user with sudo privileges&lt;/a&gt;
.&lt;/li&gt;
&lt;li&gt;The UFW firewall enabled. If you have not configured it yet, see &lt;a href="https://linuxize.com/post/ufw-command-in-linux/"&gt;ufw Command in Linux: Uncomplicated Firewall Reference&lt;/a&gt;
.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="step-1-install-apache"&gt;Step 1: Install Apache &lt;a class="headline-link" href="#step-1-install-apache" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Apache is in the default Ubuntu 26.04 repositories. Refresh the package index and install it:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install apache2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The service starts automatically after the install. Confirm it is running:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl status apache2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The output shows the service as &lt;code&gt;active (running)&lt;/code&gt;. If UFW is enabled, allow HTTP and HTTPS through the firewall:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo ufw allow &lt;span class="s1"&gt;&amp;#39;Apache Full&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Open &lt;code&gt;http://your_server_ip&lt;/code&gt; in a browser. You should see the default Apache welcome page, which confirms that Apache is serving requests.&lt;/p&gt;
&lt;p&gt;For an in-depth walkthrough of the install and the directory layout, see &lt;a href="https://linuxize.com/post/how-to-install-apache-on-ubuntu-26-04/"&gt;How to Install Apache on Ubuntu 26.04&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="step-2-install-mysql"&gt;Step 2: Install MySQL &lt;a class="headline-link" href="#step-2-install-mysql" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The database tier handles persistence. Install the MySQL server package:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install mysql-server&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Once the install completes, run the security script. It walks you through removing anonymous users, disabling remote root login, and dropping the test database:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo mysql_secure_installation&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Answer &lt;code&gt;Y&lt;/code&gt; to each hardening question. The validate password component is optional; skip it if you plan to manage credentials yourself.&lt;/p&gt;
&lt;p&gt;To verify that the service is running:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl status mysql&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For details on creating users and granting privileges, see &lt;a href="https://linuxize.com/post/how-to-install-mysql-on-ubuntu-26-04/"&gt;How to Install MySQL on Ubuntu 26.04&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="step-3-install-php"&gt;Step 3: Install PHP &lt;a class="headline-link" href="#step-3-install-php" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Apache integrates with PHP through the &lt;code&gt;libapache2-mod-php&lt;/code&gt; module, which lets Apache run PHP code in-process. Install PHP, the Apache module, and the MySQL driver:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install php libapache2-mod-php php-mysql&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On Ubuntu 26.04 this pulls in PHP 8.5. Confirm the version:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;php -v&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The output starts with &lt;code&gt;PHP 8.5.x&lt;/code&gt;. Restart Apache so the PHP module loads:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl restart apache2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If you need extra extensions such as &lt;code&gt;php-curl&lt;/code&gt;, &lt;code&gt;php-gd&lt;/code&gt;, &lt;code&gt;php-mbstring&lt;/code&gt;, &lt;code&gt;php-xml&lt;/code&gt;, or &lt;code&gt;php-zip&lt;/code&gt;, install them now:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install php-curl php-gd php-mbstring php-xml php-zip&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For information on additional modules and switching PHP versions, see &lt;a href="https://linuxize.com/post/how-to-install-php-on-ubuntu-26-04/"&gt;How to Install PHP on Ubuntu 26.04&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="step-4-configure-an-apache-virtual-host"&gt;Step 4: Configure an Apache Virtual Host &lt;a class="headline-link" href="#step-4-configure-an-apache-virtual-host" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The default Apache configuration serves files from &lt;code&gt;/var/www/html&lt;/code&gt;. For a real site you usually want a dedicated document root and a virtual host. Create the directory and a small placeholder:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo mkdir -p /var/www/example.com
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo chown -R &lt;span class="nv"&gt;$USER&lt;/span&gt;:&lt;span class="nv"&gt;$USER&lt;/span&gt; /var/www/example.com
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;h1&amp;gt;example.com is live&amp;lt;/h1&amp;gt;&amp;#34;&lt;/span&gt; &amp;gt; /var/www/example.com/index.html&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Create a new virtual host file:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nano /etc/apache2/sites-available/example.com.conf&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Paste the following content. Replace &lt;code&gt;example.com&lt;/code&gt; with your domain or your server IP if you do not have a domain yet:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="apache"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300"&gt;apache&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-apache" data-lang="apache"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;VirtualHost&lt;/span&gt; &lt;span class="s"&gt;*:80&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;ServerName&lt;/span&gt; example.com
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;ServerAlias&lt;/span&gt; www.example.com
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;ServerAdmin&lt;/span&gt; webmaster@example.com
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;DocumentRoot&lt;/span&gt; &lt;span class="sx"&gt;/var/www/example.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;Directory&lt;/span&gt; &lt;span class="s"&gt;/var/www/example.com&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;Options&lt;/span&gt; -Indexes +FollowSymLinks
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;AllowOverride&lt;/span&gt; &lt;span class="k"&gt;All&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;Require&lt;/span&gt; &lt;span class="k"&gt;all&lt;/span&gt; granted
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/Directory&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;ErrorLog&lt;/span&gt; ${APACHE_LOG_DIR}/example.com-error.log
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;CustomLog&lt;/span&gt; ${APACHE_LOG_DIR}/example.com-access.log combined
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/VirtualHost&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;AllowOverride All&lt;/code&gt; lets you use per-directory &lt;code&gt;.htaccess&lt;/code&gt; files, which is required by many PHP applications such as WordPress for clean URL rewrites. Enable the site and disable the default one:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo a2ensite example.com
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo a2dissite 000-default&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Make sure &lt;code&gt;mod_rewrite&lt;/code&gt; is enabled if your application uses it:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo a2enmod rewrite&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Test the configuration before reloading Apache:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apache2ctl configtest&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The output should end with &lt;code&gt;Syntax OK&lt;/code&gt;. Reload Apache to apply the change:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl reload apache2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="step-5-test-the-php-setup"&gt;Step 5: Test the PHP Setup &lt;a class="headline-link" href="#step-5-test-the-php-setup" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Create a PHP info file to confirm that Apache executes PHP:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;?php phpinfo();&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; sudo tee /var/www/example.com/info.php&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Open &lt;code&gt;http://example.com/info.php&lt;/code&gt; (or &lt;code&gt;http://your_server_ip/info.php&lt;/code&gt;) in a browser. You should see the PHP information page that lists the PHP version, loaded extensions, and configuration directives. If the browser downloads the file or shows the source code, the PHP module is not loaded; reinstall &lt;code&gt;libapache2-mod-php&lt;/code&gt; and restart Apache.&lt;/p&gt;
&lt;p&gt;Once you confirm PHP is working, remove the info file. It exposes details about your environment that should not be publicly readable:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo rm /var/www/example.com/info.php&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="step-6-test-the-mysql-connection-from-php"&gt;Step 6: Test the MySQL Connection from PHP &lt;a class="headline-link" href="#step-6-test-the-mysql-connection-from-php" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To confirm that PHP can talk to MySQL, create a test database and user:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo mysql&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;In the MySQL prompt, run:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sql"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-sky-100 text-sky-700 dark:bg-sky-900 dark:text-sky-300"&gt;sql&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DATABASE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lamp_test&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;lamp_user&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;IDENTIFIED&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;changeme&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;GRANT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ALL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;PRIVILEGES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lamp_test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;lamp_user&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;FLUSH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;PRIVILEGES&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;EXIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="note callout callout-warning"&gt;
&lt;div class="callout-header"&gt;&lt;svg role="img" aria-hidden="true" class="callout-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"&gt;
&lt;path d="M10 20C4.477 20 0 15.523 0 10S4.477 0 10 0s10 4.477 10 10-4.477 10-10 10zm0-2c4.418 0 8-3.582 8-8s-3.582-8-8-8-8 3.582-8 8 3.582 8 8 8zm-.5-5h1c.276 0 .5.224.5.5v1c0 .276-.224.5-.5.5h-1c-.276 0-.5-.224-.5-.5v-1c0-.276.224-.5.5-.5zm0-8h1c.276 0 .5.224.5.5V8l-.5 3-1 .5L9 8V5.5c0-.276.224-.5.5-.5z"&gt;&lt;/path&gt;
&lt;/svg&gt;
&lt;span class="callout-title"&gt;Warning&lt;/span&gt;&lt;/div&gt;
&lt;div class="callout-body"&gt;The password above is a placeholder. Replace &lt;code&gt;changeme&lt;/code&gt; with a strong value, and do not commit credentials to version control. Store secrets in environment variables or a &lt;code&gt;.env&lt;/code&gt; file outside the repository.&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Create a small connection test:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nano /var/www/example.com/db-test.php&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Add the following content:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="php"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-indigo-100 text-indigo-700 dark:bg-indigo-900 dark:text-indigo-300"&gt;php&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;?&lt;/span&gt;&lt;span class="nx"&gt;php&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$mysqli&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;mysqli&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;localhost&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;lamp_user&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;changeme&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;lamp_test&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$mysqli&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;connect_error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;die&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Connection failed: &amp;#34;&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$mysqli&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;connect_error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Connected to MySQL &amp;#34;&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$mysqli&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;server_info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$mysqli&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Open &lt;code&gt;http://example.com/db-test.php&lt;/code&gt;. The page prints &lt;code&gt;Connected to MySQL 8.4.x&lt;/code&gt; when the credentials and the PHP MySQL driver are working. Remove the file once you confirm the connection:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo rm /var/www/example.com/db-test.php&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Apache shows the default page after editing the virtual host&lt;/strong&gt;&lt;br&gt;
The default site is still enabled. Run &lt;code&gt;sudo a2dissite 000-default&lt;/code&gt;, then &lt;code&gt;sudo a2ensite example.com&lt;/code&gt;, and reload Apache.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PHP files download instead of executing&lt;/strong&gt;&lt;br&gt;
The Apache PHP module is not loaded. Reinstall it with &lt;code&gt;sudo apt install --reinstall libapache2-mod-php&lt;/code&gt;, enable it with &lt;code&gt;sudo a2enmod php8.5&lt;/code&gt;, and restart Apache.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;AllowOverride All&lt;/code&gt; is ignored&lt;/strong&gt;&lt;br&gt;
The &lt;code&gt;&amp;lt;Directory&amp;gt;&lt;/code&gt; block in the virtual host must include &lt;code&gt;AllowOverride All&lt;/code&gt;. Confirm that the path matches your &lt;code&gt;DocumentRoot&lt;/code&gt; and run &lt;code&gt;sudo apache2ctl configtest&lt;/code&gt; after the change.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;.htaccess&lt;/code&gt; rewrites do not work&lt;/strong&gt;&lt;br&gt;
The &lt;code&gt;rewrite&lt;/code&gt; module is not enabled. Run &lt;code&gt;sudo a2enmod rewrite&lt;/code&gt; and restart Apache. Verify that the &lt;code&gt;&amp;lt;Directory&amp;gt;&lt;/code&gt; block sets &lt;code&gt;AllowOverride All&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MySQL connection refused from PHP&lt;/strong&gt;&lt;br&gt;
Confirm that the &lt;code&gt;php-mysql&lt;/code&gt; package is installed, that the user has been granted privileges on the target database, and that the password in the PHP code matches the one used in &lt;code&gt;CREATE USER&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="faq"&gt;FAQ &lt;a class="headline-link" href="#faq" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;What is the difference between LAMP and LEMP?&lt;/strong&gt;&lt;br&gt;
LAMP uses Apache as the web server, while LEMP uses Nginx. Apache supports &lt;code&gt;.htaccess&lt;/code&gt; overrides and &lt;code&gt;mod_php&lt;/code&gt;, which makes shared hosting and many PHP CMS workflows simpler. Nginx is event-driven and tends to use less memory under high concurrency. For an Nginx-based stack, see &lt;a href="https://linuxize.com/post/how-to-install-lemp-stack-on-ubuntu-26-04/"&gt;How to Install LEMP Stack on Ubuntu 26.04&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Can I use MariaDB instead of MySQL?&lt;/strong&gt;&lt;br&gt;
Yes. MariaDB is a drop-in replacement for MySQL and uses the same client tools, the same protocol, and the same SQL syntax for the cases covered in this guide.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Should I use mod_php or PHP-FPM with Apache?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;mod_php&lt;/code&gt; is the simplest setup and is fine for many sites. For higher concurrency or when you run multiple PHP versions, switch to PHP-FPM with &lt;code&gt;mpm_event&lt;/code&gt; and &lt;code&gt;proxy_fcgi&lt;/code&gt;. The &lt;code&gt;php-fpm&lt;/code&gt; package and the &lt;code&gt;proxy_fcgi&lt;/code&gt; Apache module handle the connection.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How do I add HTTPS to the site?&lt;/strong&gt;&lt;br&gt;
Install Certbot and request a Let&amp;rsquo;s Encrypt certificate for your domain. The Certbot Apache plugin updates the virtual host automatically and sets up redirection from HTTP to HTTPS.&lt;/p&gt;
&lt;h2 id="next-steps"&gt;Next Steps &lt;a class="headline-link" href="#next-steps" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You now have a working LAMP stack on Ubuntu 26.04. From here you can deploy a PHP application, create additional &lt;a href="https://linuxize.com/post/how-to-set-up-apache-virtual-hosts-on-ubuntu-20-04/"&gt;Apache virtual hosts&lt;/a&gt;
for more sites, or add HTTPS using Let&amp;rsquo;s Encrypt.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/how-to-install-lamp-stack-on-ubuntu-26-04/featured_hu_1e985945359e8340.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>Nginx Location Blocks: Match Rules and Priority</title><link>https://linuxize.com/post/nginx-location-block/</link><pubDate>Mon, 11 May 2026 15:20:00 +0100</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/nginx-location-block/</guid><category>nginx</category><description>How Nginx picks a location block: prefix, exact, and regex matches, the priority order, named locations, and common configuration patterns.</description><content:encoded>&lt;p&gt;Sooner or later, every Nginx configuration grows beyond a single catch-all rule. You want PHP files to reach PHP-FPM, static assets to be served directly with long cache headers, one endpoint to be proxied to an application server, and an admin path to be protected with basic auth. Each of these rules lives in its own &lt;code&gt;location&lt;/code&gt; block.&lt;/p&gt;
&lt;p&gt;The part that trips people up is not writing a &lt;code&gt;location&lt;/code&gt; block, it is predicting which one Nginx will pick when a request could match more than one. Nginx does not scan top to bottom. It uses a specific priority order that mixes prefix length, match type, and regex ordering. This guide walks through the match types, the exact order Nginx follows, and the patterns you will use most often.&lt;/p&gt;
&lt;h2 id="location-block-syntax"&gt;Location Block Syntax &lt;a class="headline-link" href="#location-block-syntax" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A &lt;code&gt;location&lt;/code&gt; block lives inside a &lt;a href="https://linuxize.com/post/how-to-set-up-nginx-server-blocks-on-ubuntu-22-04/"&gt;&lt;code&gt;server&lt;/code&gt; block&lt;/a&gt;
and defines how Nginx handles requests whose URI matches a given pattern:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="txt"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;txt&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;location [modifier] pattern {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; # directives
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;modifier&lt;/code&gt; is optional. When it is omitted, Nginx treats the pattern as a prefix. The &lt;code&gt;pattern&lt;/code&gt; is a string (for prefix and exact matches) or a regular expression (when the modifier is &lt;code&gt;~&lt;/code&gt; or &lt;code&gt;~*&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;A minimal server block that uses two location blocks looks like this:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="nginx"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300"&gt;nginx&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;example.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;root&lt;/span&gt; &lt;span class="s"&gt;/var/www/example.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;try_files&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt; &lt;span class="nv"&gt;$uri/&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/api/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://127.0.0.1:3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Requests for &lt;code&gt;/about.html&lt;/code&gt; fall into the first block and are served as static files. Requests for &lt;code&gt;/api/users&lt;/code&gt; fall into the second block and are proxied to the application.&lt;/p&gt;
&lt;h2 id="the-five-match-types"&gt;The Five Match Types &lt;a class="headline-link" href="#the-five-match-types" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Nginx supports five kinds of location matches. Each one uses a different modifier, and each one has its own role in the priority order covered in the next section.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;location /path/&lt;/code&gt; - Prefix match. Matches any URI that begins with the given string. This is the default when no modifier is used.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;location = /path&lt;/code&gt; - Exact match. Matches only when the request URI is exactly equal to the given string.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;location ^~ /path/&lt;/code&gt; - Preferential prefix match. Same matching rules as a plain prefix match, but tells Nginx to stop searching for regex matches once this block is selected as the longest prefix.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;location ~ pattern&lt;/code&gt; - Case-sensitive regex match. Matches if the URI matches the regular expression.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;location ~* pattern&lt;/code&gt; - Case-insensitive regex match. Same as &lt;code&gt;~&lt;/code&gt;, but ignores letter case.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is also a sixth form, &lt;code&gt;location @name&lt;/code&gt;, for named locations. Named locations are not used during the normal match process; they are jumped to from directives such as &lt;code&gt;try_files&lt;/code&gt; and &lt;code&gt;error_page&lt;/code&gt;. We cover them later in this guide.&lt;/p&gt;
&lt;h2 id="how-nginx-picks-a-location"&gt;How Nginx Picks a Location &lt;a class="headline-link" href="#how-nginx-picks-a-location" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When a request comes in, Nginx does not walk through location blocks top to bottom. It picks the block that wins according to a fixed priority order.&lt;/p&gt;
&lt;p&gt;The order Nginx follows is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Look for an &lt;strong&gt;exact match&lt;/strong&gt; (&lt;code&gt;=&lt;/code&gt;). If one matches, stop and use it.&lt;/li&gt;
&lt;li&gt;Look at all &lt;strong&gt;prefix matches&lt;/strong&gt; (plain and &lt;code&gt;^~&lt;/code&gt;) and remember the longest one that matches.&lt;/li&gt;
&lt;li&gt;If the longest prefix match was defined with &lt;code&gt;^~&lt;/code&gt;, stop and use it.&lt;/li&gt;
&lt;li&gt;Otherwise, go through &lt;strong&gt;regex matches&lt;/strong&gt; (&lt;code&gt;~&lt;/code&gt; and &lt;code&gt;~*&lt;/code&gt;) in the order they appear in the configuration. The first one that matches wins.&lt;/li&gt;
&lt;li&gt;If no regex matches, fall back to the longest prefix match remembered in step 2.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The consequences of this order are worth pausing on. Regex blocks are checked in source order, but prefix blocks are not; the longest match wins regardless of position in the file. The &lt;code&gt;^~&lt;/code&gt; modifier changes the outcome by skipping the regex pass entirely when it is the longest prefix match, even if a regex block would also have matched.&lt;/p&gt;
&lt;h2 id="a-worked-example"&gt;A Worked Example &lt;a class="headline-link" href="#a-worked-example" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The easiest way to understand the priority order is to trace a few requests through a real configuration:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="nginx"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300"&gt;nginx&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;example.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;root&lt;/span&gt; &lt;span class="s"&gt;/var/www/example.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;home\n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;try_files&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt; &lt;span class="nv"&gt;$uri/&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/images/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;expires&lt;/span&gt; &lt;span class="s"&gt;30d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;^~&lt;/span&gt; &lt;span class="s"&gt;/downloads/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;autoindex&lt;/span&gt; &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt; &lt;span class="s"&gt;\.(png|jpg|jpeg|gif)&lt;/span&gt;$ &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;expires&lt;/span&gt; &lt;span class="s"&gt;7d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;access_log&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt; &lt;span class="sr"&gt;\.php$&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;fastcgi_pass&lt;/span&gt; &lt;span class="s"&gt;unix:/run/php/php-fpm.sock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;include&lt;/span&gt; &lt;span class="s"&gt;fastcgi_params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Now trace what happens for a few requests:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GET /&lt;/code&gt;: The &lt;code&gt;=&lt;/code&gt; block matches exactly and wins immediately. Nginx returns the text &lt;code&gt;home&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /about.html&lt;/code&gt;: No exact match, no &lt;code&gt;^~&lt;/code&gt; match, and no regex match. The longest prefix match is &lt;code&gt;/&lt;/code&gt;, so the &lt;code&gt;try_files&lt;/code&gt; block serves the static file.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /images/logo.png&lt;/code&gt;: The longest prefix match is &lt;code&gt;/images/&lt;/code&gt;, which is a plain prefix (no &lt;code&gt;^~&lt;/code&gt;), so regex checking still happens. The first regex to match is the image extension block (&lt;code&gt;~*&lt;/code&gt;), so that block wins and the file is served with a 7-day expiry.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /downloads/setup.exe&lt;/code&gt;: The longest prefix match is &lt;code&gt;/downloads/&lt;/code&gt;, defined with &lt;code&gt;^~&lt;/code&gt;. Because of the &lt;code&gt;^~&lt;/code&gt;, regex checking is skipped and the directory listing block wins.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /info.php&lt;/code&gt;: The longest prefix match is &lt;code&gt;/&lt;/code&gt;. Regex checking runs, and the &lt;code&gt;\.php$&lt;/code&gt; block matches, so the request is passed to PHP-FPM.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Notice that the block order in the file did not change the outcome for prefix matches. It only changed the outcome for regex matches, where the first match wins.&lt;/p&gt;
&lt;h2 id="named-locations"&gt;Named Locations &lt;a class="headline-link" href="#named-locations" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A named location starts with &lt;code&gt;@&lt;/code&gt; and does not take part in the matching logic. You can only enter a named location by being redirected there from another directive:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="nginx"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300"&gt;nginx&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;example.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;root&lt;/span&gt; &lt;span class="s"&gt;/var/www/example.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;try_files&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt; &lt;span class="nv"&gt;$uri/&lt;/span&gt; &lt;span class="s"&gt;@fallback&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;@fallback&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://127.0.0.1:3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;In this configuration, Nginx first tries to serve the request as a static file. If the file is not found, &lt;code&gt;try_files&lt;/code&gt; jumps to &lt;code&gt;@fallback&lt;/code&gt;, which proxies the request to an application server on port 3000. This is a common pattern for single-page apps and for frameworks that handle their own routing.&lt;/p&gt;
&lt;p&gt;Named locations are also useful with the &lt;code&gt;error_page&lt;/code&gt; directive, where you can route errors into a named block that returns a custom response.&lt;/p&gt;
&lt;h2 id="common-patterns"&gt;Common Patterns &lt;a class="headline-link" href="#common-patterns" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Most Nginx configurations end up using a handful of recurring &lt;code&gt;location&lt;/code&gt; patterns. The snippets below show the shapes you will reach for most often.&lt;/p&gt;
&lt;h3 id="serve-static-files-with-a-fallback"&gt;Serve Static Files with a Fallback &lt;a class="headline-link" href="#serve-static-files-with-a-fallback" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="nginx"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300"&gt;nginx&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;try_files&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt; &lt;span class="nv"&gt;$uri/&lt;/span&gt; &lt;span class="s"&gt;/index.html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Nginx tries the requested URI as a file, then as a directory, and falls back to &lt;code&gt;/index.html&lt;/code&gt;. This works well for single-page apps where the router lives in the browser.&lt;/p&gt;
&lt;h3 id="cache-long-lived-assets"&gt;Cache Long-Lived Assets &lt;a class="headline-link" href="#cache-long-lived-assets" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="nginx"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300"&gt;nginx&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt; &lt;span class="s"&gt;\.(?:css|js|woff2?|png|jpg|jpeg|gif|svg|ico)&lt;/span&gt;$ &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;expires&lt;/span&gt; &lt;span class="s"&gt;30d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;Cache-Control&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;public,&lt;/span&gt; &lt;span class="s"&gt;immutable&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;access_log&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Assets whose content is fingerprinted by the build pipeline can be cached aggressively. The &lt;code&gt;access_log off&lt;/code&gt; directive keeps the access log focused on real page requests; see the &lt;a href="https://linuxize.com/post/nginx-log-files/"&gt;Nginx log files guide&lt;/a&gt;
for where those logs live and how to tune them.&lt;/p&gt;
&lt;h3 id="pass-php-requests-to-php-fpm"&gt;Pass PHP Requests to PHP-FPM &lt;a class="headline-link" href="#pass-php-requests-to-php-fpm" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="nginx"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300"&gt;nginx&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt; &lt;span class="sr"&gt;\.php$&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;include&lt;/span&gt; &lt;span class="s"&gt;snippets/fastcgi-php.conf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;fastcgi_pass&lt;/span&gt; &lt;span class="s"&gt;unix:/run/php/php-fpm.sock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The regex matches any URI that ends with &lt;code&gt;.php&lt;/code&gt;. The &lt;code&gt;fastcgi-php.conf&lt;/code&gt; snippet shipped with most distributions sets the correct &lt;code&gt;SCRIPT_FILENAME&lt;/code&gt; and related parameters.&lt;/p&gt;
&lt;h3 id="proxy-a-path-to-an-application"&gt;Proxy a Path to an Application &lt;a class="headline-link" href="#proxy-a-path-to-an-application" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="nginx"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300"&gt;nginx&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/api/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://127.0.0.1:3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For a deeper walkthrough of the proxy headers and common pitfalls, see the &lt;a href="https://linuxize.com/post/nginx-reverse-proxy/"&gt;Nginx reverse proxy guide&lt;/a&gt;
.&lt;/p&gt;
&lt;h3 id="protect-an-admin-path"&gt;Protect an Admin Path &lt;a class="headline-link" href="#protect-an-admin-path" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="nginx"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300"&gt;nginx&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="s"&gt;^~&lt;/span&gt; &lt;span class="s"&gt;/admin/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;auth_basic&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Restricted&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;auth_basic_user_file&lt;/span&gt; &lt;span class="s"&gt;/etc/nginx/.htpasswd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;^~&lt;/code&gt; modifier makes sure this block wins over any regex block (for example, a generic static-asset rule), so the auth challenge is not accidentally bypassed by a more specific pattern. Basic auth sends credentials in clear text, so serve the site over HTTPS; see &lt;a href="https://linuxize.com/post/redirect-http-to-https-in-nginx/"&gt;how to redirect HTTP to HTTPS in Nginx&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="test-the-configuration"&gt;Test the Configuration &lt;a class="headline-link" href="#test-the-configuration" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;After changing location blocks, always test the Nginx configuration before reloading the service:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nginx -t&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If the syntax check passes, &lt;a href="https://linuxize.com/post/start-stop-restart-nginx/"&gt;reload Nginx&lt;/a&gt;
so the change takes effect without dropping active connections:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl reload nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If the test fails, Nginx prints the file name and line number that caused the error. Fix that issue first, then run &lt;code&gt;sudo nginx -t&lt;/code&gt; again.&lt;/p&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Modifier&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Priority&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exact match&lt;/td&gt;
&lt;td&gt;1 (highest)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;^~&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Preferential prefix match&lt;/td&gt;
&lt;td&gt;2 (skips regex)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;~&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Case-sensitive regex&lt;/td&gt;
&lt;td&gt;3 (in source order)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;~*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Case-insensitive regex&lt;/td&gt;
&lt;td&gt;3 (in source order)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(none)&lt;/td&gt;
&lt;td&gt;Prefix match&lt;/td&gt;
&lt;td&gt;4 (longest wins)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Named location&lt;/td&gt;
&lt;td&gt;Only reachable from directives&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="common-mistakes"&gt;Common Mistakes &lt;a class="headline-link" href="#common-mistakes" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A few patterns cause most of the confusion around &lt;code&gt;location&lt;/code&gt; blocks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Assuming top-to-bottom evaluation.&lt;/strong&gt; Moving a prefix block higher in the file does not make it more likely to match. Prefix matches are chosen by length, not by position.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Forgetting that &lt;code&gt;^~&lt;/code&gt; skips regex.&lt;/strong&gt; If a request matches both a &lt;code&gt;^~&lt;/code&gt; prefix and a regex block, the regex block is not evaluated. This is a feature, not a bug, but it is easy to forget when debugging why a regex rule is being ignored.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Trailing slashes.&lt;/strong&gt; &lt;code&gt;location /images/&lt;/code&gt; and &lt;code&gt;location /images&lt;/code&gt; behave differently. The first only matches URIs that start with &lt;code&gt;/images/&lt;/code&gt;, while the second also matches &lt;code&gt;/imagesfoo&lt;/code&gt;. Stick to trailing slashes for directory-like prefixes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Using &lt;code&gt;proxy_pass&lt;/code&gt; with a trailing slash on the upstream.&lt;/strong&gt; Writing &lt;code&gt;proxy_pass http://backend/;&lt;/code&gt; rewrites the path in a different way than &lt;code&gt;proxy_pass http://backend;&lt;/code&gt;. The &lt;a href="https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass" target="_blank" rel="noopener noreferrer"&gt;Nginx proxy_pass documentation&lt;/a&gt;
explains the URI replacement rules in detail.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Regex order.&lt;/strong&gt; When two regex blocks can match the same URI, the one that appears first in the config wins. If requests are landing in the wrong regex block, check the order.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Most Nginx misconfigurations come from assumptions about the order of evaluation, not from the directives themselves. Once you know that exact matches win first, that &lt;code&gt;^~&lt;/code&gt; can short-circuit the regex pass, and that regex blocks are tried in source order, the behavior of a complex server block becomes predictable.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/nginx-location-block/featured_hu_a78cc01d9b6f38a0.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>dd Command in Linux: Copy Disks, Partitions, and Files</title><link>https://linuxize.com/post/dd-command-in-linux/</link><pubDate>Sun, 10 May 2026 08:30:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/dd-command-in-linux/</guid><category>disk</category><category>linux commands</category><description>This guide explains how to use the dd command in Linux to write ISO images, clone disks, back up partitions, wipe drives, and test disk speed.</description><content:encoded>&lt;p&gt;Most of the time when you copy a file on Linux, you reach for &lt;code&gt;cp&lt;/code&gt;. It walks the filesystem, respects permissions, and works on individual files. But when you need to copy an entire disk, write an ISO image to a USB stick, or create a byte-for-byte backup of a partition, the filesystem view is not enough. For that, Linux ships with &lt;code&gt;dd&lt;/code&gt;, a tool that copies raw data block by block between any two files or devices.&lt;/p&gt;
&lt;p&gt;This guide explains how to use &lt;code&gt;dd&lt;/code&gt; safely to write ISO images, clone disks, back up partitions, create test files, and benchmark storage performance.&lt;/p&gt;
&lt;h2 id="dd-syntax"&gt;dd Syntax &lt;a class="headline-link" href="#dd-syntax" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The general form of the &lt;code&gt;dd&lt;/code&gt; command is:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="txt"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;txt&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dd if=INPUT of=OUTPUT [OPTIONS]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The two key operands are &lt;code&gt;if&lt;/code&gt; (input file) and &lt;code&gt;of&lt;/code&gt; (output file). Either one can be a regular file, a block device such as &lt;code&gt;/dev/sda&lt;/code&gt;, or even &lt;code&gt;/dev/zero&lt;/code&gt; and &lt;code&gt;/dev/urandom&lt;/code&gt;. Options control the block size, how many blocks to copy, and what conversions to apply.&lt;/p&gt;
&lt;div class="note callout callout-warning"&gt;
&lt;div class="callout-header"&gt;&lt;svg role="img" aria-hidden="true" class="callout-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"&gt;
&lt;path d="M10 20C4.477 20 0 15.523 0 10S4.477 0 10 0s10 4.477 10 10-4.477 10-10 10zm0-2c4.418 0 8-3.582 8-8s-3.582-8-8-8-8 3.582-8 8 3.582 8 8 8zm-.5-5h1c.276 0 .5.224.5.5v1c0 .276-.224.5-.5.5h-1c-.276 0-.5-.224-.5-.5v-1c0-.276.224-.5.5-.5zm0-8h1c.276 0 .5.224.5.5V8l-.5 3-1 .5L9 8V5.5c0-.276.224-.5.5-.5z"&gt;&lt;/path&gt;
&lt;/svg&gt;
&lt;span class="callout-title"&gt;Warning&lt;/span&gt;&lt;/div&gt;
&lt;div class="callout-body"&gt;&lt;code&gt;dd&lt;/code&gt; writes exactly what you tell it to write, to exactly the target you point it at. Pointing &lt;code&gt;of=&lt;/code&gt; at the wrong disk will overwrite that disk without warning or confirmation. Always double-check the target device with &lt;code&gt;lsblk&lt;/code&gt; or &lt;code&gt;sudo fdisk -l&lt;/code&gt; before running a &lt;code&gt;dd&lt;/code&gt; command.&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="writing-an-iso-image-to-a-usb-drive"&gt;Writing an ISO Image to a USB Drive &lt;a class="headline-link" href="#writing-an-iso-image-to-a-usb-drive" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The most common use for &lt;code&gt;dd&lt;/code&gt; is flashing a Linux ISO to a USB drive so you can boot and install from it. First, identify the USB device with &lt;code&gt;lsblk&lt;/code&gt; or &lt;code&gt;sudo fdisk -l&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lsblk&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 465.8G 0 disk
├─sda1 8:1 0 512M 0 part /boot/efi
└─sda2 8:2 0 465.3G 0 part /
sdb 8:16 1 14.5G 0 disk
└─sdb1 8:17 1 14.5G 0 part&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here &lt;code&gt;sda&lt;/code&gt; is the system disk and &lt;code&gt;sdb&lt;/code&gt; is the USB drive. Make sure the USB is unmounted before writing to it:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo umount /dev/sdb1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Then write the ISO image:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dd &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ubuntu-24.04-desktop-amd64.iso &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/sdb &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4M &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;progress &lt;span class="nv"&gt;oflag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sync&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Notice that the output target is the whole disk (&lt;code&gt;/dev/sdb&lt;/code&gt;), not a partition (&lt;code&gt;/dev/sdb1&lt;/code&gt;). Writing an ISO to a partition would leave the USB unbootable. The options do the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;bs=4M&lt;/code&gt; - Read and write 4 MB at a time, which is much faster than the 512-byte default.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;status=progress&lt;/code&gt; - Print a live progress indicator so you can see how much has been written.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;oflag=sync&lt;/code&gt; - Flush every write to the device, so the final byte count reflects data actually on disk.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When &lt;code&gt;dd&lt;/code&gt; finishes, run &lt;code&gt;sync&lt;/code&gt; once more to make sure all buffered writes hit the USB stick before you remove it:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sync&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For alternative methods and distro-specific tips, see the guide on how to &lt;a href="https://linuxize.com/post/how-to-create-a-bootable-linux-usb-drive/"&gt;create a bootable Linux USB drive&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="cloning-a-whole-disk"&gt;Cloning a Whole Disk &lt;a class="headline-link" href="#cloning-a-whole-disk" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To make an exact block-level copy of one disk onto another, point &lt;code&gt;if=&lt;/code&gt; and &lt;code&gt;of=&lt;/code&gt; at two different block devices:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dd &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/sda &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/sdb &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;64K &lt;span class="nv"&gt;conv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;noerror,sync &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;progress&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The destination disk must be at least as large as the source. Anything already on it is overwritten. The conversion flags handle read errors gracefully:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;conv=noerror&lt;/code&gt; - Keep going when a read error occurs instead of aborting.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;conv=sync&lt;/code&gt; - Pad short reads with zeros so the output stays aligned with the source.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Clone offline whenever possible. Cloning a disk that is actively being written to produces an inconsistent copy, especially for databases and journaling filesystems.&lt;/p&gt;
&lt;h2 id="backing-up-a-partition-to-an-image-file"&gt;Backing Up a Partition to an Image File &lt;a class="headline-link" href="#backing-up-a-partition-to-an-image-file" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;dd&lt;/code&gt; can also write a partition or whole disk to a regular file. That file becomes a bit-for-bit image you can restore later:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dd &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/sda1 &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/backup/sda1.img &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4M &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;progress&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To save space, pipe the output through &lt;code&gt;gzip&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dd &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/sda1 &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4M &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;progress &lt;span class="p"&gt;|&lt;/span&gt; gzip -c &amp;gt; /backup/sda1.img.gz&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The compressed image is much smaller for filesystems that contain text or empty space, though the compression step adds CPU time.&lt;/p&gt;
&lt;p&gt;To restore the image to a partition:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gunzip -c /backup/sda1.img.gz &lt;span class="p"&gt;|&lt;/span&gt; sudo dd &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/sda1 &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4M &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;progress&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The target partition must be unmounted during restore, and it must be at least as large as the original.&lt;/p&gt;
&lt;h2 id="backing-up-and-restoring-the-mbr"&gt;Backing Up and Restoring the MBR &lt;a class="headline-link" href="#backing-up-and-restoring-the-mbr" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The Master Boot Record sits in the first 512 bytes of a disk that uses a traditional BIOS partition table. You can back it up with &lt;code&gt;dd&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dd &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/sda &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/backup/mbr.img &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;512&lt;/span&gt; &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To restore the MBR, swap the input and output:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dd &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/backup/mbr.img &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/sda &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;512&lt;/span&gt; &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On modern UEFI systems the partition table uses GPT instead of MBR, so this trick only applies to older BIOS installations. For GPT disks, use &lt;code&gt;sgdisk --backup&lt;/code&gt; and &lt;code&gt;sgdisk --load-backup&lt;/code&gt; from the &lt;code&gt;gdisk&lt;/code&gt; package.&lt;/p&gt;
&lt;h2 id="creating-an-empty-file-of-a-specific-size"&gt;Creating an Empty File of a Specific Size &lt;a class="headline-link" href="#creating-an-empty-file-of-a-specific-size" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;dd&lt;/code&gt; is often used to create test files of a known size. The &lt;code&gt;count&lt;/code&gt; option sets how many blocks to write, and &lt;code&gt;bs&lt;/code&gt; sets the block size:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dd &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/zero &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;testfile &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1M &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This creates a 100 MB file filled with zeros, useful for testing backups, simulating quota limits, or preparing a swap file. To create a file filled with random data instead, read from &lt;code&gt;/dev/urandom&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dd &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/urandom &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;random.bin &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1M &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Random data is slower to generate because the kernel has to produce the bytes, while &lt;code&gt;/dev/zero&lt;/code&gt; is effectively free.&lt;/p&gt;
&lt;h2 id="creating-a-swap-file"&gt;Creating a Swap File &lt;a class="headline-link" href="#creating-a-swap-file" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One practical use of the empty-file pattern is creating a &lt;a href="https://linuxize.com/post/how-to-add-swap-space-on-ubuntu-20-04/"&gt;swap file&lt;/a&gt;
:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dd &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/zero &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/swapfile &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1M &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;2048&lt;/span&gt; &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;progress
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo chmod &lt;span class="m"&gt;600&lt;/span&gt; /swapfile
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo mkswap /swapfile
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo swapon /swapfile&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;chmod 600&lt;/code&gt; step keeps the swap file readable only by root, which is required by &lt;code&gt;mkswap&lt;/code&gt; on most distros. After &lt;code&gt;swapon&lt;/code&gt;, the 2 GB swap file is active immediately. Add an entry to &lt;code&gt;/etc/fstab&lt;/code&gt; to make it permanent.&lt;/p&gt;
&lt;h2 id="wiping-a-disk"&gt;Wiping a Disk &lt;a class="headline-link" href="#wiping-a-disk" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To destroy data on a disk before disposing of it or repurposing it, overwrite the entire device with zeros:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dd &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/zero &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/sdb &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4M &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;progress&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For a stronger wipe that makes recovery harder on traditional spinning disks, use random data:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dd &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/urandom &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/sdb &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4M &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;progress&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On SSDs, a &lt;code&gt;dd&lt;/code&gt; wipe is not the right tool. SSDs remap blocks internally, so a full write does not guarantee every cell was overwritten. Use &lt;code&gt;blkdiscard&lt;/code&gt; or the drive vendor&amp;rsquo;s secure-erase utility instead.&lt;/p&gt;
&lt;h2 id="benchmarking-disk-write-speed"&gt;Benchmarking Disk Write Speed &lt;a class="headline-link" href="#benchmarking-disk-write-speed" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;dd&lt;/code&gt; is a quick way to measure sustained write throughput. Write a 1 GB file of zeros and let &lt;code&gt;dd&lt;/code&gt; report the rate:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dd &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/zero &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;tempfile &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1M &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1024&lt;/span&gt; &lt;span class="nv"&gt;oflag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;direct&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB, 1.0 GiB) copied, 3.1 s, 346 MB/s&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;oflag=direct&lt;/code&gt; flag bypasses the page cache, so the number reflects actual disk speed rather than memory throughput. For a read benchmark, drop the caches first and then read the file back:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo sh -c &lt;span class="s2"&gt;&amp;#34;sync &amp;amp;&amp;amp; echo 3 &amp;gt; /proc/sys/vm/drop_caches&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dd &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;tempfile &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/null &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1M&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Remove the test file when you are done:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rm tempfile&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For more detailed I/O profiling, tools like &lt;code&gt;fio&lt;/code&gt; and &lt;code&gt;ioping&lt;/code&gt; produce more accurate, repeatable results.&lt;/p&gt;
&lt;h2 id="showing-progress-on-a-running-dd"&gt;Showing Progress on a Running dd &lt;a class="headline-link" href="#showing-progress-on-a-running-dd" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you started &lt;code&gt;dd&lt;/code&gt; without &lt;code&gt;status=progress&lt;/code&gt;, you can still ask it to print a status line by sending the &lt;code&gt;USR1&lt;/code&gt; signal to its process. From another terminal, run:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo &lt;span class="nb"&gt;kill&lt;/span&gt; -USR1 &lt;span class="k"&gt;$(&lt;/span&gt;pidof dd&lt;span class="k"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;dd&lt;/code&gt; will respond by printing the number of bytes copied so far and its current rate, then keep running.&lt;/p&gt;
&lt;h2 id="common-options"&gt;Common Options &lt;a class="headline-link" href="#common-options" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A quick rundown of the options covered in this guide, plus a few more worth knowing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;if=FILE&lt;/code&gt; - Input file or device.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;of=FILE&lt;/code&gt; - Output file or device.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bs=N&lt;/code&gt; - Block size for both reads and writes (e.g., &lt;code&gt;4M&lt;/code&gt; for 4 MB).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ibs=N&lt;/code&gt; / &lt;code&gt;obs=N&lt;/code&gt; - Separate input and output block sizes.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;count=N&lt;/code&gt; - Number of input blocks to copy.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;skip=N&lt;/code&gt; - Skip N blocks at the start of the input.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;seek=N&lt;/code&gt; - Skip N blocks at the start of the output.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;status=progress&lt;/code&gt; - Print progress while copying.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;conv=noerror&lt;/code&gt; - Continue past read errors.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;conv=sync&lt;/code&gt; - Pad short reads with zeros to keep block alignment.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;oflag=direct&lt;/code&gt; - Bypass the kernel page cache on write.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;oflag=sync&lt;/code&gt; - Flush each write to the device before continuing.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo dd if=image.iso of=/dev/sdX bs=4M status=progress oflag=sync&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Write an ISO image to a USB drive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo dd if=/dev/sda of=/dev/sdb bs=64K conv=noerror,sync status=progress&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Clone one disk to another&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo dd if=/dev/sda1 of=/backup/sda1.img bs=4M status=progress&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Back up a partition to an image file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo dd if=/dev/sda bs=4M status=progress | gzip -c &amp;gt; disk.img.gz&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Compressed disk image backup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo dd if=/dev/sda of=/backup/mbr.img bs=512 count=1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Back up the MBR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dd if=/dev/zero of=testfile bs=1M count=100&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a 100 MB file of zeros&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo dd if=/dev/zero of=/dev/sdX bs=4M status=progress&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Wipe a disk with zeros&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dd if=/dev/zero of=tempfile bs=1M count=1024 oflag=direct&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Benchmark disk write speed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo kill -USR1 $(pidof dd)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ask a running dd for its progress&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;dd: failed to open &amp;lsquo;/dev/sdX&amp;rsquo;: Permission denied&lt;/strong&gt;&lt;br&gt;
Writing to a block device requires root privileges. Prefix the command with &lt;code&gt;sudo&lt;/code&gt;. If you are already using &lt;code&gt;sudo&lt;/code&gt;, double-check that the device path is correct and that the device is not exclusively held by another process.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;dd: error writing &amp;lsquo;/dev/sdX&amp;rsquo;: No space left on device&lt;/strong&gt;&lt;br&gt;
The destination is smaller than the source. When cloning, the target disk or partition must be at least as large as the source. When writing an ISO, the USB drive must be larger than the ISO file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The write is much slower than expected&lt;/strong&gt;&lt;br&gt;
The default block size of 512 bytes forces millions of tiny syscalls. Set &lt;code&gt;bs=4M&lt;/code&gt; or &lt;code&gt;bs=64K&lt;/code&gt; to speed things up. Also check whether other processes are doing heavy I/O on the same device.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The USB does not boot after writing the ISO&lt;/strong&gt;&lt;br&gt;
Verify that you wrote the image to the disk (&lt;code&gt;/dev/sdb&lt;/code&gt;), not a partition (&lt;code&gt;/dev/sdb1&lt;/code&gt;). Also confirm the ISO download with its SHA256 checksum; a truncated or corrupted download produces an unbootable stick.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;dd: invalid number: &amp;lsquo;4M&amp;rsquo;&lt;/strong&gt;&lt;br&gt;
Older or minimal systems may ship a &lt;code&gt;dd&lt;/code&gt; that does not accept the &lt;code&gt;M&lt;/code&gt;, &lt;code&gt;G&lt;/code&gt;, or &lt;code&gt;K&lt;/code&gt; suffixes. On those systems, spell out the block size in bytes (&lt;code&gt;bs=4194304&lt;/code&gt; for 4 MB), or install GNU coreutils.&lt;/p&gt;
&lt;h2 id="faq"&gt;FAQ &lt;a class="headline-link" href="#faq" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;What does dd stand for?&lt;/strong&gt;&lt;br&gt;
The name comes from the IBM Job Control Language statement &amp;ldquo;Data Definition.&amp;rdquo; Because of how often the command is used to overwrite disks by mistake, many Linux users jokingly call it &amp;ldquo;disk destroyer.&amp;rdquo; Either way, the behavior is the same: it copies bytes from input to output without touching the filesystem layer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Is dd faster than cp for copying large files?&lt;/strong&gt;&lt;br&gt;
For regular files on a normal filesystem, &lt;code&gt;cp&lt;/code&gt; is usually just as fast and safer to use. &lt;code&gt;dd&lt;/code&gt; only wins when you are copying raw devices, working with exact byte offsets, or deliberately limiting how much data is copied with &lt;code&gt;count&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Can I use dd on an SSD without damaging it?&lt;/strong&gt;&lt;br&gt;
Reading and writing with &lt;code&gt;dd&lt;/code&gt; is fine. The concern with SSDs is full-disk wipes: a single pass of zeros is enough to erase visible data, but repeated full-drive writes wear out the flash cells. For secure erase, use &lt;code&gt;blkdiscard&lt;/code&gt; or the drive&amp;rsquo;s built-in secure-erase command.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How do I see how much data dd has copied?&lt;/strong&gt;&lt;br&gt;
Add &lt;code&gt;status=progress&lt;/code&gt; to the command, or send the running process the &lt;code&gt;USR1&lt;/code&gt; signal with &lt;code&gt;sudo kill -USR1 $(pidof dd)&lt;/code&gt;. Both methods print the byte count and current throughput without interrupting the copy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why use bs=4M instead of the default?&lt;/strong&gt;&lt;br&gt;
The default block size of 512 bytes means &lt;code&gt;dd&lt;/code&gt; issues a separate read and write for every 512 bytes of data. Larger block sizes such as 4 MB dramatically reduce syscall overhead and let the kernel fill the disk pipeline efficiently, often cutting copy times by a factor of ten.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;dd&lt;/code&gt; is one of the most direct tools in the Linux toolbox. It copies bytes, no more and no less, between any two files or devices. That power makes it indispensable for flashing installers, cloning disks, and building image backups, and the same power makes it unforgiving if you point it at the wrong target.&lt;/p&gt;
&lt;p&gt;For related disk tools, see the guides on &lt;a href="https://linuxize.com/post/fdisk-command-in-linux/"&gt;&lt;code&gt;fdisk&lt;/code&gt;&lt;/a&gt;
for partitioning and &lt;a href="https://linuxize.com/post/how-to-check-disk-space-in-linux-using-the-df-command/"&gt;&lt;code&gt;df&lt;/code&gt;&lt;/a&gt;
for checking free space.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/dd-command-in-linux/featured_hu_ad057e3aa5d007b9.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>How to Install and Use uv: Fast Python Package Manager</title><link>https://linuxize.com/post/how-to-install-and-use-uv/</link><pubDate>Sat, 09 May 2026 10:35:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/how-to-install-and-use-uv/</guid><category>python</category><category>linux commands</category><description>uv is a fast Python package and project manager written in Rust. This guide covers installation on Linux, creating projects, adding dependencies, managing virtual environments, and installing Python versions with uv.</description><content:encoded>&lt;p&gt;If you have worked with Python for a while, you have probably juggled &lt;code&gt;pip&lt;/code&gt;, &lt;code&gt;virtualenv&lt;/code&gt;, &lt;code&gt;pip-tools&lt;/code&gt;, &lt;code&gt;pipx&lt;/code&gt;, and &lt;code&gt;pyenv&lt;/code&gt; just to keep a few projects running. Each tool does one thing, and wiring them together is slow and fragile. &lt;code&gt;uv&lt;/code&gt; is a single binary that replaces all of them. It is written in Rust, published by Astral (the team behind &lt;code&gt;ruff&lt;/code&gt;), and is typically ten to a hundred times faster than &lt;code&gt;pip&lt;/code&gt; for common workflows.&lt;/p&gt;
&lt;p&gt;This guide explains how to install &lt;code&gt;uv&lt;/code&gt; on Linux, create and manage Python projects with it, add dependencies, work with virtual environments, and install specific Python versions.&lt;/p&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uv init my-project&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a new project with &lt;code&gt;pyproject.toml&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uv add requests&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add a dependency and update the lockfile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uv add --dev pytest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add a development dependency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uv remove requests&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove a dependency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uv sync&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install dependencies from the lockfile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uv lock&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Update the lockfile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uv run script.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run a command in the project environment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uv venv&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a virtual environment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uv pip install requests&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pip-compatible install interface&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uv python install 3.12&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install a specific Python version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uv python list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List installed and available Python versions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uv tool install ruff&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install a CLI tool globally&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uvx ruff check .&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run a tool without installing it permanently&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="installing-uv"&gt;Installing uv &lt;a class="headline-link" href="#installing-uv" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The recommended install path on Linux is the standalone script from Astral. It downloads a prebuilt binary, places it in &lt;code&gt;~/.local/bin&lt;/code&gt;, and does not require Python to be installed first:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -LsSf https://astral.sh/uv/install.sh &lt;span class="p"&gt;|&lt;/span&gt; sh&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The script prints where it put the binary and whether it updated your shell rc file. Reload your shell or run &lt;code&gt;source ~/.bashrc&lt;/code&gt; so the new &lt;code&gt;PATH&lt;/code&gt; takes effect, then verify the install:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv --version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;uv 0.11.11&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you prefer to install from a package manager, &lt;code&gt;uv&lt;/code&gt; is also available through &lt;code&gt;pip&lt;/code&gt;, &lt;code&gt;pipx&lt;/code&gt;, and Homebrew:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pip install uv&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pipx install uv&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;brew install uv&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On Ubuntu, Debian, and Derivatives, &lt;code&gt;pip&lt;/code&gt; may refuse a system-wide install because the Python environment is marked as externally managed. In that case, use &lt;code&gt;pipx install uv&lt;/code&gt; or the standalone installer.&lt;/p&gt;
&lt;p&gt;If you installed &lt;code&gt;uv&lt;/code&gt; with the standalone installer, update it to the latest release with:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv self update&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="creating-a-project"&gt;Creating a Project &lt;a class="headline-link" href="#creating-a-project" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;uv init&lt;/code&gt; scaffolds a new project with a &lt;code&gt;pyproject.toml&lt;/code&gt;, a &lt;code&gt;README.md&lt;/code&gt;, a &lt;code&gt;.python-version&lt;/code&gt; file, and a starter module:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv init my-project
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; my-project&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Initialized project `my-project`&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The generated &lt;code&gt;pyproject.toml&lt;/code&gt; looks like this:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="toml"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;pyproject.toml&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-orange-100 text-orange-700 dark:bg-orange-900 dark:text-orange-300"&gt;toml&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;my-project&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;0.1.0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Add your description here&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;readme&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;README.md&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;requires-python&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;gt;=3.12&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;uv&lt;/code&gt; has not created a virtual environment yet. That happens the first time you add a dependency or run a command in the project.&lt;/p&gt;
&lt;h2 id="adding-and-removing-dependencies"&gt;Adding and Removing Dependencies &lt;a class="headline-link" href="#adding-and-removing-dependencies" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To add a package, use &lt;code&gt;uv add&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv add requests&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Resolved 5 packages in 320ms
Installed 5 packages in 45ms
+ certifi==2025.1.31
+ charset-normalizer==3.4.1
+ idna==3.10
+ requests==2.32.3
+ urllib3==2.3.0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;On the first run, &lt;code&gt;uv&lt;/code&gt; creates a &lt;code&gt;.venv&lt;/code&gt; directory in the project root, resolves the dependency graph, writes a &lt;code&gt;uv.lock&lt;/code&gt; file that pins every package and its transitive dependencies, and installs everything into the venv. The &lt;code&gt;requests&lt;/code&gt; line is also added to &lt;code&gt;pyproject.toml&lt;/code&gt; under &lt;code&gt;dependencies&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Development-only dependencies go into a separate group with &lt;code&gt;--dev&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv add --dev pytest ruff&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;They are recorded under a &lt;code&gt;dev&lt;/code&gt; dependency group in &lt;code&gt;pyproject.toml&lt;/code&gt; and are installed into the same venv, but they are excluded when your project is published or installed as a library.&lt;/p&gt;
&lt;p&gt;Remove a dependency with &lt;code&gt;uv remove&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv remove requests&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;uv&lt;/code&gt; updates &lt;code&gt;pyproject.toml&lt;/code&gt;, updates &lt;code&gt;uv.lock&lt;/code&gt;, and uninstalls the package along with any dependencies that are no longer needed.&lt;/p&gt;
&lt;h2 id="running-code-in-the-project-environment"&gt;Running Code in the Project Environment &lt;a class="headline-link" href="#running-code-in-the-project-environment" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You do not have to activate the virtual environment manually. &lt;code&gt;uv run&lt;/code&gt; executes a command inside the project venv and syncs dependencies first if needed:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv run python main.py&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv run pytest&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Scripts declared in &lt;code&gt;pyproject.toml&lt;/code&gt; under &lt;code&gt;[project.scripts]&lt;/code&gt; are also available:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv run my-command&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If you prefer the classic workflow, activate the venv yourself:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python main.py&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Either approach works, but &lt;code&gt;uv run&lt;/code&gt; has the advantage of keeping the environment in sync with &lt;code&gt;uv.lock&lt;/code&gt; every time you invoke it.&lt;/p&gt;
&lt;h2 id="syncing-and-reproducing-an-environment"&gt;Syncing and Reproducing an Environment &lt;a class="headline-link" href="#syncing-and-reproducing-an-environment" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When you clone a project that uses &lt;code&gt;uv&lt;/code&gt;, run &lt;code&gt;uv sync&lt;/code&gt; to install exactly the versions pinned in &lt;code&gt;uv.lock&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv sync&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Resolved 12 packages in 5ms
Installed 12 packages in 110ms&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;uv sync&lt;/code&gt; is deterministic: given the same &lt;code&gt;uv.lock&lt;/code&gt;, it produces the same environment on any machine. This is the command to run in CI, in Docker builds, and on every fresh checkout.&lt;/p&gt;
&lt;p&gt;If &lt;code&gt;pyproject.toml&lt;/code&gt; changes but the lockfile is out of date, refresh the lock with:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv lock&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Use &lt;code&gt;uv lock --upgrade&lt;/code&gt; to bump every dependency to the latest version allowed by the version constraints in &lt;code&gt;pyproject.toml&lt;/code&gt;, and &lt;code&gt;uv lock --upgrade-package requests&lt;/code&gt; to bump a single package.&lt;/p&gt;
&lt;h2 id="working-with-virtual-environments-directly"&gt;Working with Virtual Environments Directly &lt;a class="headline-link" href="#working-with-virtual-environments-directly" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;uv&lt;/code&gt; can also be used as a faster drop-in for &lt;code&gt;virtualenv&lt;/code&gt;. To create a standalone venv in the current directory:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv venv&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Using Python 3.12.8
Creating virtual environment at: .venv
Activate with: source .venv/bin/activate&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Specify a Python version with &lt;code&gt;--python&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv venv --python 3.11&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Specify a different path:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv venv /tmp/myenv&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Once the venv exists, use &lt;code&gt;uv pip&lt;/code&gt; as a fast replacement for the regular &lt;code&gt;pip&lt;/code&gt; commands:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv pip install requests
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv pip install -r requirements.txt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv pip freeze
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv pip list&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;uv pip&lt;/code&gt; interface is intentionally compatible with &lt;code&gt;pip&lt;/code&gt;, which makes it easy to adopt &lt;code&gt;uv&lt;/code&gt; in existing projects without restructuring them as &lt;code&gt;pyproject.toml&lt;/code&gt;-based projects.&lt;/p&gt;
&lt;p&gt;For a refresher on classic virtual environments, see the guide on &lt;a href="https://linuxize.com/post/python-virtual-environments/"&gt;Python virtual environments&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="installing-and-managing-python-versions"&gt;Installing and Managing Python Versions &lt;a class="headline-link" href="#installing-and-managing-python-versions" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;uv&lt;/code&gt; can download and manage Python interpreters without touching your system Python. List the versions available and installed:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv python list&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Install a specific version:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv python install 3.12&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv python install 3.11 3.13&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The downloaded interpreters live under &lt;code&gt;~/.local/share/uv/python/&lt;/code&gt; and are independent of the system package manager. To use a specific version in a project, set it in the &lt;code&gt;.python-version&lt;/code&gt; file or pass &lt;code&gt;--python&lt;/code&gt; when creating the venv:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv venv --python 3.13&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;When &lt;code&gt;uv run&lt;/code&gt; starts a project, it reads &lt;code&gt;.python-version&lt;/code&gt; and the &lt;code&gt;requires-python&lt;/code&gt; constraint in &lt;code&gt;pyproject.toml&lt;/code&gt;, then picks or downloads a matching interpreter automatically.&lt;/p&gt;
&lt;h2 id="installing-cli-tools"&gt;Installing CLI Tools &lt;a class="headline-link" href="#installing-cli-tools" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;uv&lt;/code&gt; ships with a tool manager similar to &lt;code&gt;pipx&lt;/code&gt;. It installs Python-based CLIs in isolated environments so they do not pollute your system Python:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv tool install ruff&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Resolved 1 package in 80ms
Installed 1 package in 12ms
+ ruff==0.9.2
Installed 1 executable: ruff&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;ruff&lt;/code&gt; binary is now on your &lt;code&gt;PATH&lt;/code&gt;. List, upgrade, or remove tools:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv tool list
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv tool upgrade ruff
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv tool uninstall ruff&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To run a tool without installing it permanently, use &lt;code&gt;uvx&lt;/code&gt; (an alias for &lt;code&gt;uv tool run&lt;/code&gt;):&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uvx ruff check .&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;uvx&lt;/code&gt; downloads the tool into a cached environment, runs it, and reuses the cache on subsequent invocations. This is the fastest way to try a CLI without making it permanent.&lt;/p&gt;
&lt;h2 id="running-single-file-scripts"&gt;Running Single-File Scripts &lt;a class="headline-link" href="#running-single-file-scripts" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A Python script can declare its dependencies inline with a PEP 723 comment block. &lt;code&gt;uv run&lt;/code&gt; reads that block and sets up an ephemeral environment before executing the script:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="py"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;script.py&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-blue-100 text-blue-700 dark:bg-blue-900 dark:text-blue-300"&gt;py&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-py" data-lang="py"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# /// script&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# requires-python = &amp;#34;&amp;gt;=3.12&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# dependencies = [&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# &amp;#34;requests&amp;#34;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ///&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;requests&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;https://api.github.com&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Run the script with:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uv run script.py&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;uv&lt;/code&gt; creates a temporary venv, installs &lt;code&gt;requests&lt;/code&gt;, runs the script, and keeps the environment in a cache for next time. This pattern is useful for small utilities and one-off automation where a full project is overkill.&lt;/p&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;uv: command not found after install&lt;/strong&gt;&lt;br&gt;
The installer places the binary in &lt;code&gt;~/.local/bin&lt;/code&gt;, which may not be on your &lt;code&gt;PATH&lt;/code&gt;. Add it in your shell rc file: &lt;code&gt;export PATH=&amp;quot;$HOME/.local/bin:$PATH&amp;quot;&lt;/code&gt;, then reload the shell. Verify with &lt;code&gt;which uv&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Installation fails with &amp;ldquo;error: externally-managed-environment&amp;rdquo;&lt;/strong&gt;&lt;br&gt;
This message comes from system &lt;code&gt;pip&lt;/code&gt;, not from &lt;code&gt;uv&lt;/code&gt;. Use &lt;code&gt;pipx install uv&lt;/code&gt; or the standalone installer instead. Installing Python packages into the system Python is discouraged on modern Ubuntu and Debian releases.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;uv sync installs different versions than expected&lt;/strong&gt;&lt;br&gt;
Check that &lt;code&gt;uv.lock&lt;/code&gt; is committed to your repository. Without the lockfile, &lt;code&gt;uv sync&lt;/code&gt; falls back to resolving from &lt;code&gt;pyproject.toml&lt;/code&gt; and may pick newer versions. Commit &lt;code&gt;uv.lock&lt;/code&gt; to keep environments reproducible across machines.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;uv cannot find a suitable Python interpreter&lt;/strong&gt;&lt;br&gt;
Run &lt;code&gt;uv python install 3.12&lt;/code&gt; (or whichever version your project requires) to let &lt;code&gt;uv&lt;/code&gt; download a matching interpreter. The &lt;code&gt;requires-python&lt;/code&gt; field in &lt;code&gt;pyproject.toml&lt;/code&gt; tells &lt;code&gt;uv&lt;/code&gt; which versions are acceptable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Permission denied when installing to the system&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;uv&lt;/code&gt; does not need root. If a command prompts for &lt;code&gt;sudo&lt;/code&gt;, the destination path is wrong. Use a project virtual environment or &lt;code&gt;uv tool install&lt;/code&gt; for CLIs instead of writing to system directories.&lt;/p&gt;
&lt;h2 id="faq"&gt;FAQ &lt;a class="headline-link" href="#faq" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;How is uv different from pip?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;pip&lt;/code&gt; installs packages into an existing Python environment. &lt;code&gt;uv&lt;/code&gt; is a full project manager: it creates and manages virtual environments, locks dependencies, installs Python interpreters, and runs scripts and tools. &lt;code&gt;uv pip&lt;/code&gt; also provides a drop-in pip-compatible command for teams that want just the speed without adopting the full workflow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Does uv replace Poetry or Hatch?&lt;/strong&gt;&lt;br&gt;
For most projects, yes. &lt;code&gt;uv&lt;/code&gt; reads the standard &lt;code&gt;pyproject.toml&lt;/code&gt; format used by Poetry and Hatch, manages lockfiles, and handles dependency groups. If you depend on specific features of Poetry plugins or Hatch build hooks, evaluate those needs before migrating.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Is uv compatible with existing requirements.txt workflows?&lt;/strong&gt;&lt;br&gt;
Yes. &lt;code&gt;uv pip install -r requirements.txt&lt;/code&gt; works as a faster replacement for &lt;code&gt;pip install -r&lt;/code&gt;, and &lt;code&gt;uv pip compile&lt;/code&gt; generates a pinned requirements file from your unpinned input, similar to &lt;code&gt;pip-compile&lt;/code&gt; from &lt;code&gt;pip-tools&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Can I use uv with Docker?&lt;/strong&gt;&lt;br&gt;
Yes, and it is a good fit. The recommended pattern is to copy &lt;code&gt;pyproject.toml&lt;/code&gt; and &lt;code&gt;uv.lock&lt;/code&gt; first, run &lt;code&gt;uv sync --frozen --no-install-project&lt;/code&gt; to install dependencies, then copy the rest of the source and run &lt;code&gt;uv sync --frozen&lt;/code&gt;. This keeps the dependency layer cached across builds.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Where does uv store downloaded interpreters and package cache?&lt;/strong&gt;&lt;br&gt;
Interpreters go under &lt;code&gt;~/.local/share/uv/python/&lt;/code&gt; and the package cache lives in &lt;code&gt;~/.cache/uv/&lt;/code&gt;. Both paths respect the XDG environment variables, so you can override them by setting &lt;code&gt;XDG_DATA_HOME&lt;/code&gt; and &lt;code&gt;XDG_CACHE_HOME&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;uv&lt;/code&gt; rolls together the jobs that previously required &lt;code&gt;pip&lt;/code&gt;, &lt;code&gt;virtualenv&lt;/code&gt;, &lt;code&gt;pip-tools&lt;/code&gt;, &lt;code&gt;pipx&lt;/code&gt;, and &lt;code&gt;pyenv&lt;/code&gt; into one fast binary. The speed is the headline feature, but the real payoff is a single, consistent workflow for projects, tools, scripts, and Python versions.&lt;/p&gt;
&lt;p&gt;For related Python tooling on Linux, see the guide on &lt;a href="https://linuxize.com/post/how-to-install-python-on-ubuntu-24-04/"&gt;installing Python on Ubuntu 24.04&lt;/a&gt;
and the guide on &lt;a href="https://linuxize.com/post/python-virtual-environments/"&gt;Python virtual environments&lt;/a&gt;
.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/how-to-install-and-use-uv/featured_hu_d8faa6ef84c6292f.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>How to Install Python on Ubuntu 26.04</title><link>https://linuxize.com/post/how-to-install-python-on-ubuntu-26-04/</link><pubDate>Thu, 07 May 2026 20:15:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/how-to-install-python-on-ubuntu-26-04/</guid><category>python</category><category>ubuntu</category><description>Install Python on Ubuntu 26.04 using the default Ubuntu packages, the deadsnakes PPA for alternate versions, or a source build. Includes pip and virtual environments.</description><content:encoded>&lt;p&gt;When you start a Python project on Ubuntu 26.04, you already have a Python interpreter available. The usual setup work is installing &lt;code&gt;pip&lt;/code&gt;, adding the &lt;code&gt;venv&lt;/code&gt; module, or installing a separate Python version for a project that needs something other than Ubuntu&amp;rsquo;s default Python.&lt;/p&gt;
&lt;p&gt;This guide explains how to install Python tooling on Ubuntu 26.04, how to use the deadsnakes PPA for alternate Python versions, and how to build Python from source when you need a specific upstream release.&lt;/p&gt;
&lt;h2 id="ubuntu-2604-default-python-version"&gt;Ubuntu 26.04 Default Python Version &lt;a class="headline-link" href="#ubuntu-2604-default-python-version" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Ubuntu 26.04 ships with Python 3.14 as the default Python 3 interpreter. To check the version on your system, run:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python3 --version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Python 3.14.1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The exact patch version may change as Ubuntu publishes updates, but the default interpreter remains Python 3.14. Most users should keep this interpreter in place and use virtual environments for project packages.&lt;/p&gt;
&lt;p&gt;Do not replace the &lt;code&gt;/usr/bin/python3&lt;/code&gt; interpreter. Ubuntu tools expect the distribution-provided Python version. If a project needs a different Python release, install it alongside the default interpreter and call it by its versioned command, such as &lt;code&gt;python3.13&lt;/code&gt; or &lt;code&gt;python3.15&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Check Ubuntu&amp;rsquo;s default Python version&lt;/td&gt;
&lt;td&gt;&lt;code&gt;python3 --version&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Install pip and venv for the default Python&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt install python3-pip python3-venv&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Create a virtual environment with the default Python&lt;/td&gt;
&lt;td&gt;&lt;code&gt;python3 -m venv myproject&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Add deadsnakes PPA&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo add-apt-repository ppa:deadsnakes/ppa&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Install Python 3.13 from PPA&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt install python3.13&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Install Python 3.15 preview from PPA&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt install python3.15&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Install venv module for PPA Python&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt install python3.13-venv&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Create a virtual environment with PPA Python&lt;/td&gt;
&lt;td&gt;&lt;code&gt;python3.13 -m venv myproject&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Activate virtual environment&lt;/td&gt;
&lt;td&gt;&lt;code&gt;source myproject/bin/activate&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deactivate virtual environment&lt;/td&gt;
&lt;td&gt;&lt;code&gt;deactivate&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build from source&lt;/td&gt;
&lt;td&gt;&lt;code&gt;./configure --enable-optimizations &amp;amp;&amp;amp; make -j $(nproc)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Install source build safely&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo make altinstall&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="installing-pip-and-venv-for-the-default-python"&gt;Installing pip and venv for the Default Python &lt;a class="headline-link" href="#installing-pip-and-venv-for-the-default-python" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If Ubuntu&amp;rsquo;s default Python 3.14 is enough for your work, install &lt;code&gt;pip&lt;/code&gt; and the virtual environment module from the Ubuntu repositories.&lt;/p&gt;
&lt;p&gt;First, update the package index:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt update&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Install pip and the virtual environment module:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install python3-pip python3-venv&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Verify the installed commands:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python3 --version
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pip3 --version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On Ubuntu 26.04, &lt;code&gt;python3&lt;/code&gt; points to Python 3.14. Use a virtual environment for project dependencies instead of installing packages into the system Python environment.&lt;/p&gt;
&lt;p&gt;Create a virtual environment with the default Python:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python3 -m venv myproject&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Activate it:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;source&lt;/span&gt; myproject/bin/activate&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Inside the environment, &lt;code&gt;python&lt;/code&gt; and &lt;code&gt;pip&lt;/code&gt; point to the isolated project environment:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python --version
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python -m pip --version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;When you are done working in the project, deactivate the environment:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;deactivate&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="installing-python-from-the-deadsnakes-ppa"&gt;Installing Python from the Deadsnakes PPA &lt;a class="headline-link" href="#installing-python-from-the-deadsnakes-ppa" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://launchpad.net/~deadsnakes/&amp;#43;archive/ubuntu/ppa" target="_blank" rel="noopener noreferrer"&gt;deadsnakes PPA&lt;/a&gt;
provides alternate Python versions packaged for Ubuntu. It now supports Ubuntu 26.04, including packages for Python versions that Ubuntu does not provide as the default interpreter.&lt;/p&gt;
&lt;p&gt;Deadsnakes does not provide Python 3.14 for Ubuntu 26.04 because Ubuntu already includes Python 3.14. Use the PPA when you need another interpreter, such as Python 3.13 or the Python 3.15 preview builds.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install the prerequisites and add the PPA:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install software-properties-common
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo add-apt-repository ppa:deadsnakes/ppa&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Press &lt;code&gt;Enter&lt;/code&gt; when prompted to confirm.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install Python 3.13:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install python3.13&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To install the Python 3.15 preview package instead, replace &lt;code&gt;python3.13&lt;/code&gt; with &lt;code&gt;python3.15&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install python3.15&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Python 3.15 is still a preview release, so use it for testing rather than production workloads.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Verify the installation:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python3.13 --version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The command prints the installed Python 3.13 patch release. You can also confirm the binary location:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;which python3.13&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install the &lt;code&gt;venv&lt;/code&gt; module for the same interpreter:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install python3.13-venv&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If you installed Python 3.15, use:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install python3.15-venv&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a virtual environment and use &lt;code&gt;pip&lt;/code&gt; inside it:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python3.13 -m venv myproject
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;source&lt;/span&gt; myproject/bin/activate
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python -m pip install --upgrade pip&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="note callout callout-info"&gt;
&lt;div class="callout-header"&gt;&lt;svg role="img" aria-hidden="true" class="callout-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" &gt;
&lt;path d="M 16 3 C 8.832031 3 3 8.832031 3 16 C 3 23.167969 8.832031 29 16 29 C 23.167969 29 29 23.167969 29 16 C 29 8.832031 23.167969 3 16 3 Z M 16 5 C 22.085938 5 27 9.914063 27 16 C 27 22.085938 22.085938 27 16 27 C 9.914063 27 5 22.085938 5 16 C 5 9.914063 9.914063 5 16 5 Z M 15 10 L 15 12 L 17 12 L 17 10 Z M 15 14 L 15 22 L 17 22 L 17 14 Z "&gt;&lt;/path&gt;
&lt;/svg&gt;&lt;span class="callout-title"&gt;Info&lt;/span&gt;&lt;/div&gt;
&lt;div class="callout-body"&gt;The system default &lt;code&gt;python3&lt;/code&gt; still points to Python 3.14. To use a PPA version, run &lt;code&gt;python3.13&lt;/code&gt;, &lt;code&gt;python3.15&lt;/code&gt;, or another explicit version command.&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="installing-python-from-source"&gt;Installing Python from Source &lt;a class="headline-link" href="#installing-python-from-source" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Compiling Python from source allows you to install any version and customize the build options. However, you will not be able to manage the installation through the &lt;a href="https://linuxize.com/post/how-to-use-apt-command/"&gt;&lt;code&gt;apt&lt;/code&gt;&lt;/a&gt;
package manager.&lt;/p&gt;
&lt;p&gt;The following steps show how to compile Python 3.14.4. If you are installing a different version, replace the version number in the commands below.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install the libraries and dependencies required to build Python:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libreadline-dev libffi-dev libsqlite3-dev wget libbz2-dev liblzma-dev&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Download the source code from the &lt;a href="https://www.python.org/downloads/source/" target="_blank" rel="noopener noreferrer"&gt;Python download page&lt;/a&gt;
using &lt;a href="https://linuxize.com/post/wget-command-examples/"&gt;&lt;code&gt;wget&lt;/code&gt;&lt;/a&gt;
:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;wget https://www.python.org/ftp/python/3.14.4/Python-3.14.4.tgz&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://linuxize.com/post/how-to-extract-unzip-tar-gz-file/"&gt;Extract the archive&lt;/a&gt;
:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tar -xf Python-3.14.4.tgz&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Navigate to the source directory and run the &lt;code&gt;configure&lt;/code&gt; script:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; Python-3.14.4
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./configure --enable-optimizations&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;--enable-optimizations&lt;/code&gt; flag runs profile-guided optimization tests, which makes the build slower but produces a faster Python binary.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Start the build process:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;make -j &lt;span class="k"&gt;$(&lt;/span&gt;nproc&lt;span class="k"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;-j $(nproc)&lt;/code&gt; option uses all available CPU cores for a faster build.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install the Python binaries using &lt;code&gt;altinstall&lt;/code&gt;. Do not use &lt;code&gt;install&lt;/code&gt;, as it overwrites the system &lt;code&gt;python3&lt;/code&gt; binary and can break system tools that depend on it:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo make altinstall&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Verify the installation:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python3.14 --version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Python 3.14.4&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="setting-up-a-virtual-environment"&gt;Setting Up a Virtual Environment &lt;a class="headline-link" href="#setting-up-a-virtual-environment" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;After installing a new Python version, create a virtual environment for your project to keep dependencies isolated:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python3.13 -m venv myproject&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Activate the virtual environment:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;source&lt;/span&gt; myproject/bin/activate&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Your shell prompt will change to show the environment name. Inside the virtual environment, &lt;code&gt;python&lt;/code&gt; and &lt;code&gt;pip&lt;/code&gt; point to the version you used to create it.&lt;/p&gt;
&lt;p&gt;For a source-built Python 3.14 installation, use the versioned command:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python3.14 -m venv myproject&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To deactivate the virtual environment:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;deactivate&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For more details, see our guide on &lt;a href="https://linuxize.com/post/how-to-create-python-virtual-environments-on-ubuntu-18-04/"&gt;how to create Python virtual environments&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="optional-uninstalling-the-ppa-version"&gt;Optional: Uninstalling the PPA Version &lt;a class="headline-link" href="#optional-uninstalling-the-ppa-version" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To remove the PPA version:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt remove python3.13 python3.13-venv&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To remove the PPA itself:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo add-apt-repository --remove ppa:deadsnakes/ppa&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;python3 already exists after installation&lt;/strong&gt;&lt;br&gt;
This is expected. Ubuntu 26.04 includes Python 3.14 by default, so the default setup usually adds missing tools such as &lt;code&gt;pip3&lt;/code&gt; or &lt;code&gt;venv&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;pip reports an externally managed environment&lt;/strong&gt;&lt;br&gt;
Ubuntu protects system-managed Python packages. Create a virtual environment with &lt;code&gt;python3 -m venv myproject&lt;/code&gt;, activate it, and install packages with &lt;code&gt;python -m pip install package-name&lt;/code&gt; inside the environment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No module named venv&lt;/strong&gt;&lt;br&gt;
Install the matching &lt;code&gt;venv&lt;/code&gt; package. For Ubuntu&amp;rsquo;s default Python, run &lt;code&gt;sudo apt install python3-venv&lt;/code&gt;. For Python 3.13 from the PPA, run &lt;code&gt;sudo apt install python3.13-venv&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Unable to locate package python3.13 or python3.15&lt;/strong&gt;&lt;br&gt;
Run &lt;code&gt;sudo apt update&lt;/code&gt; after adding the deadsnakes PPA. If the package is still unavailable, check that the PPA supports your Ubuntu release and CPU architecture.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;python3 still shows Python 3.14&lt;/strong&gt;&lt;br&gt;
The &lt;code&gt;python3&lt;/code&gt; command should continue to use Ubuntu&amp;rsquo;s default interpreter. Run the alternate version with its versioned command, such as &lt;code&gt;python3.13&lt;/code&gt; or &lt;code&gt;python3.15&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Python 3.15 shows an alpha version&lt;/strong&gt;&lt;br&gt;
Python 3.15 is still a preview release. Use it for testing compatibility, and use Python 3.14 or another stable release for production projects.&lt;/p&gt;
&lt;h2 id="faq"&gt;FAQ &lt;a class="headline-link" href="#faq" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Should I use the PPA or build from source?&lt;/strong&gt;&lt;br&gt;
Use the Ubuntu packages if Python 3.14 is enough. Use the deadsnakes PPA when you need an alternate packaged interpreter such as Python 3.13 or a Python 3.15 preview. Build from source only if you need a specific upstream patch release or a custom build configuration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Will installing a new Python version break my system?&lt;/strong&gt;&lt;br&gt;
No. Both methods install the new version alongside the system Python 3.14. The system &lt;code&gt;python3&lt;/code&gt; command is not affected. You access the alternate version with &lt;code&gt;python3.13&lt;/code&gt;, &lt;code&gt;python3.15&lt;/code&gt;, or another explicit version command.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How do I make the new Python version the default?&lt;/strong&gt;&lt;br&gt;
You can use &lt;code&gt;update-alternatives&lt;/code&gt; to configure it, but this is not recommended. Many Ubuntu system tools depend on the default &lt;code&gt;python3&lt;/code&gt; being the version that shipped with the OS. Use virtual environments instead.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How do I install pip for the new Python version?&lt;/strong&gt;&lt;br&gt;
The recommended approach is to use a virtual environment. Install the matching &lt;code&gt;venv&lt;/code&gt; package, create a venv, activate it, and use &lt;code&gt;python -m pip&lt;/code&gt; inside the environment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What is the difference between &lt;code&gt;install&lt;/code&gt; and &lt;code&gt;altinstall&lt;/code&gt; when building from source?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;altinstall&lt;/code&gt; installs a versioned binary such as &lt;code&gt;python3.14&lt;/code&gt; without creating a &lt;code&gt;python3&lt;/code&gt; symlink. &lt;code&gt;install&lt;/code&gt; creates the symlink, which overwrites the system Python and can break Ubuntu system tools.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Does Ubuntu 26.04 include pip by default?&lt;/strong&gt;&lt;br&gt;
Ubuntu 26.04 includes Python 3.14 but does not include pip in the base installation. Install it with &lt;code&gt;sudo apt install python3-pip&lt;/code&gt;. For alternate Python versions, use &lt;code&gt;python -m pip&lt;/code&gt; inside a virtual environment.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Stick with Ubuntu&amp;rsquo;s Python 3.14 plus &lt;code&gt;python3-pip&lt;/code&gt; and &lt;code&gt;python3-venv&lt;/code&gt; for most projects. Reach for the deadsnakes PPA when a project pins to 3.13 or wants to test the 3.15 preview, and build from source only for a specific upstream patch.&lt;/p&gt;
&lt;p&gt;For more on Python package management, see our guide on &lt;a href="https://linuxize.com/post/how-to-install-pip-on-ubuntu/"&gt;how to use pip&lt;/a&gt;
.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/how-to-install-python-on-ubuntu-26-04/featured_hu_c7cd5627d7b8ff05.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>fdisk Cheatsheet</title><link>https://linuxize.com/cheatsheet/fdisk/</link><pubDate>Thu, 07 May 2026 12:10:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/cheatsheet/fdisk/</guid><description>Quick reference for fdisk commands: list disks, create partition tables, add partitions, change partition types, write changes, and quit safely</description><content:encoded>&lt;div class="card"&gt;
&lt;h3 id="basic-usage"&gt;Basic Usage &lt;a class="headline-link" href="#basic-usage" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Open a disk or print partition tables.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/fdisk-command-in-linux/"&gt;&lt;code&gt;sudo fdisk /dev/sdX&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Open a disk in interactive mode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo fdisk -l&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List all detected partition tables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo fdisk -l /dev/sdX&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List one disk partition table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo fdisk --help&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show command-line options&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo fdisk --version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show fdisk version&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="list-disks"&gt;List Disks &lt;a class="headline-link" href="#list-disks" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Identify the correct device before changing partitions.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lsblk&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show disks, partitions, and mount points&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lsblk -d -o NAME,SIZE,MODEL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show whole disks only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lsblk -f&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show filesystems and UUIDs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo fdisk -l&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show partition tables with disk labels&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo fdisk -x /dev/sdX&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show detailed partition information&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="interactive-commands"&gt;Interactive Commands &lt;a class="headline-link" href="#interactive-commands" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Commands used inside the &lt;code&gt;fdisk&lt;/code&gt; prompt.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;m&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show help menu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;p&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print the current partition table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;n&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a new partition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete a partition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;t&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change partition type&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;l&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List available partition types&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;w&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Write changes and exit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;q&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Quit without saving&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="partition-tables"&gt;Partition Tables &lt;a class="headline-link" href="#partition-tables" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Create a new disk label before adding partitions on a blank disk.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;g&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a new GPT partition table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;o&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a new MBR (DOS) partition table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;p&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Review the current table before saving&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;w&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Write the new table to disk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;q&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exit without writing changes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="create-partitions"&gt;Create Partitions &lt;a class="headline-link" href="#create-partitions" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Common answers while creating a new partition with &lt;code&gt;n&lt;/code&gt;.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;n&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start a new partition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Enter&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Accept the default partition number&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Enter&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Accept the default first sector&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;+1G&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a 1 GiB partition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;+100G&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a 100 GiB partition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Enter&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use the rest of the available disk space&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;p&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print the proposed layout&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;w&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Save the changes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="partition-types"&gt;Partition Types &lt;a class="headline-link" href="#partition-types" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Set the partition type when the partition is not a regular Linux data partition.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;l&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List partition types&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;t&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change a partition type&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Linux filesystem&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Regular Linux data partition (GPT)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Linux swap&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Swap partition (GPT)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EFI System&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;EFI System partition (GPT)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Linux LVM&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;LVM physical volume (GPT)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Linux RAID&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Linux RAID member (GPT)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;83&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Regular Linux partition (MBR)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;82&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Swap partition (MBR)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;8e&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;LVM partition (MBR)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="review-and-save"&gt;Review and Save &lt;a class="headline-link" href="#review-and-save" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Check the in-memory table before writing it to disk.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;p&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print the pending partition table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;v&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Verify the partition table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;i&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show details about a partition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;w&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Write changes to disk and exit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;q&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Quit without saving changes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="format-and-mount"&gt;Format and Mount &lt;a class="headline-link" href="#format-and-mount" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;After writing the partition table, create a filesystem and mount the partition.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo mkfs.ext4 /dev/sdX1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Format a partition as ext4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo mkswap /dev/sdX2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create swap on a partition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo swapon /dev/sdX2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enable swap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo mkdir -p /mnt/data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a mount point&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo mount /dev/sdX1 /mnt/data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mount the partition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lsblk -f&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Confirm filesystem and mount details&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="safety-checks"&gt;Safety Checks &lt;a class="headline-link" href="#safety-checks" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Commands that help avoid editing the wrong disk.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lsblk -d -o NAME,SIZE,MODEL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Compare disk names, sizes, and models&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lsblk -f&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check existing filesystems and mount points&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo fdisk -l /dev/sdX&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Review the current table before editing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mount | grep /dev/sdX&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check whether partitions are mounted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo umount /dev/sdX1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unmount a partition before changing it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo partprobe /dev/sdX&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ask the kernel to re-read the table&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="related-tools"&gt;Related Tools &lt;a class="headline-link" href="#related-tools" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;References for the full workflow around disks and partitions.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-mount-and-unmount-file-systems-in-linux/"&gt;&lt;code&gt;mount&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Mount and unmount filesystems&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-format-usb-sd-card-linux/"&gt;&lt;code&gt;mkfs.ext4&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Format a partition with a filesystem&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-check-disk-space-in-linux-using-the-df-command/"&gt;&lt;code&gt;df&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Check filesystem disk usage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/fsck-command-in-linux/"&gt;&lt;code&gt;fsck&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Check and repair filesystems&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;</content:encoded></item><item><title>SFTP Cheatsheet</title><link>https://linuxize.com/cheatsheet/sftp/</link><pubDate>Thu, 07 May 2026 07:00:00 +0100</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/cheatsheet/sftp/</guid><description>Quick reference for the Linux sftp client, covering login, navigation, transfers, and remote file management over SSH</description><content:encoded>&lt;div class="card"&gt;
&lt;h3 id="connect-and-authenticate"&gt;Connect and Authenticate &lt;a class="headline-link" href="#connect-and-authenticate" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Open an SFTP session against a remote host.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-use-linux-sftp-command-to-transfer-files/"&gt;&lt;code&gt;sftp user@hostname&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Connect to remote server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sftp user@192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Connect by IP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sftp -P 2222 user@hostname&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Connect to a custom SSH port&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sftp -i ~/.ssh/id_ed25519 user@hostname&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Connect with a specific key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sftp -b commands.txt user@hostname&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run commands from a batch file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;quit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Quit session&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bye&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Quit session (alias for &lt;code&gt;quit&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="local-and-remote-paths"&gt;Local and Remote Paths &lt;a class="headline-link" href="#local-and-remote-paths" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Navigate directories on both sides of the session.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pwd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show remote working directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lpwd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show local working directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd /remote/path&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change remote directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lcd /local/path&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change local directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ls&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List remote files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lls&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List local files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ls -la&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Long listing of remote files&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="download-files"&gt;Download Files &lt;a class="headline-link" href="#download-files" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Pull files from the remote server to the local system.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get file.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Download one file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get remote.txt local.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Download and rename locally&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get -r remote_dir&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Download a directory recursively&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get -P file.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Preserve file permissions and timestamps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mget *.log&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Download multiple files matching a pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;reget large.iso&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Resume an interrupted download&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="upload-files"&gt;Upload Files &lt;a class="headline-link" href="#upload-files" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Push local files to the remote server.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;put file.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Upload one file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;put local.txt remote.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Upload with a remote name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;put -r local_dir&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Upload a directory recursively&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;put -P file.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Preserve permissions and timestamps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mput *.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Upload multiple files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;reput large.iso&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Resume an interrupted upload&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="remote-file-management"&gt;Remote File Management &lt;a class="headline-link" href="#remote-file-management" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Manage files and directories on the SFTP server.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mkdir dirname&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create remote directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rmdir dirname&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove empty remote directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rm file.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete remote file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rename old.txt new.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rename remote file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ln source link&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a hard link on the remote&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ln -s source link&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a symbolic link on the remote&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;df -h&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show remote filesystem usage&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="permissions-and-ownership"&gt;Permissions and Ownership &lt;a class="headline-link" href="#permissions-and-ownership" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Adjust permissions and ownership on remote files.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/chmod-command-in-linux/"&gt;&lt;code&gt;chmod 644 file.txt&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Change remote file mode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/chown-command-in-linux/"&gt;&lt;code&gt;chown 1000 file.txt&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Change remote owner by UID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;chgrp 1000 file.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change remote group by GID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;umask 022&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set default permission mask&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="session-helpers"&gt;Session Helpers &lt;a class="headline-link" href="#session-helpers" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Inspect and control the active session.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;!command&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run a local shell command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;!&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Drop into a local shell&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show SFTP protocol version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;progress&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Toggle transfer progress meter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;help&lt;/code&gt; or &lt;code&gt;?&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List available SFTP commands&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="non-interactive-and-scripting"&gt;Non-Interactive and Scripting &lt;a class="headline-link" href="#non-interactive-and-scripting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Use SFTP from scripts and automated jobs.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sftp -b script.txt user@host&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run a batch file of commands&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sftp -q user@host&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Quiet mode (suppress banner and progress)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sftp -o IdentityFile=key user@host&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pass any &lt;code&gt;ssh_config&lt;/code&gt; option&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;echo &amp;quot;get file.txt&amp;quot; | sftp -b - user@host&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pipe commands via stdin&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="related-tools"&gt;Related Tools &lt;a class="headline-link" href="#related-tools" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Other ways to move files between systems.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-use-scp-command-to-securely-transfer-files/"&gt;&lt;code&gt;scp&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Secure copy over SSH&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-use-rsync-for-local-and-remote-data-transfer-and-synchronization/"&gt;&lt;code&gt;rsync&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Efficient sync and incremental transfer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-does-ssh-work/"&gt;&lt;code&gt;ssh&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Underlying secure shell protocol&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/cheatsheet/ftp/"&gt;&lt;code&gt;ftp&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Legacy unencrypted file transfer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;</content:encoded></item><item><title>Bash Split String: Split a String by Delimiter</title><link>https://linuxize.com/post/bash-split-string/</link><pubDate>Tue, 05 May 2026 15:30:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/bash-split-string/</guid><category>bash</category><description>How to split a string in Bash by a delimiter using read, IFS, tr, awk, and parameter expansion, with practical examples for scripts.</description><content:encoded>&lt;p&gt;When a script reads a CSV row, a colon-separated config line, or a piece of user input, one of the first things you usually need to do is break that string into its individual fields. Bash does not have a dedicated &lt;code&gt;split&lt;/code&gt; function, but the shell gives you several ways to do the job with nothing more than built-ins and common core utilities.&lt;/p&gt;
&lt;p&gt;This guide explains how to split a string by a delimiter in Bash using &lt;code&gt;read&lt;/code&gt;, the &lt;code&gt;IFS&lt;/code&gt; variable, &lt;code&gt;tr&lt;/code&gt;, &lt;code&gt;awk&lt;/code&gt;, and parameter expansion, with safe examples you can drop straight into a script.&lt;/p&gt;
&lt;h2 id="the-ifs-variable"&gt;The IFS Variable &lt;a class="headline-link" href="#the-ifs-variable" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Word splitting in Bash is controlled by the internal field separator, stored in the &lt;code&gt;IFS&lt;/code&gt; variable. By default, &lt;code&gt;IFS&lt;/code&gt; contains a space, a tab, and a newline, which is why unquoted expansions split on whitespace. When you set &lt;code&gt;IFS&lt;/code&gt; to a single character such as a comma or a colon, Bash uses that character to decide where one field ends and the next begins.&lt;/p&gt;
&lt;p&gt;Most of the techniques below work by changing &lt;code&gt;IFS&lt;/code&gt; for one command only, so the rest of the script keeps the default behavior.&lt;/p&gt;
&lt;h2 id="splitting-a-string-with-read"&gt;Splitting a String with read &lt;a class="headline-link" href="#splitting-a-string-with-read" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The cleanest way to split a string into named variables is the &lt;code&gt;read&lt;/code&gt; built-in combined with a temporary &lt;code&gt;IFS&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;STR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ubuntu:debian:fedora:arch&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;:&amp;#34;&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; -r first second third fourth &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$STR&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$first&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$second&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$third&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$fourth&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Running the script produces one field per line:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;ubuntu
debian
fedora
arch&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;IFS=&amp;quot;:&amp;quot;&lt;/code&gt; assignment applies only to the &lt;code&gt;read&lt;/code&gt; command that follows it, so the global &lt;code&gt;IFS&lt;/code&gt; stays untouched. The here-string (&lt;code&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/code&gt;) feeds the string into &lt;code&gt;read&lt;/code&gt; on standard input, and &lt;code&gt;-r&lt;/code&gt; keeps backslashes literal so they are not treated as escape characters.&lt;/p&gt;
&lt;h2 id="splitting-into-a-bash-array"&gt;Splitting Into a Bash Array &lt;a class="headline-link" href="#splitting-into-a-bash-array" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When the number of fields is not known ahead of time, split the string into an array instead of a fixed list of variables. Pass &lt;code&gt;-a&lt;/code&gt; to &lt;code&gt;read&lt;/code&gt; to tell it the target is an array:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;STR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;1,2,3,4,5&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;,&amp;#34;&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; -r -a numbers &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$STR&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; n in &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$n&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;numbers&lt;/code&gt; array now holds one value per comma-separated field, and the loop prints each one on its own line. Always expand arrays with &lt;code&gt;&amp;quot;${numbers[@]}&amp;quot;&lt;/code&gt; inside double quotes so elements that contain spaces stay intact.&lt;/p&gt;
&lt;p&gt;For a deeper look at what else arrays can do, see our guide on &lt;a href="https://linuxize.com/post/bash-arrays/"&gt;Bash arrays&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="splitting-with-a-multi-character-delimiter"&gt;Splitting With a Multi-Character Delimiter &lt;a class="headline-link" href="#splitting-with-a-multi-character-delimiter" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;IFS&lt;/code&gt; treats each character in its value as a separate delimiter, so setting &lt;code&gt;IFS=&amp;quot;::&amp;quot;&lt;/code&gt; will still split on single colons. For a multi-character delimiter such as &lt;code&gt;::&lt;/code&gt; or &lt;code&gt;-&lt;/code&gt;, convert it to a single character first with &lt;code&gt;sed&lt;/code&gt; or use parameter expansion:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;STR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;alpha::beta::gamma&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;|&amp;#34;&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; -r -a parts &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;STR&lt;/span&gt;&lt;span class="p"&gt;//::/|&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; p in &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$p&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;${STR//::/|}&lt;/code&gt; expansion replaces every occurrence of &lt;code&gt;::&lt;/code&gt; with a pipe character, and then &lt;code&gt;read&lt;/code&gt; splits the resulting string on the pipe. Pick a placeholder character that you are sure the input does not contain.&lt;/p&gt;
&lt;h2 id="splitting-with-tr"&gt;Splitting With tr &lt;a class="headline-link" href="#splitting-with-tr" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When you only need the fields printed one per line, &lt;code&gt;tr&lt;/code&gt; is often enough. It translates the delimiter into a newline:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;%s\n&amp;#39;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;red,green,blue&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; tr &lt;span class="s1"&gt;&amp;#39;,&amp;#39;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;\n&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;red
green
blue&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This approach pairs well with pipelines. For example, you can count the number of fields by piping the output into &lt;code&gt;wc -l&lt;/code&gt;, or loop over the values with a &lt;code&gt;while read&lt;/code&gt; block:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;STR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;red,green,blue&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; -r color&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Color: &lt;/span&gt;&lt;span class="nv"&gt;$color&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt; &amp;lt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;%s\n&amp;#39;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$STR&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; tr &lt;span class="s1"&gt;&amp;#39;,&amp;#39;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;\n&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The process substitution &lt;code&gt;&amp;lt;(...)&lt;/code&gt; sends the output of the pipeline into the loop without using a subshell for the loop body, so any variables you set inside the loop remain available after it ends.&lt;/p&gt;
&lt;h2 id="splitting-with-awk"&gt;Splitting With awk &lt;a class="headline-link" href="#splitting-with-awk" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For more structured splitting, &lt;code&gt;awk&lt;/code&gt; is the right tool. It reads input one record at a time and exposes each field as &lt;code&gt;$1&lt;/code&gt;, &lt;code&gt;$2&lt;/code&gt;, and so on. Use &lt;code&gt;-F&lt;/code&gt; to set the field separator:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;jane:x:1001:1001:Jane Doe:/home/jane:/bin/bash&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; awk -F&lt;span class="s1"&gt;&amp;#39;:&amp;#39;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;{ print $1, $5 }&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;jane Jane Doe&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;awk&lt;/code&gt; is also a good pick when the delimiter is a regular expression, when you need to skip a header line, or when you want to act on a specific field without touching the rest. For more patterns, see our &lt;a href="https://linuxize.com/post/awk-command/"&gt;awk command guide&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="splitting-with-parameter-expansion"&gt;Splitting With Parameter Expansion &lt;a class="headline-link" href="#splitting-with-parameter-expansion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Bash parameter expansion can pull a prefix or a suffix off a string without calling any external command. This is the fastest option when you only need one part, not all of them:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;PATH_LINE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/usr/local/bin/script.sh&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PATH_LINE&lt;/span&gt;&lt;span class="p"&gt;##*/&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PATH_LINE&lt;/span&gt;&lt;span class="p"&gt;%/*&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Directory: &lt;/span&gt;&lt;span class="nv"&gt;$DIR&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Basename: &lt;/span&gt;&lt;span class="nv"&gt;$BASE&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Directory: /usr/local/bin
Basename: script.sh&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;##*/&lt;/code&gt; strips the longest match of &lt;code&gt;*/&lt;/code&gt; from the start of the string, leaving the basename. &lt;code&gt;%/*&lt;/code&gt; strips the shortest match of &lt;code&gt;/*&lt;/code&gt; from the end, leaving the directory. The same pattern works for any delimiter, not only the slash.&lt;/p&gt;
&lt;p&gt;For full coverage of these expansions, see our guide on &lt;a href="https://linuxize.com/post/bash-concatenate-strings/"&gt;Bash string manipulation&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="preserving-empty-fields"&gt;Preserving Empty Fields &lt;a class="headline-link" href="#preserving-empty-fields" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A common surprise with &lt;code&gt;read&lt;/code&gt; is that trailing empty fields can be dropped. Consider the string &lt;code&gt;a,,b,&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="sh"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;sh&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;STR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;a,,b,&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;,&amp;#34;&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; -r -a fields &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;STR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;,&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Count: &lt;/span&gt;&lt;span class="si"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;[%s]\n&amp;#39;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Count: 4
[a]
[]
[b]
[]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Bash keeps empty fields in the middle, but &lt;code&gt;read -a&lt;/code&gt; drops a trailing empty field when the delimiter is the last character. Appending one extra delimiter before reading gives &lt;code&gt;read&lt;/code&gt; a final empty value to assign, which preserves the trailing field from the original string.&lt;/p&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The table below summarizes which technique to reach for based on what the script needs to do.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Goal&lt;/th&gt;
&lt;th&gt;Technique&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Split into named variables&lt;/td&gt;
&lt;td&gt;&lt;code&gt;IFS=&amp;quot;:&amp;quot; read -r a b c &amp;lt;&amp;lt;&amp;lt; &amp;quot;$STR&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Split into an array&lt;/td&gt;
&lt;td&gt;&lt;code&gt;IFS=&amp;quot;,&amp;quot; read -r -a arr &amp;lt;&amp;lt;&amp;lt; &amp;quot;$STR&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-character delimiter&lt;/td&gt;
&lt;td&gt;Replace with &lt;code&gt;${STR//::/|}&lt;/code&gt; first, then split&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Print one field per line&lt;/td&gt;
&lt;td&gt;&lt;code&gt;printf '%s\n' &amp;quot;$STR&amp;quot; | tr ',' '\n'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Select specific fields&lt;/td&gt;
&lt;td&gt;&lt;code&gt;awk -F',' '{ print $2 }'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Take prefix or suffix only&lt;/td&gt;
&lt;td&gt;&lt;code&gt;${STR%%...}&lt;/code&gt; / &lt;code&gt;${STR##...}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For a printable quick reference, see the &lt;a href="https://linuxize.com/cheatsheet/bash/"&gt;bash cheatsheet&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="faq"&gt;FAQ &lt;a class="headline-link" href="#faq" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;How do I split a string by whitespace in Bash?&lt;/strong&gt;&lt;br&gt;
Leave &lt;code&gt;IFS&lt;/code&gt; at its default and use &lt;code&gt;read -r -a arr &amp;lt;&amp;lt;&amp;lt; &amp;quot;$STR&amp;quot;&lt;/code&gt;. Any run of spaces, tabs, or newlines will act as a separator.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why does my array lose the last empty field?&lt;/strong&gt;&lt;br&gt;
If the delimiter is at the very end of the string, &lt;code&gt;read -a&lt;/code&gt; can drop the trailing empty element. Append one extra delimiter before reading, for example &lt;code&gt;IFS=',' read -r -a arr &amp;lt;&amp;lt;&amp;lt; &amp;quot;${STR},&amp;quot;&lt;/code&gt;, when the trailing empty field matters.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Can I split on a newline?&lt;/strong&gt;&lt;br&gt;
Yes. For line-based strings, use &lt;code&gt;mapfile -t lines &amp;lt;&amp;lt;&amp;lt; &amp;quot;$STR&amp;quot;&lt;/code&gt; so each input line becomes one array element. Plain &lt;code&gt;read&lt;/code&gt; stops at the first newline unless you change how it reads the input.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Is there a built-in split function in Bash?&lt;/strong&gt;&lt;br&gt;
No. Word splitting driven by &lt;code&gt;IFS&lt;/code&gt; plus the &lt;code&gt;read&lt;/code&gt; built-in is the idiomatic replacement for a dedicated &lt;code&gt;split&lt;/code&gt; function.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Splitting strings in Bash comes down to picking the right tool for the shape of the input: &lt;code&gt;read&lt;/code&gt; with &lt;code&gt;IFS&lt;/code&gt; for structured parsing, &lt;code&gt;tr&lt;/code&gt; or &lt;code&gt;awk&lt;/code&gt; for pipelines, and parameter expansion for quick prefix or suffix work. When the input is untrusted, quote every expansion and handle empty fields on purpose rather than by accident.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/bash-split-string/featured_hu_b4b5041647f7aca4.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>cd Cheatsheet</title><link>https://linuxize.com/cheatsheet/cd/</link><pubDate>Sun, 03 May 2026 22:55:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/cheatsheet/cd/</guid><description>Quick reference for changing directories with cd in Linux</description><content:encoded>&lt;div class="card"&gt;
&lt;h3 id="basic-syntax"&gt;Basic Syntax &lt;a class="headline-link" href="#basic-syntax" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Core command forms for changing directories.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd [DIRECTORY]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change to a directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change to your home directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd -- DIRECTORY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change to a directory whose name may start with &lt;code&gt;-&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pwd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print the current working directory&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="everyday-navigation"&gt;Everyday Navigation &lt;a class="headline-link" href="#everyday-navigation" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Common ways to move around the filesystem.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd /etc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change to an absolute path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd Downloads&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change to a relative path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd ..&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Move up one directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd ../..&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Move up two directories&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd ./scripts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change to a directory under the current directory&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="home-directories"&gt;Home Directories &lt;a class="headline-link" href="#home-directories" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Use shell shortcuts for your home directory and other users&amp;rsquo; homes.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd ~&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change to your home directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd ~/Downloads&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change to &lt;code&gt;Downloads&lt;/code&gt; inside your home directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd ~username&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change to another user&amp;rsquo;s home directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd &amp;quot;$HOME&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change to the directory stored in &lt;code&gt;$HOME&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="relative-paths"&gt;Relative Paths &lt;a class="headline-link" href="#relative-paths" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Build paths from your current directory.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd .&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stay in the current directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd ..&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Move to the parent directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd ../src&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Move up one level, then into &lt;code&gt;src&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd ../../var&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Move up two levels, then into &lt;code&gt;var&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd project/docs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Move through nested directories&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="previous-directory"&gt;Previous Directory &lt;a class="headline-link" href="#previous-directory" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Switch between recently used directories.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd -&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change to the previous working directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;echo &amp;quot;$OLDPWD&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the previous working directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd &amp;quot;$OLDPWD&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change to the previous directory without using &lt;code&gt;cd -&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pushd /path&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change directory and save the old one on the stack&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;popd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Return to a directory from the stack&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="paths-with-spaces"&gt;Paths with Spaces &lt;a class="headline-link" href="#paths-with-spaces" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Quote or escape paths that contain spaces or shell metacharacters.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd &amp;quot;Project Files&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Quote a directory name with spaces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd 'Project Files'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use single quotes for a literal path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd Project\ Files&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Escape the space with a backslash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd -- &amp;quot;-reports&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enter a directory whose name starts with &lt;code&gt;-&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="symlinks-and-physical-paths"&gt;Symlinks and Physical Paths &lt;a class="headline-link" href="#symlinks-and-physical-paths" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Control whether &lt;code&gt;cd&lt;/code&gt; follows logical or physical paths.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd -L linkdir&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Follow symbolic links (default in Bash)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd -P linkdir&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Resolve to the physical directory path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pwd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the shell&amp;rsquo;s logical current directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pwd -P&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the physical current directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd -P ..&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Move using the physical directory structure&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="cdpath"&gt;CDPATH &lt;a class="headline-link" href="#cdpath" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Search extra base directories when changing by name.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;export CDPATH=.:~/projects:/opt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search current directory, &lt;code&gt;~/projects&lt;/code&gt;, and &lt;code&gt;/opt&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Try matching &lt;code&gt;myapp&lt;/code&gt; in each &lt;code&gt;CDPATH&lt;/code&gt; entry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;unset CDPATH&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Disable &lt;code&gt;CDPATH&lt;/code&gt; for the current shell&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CDPATH= cd myapp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run one &lt;code&gt;cd&lt;/code&gt; command without &lt;code&gt;CDPATH&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Quick checks for common directory-change errors.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Issue&lt;/th&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;No such file or directory&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Verify the path with &lt;code&gt;ls -ld path&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Permission denied&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check execute permission on the directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Path with spaces fails&lt;/td&gt;
&lt;td&gt;Quote the path or escape spaces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd -&lt;/code&gt; fails&lt;/td&gt;
&lt;td&gt;&lt;code&gt;$OLDPWD&lt;/code&gt; is not set yet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unexpected target with &lt;code&gt;CDPATH&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run &lt;code&gt;unset CDPATH&lt;/code&gt; or use an absolute path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Symlink path looks different&lt;/td&gt;
&lt;td&gt;Compare &lt;code&gt;pwd&lt;/code&gt; and &lt;code&gt;pwd -P&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="related-guides"&gt;Related Guides &lt;a class="headline-link" href="#related-guides" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Use these guides for detailed directory navigation workflows.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Guide&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/linux-cd-command/"&gt;&lt;code&gt;cd Command in Linux: Change Directories&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Full &lt;code&gt;cd&lt;/code&gt; guide with examples&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/current-working-directory/"&gt;&lt;code&gt;How to Get the Current Working Directory in Linux&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;pwd&lt;/code&gt; and understand the current directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/popd-and-pushd-commands-in-linux/"&gt;&lt;code&gt;pushd and popd Commands in Linux&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Work with the directory stack&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/cheatsheet/linux-commands/"&gt;&lt;code&gt;Linux Commands Cheatsheet&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;General Linux command quick reference&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;</content:encoded></item><item><title>Initial Server Setup on Ubuntu 26.04</title><link>https://linuxize.com/post/initial-server-setup-on-ubuntu-26-04/</link><pubDate>Sun, 03 May 2026 09:30:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/initial-server-setup-on-ubuntu-26-04/</guid><category>ssh</category><category>security</category><category>ubuntu</category><description>Prepare a new Ubuntu 26.04 server with a sudo user, SSH key authentication, UFW firewall rules, a hostname, timezone settings, and system updates.</description><content:encoded>&lt;p&gt;A fresh Ubuntu 26.04 server ships with root SSH access, no regular user, and no firewall rules. That works for the first login, but it is not a safe state to leave running on a public VPS.&lt;/p&gt;
&lt;p&gt;This guide walks through the first tasks to perform on a new Ubuntu 26.04 server: creating a sudo user, enabling SSH key authentication, locking down SSH, configuring UFW, setting the hostname and timezone, and applying package updates.&lt;/p&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Command or file&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Log in as root&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ssh root@server_ip_address&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Create a user&lt;/td&gt;
&lt;td&gt;&lt;code&gt;adduser username&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grant sudo access&lt;/td&gt;
&lt;td&gt;&lt;code&gt;usermod -aG sudo username&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Copy root SSH keys&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rsync --archive --chown=username:username /root/.ssh /home/username&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Add a local key&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ssh-copy-id username@server_ip_address&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSH hardening file&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/etc/ssh/sshd_config.d/99-hardening.conf&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test SSH config&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo sshd -t&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Allow SSH in UFW&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo ufw allow OpenSSH&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Set hostname&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo hostnamectl set-hostname server-name&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Set timezone&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo timedatectl set-timezone Europe/Berlin&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites &lt;a class="headline-link" href="#prerequisites" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before starting, make sure you have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A new Ubuntu 26.04 server with a public IP address.&lt;/li&gt;
&lt;li&gt;Root access over SSH, either with a password or a provider-supplied key.&lt;/li&gt;
&lt;li&gt;A local SSH key pair on your workstation. If you do not have one yet, see &lt;a href="https://linuxize.com/post/how-to-generate-ssh-keys-on-linux/"&gt;how to generate SSH keys on Linux&lt;/a&gt;
.&lt;/li&gt;
&lt;li&gt;Access to the provider web console as a backup path in case SSH access stops working.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Keep your original root SSH session open until you have tested the new user login and the hardened SSH configuration.&lt;/p&gt;
&lt;h2 id="log-in-as-root"&gt;Log In as Root &lt;a class="headline-link" href="#log-in-as-root" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Open a terminal on your local machine and connect to the server using the public IP address from your hosting provider:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh root@server_ip_address&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Accept the host key when prompted and enter the root password if password authentication is still enabled. If your provider created the server with an SSH key, the connection should use that key automatically.&lt;/p&gt;
&lt;h2 id="create-a-new-sudo-user"&gt;Create a New Sudo User &lt;a class="headline-link" href="#create-a-new-sudo-user" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Working as root for daily administration is risky because every command runs with full privileges. Create a regular user account and give it administrative access through the &lt;code&gt;sudo&lt;/code&gt; group.&lt;/p&gt;
&lt;p&gt;Replace &lt;code&gt;username&lt;/code&gt; with the account name you want to use:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;adduser username&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The command prompts for a password and optional user details. Enter a strong password, then press &lt;code&gt;Enter&lt;/code&gt; to skip any fields you do not need.&lt;/p&gt;
&lt;p&gt;Add the new user to the &lt;code&gt;sudo&lt;/code&gt; group:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;usermod -aG sudo username&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The account can now run administrative commands with &lt;code&gt;sudo&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="set-up-ssh-key-authentication"&gt;Set Up SSH Key Authentication &lt;a class="headline-link" href="#set-up-ssh-key-authentication" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;SSH keys are safer than password logins and are easier to use once configured. The exact command depends on where your public key is currently stored.&lt;/p&gt;
&lt;p&gt;If your public key is already present under the root account, copy the root SSH directory to the new user:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rsync --archive --chown&lt;span class="o"&gt;=&lt;/span&gt;username:username /root/.ssh /home/username&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If you need to copy a key from your local workstation, run this command from the local machine:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh-copy-id username@server_ip_address&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Open a new terminal window and test the login before changing the SSH server configuration:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh username@server_ip_address&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The connection should succeed as the new user. Keep both the root session and the new user session open while you continue.&lt;/p&gt;
&lt;h2 id="disable-root-login-and-password-authentication"&gt;Disable Root Login and Password Authentication &lt;a class="headline-link" href="#disable-root-login-and-password-authentication" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;After key-based login works, configure OpenSSH to reject direct root logins and password authentication. Ubuntu includes files from &lt;code&gt;/etc/ssh/sshd_config.d/&lt;/code&gt;, which keeps local changes separate from the main SSH configuration file.&lt;/p&gt;
&lt;p&gt;Create a hardening snippet:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nano /etc/ssh/sshd_config.d/99-hardening.conf&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Add the following lines:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="txt"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;/etc/ssh/sshd_config.d/99-hardening.conf&lt;/span&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;txt&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;PermitRootLogin no
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;PasswordAuthentication no&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Save the file and test the SSH configuration syntax:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo sshd -t&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If the command prints no output, the configuration is valid. Reload SSH to apply the change:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl reload ssh&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Open another terminal and confirm that you can still log in as the regular user:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh username@server_ip_address&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Do not close your existing sessions until this test succeeds.&lt;/p&gt;
&lt;h2 id="set-up-the-firewall-with-ufw"&gt;Set Up the Firewall with UFW &lt;a class="headline-link" href="#set-up-the-firewall-with-ufw" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Ubuntu uses UFW (Uncomplicated Firewall) as a simple front-end for managing host firewall rules. Start by allowing SSH so the firewall does not block your current access:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo ufw allow OpenSSH&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Enable the firewall:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo ufw enable&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Confirm the prompt with &lt;code&gt;y&lt;/code&gt;, then check the active rules:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo ufw status&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The output should show that OpenSSH is allowed:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Status: active
To Action From
-- ------ ----
OpenSSH ALLOW Anywhere
OpenSSH (v6) ALLOW Anywhere (v6)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When you install services such as Nginx or Apache, open their profiles before expecting traffic to reach them. For example, an Nginx server that should accept HTTP and HTTPS traffic needs:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo ufw allow &lt;span class="s1"&gt;&amp;#39;Nginx Full&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For more examples, see &lt;a href="https://linuxize.com/post/how-to-setup-a-firewall-with-ufw-on-ubuntu-24-04/"&gt;how to set up a firewall with UFW&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="set-the-hostname"&gt;Set the Hostname &lt;a class="headline-link" href="#set-the-hostname" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A descriptive hostname makes logs, shell prompts, monitoring alerts, and dashboards easier to read. Set the hostname with &lt;code&gt;hostnamectl&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo hostnamectl set-hostname server-name&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Replace &lt;code&gt;server-name&lt;/code&gt; with a short name that matches the server role, such as &lt;code&gt;web-01&lt;/code&gt; or &lt;code&gt;db-01&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Check the result:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hostnamectl&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You can update DNS records or your local SSH config separately if you want to connect by name instead of IP address.&lt;/p&gt;
&lt;h2 id="set-the-timezone"&gt;Set the Timezone &lt;a class="headline-link" href="#set-the-timezone" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Set the server timezone so logs, cron jobs, and timestamps match the region you use for operations:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo timedatectl set-timezone Europe/Berlin&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;List available zones if you are unsure of the exact name:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;timedatectl list-timezones&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;See &lt;a href="https://linuxize.com/post/how-to-set-or-change-timezone-on-ubuntu-20-04/"&gt;how to set or change the timezone on Ubuntu&lt;/a&gt;
for a deeper explanation.&lt;/p&gt;
&lt;h2 id="update-the-system"&gt;Update the System &lt;a class="headline-link" href="#update-the-system" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Refresh the package index and install pending updates:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt upgrade&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If the upgrade installed a new kernel or core system libraries, reboot the server:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo reboot&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;After the reboot, reconnect as the regular sudo user:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh username@server_ip_address&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Locked out after disabling password authentication&lt;/strong&gt;&lt;br&gt;
Use your provider web console or recovery mode to log in. Edit &lt;code&gt;/etc/ssh/sshd_config.d/99-hardening.conf&lt;/code&gt;, temporarily set &lt;code&gt;PasswordAuthentication yes&lt;/code&gt;, run &lt;code&gt;sudo sshd -t&lt;/code&gt;, reload SSH, and test key login again before disabling passwords.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;usermod: group 'sudo' does not exist&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Some minimal images may not include the &lt;code&gt;sudo&lt;/code&gt; package. Install it with &lt;code&gt;apt install sudo&lt;/code&gt;, then rerun &lt;code&gt;usermod -aG sudo username&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;sshd -t&lt;/code&gt; reports an error&lt;/strong&gt;&lt;br&gt;
Read the line number in the error message, fix the snippet in &lt;code&gt;/etc/ssh/sshd_config.d/99-hardening.conf&lt;/code&gt;, and run &lt;code&gt;sudo sshd -t&lt;/code&gt; again. Do not reload SSH until the syntax test passes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UFW blocks an expected service&lt;/strong&gt;&lt;br&gt;
Check the active rules with &lt;code&gt;sudo ufw status&lt;/code&gt;. Allow the needed service profile or port, such as &lt;code&gt;sudo ufw allow 'Nginx Full'&lt;/code&gt; for Nginx web traffic, then test the connection again.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You now have an Ubuntu 26.04 server with a sudo user, key-based SSH access, direct root logins disabled, a basic firewall, and current packages. A good next step is to enable &lt;a href="https://linuxize.com/post/how-to-set-up-automatic-updates-on-ubuntu-18-04/"&gt;automatic security updates&lt;/a&gt;
before installing the rest of your stack.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/initial-server-setup-on-ubuntu-26-04/featured_hu_7cd62ec38f6a368b.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>netstat Cheatsheet</title><link>https://linuxize.com/cheatsheet/netstat/</link><pubDate>Sat, 02 May 2026 10:30:00 +0100</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/cheatsheet/netstat/</guid><description>Quick reference for listing network connections, listening ports, routes, interface counters, and protocol statistics with netstat</description><content:encoded>&lt;div class="card"&gt;
&lt;h3 id="basic-syntax"&gt;Basic Syntax &lt;a class="headline-link" href="#basic-syntax" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Core &lt;code&gt;netstat&lt;/code&gt; command forms and output controls.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;netstat&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show active non-listening sockets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show all sockets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -n&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show numeric addresses and ports&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -p&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show PID and program name where available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -c&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Refresh output every second&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="listening-ports"&gt;Listening Ports &lt;a class="headline-link" href="#listening-ports" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Find services that are accepting local connections.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -tuln&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show TCP and UDP listening sockets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -tulnp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show listening sockets with PID and process name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -tnlp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show listening TCP sockets only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -unlp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show listening UDP sockets only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -tulnp | grep ':80'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Find the process listening on port 80&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="connections-and-tcp-states"&gt;Connections and TCP States &lt;a class="headline-link" href="#connections-and-tcp-states" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Inspect active connections and common TCP states.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -at&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show all TCP sockets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -au&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show all UDP sockets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -ant&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show TCP sockets with numeric addresses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -ant | grep ESTABLISHED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show established TCP connections&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -ant | grep TIME_WAIT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show TCP connections in &lt;code&gt;TIME_WAIT&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -atnc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Watch TCP connection output continuously&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="counts-and-filters"&gt;Counts and Filters &lt;a class="headline-link" href="#counts-and-filters" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Use shell filters with &lt;code&gt;netstat&lt;/code&gt; output.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -ant | wc -l&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Count TCP output rows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -ant | grep ':80' | wc -l&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Count TCP connections involving port 80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -ant | grep ESTABLISHED | wc -l&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Count established TCP connections&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -tulnp | grep nginx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Find sockets owned by &lt;code&gt;nginx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -ant | grep 203.0.113.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Filter connections by remote IP address&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="routes-interfaces-and-stats"&gt;Routes, Interfaces, and Stats &lt;a class="headline-link" href="#routes-interfaces-and-stats" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Show routing, interface, and protocol information.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;netstat -rn&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the routing table with numeric addresses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;netstat -r&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the routing table with name resolution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;netstat -i&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show interface counters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;netstat -ie&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show extended interface details&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;netstat -s&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show protocol statistics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;netstat -st&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show TCP protocol statistics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;netstat -su&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show UDP protocol statistics&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="modern-replacements"&gt;Modern Replacements &lt;a class="headline-link" href="#modern-replacements" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Use current Linux tools for new workflows and scripts.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;netstat Command&lt;/th&gt;
&lt;th&gt;Modern Command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;netstat -tuln&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ss -tuln&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -tulnp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo ss -tulnp&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;netstat -rn&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ip route&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;netstat -i&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ip -s link&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;netstat -s&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ss -s&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Common &lt;code&gt;netstat&lt;/code&gt; issues and quick fixes.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Issue&lt;/th&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;netstat: command not found&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install the &lt;code&gt;net-tools&lt;/code&gt; package or use &lt;code&gt;ss&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Process column is empty&lt;/td&gt;
&lt;td&gt;Run with &lt;code&gt;sudo&lt;/code&gt; when using &lt;code&gt;-p&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output is slow&lt;/td&gt;
&lt;td&gt;Add &lt;code&gt;-n&lt;/code&gt; to disable name resolution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Port lookup matches too much&lt;/td&gt;
&lt;td&gt;Search with a colon, such as &lt;code&gt;grep ':80'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Need listeners only&lt;/td&gt;
&lt;td&gt;Add &lt;code&gt;-l&lt;/code&gt; with protocol flags such as &lt;code&gt;-tuln&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="related-guides"&gt;Related Guides &lt;a class="headline-link" href="#related-guides" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Use these guides for full walkthroughs and modern alternatives.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Guide&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/netstat-command-in-linux/"&gt;&lt;code&gt;netstat&lt;/code&gt; Command in Linux&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Full &lt;code&gt;netstat&lt;/code&gt; guide with examples&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/ss-command-in-linux/"&gt;&lt;code&gt;ss&lt;/code&gt; Command in Linux&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Modern socket inspection tool&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/linux-ip-command/"&gt;&lt;code&gt;ip&lt;/code&gt; Command in Linux&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Modern routes and interface management&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/check-listening-ports-linux/"&gt;How to Check Listening Ports in Linux&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Compare &lt;code&gt;ss&lt;/code&gt;, &lt;code&gt;netstat&lt;/code&gt;, and &lt;code&gt;lsof&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/lsof-command-in-linux/"&gt;&lt;code&gt;lsof&lt;/code&gt; Command in Linux&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Tie sockets and files back to processes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;</content:encoded></item><item><title>netstat Command in Linux: Network Connections and Statistics</title><link>https://linuxize.com/post/netstat-command-in-linux/</link><pubDate>Sat, 02 May 2026 09:00:00 +0100</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/netstat-command-in-linux/</guid><category>linux commands</category><description>Use the netstat command to list network connections, listening ports, routing tables, interface counters, and TCP states on Linux.</description><content:encoded>&lt;p&gt;When something on a server is holding port 80, a connection is refusing to close, or a service is not reachable from the outside, the first question is almost always the same: what is actually listening, and who is talking to whom. For years, the answer on Linux was the &lt;code&gt;netstat&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;netstat&lt;/code&gt; is part of the classic &lt;code&gt;net-tools&lt;/code&gt; package and prints network connections, listening ports, routing tables, interface counters, and per-protocol statistics. It has been deprecated in favor of &lt;a href="https://linuxize.com/post/ss-command-in-linux/"&gt;&lt;code&gt;ss&lt;/code&gt;&lt;/a&gt;
and &lt;a href="https://linuxize.com/post/linux-ip-command/"&gt;&lt;code&gt;ip&lt;/code&gt;&lt;/a&gt;
, but it is still installed on many systems and the tool many sysadmins reach for first. This guide explains how to read its output and which flags cover the cases you run into day to day.&lt;/p&gt;
&lt;h2 id="install-netstat"&gt;Install netstat &lt;a class="headline-link" href="#install-netstat" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;On most modern distributions, &lt;code&gt;net-tools&lt;/code&gt; is not installed by default. If &lt;code&gt;netstat&lt;/code&gt; is not available, install it with your package manager.&lt;/p&gt;
&lt;p&gt;On Ubuntu, Debian, and Derivatives:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install net-tools&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On Fedora, RHEL, and Derivatives:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dnf install net-tools&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Once the package is installed, verify the binary is on your path:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;netstat --version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="netstat-syntax"&gt;netstat Syntax &lt;a class="headline-link" href="#netstat-syntax" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The general form of the command is:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="txt"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;txt&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;netstat [OPTIONS]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Run without options, &lt;code&gt;netstat&lt;/code&gt; prints a list of open non-listening sockets, which is rarely what you want. In practice, you almost always pass a combination of flags that describe what kind of sockets you care about (TCP, UDP, Unix), what state they are in, and how you want the output formatted.&lt;/p&gt;
&lt;h2 id="list-all-connections"&gt;List All Connections &lt;a class="headline-link" href="#list-all-connections" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To list every connection, both listening and established, use the &lt;code&gt;-a&lt;/code&gt; option:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo netstat -a&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The output is divided into two parts. The top lists Internet sockets with columns for protocol, receive and send queue sizes, local and foreign address, and state. The bottom lists Unix domain sockets used for local inter-process communication.&lt;/p&gt;
&lt;p&gt;Running &lt;code&gt;netstat&lt;/code&gt; with &lt;code&gt;sudo&lt;/code&gt; is recommended because without it, the command cannot read socket ownership for processes that belong to other users.&lt;/p&gt;
&lt;h2 id="show-tcp-and-udp-connections"&gt;Show TCP and UDP Connections &lt;a class="headline-link" href="#show-tcp-and-udp-connections" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;-t&lt;/code&gt; and &lt;code&gt;-u&lt;/code&gt; flags filter the output by protocol. To show only TCP connections:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo netstat -at&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To show only UDP connections:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo netstat -au&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You can combine both flags to get TCP and UDP together:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo netstat -atu&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="show-listening-ports"&gt;Show Listening Ports &lt;a class="headline-link" href="#show-listening-ports" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When you want to know which services are accepting new connections, use the &lt;code&gt;-l&lt;/code&gt; option. For TCP, it restricts the output to sockets in the &lt;code&gt;LISTEN&lt;/code&gt; state. UDP does not use the same connection states, but UDP sockets that are ready to receive traffic are still shown.&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo netstat -tuln&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The combined flags read as: show TCP (&lt;code&gt;-t&lt;/code&gt;) and UDP (&lt;code&gt;-u&lt;/code&gt;) sockets that are listening (&lt;code&gt;-l&lt;/code&gt;), with numeric addresses and ports (&lt;code&gt;-n&lt;/code&gt;). The output looks similar to this:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN
tcp6 0 0 :::80 :::* LISTEN
udp 0 0 0.0.0.0:68 0.0.0.0:*&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;From the output, we can see that SSH is listening on all interfaces on port 22, MySQL is bound to localhost only on port 3306, and a web server is listening on port 80 over IPv6.&lt;/p&gt;
&lt;h2 id="show-the-process-using-a-port"&gt;Show the Process Using a Port &lt;a class="headline-link" href="#show-the-process-using-a-port" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To find out which program owns a socket, add the &lt;code&gt;-p&lt;/code&gt; option. It prints the PID and the process name next to each connection:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo netstat -tulnp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;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 812/sshd
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN 1034/mysqld
tcp6 0 0 :::80 :::* LISTEN 1591/nginx: master&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;PID/Program name&lt;/code&gt; column ties each listening port to a process. This is the quickest way to find out which service is holding a port when you get an &lt;code&gt;Address already in use&lt;/code&gt; error.&lt;/p&gt;
&lt;p&gt;To check one specific port, pipe the output to &lt;a href="https://linuxize.com/post/how-to-use-grep-command-to-search-files-in-linux/"&gt;&lt;code&gt;grep&lt;/code&gt;&lt;/a&gt;
:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo netstat -tulnp &lt;span class="p"&gt;|&lt;/span&gt; grep &lt;span class="s1"&gt;&amp;#39;:80&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If the command prints a row, a process is listening on port &lt;code&gt;80&lt;/code&gt;. If it prints nothing, no TCP or UDP listener matched that port on the local system.&lt;/p&gt;
&lt;h2 id="show-numeric-output"&gt;Show Numeric Output &lt;a class="headline-link" href="#show-numeric-output" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;By default, &lt;code&gt;netstat&lt;/code&gt; resolves IP addresses to hostnames and port numbers to service names (for example, &lt;code&gt;22&lt;/code&gt; becomes &lt;code&gt;ssh&lt;/code&gt;). On a busy system, this can be slow because each address needs a DNS lookup.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;-n&lt;/code&gt; option disables name resolution and prints raw numbers:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo netstat -n&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Numeric output is also easier to pipe into other tools such as &lt;code&gt;grep&lt;/code&gt; or &lt;code&gt;awk&lt;/code&gt;, because the field values are predictable.&lt;/p&gt;
&lt;h2 id="continuous-monitoring"&gt;Continuous Monitoring &lt;a class="headline-link" href="#continuous-monitoring" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;-c&lt;/code&gt; option tells &lt;code&gt;netstat&lt;/code&gt; to print the output every second until you stop it with &lt;code&gt;Ctrl+C&lt;/code&gt;. It is useful when you want to watch a connection appear, change state, and close:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo netstat -atnc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Each iteration prints the full table again, so it is best used together with a filter such as &lt;code&gt;grep&lt;/code&gt; to isolate a single connection.&lt;/p&gt;
&lt;h2 id="show-tcp-connection-states"&gt;Show TCP Connection States &lt;a class="headline-link" href="#show-tcp-connection-states" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;TCP connections move through states such as &lt;code&gt;ESTABLISHED&lt;/code&gt;, &lt;code&gt;LISTEN&lt;/code&gt;, &lt;code&gt;TIME_WAIT&lt;/code&gt;, and &lt;code&gt;CLOSE_WAIT&lt;/code&gt;. To show active TCP sockets with numeric addresses, run:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo netstat -ant&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;State&lt;/code&gt; column tells you where each connection is in the TCP lifecycle:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 192.168.1.10:22 192.168.1.5:52710 ESTABLISHED
tcp 0 0 192.168.1.10:80 203.0.113.25:51422 TIME_WAIT
tcp 0 0 127.0.0.1:3306 127.0.0.1:41834 CLOSE_WAIT&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;ESTABLISHED&lt;/code&gt; means the connection is active. &lt;code&gt;TIME_WAIT&lt;/code&gt; is normal after a connection closes. A large number of &lt;code&gt;CLOSE_WAIT&lt;/code&gt; entries can mean the local application is not closing sockets correctly.&lt;/p&gt;
&lt;p&gt;To show only established TCP connections, filter the output:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo netstat -ant &lt;span class="p"&gt;|&lt;/span&gt; grep ESTABLISHED&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For new troubleshooting work, &lt;a href="https://linuxize.com/post/ss-command-in-linux/"&gt;&lt;code&gt;ss&lt;/code&gt;&lt;/a&gt;
gives better built-in state filters, such as &lt;code&gt;ss -tn state established&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="count-connections"&gt;Count Connections &lt;a class="headline-link" href="#count-connections" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;On busy web servers, you may want a quick count instead of a full connection table. To count all current TCP connections that involve port &lt;code&gt;80&lt;/code&gt;, run:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo netstat -ant &lt;span class="p"&gt;|&lt;/span&gt; grep &lt;span class="s1"&gt;&amp;#39;:80&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; wc -l&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To count only established connections on port &lt;code&gt;80&lt;/code&gt;, add the TCP state to the filter:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo netstat -ant &lt;span class="p"&gt;|&lt;/span&gt; grep &lt;span class="s1"&gt;&amp;#39;:80&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep ESTABLISHED &lt;span class="p"&gt;|&lt;/span&gt; wc -l&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;These counts are useful as a quick signal, but they are not a replacement for application metrics. The number can change while the pipeline is running, especially on high-traffic systems.&lt;/p&gt;
&lt;h2 id="display-the-routing-table"&gt;Display the Routing Table &lt;a class="headline-link" href="#display-the-routing-table" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To print the kernel IP routing table, use the &lt;code&gt;-r&lt;/code&gt; option:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;netstat -rn&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
0.0.0.0 192.168.1.1 0.0.0.0 UG 0 0 0 eth0
192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The first row is the default route, which sends all traffic not matching another rule to &lt;code&gt;192.168.1.1&lt;/code&gt;. The &lt;code&gt;ip route&lt;/code&gt; command from &lt;a href="https://linuxize.com/post/linux-ip-command/"&gt;&lt;code&gt;ip&lt;/code&gt;&lt;/a&gt;
is the modern equivalent and is what you should use on new systems.&lt;/p&gt;
&lt;h2 id="interface-statistics"&gt;Interface Statistics &lt;a class="headline-link" href="#interface-statistics" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;-i&lt;/code&gt; option prints a per-interface summary that includes received and transmitted packets, errors, drops, and the MTU:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;netstat -i&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="output"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"&gt;&lt;/path&gt;
&lt;polyline points="14 2 14 8 20 8"&gt;&lt;/polyline&gt;
&lt;/svg&gt;&lt;span class="px-2 py-0.5 rounded text-xs font-medium bg-gray-200 text-gray-700 dark:bg-slate-600 dark:text-slate-300"&gt;output&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-output" data-lang="output"&gt;Kernel Interface table
Iface MTU RX-OK RX-ERR RX-DRP TX-OK TX-ERR TX-DRP Flg
eth0 1500 2345678 0 12 1987654 0 0 BMRU
lo 65536 55432 0 0 55432 0 0 LRU&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A growing &lt;code&gt;RX-ERR&lt;/code&gt; or &lt;code&gt;TX-ERR&lt;/code&gt; column is a hint that you have a cabling, duplex, or driver issue on that interface.&lt;/p&gt;
&lt;h2 id="protocol-statistics"&gt;Protocol Statistics &lt;a class="headline-link" href="#protocol-statistics" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To print a summary of statistics for each protocol, use &lt;code&gt;-s&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;netstat -s&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The output is long and grouped by protocol (&lt;code&gt;Ip&lt;/code&gt;, &lt;code&gt;Tcp&lt;/code&gt;, &lt;code&gt;Udp&lt;/code&gt;, and so on). It includes counters such as total packets received, segments retransmitted, and connection resets. You can narrow the output to a single protocol by combining &lt;code&gt;-s&lt;/code&gt; with &lt;code&gt;-t&lt;/code&gt; or &lt;code&gt;-u&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;netstat -st&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="netstat-vs-ss"&gt;netstat vs ss &lt;a class="headline-link" href="#netstat-vs-ss" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;On modern Linux distributions, &lt;code&gt;ss&lt;/code&gt; is the recommended replacement for &lt;code&gt;netstat&lt;/code&gt;. It is faster and reads information directly from the kernel through Netlink, with richer filter support. The &lt;code&gt;ip&lt;/code&gt; command replaces the routing and interface parts of &lt;code&gt;netstat&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A few common translations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;netstat -tuln&lt;/code&gt; is equivalent to &lt;code&gt;ss -tuln&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;netstat -tulnp&lt;/code&gt; is equivalent to &lt;code&gt;sudo ss -tulnp&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;netstat -s&lt;/code&gt; maps to &lt;code&gt;ss -s&lt;/code&gt; (a much shorter summary)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;netstat -i&lt;/code&gt; maps to &lt;code&gt;ip -s link&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;netstat -r&lt;/code&gt; maps to &lt;code&gt;ip route&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a full walkthrough of the replacement tool, see the guide on the &lt;a href="https://linuxize.com/post/ss-command-in-linux/"&gt;ss command&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="quick-reference"&gt;Quick Reference &lt;a class="headline-link" href="#quick-reference" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For a printable quick reference, see the &lt;a href="https://linuxize.com/cheatsheet/netstat/"&gt;netstat cheatsheet&lt;/a&gt;
.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;netstat&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show active non-listening sockets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show all listening and non-listening sockets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -at&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show all TCP sockets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -au&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show all UDP sockets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -tuln&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show TCP and UDP listening sockets with numeric output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -tulnp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show listening sockets with PID and process name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -tulnp | grep ':80'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Find the process listening on port 80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -ant&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show all TCP sockets with numeric addresses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -ant | grep ESTABLISHED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show established TCP connections&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -ant | grep ':80' | wc -l&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Count TCP connections involving port 80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -atnc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Refresh TCP connection output every second&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;netstat -rn&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the routing table with numeric addresses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;netstat -i&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show interface statistics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;netstat -s&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show protocol statistics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;netstat -st&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show TCP protocol statistics&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="troubleshooting"&gt;Troubleshooting &lt;a class="headline-link" href="#troubleshooting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;netstat: command not found&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
The &lt;code&gt;net-tools&lt;/code&gt; package is not installed. Install it with your package manager, or use &lt;code&gt;ss&lt;/code&gt; instead.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No &lt;code&gt;PID/Program name&lt;/code&gt; column shown&lt;/strong&gt;&lt;br&gt;
You need to run the command with &lt;code&gt;sudo&lt;/code&gt;. Without root privileges, &lt;code&gt;netstat&lt;/code&gt; cannot read process information for sockets owned by other users.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Output is slow or hangs&lt;/strong&gt;&lt;br&gt;
DNS resolution is slow or failing. Add the &lt;code&gt;-n&lt;/code&gt; option to disable name lookups and print numeric addresses and ports.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A port shows up as &lt;code&gt;LISTEN&lt;/code&gt; on &lt;code&gt;::&lt;/code&gt; but not on &lt;code&gt;0.0.0.0&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
The service is listening on the IPv6 wildcard address. On systems with dual-stack enabled, this accepts IPv4 traffic too. Check your IPv6 configuration if only IPv4 clients are failing.&lt;/p&gt;
&lt;h2 id="faq"&gt;FAQ &lt;a class="headline-link" href="#faq" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Is netstat deprecated?&lt;/strong&gt;&lt;br&gt;
Yes. The &lt;code&gt;net-tools&lt;/code&gt; suite, which includes &lt;code&gt;netstat&lt;/code&gt;, &lt;code&gt;ifconfig&lt;/code&gt;, and &lt;code&gt;route&lt;/code&gt;, has been deprecated for years in favor of the &lt;code&gt;iproute2&lt;/code&gt; tools &lt;code&gt;ss&lt;/code&gt; and &lt;code&gt;ip&lt;/code&gt;. It is still functional and still shipped by most distributions, but new scripts should target &lt;code&gt;ss&lt;/code&gt; and &lt;code&gt;ip&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What is the difference between netstat on Linux and Windows?&lt;/strong&gt;&lt;br&gt;
The command name is the same, and the general idea is the same, but the flags are different. This guide covers the Linux version from &lt;code&gt;net-tools&lt;/code&gt;. On Windows, the closest equivalents to &lt;code&gt;netstat -tulnp&lt;/code&gt; are &lt;code&gt;netstat -ano&lt;/code&gt; and &lt;code&gt;Get-NetTCPConnection&lt;/code&gt; in PowerShell.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How do I find what process is using a specific port?&lt;/strong&gt;&lt;br&gt;
Run &lt;code&gt;sudo netstat -tulnp | grep ':PORT'&lt;/code&gt;, replacing &lt;code&gt;PORT&lt;/code&gt; with the port number. The last column shows the PID and program name. With &lt;code&gt;ss&lt;/code&gt;, the same query is &lt;code&gt;sudo ss -tulnp 'sport = :PORT'&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why is a port still &lt;code&gt;TIME_WAIT&lt;/code&gt; after I stopped the service?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;TIME_WAIT&lt;/code&gt; is a normal TCP state that keeps a closed connection around for a short period so late packets can be handled safely. The kernel clears these entries automatically, usually within one or two minutes.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion &lt;a class="headline-link" href="#conclusion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;netstat&lt;/code&gt; remains useful when you need to inspect network connections on systems that still have &lt;code&gt;net-tools&lt;/code&gt; installed. For new scripts and current Linux systems, prefer &lt;code&gt;ss&lt;/code&gt; for sockets and &lt;code&gt;ip&lt;/code&gt; for routes and interface statistics.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/netstat-command-in-linux/featured_hu_dedabc9a736c5934.webp" medium="image" type="image/webp" width="1200" height="675"/></item></channel></rss>