<?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>Linux Tips, Tricks and Tutorials on Linuxize</title><link>https://linuxize.com/</link><description>Recent content in Linux Tips, Tricks and Tutorials 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>Wed, 07 Oct 2026 14:45:00 +0200</lastBuildDate><atom:link href="https://linuxize.com/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>Bash Pattern Matching: =~ Operator and Regular Expressions</title><link>https://linuxize.com/post/bash-pattern-matching/</link><pubDate>Wed, 07 Oct 2026 14:45:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/bash-pattern-matching/</guid><category>bash</category><category>linux commands</category><description>Match strings in Bash with globs, extglob, and the =~ regex operator. Use anchors, quoting, and BASH_REMATCH to test input and capture groups.</description><content:encoded>&lt;p&gt;Much of the string work in shell scripts is matching: does this filename end in &lt;code&gt;.log&lt;/code&gt;, does this version number begin with &lt;code&gt;v2&lt;/code&gt;, does this input contain only digits? Bash supports shell glob patterns, including the &lt;code&gt;extglob&lt;/code&gt; extension, and POSIX extended regular expressions. Knowing which to use saves you from calling &lt;code&gt;grep&lt;/code&gt; every time you want to ask a yes-or-no question about a string.&lt;/p&gt;
&lt;p&gt;This guide explains how to use glob patterns, &lt;code&gt;extglob&lt;/code&gt;, and the &lt;code&gt;=~&lt;/code&gt; regex operator inside &lt;code&gt;[[ ]]&lt;/code&gt;, including how to capture groups with &lt;code&gt;BASH_REMATCH&lt;/code&gt;. Run the examples in Bash; &lt;code&gt;[[ ]]&lt;/code&gt; and &lt;code&gt;=~&lt;/code&gt; are not POSIX &lt;code&gt;sh&lt;/code&gt; syntax.&lt;/p&gt;
&lt;h2 id="glob-patterns-vs-regex"&gt;Glob Patterns vs Regex &lt;a class="headline-link" href="#glob-patterns-vs-regex" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Globs and regexes look alike but are not the same:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A glob is what the shell uses when it expands &lt;code&gt;*.txt&lt;/code&gt; or &lt;code&gt;data?.csv&lt;/code&gt;. Wildcards are &lt;code&gt;*&lt;/code&gt; (any string), &lt;code&gt;?&lt;/code&gt; (any single character), and &lt;code&gt;[abc]&lt;/code&gt; (a bracket expression matching one listed character).&lt;/li&gt;
&lt;li&gt;A regex uses anchors (&lt;code&gt;^&lt;/code&gt;, &lt;code&gt;$&lt;/code&gt;), quantifiers (&lt;code&gt;+&lt;/code&gt;, &lt;code&gt;*&lt;/code&gt;, &lt;code&gt;{m,n}&lt;/code&gt;), and grouping (&lt;code&gt;(...)&lt;/code&gt;). Its syntax differs from globs: &lt;code&gt;*&lt;/code&gt; repeats the preceding expression, while &lt;code&gt;.*&lt;/code&gt; matches any string.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bash supports globs in filename expansion and in pattern-matching constructs (&lt;code&gt;case&lt;/code&gt;, &lt;code&gt;[[ string == pattern ]]&lt;/code&gt;). The &lt;code&gt;=~&lt;/code&gt; operator inside &lt;code&gt;[[ ]]&lt;/code&gt; uses POSIX extended regex. A glob must match the entire string, while an unanchored regex can match just part of it.&lt;/p&gt;
&lt;h2 id="glob-matching-with--"&gt;Glob Matching with [[ ]] &lt;a class="headline-link" href="#glob-matching-with--" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;==&lt;/code&gt; operator inside &lt;code&gt;[[ ]]&lt;/code&gt; treats the right-hand side as a glob pattern. Leave the wildcard unquoted to check whether a filename ends in &lt;code&gt;.log&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;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;server.log&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;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;$file&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; *.log &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;log file&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;log file&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;*.log&lt;/code&gt; pattern matches &lt;code&gt;server.log&lt;/code&gt; because &lt;code&gt;*&lt;/code&gt; covers the filename before the suffix. To check a substring instead, surround it 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="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;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;server.log.2026-01-15&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;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;$file&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; *log* &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;contains &amp;#39;log&amp;#39;&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;contains &amp;#39;log&amp;#39;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here, &lt;code&gt;*log*&lt;/code&gt; matches the whole string with any characters before and after &lt;code&gt;log&lt;/code&gt;. Use &lt;code&gt;!=&lt;/code&gt; to test that a string does not match a pattern, such as &lt;code&gt;[[ &amp;quot;$file&amp;quot; != *.log ]]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For literal equality, quote the right-hand side. This also prevents wildcard characters inside a variable from becoming 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="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;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;server.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="nv"&gt;expected&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;server.log&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;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;$file&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;&lt;/span&gt;&lt;span class="nv"&gt;$expected&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="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;exact match&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;The example prints &lt;code&gt;exact match&lt;/code&gt;. Quoting only part of a pattern makes that part literal while leaving unquoted wildcards active. For example, &lt;code&gt;[[ &amp;quot;$file&amp;quot; == *&amp;quot;$substring&amp;quot;* ]]&lt;/code&gt; searches for the literal value of &lt;code&gt;$substring&lt;/code&gt;. For other comparison operators, see our guide on &lt;a href="https://linuxize.com/post/how-to-compare-strings-in-bash/"&gt;comparing strings in Bash&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="character-classes"&gt;Character Classes &lt;a class="headline-link" href="#character-classes" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Globs and regexes both support bracket expressions. For example, &lt;code&gt;[0-9]&lt;/code&gt; matches one digit:&lt;/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;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;v2.5&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;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;$version&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; v&lt;span class="o"&gt;[&lt;/span&gt;0-9&lt;span class="o"&gt;]&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;version starts with v then a digit&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;version starts with v then a digit&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The pattern matches a leading &lt;code&gt;v&lt;/code&gt;, one digit, and any remaining characters. The &lt;code&gt;*&lt;/code&gt; does not mean &amp;ldquo;more digits&amp;rdquo;; it can also match periods, letters, and other characters.&lt;/p&gt;
&lt;p&gt;The POSIX named classes work in patterns too: &lt;code&gt;[[:alpha:]]&lt;/code&gt;, &lt;code&gt;[[:digit:]]&lt;/code&gt;, &lt;code&gt;[[:alnum:]]&lt;/code&gt;, &lt;code&gt;[[:space:]]&lt;/code&gt;, &lt;code&gt;[[:upper:]]&lt;/code&gt;, &lt;code&gt;[[:lower:]]&lt;/code&gt;, and &lt;code&gt;[[:punct:]]&lt;/code&gt;. They describe character categories in the current locale. Here we check for a leading letter and a digit later in 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="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;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;abc123&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;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;$input&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt;:alpha:&lt;span class="o"&gt;]]&lt;/span&gt;*&lt;span class="o"&gt;[[&lt;/span&gt;:digit:&lt;span class="o"&gt;]]&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;starts with a letter and contains a later digit&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;The example prints the message, but it also accepts &lt;code&gt;a-7?&lt;/code&gt; because each &lt;code&gt;*&lt;/code&gt; matches arbitrary characters. To require only letters followed by digits, use the anchored regex &lt;code&gt;^[[:alpha:]]+[[:digit:]]+$&lt;/code&gt; with &lt;code&gt;=~&lt;/code&gt;, as explained below.&lt;/p&gt;
&lt;h2 id="extended-globs"&gt;Extended Globs &lt;a class="headline-link" href="#extended-globs" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Turn on the &lt;code&gt;extglob&lt;/code&gt; shell option to use extended glob patterns in filename expansion and &lt;code&gt;case&lt;/code&gt; statements:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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;shopt&lt;/span&gt; -s extglob&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Enable the option before Bash parses a function or compound command containing those patterns. Put the &lt;code&gt;shopt&lt;/code&gt; command on its own line before the construct. Modern Bash also supports extended glob matching on the right of &lt;code&gt;==&lt;/code&gt; and &lt;code&gt;!=&lt;/code&gt; inside &lt;code&gt;[[ ]]&lt;/code&gt; without enabling the option.&lt;/p&gt;
&lt;p&gt;Extended globs provide these constructs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;?(pattern-list)&lt;/code&gt; matches zero or one occurrence.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*(pattern-list)&lt;/code&gt; matches zero or more.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;+(pattern-list)&lt;/code&gt; matches one or more.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@(pattern-list)&lt;/code&gt; matches one of the given patterns.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;!(pattern-list)&lt;/code&gt; matches anything except the patterns.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;pattern-list&lt;/code&gt; is one or more patterns separated by &lt;code&gt;|&lt;/code&gt;. This example checks a fixed list of names, so you can run it without creating 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="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;shopt&lt;/span&gt; -s extglob
&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; f in report.jpg report.png notes.txt&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="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; in
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; report.@&lt;span class="o"&gt;(&lt;/span&gt;jpg&lt;span class="p"&gt;|&lt;/span&gt;png&lt;span class="p"&gt;|&lt;/span&gt;webp&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;image: %s\n&amp;#39;&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="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;/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;image: report.jpg
image: report.png&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The two image names match &lt;code&gt;report.@(jpg|png|webp)&lt;/code&gt;, while &lt;code&gt;notes.txt&lt;/code&gt; does not.&lt;/p&gt;
&lt;p&gt;You can also exclude names during filename expansion. The following loop previews regular files in the current directory, excluding names ending in &lt;code&gt;.tmp&lt;/code&gt; or &lt;code&gt;.bak&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="nb"&gt;shopt&lt;/span&gt; -s extglob nullglob
&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; f in !&lt;span class="o"&gt;(&lt;/span&gt;*.tmp&lt;span class="p"&gt;|&lt;/span&gt;*.bak&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="o"&gt;[[&lt;/span&gt; -f &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 class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;continue&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;would process: %s\n&amp;#39;&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;The &lt;code&gt;-f&lt;/code&gt; check skips directories. &lt;code&gt;nullglob&lt;/code&gt; makes the loop run zero times if no names match, rather than treating the pattern as a filename. These shell options remain enabled in the current shell. With &lt;code&gt;dotglob&lt;/code&gt; off (the default), hidden names are not included, and the pattern does not search subdirectories.&lt;/p&gt;
&lt;h2 id="the--regex-operator"&gt;The =~ Regex Operator &lt;a class="headline-link" href="#the--regex-operator" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Inside &lt;code&gt;[[ ]]&lt;/code&gt;, &lt;code&gt;=~&lt;/code&gt; matches the left-hand string against a POSIX extended regular expression on the right:&lt;/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;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;192.168.1.10&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;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;$input&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;~ ^&lt;span class="o"&gt;[&lt;/span&gt;0-9&lt;span class="o"&gt;]&lt;/span&gt;+&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;0-9&lt;span class="o"&gt;]&lt;/span&gt;+&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;0-9&lt;span class="o"&gt;]&lt;/span&gt;+&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;0-9&lt;span class="o"&gt;]&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;looks like an IPv4 address&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;looks like an IPv4 address&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The pattern matches four groups of digits separated by literal periods. This is only a shape check: it also accepts &lt;code&gt;999.999.999.999&lt;/code&gt; and does not validate octet ranges or leading zeros.&lt;/p&gt;
&lt;p&gt;The anchors (&lt;code&gt;^&lt;/code&gt; and &lt;code&gt;$&lt;/code&gt;) require the regex to match the whole string; without them, the pattern can match anywhere in the input. Bracket expressions (&lt;code&gt;[0-9]&lt;/code&gt;) and quantifiers (&lt;code&gt;+&lt;/code&gt;, &lt;code&gt;*&lt;/code&gt;, &lt;code&gt;?&lt;/code&gt;, &lt;code&gt;{m,n}&lt;/code&gt;) work with POSIX extended regex syntax.&lt;/p&gt;
&lt;p&gt;Leave regex operators unquoted when you want Bash to interpret them. A fully quoted regex (&lt;code&gt;&amp;quot;[0-9]+&amp;quot;&lt;/code&gt;) is matched as a literal string. You can also store the pattern in a variable and expand that variable unquoted:&lt;/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;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;192.168.1.10&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;pattern&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$&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="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;$input&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;~ &lt;span class="nv"&gt;$pattern&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;match&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;This prints &lt;code&gt;match&lt;/code&gt;. The single quotes in the assignment preserve the regex text, including its backslashes. The unquoted &lt;code&gt;$pattern&lt;/code&gt; on the right of &lt;code&gt;=~&lt;/code&gt; lets Bash interpret that text as a regex. Inside &lt;code&gt;[[ ]]&lt;/code&gt;, the expansion does not undergo word splitting or filename expansion.&lt;/p&gt;
&lt;p&gt;The test returns status &lt;code&gt;0&lt;/code&gt; for a match, &lt;code&gt;1&lt;/code&gt; for no match, and &lt;code&gt;2&lt;/code&gt; for an invalid regex. POSIX ERE does not provide PCRE lookarounds, named captures, or non-capturing groups. Use &lt;code&gt;[[:digit:]]&lt;/code&gt; or &lt;code&gt;[0-9]&lt;/code&gt; for digits instead of the PCRE shorthand &lt;code&gt;\d&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="capture-groups-with-bash_rematch"&gt;Capture Groups with BASH_REMATCH &lt;a class="headline-link" href="#capture-groups-with-bash_rematch" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;=~&lt;/code&gt; populates the &lt;code&gt;BASH_REMATCH&lt;/code&gt; array with the matched substrings. &lt;code&gt;BASH_REMATCH[0]&lt;/code&gt; is the full match, and subsequent indices are the capture groups in order:&lt;/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;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;version=2.5.3-rc1&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;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;$input&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;~ &lt;span class="nv"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=([&lt;/span&gt;0-9&lt;span class="o"&gt;]&lt;/span&gt;+&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;0-9&lt;span class="o"&gt;]&lt;/span&gt;+&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;0-9&lt;span class="o"&gt;]&lt;/span&gt;+&lt;span class="o"&gt;)(&lt;/span&gt;-&lt;span class="o"&gt;([&lt;/span&gt;a-z0-9&lt;span class="o"&gt;]&lt;/span&gt;+&lt;span class="o"&gt;))&lt;/span&gt;? &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="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;major&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;BASH_REMATCH&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;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;minor&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;BASH_REMATCH&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;patch&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;BASH_REMATCH&lt;/span&gt;&lt;span class="p"&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;suffix&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;BASH_REMATCH&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;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;major=&lt;/span&gt;&lt;span class="nv"&gt;$major&lt;/span&gt;&lt;span class="s2"&gt; minor=&lt;/span&gt;&lt;span class="nv"&gt;$minor&lt;/span&gt;&lt;span class="s2"&gt; patch=&lt;/span&gt;&lt;span class="nv"&gt;$patch&lt;/span&gt;&lt;span class="s2"&gt; suffix=&lt;/span&gt;&lt;span class="nv"&gt;$suffix&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;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;major=2 minor=5 patch=3 suffix=rc1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Groups 1 through 3 capture the version components. Group 4 contains &lt;code&gt;-rc1&lt;/code&gt;, including the hyphen, and the nested group 5 contains &lt;code&gt;rc1&lt;/code&gt;. If the optional suffix is absent, those captures are empty. This pattern searches for a version within the input; add &lt;code&gt;^&lt;/code&gt; and &lt;code&gt;$&lt;/code&gt; if the entire value must match.&lt;/p&gt;
&lt;p&gt;Read or copy the captures immediately after a successful test. A later successful &lt;code&gt;=~&lt;/code&gt; test replaces them, and a test that returns &amp;ldquo;no match&amp;rdquo; clears the array. Keep the test and the code reading &lt;code&gt;BASH_REMATCH&lt;/code&gt; in the same shell, since a subshell does not update its parent shell&amp;rsquo;s array. For indexed array syntax, see our &lt;a href="https://linuxize.com/post/bash-arrays/"&gt;Bash arrays&lt;/a&gt;
guide.&lt;/p&gt;
&lt;h2 id="the-case-statement"&gt;The case Statement &lt;a class="headline-link" href="#the-case-statement" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For several alternatives, &lt;code&gt;case&lt;/code&gt; reads better than chained &lt;code&gt;if [[ ]]&lt;/code&gt; blocks. Each clause contains glob patterns, and &lt;code&gt;;;&lt;/code&gt; ends the statement after the matching clause runs. Here we classify a filename extension:&lt;/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;ext&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;png&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;case&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$ext&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; jpg&lt;span class="p"&gt;|&lt;/span&gt;jpeg&lt;span class="p"&gt;|&lt;/span&gt;png&lt;span class="p"&gt;|&lt;/span&gt;webp&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;image&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; mp4&lt;span class="p"&gt;|&lt;/span&gt;mkv&lt;span class="p"&gt;|&lt;/span&gt;webm&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;video&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; md&lt;span class="p"&gt;|&lt;/span&gt;txt&lt;span class="p"&gt;|&lt;/span&gt;rst&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;text&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="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&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="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;This prints &lt;code&gt;image&lt;/code&gt; because &lt;code&gt;png&lt;/code&gt; matches the first clause. The &lt;code&gt;*)&lt;/code&gt; branch is the catch-all. In a &lt;code&gt;case&lt;/code&gt; pattern, &lt;code&gt;^&lt;/code&gt; and &lt;code&gt;$&lt;/code&gt; are not regex anchors, and a bare &lt;code&gt;+&lt;/code&gt; is not a regex quantifier. Extended glob forms such as &lt;code&gt;+(pattern)&lt;/code&gt; have their own syntax and require &lt;code&gt;extglob&lt;/code&gt; here. See our &lt;a href="https://linuxize.com/post/bash-case-statement/"&gt;Bash case statement&lt;/a&gt;
guide for argument parsing and other branching examples.&lt;/p&gt;
&lt;h2 id="match-file-extensions"&gt;Match File Extensions &lt;a class="headline-link" href="#match-file-extensions" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A common task is splitting a filename into name and extension. Both globs and &lt;code&gt;=~&lt;/code&gt; work; pick what reads better.&lt;/p&gt;
&lt;p&gt;Use parameter expansion to split off the last extension:&lt;/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;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;report.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="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;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 class="c1"&gt;# report.tar&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;ext&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;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 class="c1"&gt;# gz&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; operator strips the shortest suffix matching &lt;code&gt;.*&lt;/code&gt;, leaving &lt;code&gt;report.tar&lt;/code&gt;. The &lt;code&gt;##&lt;/code&gt; operator strips the longest prefix matching &lt;code&gt;*.&lt;/code&gt;, leaving &lt;code&gt;gz&lt;/code&gt;. If there is no period, both expansions return the original value, so check the name before assuming it has an extension.&lt;/p&gt;
&lt;p&gt;With &lt;code&gt;=~&lt;/code&gt;, you can recognize several archive types and capture the base 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="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;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;report.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&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;$file&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;~ ^&lt;span class="o"&gt;(&lt;/span&gt;.+&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;tar&lt;span class="se"&gt;\.&lt;/span&gt;gz&lt;span class="p"&gt;|&lt;/span&gt;tar&lt;span class="se"&gt;\.&lt;/span&gt;bz2&lt;span class="p"&gt;|&lt;/span&gt;zip&lt;span class="p"&gt;|&lt;/span&gt;7z&lt;span class="o"&gt;)&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;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;BASH_REMATCH&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;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;archive_type&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;BASH_REMATCH&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&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;archive base=&lt;/span&gt;&lt;span class="nv"&gt;$base&lt;/span&gt;&lt;span class="s2"&gt; type=&lt;/span&gt;&lt;span class="nv"&gt;$archive_type&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;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;archive base=report type=tar.gz&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The second capture contains the complete &lt;code&gt;tar.gz&lt;/code&gt; extension because it is one of the regex alternatives. Parameter expansion can also remove a known compound suffix directly: &lt;code&gt;${file%.tar.gz}&lt;/code&gt; gives &lt;code&gt;report&lt;/code&gt;. Use the regex when you want to recognize several suffixes and capture which one matched. Our &lt;a href="https://linuxize.com/post/bash-parameter-expansion/"&gt;Bash parameter expansion&lt;/a&gt;
guide covers the other prefix and suffix operators.&lt;/p&gt;
&lt;h2 id="anchoring-and-whole-string-matches"&gt;Anchoring and Whole-String Matches &lt;a class="headline-link" href="#anchoring-and-whole-string-matches" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Without &lt;code&gt;^&lt;/code&gt; and &lt;code&gt;$&lt;/code&gt;, &lt;code&gt;=~&lt;/code&gt; finds the pattern anywhere in 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="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; &lt;span class="s2"&gt;&amp;#34;abc1234def&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;~ &lt;span class="o"&gt;[&lt;/span&gt;0-9&lt;span class="o"&gt;]&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;contains digits&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;contains digits&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The test succeeds because &lt;code&gt;1234&lt;/code&gt; is a matching substring. To require the entire string to be digits, anchor both ends:&lt;/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; &lt;span class="s2"&gt;&amp;#34;12345&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;~ ^&lt;span class="o"&gt;[&lt;/span&gt;0-9&lt;span class="o"&gt;]&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;all digits&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;This prints &lt;code&gt;all digits&lt;/code&gt;. Unlike &lt;code&gt;=~&lt;/code&gt;, glob matching with &lt;code&gt;==&lt;/code&gt; already checks the whole string; use &lt;code&gt;*&lt;/code&gt; around a literal substring when you want to allow surrounding characters.&lt;/p&gt;
&lt;p&gt;Bash regex matching can span newlines. In particular, &lt;code&gt;.&lt;/code&gt; matches a newline, and &lt;code&gt;^&lt;/code&gt; and &lt;code&gt;$&lt;/code&gt; anchor the complete string rather than each line. You can check this with a string containing an actual 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="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;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;$&amp;#39;a\nb&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;pattern&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;^a.b$&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="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;$input&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;~ &lt;span class="nv"&gt;$pattern&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;matched across a newline&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;This prints &lt;code&gt;matched across a newline&lt;/code&gt;. The &lt;code&gt;$'a\nb'&lt;/code&gt; assignment creates the newline; writing &lt;code&gt;\n&lt;/code&gt; in an ordinary single-quoted regex does not create one. If you need to match each line separately, read and test one line at a time.&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;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[[ &amp;quot;$value&amp;quot; == *.log ]]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Match the entire value against a glob&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[[ &amp;quot;$value&amp;quot; == &amp;quot;$expected&amp;quot; ]]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Compare literal strings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[[ &amp;quot;$value&amp;quot; != *.log ]]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check that a value does not match a glob&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[[ &amp;quot;$value&amp;quot; =~ $pattern ]]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Find a regex match anywhere in a value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pattern='^[0-9]+$'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Regex requiring one or more digits and nothing else&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;${BASH_REMATCH[0]}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read the complete regex match after a successful test&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;${BASH_REMATCH[1]}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read the first parenthesized capture&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;${file%.tar.gz}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove a known compound suffix&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;Quoted regex never matches&lt;/strong&gt;&lt;br&gt;
Quoting the entire right-hand side makes it literal. Store the regex in a variable and expand it as &lt;code&gt;$pattern&lt;/code&gt;, not &lt;code&gt;&amp;quot;$pattern&amp;quot;&lt;/code&gt;, on the right of &lt;code&gt;=~&lt;/code&gt;. You can still quote the string on the left.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extglob patterns are a syntax error&lt;/strong&gt;&lt;br&gt;
For filename expansion or &lt;code&gt;case&lt;/code&gt;, enable &lt;code&gt;shopt -s extglob&lt;/code&gt; before Bash parses the construct containing the pattern. Enabling it inside the same function or compound command can be too late. For a &lt;code&gt;bash -c&lt;/code&gt; command containing these patterns, use &lt;code&gt;bash -O extglob -c '...'&lt;/code&gt; to enable the option before parsing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Regex test returns status 2&lt;/strong&gt;&lt;br&gt;
The regex is syntactically invalid, and Bash prints an error such as &lt;code&gt;[[: invalid regular expression&lt;/code&gt;. Check for unmatched brackets or parentheses and unsupported PCRE syntax such as &lt;code&gt;(?:...)&lt;/code&gt;. Bash may leave earlier captures in place after an invalid test, so read &lt;code&gt;BASH_REMATCH&lt;/code&gt; only when the current test succeeds.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Captures are empty or belong to another match&lt;/strong&gt;&lt;br&gt;
Check the test result before reading &lt;code&gt;BASH_REMATCH&lt;/code&gt;, and copy the values before another &lt;code&gt;=~&lt;/code&gt; test runs. If the match happens in &lt;code&gt;( ... )&lt;/code&gt; or a command substitution, read the captures there rather than expecting them to reach the parent shell.&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;When you write a match, decide whether the whole value must conform to the pattern or whether a substring is enough, then check the right-hand quoting. If the match controls a script&amp;rsquo;s next action, put it inside an &lt;a href="https://linuxize.com/post/bash-if-else-statement/"&gt;if statement&lt;/a&gt;
and read any captures in the successful branch.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/bash-pattern-matching/featured_hu_89345a787f2b6b22.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>SSH Config Cheatsheet</title><link>https://linuxize.com/cheatsheet/ssh-config/</link><pubDate>Tue, 06 Oct 2026 18:20:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/cheatsheet/ssh-config/</guid><description>SSH client config directives at a glance: Host and Match patterns, keys, jump hosts, keepalives, multiplexing, port forwarding, host key checking, and tokens for ~/.ssh/config.</description><content:encoded>&lt;div class="card wrap-commands"&gt;
&lt;h3 id="file-locations"&gt;File Locations &lt;a class="headline-link" href="#file-locations" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;ssh reads the command line first, then your config, then the system config.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Path&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/using-the-ssh-config-file/"&gt;&lt;code&gt;~/.ssh/config&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Per-user client config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/etc/ssh/ssh_config&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;System-wide client config for every user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/etc/ssh/ssh_config.d/*.conf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;System drop-ins, loaded by &lt;code&gt;Include&lt;/code&gt; on Ubuntu and Debian&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;chmod 600 ~/.ssh/config&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Recommended permissions; ssh rejects a file others can write&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ssh -F ~/alt-config host&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use a different config file and skip the defaults&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ssh -F none host&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ignore every config file&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card wrap-commands"&gt;
&lt;h3 id="inspect-the-effective-config"&gt;Inspect the Effective Config &lt;a class="headline-link" href="#inspect-the-effective-config" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Check what ssh will use before you connect.&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;ssh -G host&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print every option that applies to &lt;code&gt;host&lt;/code&gt;, then exit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ssh -G host | grep -i proxyjump&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check one option&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ssh -v host&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show which config lines and keys are applied while connecting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ssh -o User=admin host&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Override a config value for one connection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ssh -T git@github.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Test a host without opening a shell&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card wrap-commands"&gt;
&lt;h3 id="host-patterns"&gt;Host Patterns &lt;a class="headline-link" href="#host-patterns" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A &lt;code&gt;Host&lt;/code&gt; line starts a block that runs until the next &lt;code&gt;Host&lt;/code&gt; or &lt;code&gt;Match&lt;/code&gt; line.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Matches&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Host web&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The alias &lt;code&gt;web&lt;/code&gt; only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Host web1 web2 db&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Any of the listed names&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Host *.example.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every host under &lt;code&gt;example.com&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Host 192.168.1.?&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Single-character wildcard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Host *.example.com !legacy.example.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All but the negated host&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Host *&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every host; put it at the end for defaults&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The first value wins for most directives; repeated &lt;code&gt;IdentityFile&lt;/code&gt; entries accumulate. Put specific blocks above general ones and &lt;code&gt;Host *&lt;/code&gt; last.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card wrap-commands"&gt;
&lt;h3 id="match-blocks"&gt;Match Blocks &lt;a class="headline-link" href="#match-blocks" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Match&lt;/code&gt; applies a block based on conditions, not just the name.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive&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;Match host db*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hostname after &lt;code&gt;HostName&lt;/code&gt; is applied&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Match originalhost db*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Name as typed on the command line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Match user root&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remote user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Match localuser alice&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Local account running ssh&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Match host *.corp exec &amp;quot;nc -z vpn.corp 22&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Apply only when a command exits 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Match all&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Close a conditional block and match everything again&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card wrap-commands"&gt;
&lt;h3 id="connection-basics"&gt;Connection Basics &lt;a class="headline-link" href="#connection-basics" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The directives most host blocks need.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive&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;HostName 203.0.113.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Real hostname or IP behind the alias&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;User admin&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remote login name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-change-ssh-port-in-linux/"&gt;&lt;code&gt;Port 2222&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Remote port; default is &lt;code&gt;22&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AddressFamily inet&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Force IPv4 (&lt;code&gt;inet6&lt;/code&gt; for IPv6)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ConnectTimeout 10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Give up after 10 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ConnectionAttempts 3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Retry the connection before failing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Compression yes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Compress traffic on slow links&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card wrap-commands"&gt;
&lt;h3 id="keys-and-authentication"&gt;Keys and Authentication &lt;a class="headline-link" href="#keys-and-authentication" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Pick the key per host and stop ssh from trying every key it has.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive&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;IdentityFile ~/.ssh/id_ed25519_work&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Private key for this host&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IdentitiesOnly yes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Offer only the configured keys, not every agent key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AddKeysToAgent yes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add the key to the running agent after first use&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CertificateFile ~/.ssh/id_ed25519-cert.pub&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SSH certificate to present&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PreferredAuthentications publickey&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Try public-key authentication only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PasswordAuthentication no&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Disable password authentication; keyboard-interactive prompts can still occur&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;KbdInteractiveAuthentication no&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Disable keyboard-interactive authentication, including its password prompts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IgnoreUnknown UseKeychain&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Skip options that this ssh build does not know&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UseKeychain yes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;macOS only: store key passphrases in the keychain&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card wrap-commands"&gt;
&lt;h3 id="jump-hosts"&gt;Jump Hosts &lt;a class="headline-link" href="#jump-hosts" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Reach private hosts through a bastion.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive&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;ProxyJump bastion&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Connect through the &lt;code&gt;bastion&lt;/code&gt; alias&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ProxyJump admin@bastion.example.com:2222&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Jump host with user and port&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ProxyJump bastion1,bastion2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Chain several jump hosts in order&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ProxyJump none&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Disable jumping; place this before a matching broader block&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ProxyCommand ssh -W %h:%p bastion&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Older equivalent of &lt;code&gt;ProxyJump&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ssh -J bastion internal&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Same jump from the command line&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card wrap-commands"&gt;
&lt;h3 id="keepalives"&gt;Keepalives &lt;a class="headline-link" href="#keepalives" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Stop idle sessions from being dropped by NAT or firewalls.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive&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;ServerAliveInterval 60&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Send a keepalive after 60 seconds without server data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ServerAliveCountMax 3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Disconnect after 3 unanswered keepalives&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TCPKeepAlive yes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;TCP-level keepalives; on by default&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card wrap-commands"&gt;
&lt;h3 id="multiplexing"&gt;Multiplexing &lt;a class="headline-link" href="#multiplexing" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Reuse one TCP connection for later sessions to the same host.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive or 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;ControlMaster auto&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Open a master connection, or reuse an existing one&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ControlPath ~/.ssh/cm-%C&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Socket path; &lt;code&gt;%C&lt;/code&gt; keeps it short and unique&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ControlPersist 10m&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Keep the master open 10 minutes after the last session&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ssh -O check host&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check whether a master connection is running&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ssh -O exit host&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Close the master connection&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card wrap-commands"&gt;
&lt;h3 id="port-forwarding"&gt;Port Forwarding &lt;a class="headline-link" href="#port-forwarding" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Set up tunnels every time you connect to a host.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive&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-setup-ssh-tunneling/"&gt;&lt;code&gt;LocalForward 8080 localhost:80&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Local port 8080 to port 80 on the remote side&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LocalForward 3307 db.internal:3306&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reach a host that only the server can see&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RemoteForward 9000 localhost:3000&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remote port 9000 to local port 3000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-setup-ssh-socks-tunnel-for-private-browsing/"&gt;&lt;code&gt;DynamicForward 1080&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;SOCKS proxy on local port 1080&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ExitOnForwardFailure yes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Abort if a forward cannot be set up&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ForwardAgent yes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Forward your agent; set it only for trusted hosts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ForwardX11 yes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Forward X11 for GUI applications&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card wrap-commands"&gt;
&lt;h3 id="host-key-checking"&gt;Host Key Checking &lt;a class="headline-link" href="#host-key-checking" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Control how ssh treats new and changed server keys.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive&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;StrictHostKeyChecking ask&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prompt for new hosts; the default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;StrictHostKeyChecking accept-new&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Trust new hosts, still refuse changed keys&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;StrictHostKeyChecking yes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Refuse any host not in &lt;code&gt;known_hosts&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UserKnownHostsFile ~/.ssh/known_hosts_lab&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Separate &lt;code&gt;known_hosts&lt;/code&gt; file for a group of hosts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UserKnownHostsFile /dev/null&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Do not record keys; for throwaway VMs only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;HashKnownHosts yes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hash hostnames written to &lt;code&gt;known_hosts&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UpdateHostKeys yes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Accept additional keys the server announces&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card wrap-commands"&gt;
&lt;h3 id="session-and-environment"&gt;Session and Environment &lt;a class="headline-link" href="#session-and-environment" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Change what happens after you log in.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive&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;RemoteCommand tmux new -A -s main&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run a command instead of a login shell&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RequestTTY yes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allocate a terminal; needed with &lt;code&gt;RemoteCommand&lt;/code&gt; for interactive tools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SetEnv APP_ENV=prod&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Send a variable; the server must allow it with &lt;code&gt;AcceptEnv&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SendEnv LANG LC_*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pass local variables by name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LogLevel ERROR&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hide warnings; &lt;code&gt;DEBUG&lt;/code&gt; for troubleshooting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Include ~/.ssh/config.d/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Load more files; put it at the top of the file&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card wrap-commands"&gt;
&lt;h3 id="tokens"&gt;Tokens &lt;a class="headline-link" href="#tokens" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Expanded in &lt;code&gt;ControlPath&lt;/code&gt;, &lt;code&gt;IdentityFile&lt;/code&gt;, &lt;code&gt;RemoteCommand&lt;/code&gt;, and similar directives. Supported tokens vary by directive.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Token&lt;/th&gt;
&lt;th&gt;Expands To&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;%h&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remote hostname after &lt;code&gt;HostName&lt;/code&gt;&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;Hostname as typed on the command line&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;Remote port&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;%r&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remote user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;%u&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Local user&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;Local home directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;%C&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hash of local host, remote host, port, remote user, and jump-host setting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;%%&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A literal &lt;code&gt;%&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;ProxyCommand&lt;/code&gt; accepts only &lt;code&gt;%%&lt;/code&gt;, &lt;code&gt;%h&lt;/code&gt;, &lt;code&gt;%n&lt;/code&gt;, &lt;code&gt;%p&lt;/code&gt;, and &lt;code&gt;%r&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card wrap-commands"&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;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&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;Bad owner or permissions on ~/.ssh/config&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check ownership with &lt;code&gt;ls -l ~/.ssh/config&lt;/code&gt;; the owner must be you or root. Fix incorrect ownership, then run &lt;code&gt;chmod 600 ~/.ssh/config&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Bad configuration option&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Typo, or an option this ssh version lacks; see &lt;code&gt;man ssh_config&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Option is ignored&lt;/td&gt;
&lt;td&gt;An earlier block set it first; check &lt;code&gt;ssh -G host&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Too many authentication failures&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add &lt;code&gt;IdentitiesOnly yes&lt;/code&gt; and an &lt;code&gt;IdentityFile&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/fix-ssh-permission-denied-publickey/"&gt;&lt;code&gt;Permission denied (publickey)&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ssh -v host&lt;/code&gt; and look for &lt;code&gt;Offering public key&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;too long for Unix domain socket&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Shorten &lt;code&gt;ControlPath&lt;/code&gt;; use &lt;code&gt;%C&lt;/code&gt; instead of &lt;code&gt;%r@%h:%p&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card wrap-commands"&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 full SSH client 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/using-the-ssh-config-file/"&gt;&lt;code&gt;Using the SSH Config File&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Host blocks, patterns, precedence, and examples&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;ssh options and connection examples&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-setup-ssh-tunneling/"&gt;&lt;code&gt;How to Set Up SSH Tunneling&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Local, remote, and dynamic port forwarding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/ssh-folder-files-and-permissions/"&gt;&lt;code&gt;SSH Folder Files and Permissions&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;What lives in &lt;code&gt;~/.ssh&lt;/code&gt; and the correct modes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/cheatsheet/ssh/"&gt;&lt;code&gt;SSH Cheatsheet&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;ssh, keys, agent, scp, and sftp commands&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;</content:encoded></item><item><title>How to Resize and Extend a Linux Partition and Filesystem</title><link>https://linuxize.com/post/how-to-resize-and-extend-a-linux-partition/</link><pubDate>Tue, 06 Oct 2026 11:25:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/how-to-resize-and-extend-a-linux-partition/</guid><category>disk</category><description>Extend a Linux partition and grow ext4, XFS, Btrfs, or LVM storage after a disk resize, with layout checks, safe ext4 shrink steps, and troubleshooting.</description><content:encoded>&lt;p&gt;You resized the disk of your cloud server from 50 GiB to 100 GiB, rebooted, and &lt;code&gt;df -h&lt;/code&gt; still shows the old size. This is one of the most common storage tasks on Linux: the disk grew, but the partition and the filesystem on top of it did not. Each layer has to be extended separately before the space becomes usable.&lt;/p&gt;
&lt;p&gt;This guide explains how to grow a partition and its filesystem to fill new disk space, including the extra steps needed on LVM setups, and how to shrink a partition when you have to.&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;Take a backup or snapshot before changing partition boundaries. Confirm the disk, partition number, filesystem type, and mount point before every write operation. The device names below are examples; replace them with the devices on your system, and never change a partition&amp;rsquo;s start position to extend it.&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;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;Preview growing partition 1 on /dev/sda&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo growpart --dry-run /dev/sda 1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grow partition 1 on /dev/sda&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo growpart /dev/sda 1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grow an ext4 filesystem&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo resize2fs /dev/sda1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grow an XFS filesystem mounted at /&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo xfs_growfs -d /&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grow a single-device Btrfs filesystem mounted at /&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo btrfs filesystem resize max /&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grow an LVM physical volume&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo pvresize /dev/sda3&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extend an LVM volume and its ext4 or XFS filesystem&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo lvextend -r -l +100%FREE /dev/ubuntu-vg/ubuntu-lv&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Use only the commands that match your layout. &lt;code&gt;+100%FREE&lt;/code&gt; consumes all free extents in the volume group; use a fixed increase when you need to keep space for other volumes or snapshots.&lt;/p&gt;
&lt;h2 id="disk-partition-and-filesystem-layers"&gt;Disk, Partition, and Filesystem Layers &lt;a class="headline-link" href="#disk-partition-and-filesystem-layers" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://linuxize.com/post/linux-block-devices-partitions-filesystems/"&gt;Storage on Linux is stacked&lt;/a&gt;
. At the bottom is the disk (&lt;code&gt;/dev/sda&lt;/code&gt;), which holds a partition table describing one or more partitions (&lt;code&gt;/dev/sda1&lt;/code&gt;). A plain partition contains a filesystem such as ext4 or XFS. With LVM, a physical volume supplies space to a volume group, which allocates that space to logical volumes. The filesystem sits on a logical volume.&lt;/p&gt;
&lt;p&gt;Enlarging a virtual disk only changes the bottom layer. To use the new space you extend each layer above it, in order: partition, LVM physical volume and logical volume if present, then filesystem. If only the filesystem is smaller than its partition, growing the filesystem alone is enough. If an LVM volume group already has free extents, you can extend a logical volume without enlarging the disk or partition.&lt;/p&gt;
&lt;p&gt;The examples cover plain partitions and ordinary LVM logical volumes. Encrypted mappings, software RAID, and LVM thin pools need additional steps; do not apply this sequence unchanged to those layouts.&lt;/p&gt;
&lt;h2 id="step-1-identify-the-layout"&gt;Step 1: Identify the Layout &lt;a class="headline-link" href="#step-1-identify-the-layout" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Start by looking at what you have. The &lt;a href="https://linuxize.com/post/lsblk-command-in-linux/"&gt;&lt;code&gt;lsblk&lt;/code&gt; command&lt;/a&gt;
shows the disks, partitions, filesystem types, and mount points:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS&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 SIZE TYPE FSTYPE MOUNTPOINTS
sda 100G disk
└─sda1 50G part ext4 /&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The disk &lt;code&gt;sda&lt;/code&gt; is 100 GiB, but the partition &lt;code&gt;sda1&lt;/code&gt; holding the root filesystem is still 50 GiB. &lt;code&gt;lsblk&lt;/code&gt; uses powers of 1024 for its size suffixes, so &lt;code&gt;G&lt;/code&gt; here means GiB. We still need to check where the free space sits before growing the partition.&lt;/p&gt;
&lt;p&gt;Next, check the filesystem type, because the grow command differs between ext4, XFS, and Btrfs:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;df -hT /&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;Filesystem Type Size Used Avail Use% Mounted on
/dev/sda1 ext4 49G 41G 5.6G 88% /&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In this example the root filesystem is ext4 on &lt;code&gt;/dev/sda1&lt;/code&gt;. On a data volume, replace &lt;code&gt;/&lt;/code&gt; with its mount point. If &lt;code&gt;lsblk&lt;/code&gt; shows entries of type &lt;code&gt;lvm&lt;/code&gt;, follow the &lt;a href="#extending-lvm-volumes"&gt;LVM section&lt;/a&gt;
, which uses partition 3 rather than partition 1.&lt;/p&gt;
&lt;p&gt;On NVMe devices, partition names include a &lt;code&gt;p&lt;/code&gt;: partition 1 of &lt;code&gt;/dev/nvme0n1&lt;/code&gt; is &lt;code&gt;/dev/nvme0n1p1&lt;/code&gt;. Virtio disks commonly use names such as &lt;code&gt;/dev/vda&lt;/code&gt; and &lt;code&gt;/dev/vda1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you enlarged the disk while the VM was running and &lt;code&gt;lsblk&lt;/code&gt; still reports the old disk size, first confirm that the hypervisor or cloud provider completed the resize. For a SCSI device that exposes a &lt;code&gt;rescan&lt;/code&gt; file, ask the kernel to rescan 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;echo&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; sudo tee /sys/class/block/sda/device/rescan&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This path is specific to the device driver; it is not a general NVMe or virtio rescan command. If the file does not exist, follow your platform&amp;rsquo;s rescan procedure or reboot. Run &lt;code&gt;lsblk&lt;/code&gt; again and confirm the larger disk size before continuing.&lt;/p&gt;
&lt;p&gt;Next, inspect the partition boundaries and free regions with &lt;a href="https://linuxize.com/post/parted-command-in-linux/"&gt;&lt;code&gt;parted&lt;/code&gt;&lt;/a&gt;
. If it is missing, install the &lt;code&gt;parted&lt;/code&gt; package with 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 parted /dev/sda -- unit MiB print free&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;Free Space&lt;/code&gt; region you want must begin directly after the target partition. If another partition sits between it and the new space, &lt;code&gt;growpart&lt;/code&gt; cannot move that partition out of the way. On an enlarged GPT disk, &lt;code&gt;parted&lt;/code&gt; may offer to repair the backup header; see &lt;a href="#troubleshooting"&gt;Troubleshooting&lt;/a&gt;
before accepting that repair.&lt;/p&gt;
&lt;h2 id="step-2-grow-the-partition"&gt;Step 2: Grow the Partition &lt;a class="headline-link" href="#step-2-grow-the-partition" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The easiest way to grow a partition is &lt;code&gt;growpart&lt;/code&gt;, a tool built for exactly this job. It supports MBR and GPT disks and can extend a partition in use when the kernel supports updating its size. It changes the end boundary without moving the start.&lt;/p&gt;
&lt;p&gt;On Ubuntu, Debian, and Derivatives, install it 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 apt update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install cloud-guest-utils&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;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 cloud-utils-growpart&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;growpart&lt;/code&gt; takes the whole disk and the partition number as separate arguments. Preview the change 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;sudo growpart --dry-run /dev/sda &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;The preview reports the proposed change and the old and new partition tables without writing them. Check that partition 1 is the intended target and its start stays the same, then 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 growpart /dev/sda &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;For partition 1 on an NVMe disk, the equivalent grow 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="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 growpart /dev/nvme0n1 &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;Run the command for your device, not both examples. A &lt;code&gt;CHANGED&lt;/code&gt; message confirms that the partition table was updated. The partition grows into adjacent free space, stopping at the next partition or the last usable part of the disk. &lt;code&gt;NOCHANGE&lt;/code&gt; means no qualifying growth was possible; it does not necessarily mean the filesystem fills the disk.&lt;/p&gt;
&lt;h3 id="alternative-growing-the-partition-with-parted"&gt;Alternative: Growing the Partition with parted &lt;a class="headline-link" href="#alternative-growing-the-partition-with-parted" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If &lt;code&gt;parted&lt;/code&gt; is installed, you can use its &lt;code&gt;resizepart&lt;/code&gt; command instead of &lt;code&gt;growpart&lt;/code&gt;. Start it against the disk:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 parted /dev/sda&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;At the prompt, inspect the layout again. If partition 1 is the last partition and free space follows it, resize its end to the last usable part of the disk:&lt;/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;(parted) unit MiB
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;(parted) print free
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;(parted) resizepart 1 100%
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;(parted) print
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;(parted) quit&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;These are commands entered at the &lt;code&gt;(parted)&lt;/code&gt; prompt, not shell commands. &lt;code&gt;parted&lt;/code&gt; applies a resize immediately; &lt;code&gt;quit&lt;/code&gt; does not save a staged change. If it asks about modifying an in-use partition, confirm only after checking the target and that you are extending its end. Do not use &lt;code&gt;100%&lt;/code&gt; when another partition follows the target.&lt;/p&gt;
&lt;p&gt;Both tools only change the partition table. Recent versions of &lt;a href="https://linuxize.com/post/fdisk-command-in-linux/"&gt;&lt;code&gt;fdisk&lt;/code&gt;&lt;/a&gt;
also provide an &lt;code&gt;e&lt;/code&gt; resize command; check the &lt;code&gt;m&lt;/code&gt; menu in your installed version.&lt;/p&gt;
&lt;p&gt;Before growing the filesystem, confirm that the kernel sees the larger 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;lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS /dev/sda&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If the partition row still has its old size, try notifying the kernel with &lt;code&gt;sudo partprobe /dev/sda&lt;/code&gt;. If that fails because the device is busy, reboot and verify with &lt;code&gt;lsblk&lt;/code&gt; before proceeding.&lt;/p&gt;
&lt;h2 id="step-3-grow-the-filesystem"&gt;Step 3: Grow the Filesystem &lt;a class="headline-link" href="#step-3-grow-the-filesystem" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Run only the command for the filesystem type you found in step 1. Mounted ext4, XFS, and Btrfs filesystems can normally grow online, provided the kernel already sees the larger underlying device. LVM users should follow the next section first.&lt;/p&gt;
&lt;h3 id="ext4"&gt;ext4 &lt;a class="headline-link" href="#ext4" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For ext4, run &lt;code&gt;resize2fs&lt;/code&gt; against the partition device. Without a size argument it grows the filesystem to fill the 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;sudo resize2fs /dev/sda1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For the NVMe example, the filesystem device is &lt;code&gt;/dev/nvme0n1p1&lt;/code&gt;, so you would use &lt;code&gt;sudo resize2fs /dev/nvme0n1p1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;On mounted ext4, the output reports an online resize and the resulting block count. The tool also supports ext2 and ext3, but online resizing depends on filesystem and kernel support. If an offline check is required, unmount a data filesystem or use live media for a root filesystem. Never run &lt;code&gt;e2fsck&lt;/code&gt; on a mounted filesystem.&lt;/p&gt;
&lt;h3 id="xfs"&gt;XFS &lt;a class="headline-link" href="#xfs" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;XFS uses its own tool. Pass the mount point, and use &lt;code&gt;-d&lt;/code&gt; to grow the data section to the maximum available size. The filesystem must be mounted:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 xfs_growfs -d /&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For a data filesystem, replace &lt;code&gt;/&lt;/code&gt; with its mount point. If the command reports that the data size is unchanged, check the partition or logical volume size rather than rerunning it.&lt;/p&gt;
&lt;h3 id="btrfs"&gt;Btrfs &lt;a class="headline-link" href="#btrfs" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Btrfs also resizes by mount point. For a single-device filesystem, 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 btrfs filesystem resize max /&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;max&lt;/code&gt; keyword grows the filesystem to fill the underlying device. It defaults to device ID 1. For a multi-device filesystem, find the enlarged device&amp;rsquo;s ID with &lt;code&gt;sudo btrfs filesystem show /&lt;/code&gt;, then specify it explicitly, such as &lt;code&gt;sudo btrfs filesystem resize 2:max /&lt;/code&gt; for device ID 2. Resizing one device does not resize every device in the filesystem.&lt;/p&gt;
&lt;p&gt;If a grow command is missing, install its filesystem package: &lt;code&gt;e2fsprogs&lt;/code&gt; for ext4, &lt;code&gt;xfsprogs&lt;/code&gt; for XFS, or &lt;code&gt;btrfs-progs&lt;/code&gt; for Btrfs.&lt;/p&gt;
&lt;h2 id="extending-lvm-volumes"&gt;Extending LVM Volumes &lt;a class="headline-link" href="#extending-lvm-volumes" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;On an LVM system, identify the physical volume, volume group, and logical volume before changing anything:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 pvs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo vgs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo lvs -o lv_path,vg_name,lv_size&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Use the &lt;code&gt;PV&lt;/code&gt; and &lt;code&gt;LV Path&lt;/code&gt; values from your output rather than assuming the Ubuntu names below. &lt;code&gt;VFree&lt;/code&gt; in the &lt;code&gt;vgs&lt;/code&gt; output shows space the volume group can already allocate. If enough space is available there, skip the partition growth and &lt;code&gt;pvresize&lt;/code&gt; steps.&lt;/p&gt;
&lt;p&gt;In this example, &lt;code&gt;/dev/sda3&lt;/code&gt; is the physical volume. If it needs to grow and adjacent free space is available, preview and then extend partition 3:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 growpart --dry-run /dev/sda &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;p&gt;Check that the preview targets partition 3 and preserves its start, then apply 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 growpart /dev/sda &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;p&gt;After verifying that the kernel sees the larger partition, &lt;code&gt;lsblk&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="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 100G 0 disk
├─sda1 8:1 0 1M 0 part
├─sda2 8:2 0 2G 0 part /boot
└─sda3 8:3 0 98G 0 part
└─ubuntu--vg-ubuntu--lv 252:0 0 48G 0 lvm /&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The partition &lt;code&gt;sda3&lt;/code&gt; is already about 98 GiB, but the logical volume holding the root filesystem is still 48 GiB. The physical volume and logical volume need to expose the extra space to the filesystem.&lt;/p&gt;
&lt;p&gt;First, extend the physical volume so LVM sees the larger 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;sudo pvresize /dev/sda3&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; Physical volume &amp;#34;/dev/sda3&amp;#34; changed
1 physical volume(s) resized or updated / 0 physical volume(s) not resized&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This makes the extra extents available in the volume group. Check &lt;code&gt;sudo vgs&lt;/code&gt; again to confirm the increase in &lt;code&gt;VFree&lt;/code&gt;. If the physical volume occupies a whole disk rather than a partition, skip &lt;code&gt;growpart&lt;/code&gt; and give &lt;code&gt;pvresize&lt;/code&gt; that whole-disk device instead.&lt;/p&gt;
&lt;p&gt;For an ordinary logical volume with ext4 or XFS, extend it into the available space and grow its filesystem in the same 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;sudo lvextend -r -l +100%FREE /dev/ubuntu-vg/ubuntu-lv&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The options are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-r&lt;/code&gt; - Resize the filesystem along with the logical volume, using the appropriate installed filesystem helper.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-l +100%FREE&lt;/code&gt; - Add all remaining free extents in the volume group to this logical volume.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If both parts succeed, you do not need a separate &lt;code&gt;resize2fs&lt;/code&gt; or &lt;code&gt;xfs_growfs&lt;/code&gt; run. To add 20 GiB while keeping the remaining space available, use &lt;code&gt;sudo lvextend -r -L +20G /dev/ubuntu-vg/ubuntu-lv&lt;/code&gt; instead.&lt;/p&gt;
&lt;p&gt;For Btrfs on a logical volume, extend the logical volume without &lt;code&gt;-r&lt;/code&gt;, then run &lt;code&gt;btrfs filesystem resize max&lt;/code&gt; on its mount point. The filesystem helpers used by &lt;code&gt;lvextend -r&lt;/code&gt; do not provide a general Btrfs resize workflow.&lt;/p&gt;
&lt;h2 id="verifying-the-new-size"&gt;Verifying the New Size &lt;a class="headline-link" href="#verifying-the-new-size" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Whichever path you took, confirm the filesystem capacity:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;df -h /&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;Filesystem Size Used Avail Use% Mounted on
/dev/sda1 98G 41G 53G 44% /&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For the plain ext4 example, the root filesystem now reports about 98 GiB. Its reported capacity is lower than the disk&amp;rsquo;s size because of filesystem overhead, and available space also accounts for ext4&amp;rsquo;s reserved blocks. On LVM, the &lt;code&gt;Filesystem&lt;/code&gt; column shows the logical volume instead. For more ways to read this output, see our guide to &lt;a href="https://linuxize.com/post/how-to-check-disk-space-in-linux-using-the-df-command/"&gt;checking disk space with df&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="shrinking-a-partition"&gt;Shrinking a Partition &lt;a class="headline-link" href="#shrinking-a-partition" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Shrinking is riskier than growing and supports fewer filesystems.&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;Shrinking works in the opposite order: shrink the filesystem first, then the partition. Shrinking the partition first can cut off filesystem data. Keep a backup, and stop if any check or resize command fails. This example is for an unencrypted ext4 data partition without LVM; shrinking an LVM setup also requires reducing its intervening layers in the correct order.&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code&gt;resize2fs&lt;/code&gt; can shrink ext2, ext3, and ext4 only while unmounted. For a root filesystem, boot from live media and make sure it has not automatically mounted the partition. Btrfs has its own mounted resize command. Current &lt;a href="https://man7.org/linux/man-pages/man8/xfs_growfs.8.html" target="_blank" rel="noopener noreferrer"&gt;XFS documentation&lt;/a&gt;
describes limited shrinking of the last allocation group, which is not a general way to shrink an arbitrary XFS filesystem. For a substantial XFS reduction, plan to back up, recreate a smaller filesystem, and restore.&lt;/p&gt;
&lt;p&gt;For this ext4 example, assume &lt;code&gt;/dev/sdb1&lt;/code&gt; starts at &lt;code&gt;1MiB&lt;/code&gt; and is currently larger than 41 GiB. Inspect its actual start before proceeding:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 parted /dev/sdb -- unit MiB print&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The target filesystem size must leave enough room for the files and metadata. Stop processes using the data volume, then unmount 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;Once unmounting succeeds, run a forced &lt;a href="https://linuxize.com/post/fsck-command-in-linux/"&gt;filesystem check&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 e2fsck -f /dev/sdb1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Resolve any reported errors before continuing. Then shrink the filesystem to 40 GiB:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 resize2fs /dev/sdb1 40G&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Here &lt;code&gt;G&lt;/code&gt; means GiB, or powers of 1024. Do not replace it with &lt;code&gt;GB&lt;/code&gt; in the partition command: &lt;code&gt;parted&lt;/code&gt; treats &lt;code&gt;GB&lt;/code&gt; as decimal gigabytes. Also, &lt;code&gt;resizepart&lt;/code&gt; takes an absolute disk endpoint, not a partition size.&lt;/p&gt;
&lt;p&gt;We will leave a 1 GiB margin by making the partition 41 GiB long. With the verified start at &lt;code&gt;1MiB&lt;/code&gt;, its endpoint is &lt;code&gt;1 + (41 * 1024) = 41985MiB&lt;/code&gt;. If your partition starts elsewhere, calculate its endpoint from that actual start instead. Open &lt;code&gt;parted&lt;/code&gt; on the disk:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 parted /dev/sdb&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Confirm the start again, change only the end, accept the shrink warning only after the filesystem resize succeeded, then print 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="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;(parted) unit MiB
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;(parted) print
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;(parted) resizepart 1 41985MiB
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;(parted) print
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;(parted) quit&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Verify that the kernel sees the new 41 GiB partition. If necessary, run &lt;code&gt;sudo partprobe /dev/sdb&lt;/code&gt; and check 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;lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS /dev/sdb&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;While the filesystem is still unmounted, check it 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 e2fsck -f /dev/sdb1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;After a successful check, grow the filesystem to fill the new partition exactly:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 resize2fs /dev/sdb1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You can now &lt;a href="https://linuxize.com/post/how-to-mount-and-unmount-file-systems-in-linux/"&gt;mount the filesystem&lt;/a&gt;
at its original mount point and verify its capacity with &lt;code&gt;df -h&lt;/code&gt;. The &lt;a href="https://www.gnu.org/software/parted/manual/html_node/unit.html" target="_blank" rel="noopener noreferrer"&gt;GNU Parted unit reference&lt;/a&gt;
explains why explicit binary units are useful when setting exact boundaries.&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;growpart prints NOCHANGE&lt;/strong&gt;&lt;br&gt;
There may be no adjacent free space, another partition may block growth, or the increase may be below the tool&amp;rsquo;s threshold. Confirm that the disk itself grew, then inspect &lt;code&gt;parted&lt;/code&gt;&amp;rsquo;s &lt;code&gt;print free&lt;/code&gt; output. If the partition already uses the available space, check the LVM and filesystem layers instead. &lt;code&gt;NOCHANGE&lt;/code&gt; returns exit status 1; it is not a successful resize.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;df still shows the old size after growing the partition&lt;/strong&gt;&lt;br&gt;
Compare the sizes in &lt;code&gt;lsblk&lt;/code&gt; and &lt;code&gt;df -hT&lt;/code&gt;. If the partition is larger but the filesystem is unchanged, run its matching grow command. On LVM, verify that the physical volume and logical volume grew before resizing the filesystem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The table changed, but lsblk still shows the old partition size&lt;/strong&gt;&lt;br&gt;
The kernel has not adopted the new boundary. Try &lt;code&gt;sudo partprobe /dev/sda&lt;/code&gt;. If it reports that the device is busy, reboot and check the partition size before growing the next layer. Do not repeatedly rewrite the table to fix a kernel reread failure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;lsblk does not show the new disk size&lt;/strong&gt;&lt;br&gt;
Confirm that the provider completed the resize. Use the SCSI rescan from step 1 only if your device exposes that file; NVMe and virtio devices may need their platform&amp;rsquo;s procedure or a reboot.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;parted warns about unusable space on a GPT disk&lt;/strong&gt;&lt;br&gt;
GPT keeps a backup header at the end of the disk. After a disk grows, the header may still be at its former end. If the warning explicitly refers to the disk&amp;rsquo;s newly available space or the backup table&amp;rsquo;s old location, confirm the disk and backup, then accept &lt;code&gt;Fix&lt;/code&gt;. Do not accept an unrelated corruption warning as routine resize maintenance.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;lvextend grew the volume but failed to resize the filesystem&lt;/strong&gt;&lt;br&gt;
Check &lt;code&gt;sudo lvs -o lv_path,lv_size&lt;/code&gt; and &lt;code&gt;df -hT&lt;/code&gt; before retrying. The logical volume may already have the requested capacity. Install the missing filesystem helper or resolve the reported error, then grow ext4 on the logical-volume device or XFS on its mount point. Do not repeat a relative &lt;code&gt;-L +20G&lt;/code&gt; increase without checking, since it can allocate another 20 GiB.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;growpart cannot create its temporary directory&lt;/strong&gt;&lt;br&gt;
The filesystem holding &lt;code&gt;/tmp&lt;/code&gt; may be full. Free a little space and try again. If deleting files does not recover capacity, check our guide to &lt;a href="https://linuxize.com/post/fix-no-space-left-on-device/"&gt;fixing No space left on device&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;Before another capacity increase, save the &lt;code&gt;lsblk&lt;/code&gt; and &lt;code&gt;df -hT&lt;/code&gt; output so you can see which layer changed. Keeping free extents in an LVM volume group also gives you room to extend individual volumes later without resizing the disk again.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/how-to-resize-and-extend-a-linux-partition/featured_hu_2655ea1aef1cf82a.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>SQLite Cheatsheet</title><link>https://linuxize.com/cheatsheet/sqlite/</link><pubDate>Sun, 04 Oct 2026 16:55:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/cheatsheet/sqlite/</guid><description>SQLite command-line reference for opening databases, running SQL, formatting results, importing CSV files, and making backups.</description><content:encoded>&lt;div class="card"&gt;
&lt;h3 id="open-and-inspect"&gt;Open and Inspect &lt;a class="headline-link" href="#open-and-inspect" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For worked examples, see our &lt;a href="https://linuxize.com/post/sqlite3-command-line/"&gt;sqlite3 command 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;sqlite3 tasks.db&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Open a database; create the file on the first write if it is missing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sqlite3 -readonly tasks.db&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Inspect an existing database without allowing writes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sqlite3 :memory:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Open a database that disappears when the shell exits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sqlite3 --version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the installed SQLite version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.databases&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show open databases and their file paths&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.tables&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List tables and views&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.schema tasks&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the SQL that defines a table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.help&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show available dot commands&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;Exit the SQLite shell&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="run-sql"&gt;Run SQL &lt;a class="headline-link" href="#run-sql" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;End SQL statements with a semicolon. Dot commands do not need one. Use &lt;code&gt;WHERE&lt;/code&gt; to limit which rows an update changes.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;SQL&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;CREATE TABLE tasks (id INTEGER PRIMARY KEY, title TEXT NOT NULL, done INTEGER DEFAULT 0);&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a task table with automatically assigned IDs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;INSERT INTO tasks (title) VALUES ('Check backups');&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add a task with the default &lt;code&gt;done&lt;/code&gt; value of &lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SELECT * FROM tasks LIMIT 10;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Preview up to ten rows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SELECT title FROM tasks WHERE done = 0;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List unfinished tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SELECT count(*) FROM tasks;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Count all rows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UPDATE tasks SET done = 1 WHERE id = 1;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mark one task as completed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="output-formats"&gt;Output Formats &lt;a class="headline-link" href="#output-formats" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Set the mode before changing headers. Column spacing and alignment can vary by SQLite version.&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;.mode list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Separate columns with pipes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.mode column&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Align values in columns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.mode box&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add table borders&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.mode json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print results as JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.mode csv&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Format results as CSV&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.headers on&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Include column names where the mode supports them&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.headers off&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hide column names where the mode supports it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="csv-import-and-export"&gt;CSV Import and Export &lt;a class="headline-link" href="#csv-import-and-export" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Imports append rows. Use &lt;code&gt;--skip 1&lt;/code&gt; for a CSV header when the target table already exists. To export, set &lt;code&gt;.mode csv&lt;/code&gt; and &lt;code&gt;.headers on&lt;/code&gt; before &lt;code&gt;.once&lt;/code&gt;. Choose an unused output filename.&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;.import --csv contacts.csv contacts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Import CSV; a new table uses the first row as column names&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.import --csv --skip 1 contacts.csv contacts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Skip the header when importing into an existing table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.once report.csv&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Send the next query result to a file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SELECT id, title FROM tasks;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run after &lt;code&gt;.once&lt;/code&gt; to export the selected columns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.output report.csv&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Send query results to a file until output is changed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.output&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Send query results back to the terminal&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="transactions-and-locks"&gt;Transactions and Locks &lt;a class="headline-link" href="#transactions-and-locks" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Back up first and preview the matching rows before deleting. Use &lt;code&gt;ROLLBACK&lt;/code&gt; to discard changes or &lt;code&gt;COMMIT&lt;/code&gt; to keep 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;BEGIN;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start a transaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SELECT * FROM tasks WHERE done = 1;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Preview completed tasks before deletion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DELETE FROM tasks WHERE done = 1;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete completed tasks inside the transaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ROLLBACK;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Undo the uncommitted changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;COMMIT;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Keep the changes; they can no longer be rolled back&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.timeout 5000&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Wait up to five seconds for a lock; does not release another connection&amp;rsquo;s lock&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="backup-and-restore"&gt;Backup and Restore &lt;a class="headline-link" href="#backup-and-restore" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Run these commands in your terminal. Choose unused backup filenames and restore SQL into a new database. Use &lt;code&gt;.backup&lt;/code&gt; for a live database, including committed data still in its WAL file.&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;sqlite3 -readonly tasks.db &amp;quot;.backup backup.db&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a consistent database copy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sqlite3 -readonly tasks.db .dump &amp;gt; backup.sql&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Export the database as SQL statements&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sqlite3 -bail restored.db &amp;lt; backup.sql&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Restore SQL into a new database and stop on errors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sqlite3 -readonly backup.db &amp;quot;PRAGMA integrity_check;&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check database structure; &lt;code&gt;ok&lt;/code&gt; means the check passed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;</content:encoded></item><item><title>sqlite3 Command Line: Create and Query SQLite Databases</title><link>https://linuxize.com/post/sqlite3-command-line/</link><pubDate>Sun, 04 Oct 2026 14:20:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/sqlite3-command-line/</guid><category>database</category><category>linux commands</category><description>Create and query SQLite databases with sqlite3 on Linux. Import CSV files, format query results, and back up or restore your data.</description><content:encoded>&lt;p&gt;When you need to inspect an application&amp;rsquo;s database or query a CSV file, you do not always need a database server. SQLite stores a database in a file, with no service to configure, and the &lt;code&gt;sqlite3&lt;/code&gt; command gives you an interactive SQL shell for working with it.&lt;/p&gt;
&lt;p&gt;This guide explains how to use the &lt;code&gt;sqlite3&lt;/code&gt; command-line shell to create databases, run queries, import and export data, and make backups.&lt;/p&gt;
&lt;h2 id="installing-sqlite3"&gt;Installing sqlite3 &lt;a class="headline-link" href="#installing-sqlite3" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;On Ubuntu, Debian, and Derivatives, install the command-line shell with the following 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;sudo apt install sqlite3&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 package is named &lt;code&gt;sqlite&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 sqlite&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&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;sqlite3 --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 SQLite version, release date, and source identifier. The installed version depends on your distribution.&lt;/p&gt;
&lt;h2 id="opening-and-creating-a-database"&gt;Opening and Creating a Database &lt;a class="headline-link" href="#opening-and-creating-a-database" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;sqlite3&lt;/code&gt; command has the following 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="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;sqlite3 [OPTIONS] [DATABASE_FILE] [SQL_OR_DOT_COMMAND...]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Pass a filename to open a database. If the file does not exist, SQLite creates it when you first write to the 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;sqlite3 tasks.db&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You are now in the interactive shell at the &lt;code&gt;sqlite&amp;gt;&lt;/code&gt; prompt. Opening a nonexistent database and quitting immediately leaves no database file behind. Running &lt;code&gt;sqlite3&lt;/code&gt; with no filename opens a temporary in-memory database, useful for trying out SQL that you do not want to keep.&lt;/p&gt;
&lt;p&gt;Two kinds of input work at the &lt;code&gt;sqlite&amp;gt;&lt;/code&gt; prompt: SQL statements, which end with a semicolon, and dot commands like &lt;code&gt;.help&lt;/code&gt; and &lt;code&gt;.quit&lt;/code&gt;, which start with a dot and control the shell itself. Put each dot command on its own line without a semicolon. Exit with &lt;code&gt;.quit&lt;/code&gt; or Ctrl+D.&lt;/p&gt;
&lt;p&gt;The examples below use a new &lt;code&gt;tasks.db&lt;/code&gt; database. Enter commands marked with &lt;code&gt;sqlite&amp;gt;&lt;/code&gt; inside the SQLite shell, and commands marked with &lt;code&gt;$&lt;/code&gt; in your Linux terminal. You do not need to type either prompt.&lt;/p&gt;
&lt;h2 id="creating-tables-and-inserting-data"&gt;Creating Tables and Inserting Data &lt;a class="headline-link" href="#creating-tables-and-inserting-data" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Create a table and add a few rows:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="sqlite&amp;gt;"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;CREATE TABLE tasks &lt;span class="o"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; id INTEGER PRIMARY KEY,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; title TEXT NOT NULL,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; priority INTEGER DEFAULT 3,
&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; INTEGER DEFAULT &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="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;INSERT INTO tasks &lt;span class="o"&gt;(&lt;/span&gt;title, priority&lt;span class="o"&gt;)&lt;/span&gt; VALUES &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Rotate backup drives&amp;#39;&lt;/span&gt;, 1&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;INSERT INTO tasks &lt;span class="o"&gt;(&lt;/span&gt;title, priority&lt;span class="o"&gt;)&lt;/span&gt; VALUES &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Update nginx config&amp;#39;&lt;/span&gt;, 2&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;INSERT INTO tasks &lt;span class="o"&gt;(&lt;/span&gt;title&lt;span class="o"&gt;)&lt;/span&gt; VALUES &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Clean up /tmp scripts&amp;#39;&lt;/span&gt;&lt;span class="o"&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;An &lt;code&gt;INTEGER PRIMARY KEY&lt;/code&gt; column receives an ID automatically when you omit it from an insert. In this new table, the rows receive IDs &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;2&lt;/code&gt;, and &lt;code&gt;3&lt;/code&gt;. SQLite normally chooses one more than the largest existing ID, so it can reuse an ID after the row with the highest ID is deleted. The separate &lt;code&gt;AUTOINCREMENT&lt;/code&gt; keyword prevents reuse of previously committed IDs, but adds overhead and is usually unnecessary.&lt;/p&gt;
&lt;p&gt;SQLite uses flexible typing for ordinary columns. For example, the &lt;code&gt;priority&lt;/code&gt; column can hold text that cannot be converted to an integer. The &lt;code&gt;INTEGER PRIMARY KEY&lt;/code&gt; column is an exception and must contain an integer. If you need stricter type checks, SQLite 3.37.0 and later support tables declared with &lt;code&gt;STRICT&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Check what you have:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="sqlite&amp;gt;"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.tables
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.schema tasks&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;SQLite lists the table name and its 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="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;tasks
CREATE TABLE tasks (
id INTEGER PRIMARY KEY,
title TEXT NOT NULL,
priority INTEGER DEFAULT 3,
done INTEGER DEFAULT 0
);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;.tables&lt;/code&gt; lists all tables in the database, and &lt;code&gt;.schema&lt;/code&gt; prints the SQL that defines them, which is the fastest way to orient yourself in an unfamiliar database file.&lt;/p&gt;
&lt;h2 id="querying-data"&gt;Querying Data &lt;a class="headline-link" href="#querying-data" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To list unfinished tasks in priority order, run a &lt;code&gt;SELECT&lt;/code&gt; statement. We explicitly select list mode and turn headers off to get the same pipe-separated output across SQLite versions:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="sqlite&amp;gt;"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.mode list
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.headers off
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;SELECT * FROM tasks WHERE &lt;span class="k"&gt;done&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; ORDER BY priority&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 output 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="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|Rotate backup drives|1|0
2|Update nginx config|2|0
3|Clean up /tmp scripts|3|0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Each line contains the task ID, title, priority, and completion flag. All three tasks have a &lt;code&gt;done&lt;/code&gt; value of &lt;code&gt;0&lt;/code&gt;, so they are included.&lt;/p&gt;
&lt;p&gt;SQLite 3.52.0 and later use a boxed table by default in interactive sessions. Earlier versions use pipe-separated list output, which remains the default for batch scripts. Setting the mode explicitly avoids relying on that default. See the &lt;a href="https://sqlite.org/climode.html" target="_blank" rel="noopener noreferrer"&gt;SQLite output formatting documentation&lt;/a&gt;
for details.&lt;/p&gt;
&lt;p&gt;For aligned columns with column names, switch to column mode and enable headers:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="sqlite&amp;gt;"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.mode column
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.headers on
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;SELECT * FROM tasks WHERE &lt;span class="k"&gt;done&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; ORDER BY priority&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 output will look 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;id title priority done
-- --------------------- -------- ----
1 Rotate backup drives 1 0
2 Update nginx config 2 0
3 Clean up /tmp scripts 3 0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The rows are unchanged, but the headers and spacing make the columns easier to read. Column spacing and alignment can vary by SQLite version. You can also use &lt;code&gt;.mode box&lt;/code&gt; for table borders, &lt;code&gt;.mode json&lt;/code&gt; for JSON output, and &lt;code&gt;.mode csv&lt;/code&gt; for CSV export. The mode stays in effect until you change it or exit the shell.&lt;/p&gt;
&lt;h3 id="updating-and-deleting-rows"&gt;Updating and Deleting Rows &lt;a class="headline-link" href="#updating-and-deleting-rows" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To mark the second task as completed, use &lt;code&gt;UPDATE&lt;/code&gt; with a &lt;code&gt;WHERE&lt;/code&gt; clause, then check the matching row:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="sqlite&amp;gt;"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;UPDATE tasks SET &lt;span class="k"&gt;done&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; WHERE &lt;span class="nv"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 2&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;SELECT * FROM tasks WHERE &lt;span class="k"&gt;done&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 1&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 query shows task &lt;code&gt;2&lt;/code&gt; with a &lt;code&gt;done&lt;/code&gt; value of &lt;code&gt;1&lt;/code&gt;. Without a &lt;code&gt;WHERE&lt;/code&gt; clause, an &lt;code&gt;UPDATE&lt;/code&gt; or &lt;code&gt;DELETE&lt;/code&gt; statement affects every row in the table.&lt;/p&gt;
&lt;p&gt;Before deleting rows from an important database, make a backup. You can try a deletion inside a transaction and undo it with &lt;code&gt;ROLLBACK&lt;/code&gt;. The following example temporarily deletes completed tasks, shows the remaining rows, and restores the deleted 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="sqlite&amp;gt;"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;BEGIN&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;DELETE FROM tasks WHERE &lt;span class="k"&gt;done&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 1&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;SELECT * FROM tasks ORDER BY id&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ROLLBACK&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 query shows tasks &lt;code&gt;1&lt;/code&gt; and &lt;code&gt;3&lt;/code&gt;, but &lt;code&gt;ROLLBACK&lt;/code&gt; brings task &lt;code&gt;2&lt;/code&gt; back. To keep the deletion, run the transaction again and replace &lt;code&gt;ROLLBACK;&lt;/code&gt; with &lt;code&gt;COMMIT;&lt;/code&gt; after checking the result. Once committed, the deletion cannot be undone with &lt;code&gt;ROLLBACK&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="running-queries-from-the-shell"&gt;Running Queries from the Shell &lt;a class="headline-link" href="#running-queries-from-the-shell" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Run the commands in this section from your Linux terminal. If you are still at the &lt;code&gt;sqlite&amp;gt;&lt;/code&gt; prompt, leave the SQLite shell 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="sqlite&amp;gt;"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.quit&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For scripting, skip the interactive prompt entirely by passing SQL as an 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;sqlite3 tasks.db &lt;span class="s2"&gt;&amp;#34;SELECT title FROM tasks WHERE priority = 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 output 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="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;Rotate backup drives&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The query returns the title of the task with priority &lt;code&gt;1&lt;/code&gt;, then the command exits.&lt;/p&gt;
&lt;p&gt;Formatting options give you the same control as the dot commands. For a readable report that also opens the database read-only, 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;sqlite3 -readonly -header -column tasks.db &lt;span class="s2"&gt;&amp;#34;SELECT * FROM tasks ORDER BY 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;The options used here are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-readonly&lt;/code&gt; - Open the database without allowing writes. The file must already exist.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-header&lt;/code&gt; - Include column names in the output.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-column&lt;/code&gt; - Align the output in columns.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Use &lt;code&gt;-json&lt;/code&gt; or &lt;code&gt;-csv&lt;/code&gt; instead of &lt;code&gt;-column&lt;/code&gt; when another program needs to read the results. This one-shot form is useful in &lt;a href="https://linuxize.com/post/scheduling-cron-jobs-with-crontab/"&gt;cron jobs&lt;/a&gt;
and shell pipelines: the command opens a file, runs the query, and exits.&lt;/p&gt;
&lt;h2 id="importing-and-exporting-csv"&gt;Importing and Exporting CSV &lt;a class="headline-link" href="#importing-and-exporting-csv" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For this example, save the following contents as &lt;code&gt;contacts.csv&lt;/code&gt; in the same directory as &lt;code&gt;tasks.db&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;contacts.csv&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;name,email
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Alice,alice@example.com
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Bob,bob@example.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Reopen the database from your 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;sqlite3 tasks.db&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To load the CSV file, use &lt;code&gt;.import --csv&lt;/code&gt; inside the SQLite shell. If the target table does not exist, SQLite creates it and uses the first row as column names:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="sqlite&amp;gt;"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.import --csv contacts.csv contacts
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.mode column
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.headers on
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;SELECT * FROM contacts&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 output will look 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;name email
----- -----------------
Alice alice@example.com
Bob bob@example.com&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The first CSV row supplied the &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;email&lt;/code&gt; column names. Only Alice and Bob were imported as data.&lt;/p&gt;
&lt;p&gt;If the target table already exists, the first row is imported as data too. Use &lt;code&gt;--skip 1&lt;/code&gt; when the CSV file has a header. For example, we can create a separate table and import the same file into 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="sqlite&amp;gt;"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;CREATE TABLE mailing_list &lt;span class="o"&gt;(&lt;/span&gt;name TEXT, email TEXT&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;.import --csv --skip &lt;span class="m"&gt;1&lt;/span&gt; contacts.csv mailing_list&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This imports the two data rows without adding a row containing the column labels. Importing the same file again appends more rows; it does not replace existing data.&lt;/p&gt;
&lt;p&gt;To export unfinished tasks, set CSV mode, enable headers, and send the next query to a file with &lt;code&gt;.once&lt;/code&gt;. Choose an unused filename because an existing output file will be overwritten:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="sqlite&amp;gt;"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.mode csv
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.headers on
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.once report.csv
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;SELECT title, priority FROM tasks WHERE &lt;span class="k"&gt;done&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; ORDER BY priority&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 result lands in &lt;code&gt;report.csv&lt;/code&gt; with a header row. Subsequent queries print to the terminal again, still in CSV mode. Use &lt;code&gt;.output report.csv&lt;/code&gt; instead of &lt;code&gt;.once&lt;/code&gt; when several queries should go to the same file, and &lt;code&gt;.output&lt;/code&gt; alone to return to the terminal.&lt;/p&gt;
&lt;h2 id="backing-up-and-restoring"&gt;Backing Up and Restoring &lt;a class="headline-link" href="#backing-up-and-restoring" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Run the backup and restore commands below from your Linux terminal. Use &lt;code&gt;.quit&lt;/code&gt; first if you are still in the SQLite shell.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;.backup&lt;/code&gt; command creates a consistent copy of the database, including committed data that is still in a write-ahead log (WAL). Choose an unused backup filename because &lt;code&gt;.backup&lt;/code&gt; can overwrite an existing 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;sqlite3 -readonly tasks.db &lt;span class="s2"&gt;&amp;#34;.backup tasks-backup.db&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 backup is a database file you can open directly with &lt;code&gt;sqlite3&lt;/code&gt;. To check its structure, 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;sqlite3 -readonly tasks-backup.db &lt;span class="s2"&gt;&amp;#34;PRAGMA integrity_check;&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;For a database that passes the check, the output 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="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;ok&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This checks the database structure. It does not confirm that the backup contains every row you expected, so also check important tables before relying on it.&lt;/p&gt;
&lt;p&gt;For a plain-text SQL backup, use &lt;code&gt;.dump&lt;/code&gt;. The shell redirection also overwrites an existing file, so choose an unused 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;sqlite3 -readonly tasks.db .dump &amp;gt; tasks-backup.sql&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Restore the SQL into a new database file. Make sure &lt;code&gt;restored.db&lt;/code&gt; does not already exist. The dump contains statements to recreate the tables and insert the rows:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sqlite3 -bail restored.db &amp;lt; tasks-backup.sql&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;-bail&lt;/code&gt; option stops processing if a statement fails. The SQL dump is plain text, so you can inspect or compress it before restoring. If you also work with MySQL, see our guide to &lt;a href="https://linuxize.com/post/how-to-back-up-and-restore-mysql-databases-with-mysqldump/"&gt;backing up and restoring databases with mysqldump&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;Use &lt;code&gt;.backup&lt;/code&gt; for databases that an application is using. Copying only the &lt;code&gt;.db&lt;/code&gt; file can miss committed data stored in a &lt;code&gt;-wal&lt;/code&gt; file, and copying files during a write can produce an inconsistent backup. See SQLite&amp;rsquo;s &lt;a href="https://sqlite.org/howtocorrupt.html#_backup_or_restore_while_a_transaction_is_active" target="_blank" rel="noopener noreferrer"&gt;backup guidance&lt;/a&gt;
for the conditions needed to make a safe file copy.&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/sqlite/"&gt;SQLite cheatsheet&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;For a printable quick reference, see the &lt;a href="https://linuxize.com/cheatsheet/sqlite/"&gt;SQLite 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;Open or create a database&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sqlite3 file.db&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inspect an existing database read-only&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sqlite3 -readonly file.db&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run one query and exit&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sqlite3 file.db &amp;quot;SELECT ...;&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List tables&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.tables&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Show table definitions&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.schema&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Readable output&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.mode column&lt;/code&gt; then &lt;code&gt;.headers on&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JSON or CSV output&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.mode json&lt;/code&gt; / &lt;code&gt;.mode csv&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Import CSV into a new table&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.import --csv data.csv tablename&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Import CSV with a header into an existing table&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.import --csv --skip 1 data.csv tablename&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Export a query to a file&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.once out.csv&lt;/code&gt; before the query&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Back up as SQL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sqlite3 -readonly file.db .dump &amp;gt; backup.sql&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restore SQL into a new database&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sqlite3 -bail new.db &amp;lt; backup.sql&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Safe binary backup&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.backup backup.db&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check database structure&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PRAGMA integrity_check;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exit the shell&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.quit&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;Database is locked&lt;/strong&gt;&lt;br&gt;
Another connection may be holding a write transaction. Finish it with &lt;code&gt;COMMIT&lt;/code&gt; or &lt;code&gt;ROLLBACK&lt;/code&gt;, or wait for the application to finish. Inside your SQLite session, &lt;code&gt;.timeout 5000&lt;/code&gt; makes operations wait up to five seconds for a lock before failing. It does not release another connection&amp;rsquo;s lock.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Unable to open the database file&lt;/strong&gt;&lt;br&gt;
Check the path and permissions. SQLite can create a database file, but it cannot create a missing parent directory. For writes, you also need permission to create journal or WAL files in the database directory. With &lt;code&gt;-readonly&lt;/code&gt;, the database file must already exist.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No such table&lt;/strong&gt;&lt;br&gt;
Check &lt;code&gt;.databases&lt;/code&gt; to see which file is open and &lt;code&gt;.tables&lt;/code&gt; to see its tables. A relative path is resolved from your current directory, so a typo can open a new, empty database. Use an absolute path or &lt;code&gt;-readonly&lt;/code&gt; when inspecting an existing database.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SQLite keeps showing the continuation prompt&lt;/strong&gt;&lt;br&gt;
A SQL statement is incomplete. Add the missing semicolon or close an unmatched quote or parenthesis. Press Ctrl+C to cancel the current input, then enter the statement again. Dot commands must start on their own line and cannot be entered in the middle of an unfinished SQL statement.&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;When inspecting an existing application database, start with &lt;code&gt;-readonly&lt;/code&gt; and &lt;code&gt;.tables&lt;/code&gt;. Before changing its data, make a &lt;code&gt;.backup&lt;/code&gt; copy and check that you can open it.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/sqlite3-command-line/featured_hu_fa53e7007db61dbe.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>Fix npm Error: unable to get local issuer certificate</title><link>https://linuxize.com/post/fix-npm-unable-to-get-local-issuer-certificate/</link><pubDate>Sat, 03 Oct 2026 14:10:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/fix-npm-unable-to-get-local-issuer-certificate/</guid><category>nodejs</category><description>Resolve the npm UNABLE_TO_GET_ISSUER_CERT_LOCALLY error by trusting the right CA certificate instead of disabling TLS verification.</description><content:encoded>&lt;p&gt;You run &lt;code&gt;npm install&lt;/code&gt; and the command fails with &lt;code&gt;npm error code UNABLE_TO_GET_ISSUER_CERT_LOCALLY&lt;/code&gt; and the message &lt;code&gt;unable to get local issuer certificate&lt;/code&gt;. The registry may open normally in a browser, but npm cannot verify its HTTPS connection.&lt;/p&gt;
&lt;p&gt;This guide explains what the error means and how to fix it by trusting the correct certificate authority while keeping TLS verification enabled.&lt;/p&gt;
&lt;h2 id="what-the-error-means"&gt;What the Error Means &lt;a class="headline-link" href="#what-the-error-means" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A typical failure 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;npm error code UNABLE_TO_GET_ISSUER_CERT_LOCALLY
npm error errno UNABLE_TO_GET_ISSUER_CERT_LOCALLY
npm error request to https://registry.npmjs.org/express failed, reason: unable to get local issuer certificate&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When npm connects to a registry over HTTPS, Node.js checks the server&amp;rsquo;s certificate against a list of trusted certificate authorities. To trust the server, Node must follow the chain from the server certificate up to a root CA it already has. &lt;code&gt;UNABLE_TO_GET_ISSUER_CERT_LOCALLY&lt;/code&gt; means it could not find an issuer needed to complete that chain, so the connection fails.&lt;/p&gt;
&lt;p&gt;Common causes include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A corporate proxy, firewall, or antivirus that inspects HTTPS and signs certificates with an internal CA.&lt;/li&gt;
&lt;li&gt;A private registry whose certificate was issued by a CA that Node does not trust.&lt;/li&gt;
&lt;li&gt;A registry or proxy that does not send a required intermediate certificate.&lt;/li&gt;
&lt;li&gt;An outdated CA bundle, or an npm setting that points to the wrong certificates.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Node.js normally uses its own bundled root certificates. Your browser may trust an internal CA through a different certificate store, which explains why the browser works while npm fails. The Node.js &lt;a href="https://nodejs.org/learn/http/enterprise-network-configuration" target="_blank" rel="noopener noreferrer"&gt;enterprise network guide&lt;/a&gt;
describes the available trust settings.&lt;/p&gt;
&lt;h2 id="check-the-npm-configuration"&gt;Check the npm Configuration &lt;a class="headline-link" href="#check-the-npm-configuration" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before we change certificate settings, check the Node.js version and npm configuration from the directory where the install fails:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 config get registry
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm config get cafile
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm config get ca
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm config get strict-ssl&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The public registry is &lt;code&gt;https://registry.npmjs.org/&lt;/code&gt;. The &lt;code&gt;ca&lt;/code&gt; and &lt;code&gt;cafile&lt;/code&gt; settings normally return &lt;code&gt;null&lt;/code&gt;, and &lt;code&gt;strict-ssl&lt;/code&gt; should return &lt;code&gt;true&lt;/code&gt;. A configured &lt;code&gt;cafile&lt;/code&gt; is not necessarily wrong, but the file must contain the CA certificates needed for your registry. If you previously disabled verification, &lt;a href="#keep-tls-verification-enabled"&gt;restore it&lt;/a&gt;
before testing the connection.&lt;/p&gt;
&lt;p&gt;npm reads settings from several places. A project&amp;rsquo;s &lt;code&gt;.npmrc&lt;/code&gt; overrides your user configuration, usually &lt;code&gt;~/.npmrc&lt;/code&gt;. Environment variables and command-line options can override both, so check those sources if a setting differs from what you expect.&lt;/p&gt;
&lt;p&gt;Scoped packages can use a separate registry. Replace &lt;code&gt;@myorg&lt;/code&gt; with your package scope and check its mapping:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 config get @myorg:registry&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If this returns a URL, packages under that scope use that registry. Also check the URL in the original error: the failed connection may be to a package download host rather than the default registry.&lt;/p&gt;
&lt;h2 id="find-the-missing-certificate"&gt;Find the Missing Certificate &lt;a class="headline-link" href="#find-the-missing-certificate" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;On a managed workstation, ask your IT team for the root CA used by the HTTPS inspection proxy. For a private registry, ask its administrator which CA issued the certificate. Obtain the CA from a trusted source; do not copy a certificate from the failed connection and assume it is safe to trust.&lt;/p&gt;
&lt;p&gt;The CA file must be in PEM format, with a &lt;code&gt;-----BEGIN CERTIFICATE-----&lt;/code&gt; header. A &lt;code&gt;.crt&lt;/code&gt; or &lt;code&gt;.cer&lt;/code&gt; extension alone does not tell you how the certificate is encoded.&lt;/p&gt;
&lt;p&gt;To inspect the certificates presented by the public registry, 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 s_client -connect registry.npmjs.org:443 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -servername registry.npmjs.org -showcerts &amp;lt;/dev/null&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For another registry, replace both occurrences of &lt;code&gt;registry.npmjs.org&lt;/code&gt; with the hostname from the failed URL. The &lt;code&gt;-servername&lt;/code&gt; option sends the hostname so the server can select the right certificate, and &lt;code&gt;-showcerts&lt;/code&gt; prints the certificates it sends. The input redirection closes the session without waiting for keyboard input.&lt;/p&gt;
&lt;p&gt;Compare the subject and issuer names in the output. An internal CA name can help identify an inspection proxy, but this is not a verified chain, and servers normally omit the root certificate. OpenSSL also uses its own trust settings, so a successful OpenSSL check does not prove that Node trusts the same CA. See the &lt;a href="https://docs.openssl.org/master/man1/openssl-s_client/" target="_blank" rel="noopener noreferrer"&gt;s_client documentation&lt;/a&gt;
for details.&lt;/p&gt;
&lt;p&gt;If npm connects through an explicit HTTP proxy, use that proxy when inspecting the connection. Replace &lt;code&gt;proxy.example.com:8080&lt;/code&gt; with its hostname and port:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 -proxy proxy.example.com:8080 &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -connect registry.npmjs.org:443 -servername registry.npmjs.org &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -showcerts &amp;lt;/dev/null&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;OpenSSL does not read npm&amp;rsquo;s proxy configuration. Without &lt;code&gt;-proxy&lt;/code&gt;, you may inspect a direct connection that presents different certificates from the connection npm uses.&lt;/p&gt;
&lt;h2 id="method-1-trust-the-ca-certificate"&gt;Method 1: Trust the CA Certificate &lt;a class="headline-link" href="#method-1-trust-the-ca-certificate" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Save the verified CA file somewhere your user can read it. Replace &lt;code&gt;/path/to/company-ca.pem&lt;/code&gt; in the examples below with its absolute path.&lt;/p&gt;
&lt;p&gt;To add the CA to Node&amp;rsquo;s bundled roots, set &lt;code&gt;NODE_EXTRA_CA_CERTS&lt;/code&gt;, then test the registry 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;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;NODE_EXTRA_CA_CERTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/path/to/company-ca.pem&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm ping&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;A successful &lt;code&gt;npm ping&lt;/code&gt; reports &lt;code&gt;PONG&lt;/code&gt;. It checks the configured registry without installing packages. Node reads &lt;code&gt;NODE_EXTRA_CA_CERTS&lt;/code&gt; when a process starts, so start a new npm command after setting it. For other running Node tools, restart the process too.&lt;/p&gt;
&lt;p&gt;To keep the setting across terminal sessions, add the export line to your shell configuration, such as &lt;code&gt;~/.bashrc&lt;/code&gt; for Bash or &lt;code&gt;~/.zshrc&lt;/code&gt; for Zsh. In CI, set the variable in the job environment before npm starts and make the certificate file available to the runner.&lt;/p&gt;
&lt;h3 id="set-a-ca-bundle-for-npm-only"&gt;Set a CA Bundle for npm Only &lt;a class="headline-link" href="#set-a-ca-bundle-for-npm-only" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you want the certificate setting to apply to npm only, use &lt;code&gt;cafile&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 config &lt;span class="nb"&gt;set&lt;/span&gt; cafile &lt;span class="s2"&gt;&amp;#34;/path/to/company-ca.pem&amp;#34;&lt;/span&gt; --location&lt;span class="o"&gt;=&lt;/span&gt;user
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm ping&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This writes the setting to your user npm configuration. The file can contain one or more PEM CA certificates. Unlike &lt;code&gt;NODE_EXTRA_CA_CERTS&lt;/code&gt;, npm&amp;rsquo;s &lt;code&gt;cafile&lt;/code&gt; supplies an explicit CA list that replaces Node&amp;rsquo;s default trusted roots for those connections. If you need both internal and public CAs, use a bundle that contains the required authorities.&lt;/p&gt;
&lt;div class="note callout callout-note"&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;Note&lt;/span&gt;&lt;/div&gt;
&lt;div class="callout-body"&gt;An explicit npm &lt;code&gt;ca&lt;/code&gt; or &lt;code&gt;cafile&lt;/code&gt; setting takes precedence over &lt;code&gt;NODE_EXTRA_CA_CERTS&lt;/code&gt;. Setting the environment variable will not add certificates to npm&amp;rsquo;s configured CA list. The same applies to Node tools that supply their own TLS &lt;code&gt;ca&lt;/code&gt; option.&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;To switch back to &lt;code&gt;NODE_EXTRA_CA_CERTS&lt;/code&gt;, remove the &lt;code&gt;ca&lt;/code&gt; and &lt;code&gt;cafile&lt;/code&gt; entries you previously added to your user 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;npm config delete ca cafile --location&lt;span class="o"&gt;=&lt;/span&gt;user&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This changes the user configuration only. Run &lt;code&gt;npm config get ca&lt;/code&gt; and &lt;code&gt;npm config get cafile&lt;/code&gt; again from your project directory. If either setting is still present, check the project&amp;rsquo;s &lt;code&gt;.npmrc&lt;/code&gt;, the global configuration, and environment variables such as &lt;code&gt;npm_config_cafile&lt;/code&gt;. Remove the conflicting setting from the source that defines it.&lt;/p&gt;
&lt;p&gt;Once &lt;code&gt;npm ping&lt;/code&gt; succeeds with verification enabled, retry 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;npm install&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If the install still fails, compare its error URL with the registry checked by &lt;code&gt;npm ping&lt;/code&gt;. Packages can be downloaded from other hosts, which may need a different certificate chain.&lt;/p&gt;
&lt;h2 id="method-2-use-the-system-ca-store"&gt;Method 2: Use the System CA Store &lt;a class="headline-link" href="#method-2-use-the-system-ca-store" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If the system certificate bundle is missing or outdated, reinstall it and rebuild the store. You can also add an internal CA to the system store when other applications need it.&lt;/p&gt;
&lt;h3 id="ubuntu-debian-and-derivatives"&gt;Ubuntu, Debian, and Derivatives &lt;a class="headline-link" href="#ubuntu-debian-and-derivatives" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Reinstall the &lt;code&gt;ca-certificates&lt;/code&gt; package and rebuild the store:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 --reinstall ca-certificates
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo update-ca-certificates&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To add a verified internal CA, copy it into &lt;code&gt;/usr/local/share/ca-certificates/&lt;/code&gt; with a &lt;code&gt;.crt&lt;/code&gt; extension, then update the store:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 cp /path/to/company-ca.pem /usr/local/share/ca-certificates/company-ca.crt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo update-ca-certificates&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The certificate must be PEM encoded, with one certificate per file. If your CA bundle contains several certificates, install them as separate &lt;code&gt;.crt&lt;/code&gt; files. The &lt;a href="https://manpages.debian.org/trixie/ca-certificates/update-ca-certificates.8.en.html" target="_blank" rel="noopener noreferrer"&gt;update-ca-certificates manual&lt;/a&gt;
explains these requirements.&lt;/p&gt;
&lt;h3 id="fedora-rhel-and-derivatives"&gt;Fedora, RHEL, and Derivatives &lt;a class="headline-link" href="#fedora-rhel-and-derivatives" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Reinstall the certificate package and regenerate the trust store:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 reinstall ca-certificates
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo update-ca-trust extract&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To add a verified internal CA, place it in the system&amp;rsquo;s trust anchors 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 cp /path/to/company-ca.pem /etc/pki/ca-trust/source/anchors/company-ca.pem
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo update-ca-trust extract&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The second command rebuilds the trust store with the added CA. Red Hat documents this process in its guide to &lt;a href="https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/securing_networks/using-shared-system-certificates_securing-networks" target="_blank" rel="noopener noreferrer"&gt;shared system certificates&lt;/a&gt;
.&lt;/p&gt;
&lt;h3 id="enable-system-certificates-in-nodejs"&gt;Enable System Certificates in Node.js &lt;a class="headline-link" href="#enable-system-certificates-in-nodejs" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Updating the system store does not automatically change the certificates used by most Node.js installations. On Node.js 22.19.0 or later in the 22.x series, or Node.js 24.6.0 and later, enable system CAs 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;export&lt;/span&gt; &lt;span class="nv"&gt;NODE_USE_SYSTEM_CA&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;span class="line"&gt;&lt;span class="cl"&gt;npm ping&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This adds system CAs alongside Node&amp;rsquo;s bundled roots. As with &lt;code&gt;NODE_EXTRA_CA_CERTS&lt;/code&gt;, an explicit npm &lt;code&gt;ca&lt;/code&gt; or &lt;code&gt;cafile&lt;/code&gt; setting overrides that trust list. Older Node versions can use the CA file through &lt;code&gt;NODE_EXTRA_CA_CERTS&lt;/code&gt;; if the bundled public roots are outdated, update Node.js to a supported release.&lt;/p&gt;
&lt;h2 id="keep-tls-verification-enabled"&gt;Keep TLS Verification Enabled &lt;a class="headline-link" href="#keep-tls-verification-enabled" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Setting &lt;code&gt;strict-ssl&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt; or &lt;code&gt;NODE_TLS_REJECT_UNAUTHORIZED&lt;/code&gt; to &lt;code&gt;0&lt;/code&gt; bypasses certificate verification. It hides the problem by allowing a connection whose certificate cannot be verified.&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;Disabling TLS verification allows a third party to impersonate the registry and tamper with package downloads. Trust the correct CA or fix the server&amp;rsquo;s certificate chain while keeping verification enabled.&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;If you previously disabled verification, restore it 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 config &lt;span class="nb"&gt;set&lt;/span&gt; strict-ssl &lt;span class="nb"&gt;true&lt;/span&gt; --location&lt;span class="o"&gt;=&lt;/span&gt;user
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;unset&lt;/span&gt; NODE_TLS_REJECT_UNAUTHORIZED
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm config get strict-ssl&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The last command should return &lt;code&gt;true&lt;/code&gt;. If it does not, check for an override in the project&amp;rsquo;s &lt;code&gt;.npmrc&lt;/code&gt; or environment. Also remove any &lt;code&gt;NODE_TLS_REJECT_UNAUTHORIZED=0&lt;/code&gt; assignment from your shell configuration or CI settings so it does not return in the next session.&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;NODE_EXTRA_CA_CERTS does not fix the error&lt;/strong&gt;&lt;br&gt;
Check for an overriding &lt;code&gt;ca&lt;/code&gt; or &lt;code&gt;cafile&lt;/code&gt; setting first. Confirm that the file is readable and contains PEM certificates, then start a new npm command. A running Node process does not reload the variable when you change it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;npm ping works, but npm install still fails&lt;/strong&gt;&lt;br&gt;
Check the URL in the install error. A scoped registry, a tarball URL in &lt;code&gt;package-lock.json&lt;/code&gt;, or a Git dependency may connect to another host. An install script may also run a tool with its own certificate settings. Diagnose that connection separately.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The install works locally but fails in CI or a container&lt;/strong&gt;&lt;br&gt;
The runner or container has its own environment and certificate store. Copy the trusted CA into that environment, set the certificate variable before npm starts, and confirm the path is readable there. A host&amp;rsquo;s user &lt;code&gt;.npmrc&lt;/code&gt; or system CA store is not automatically available inside a container.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A private registry sends an incomplete chain&lt;/strong&gt;&lt;br&gt;
Ask its administrator to configure the registry or reverse proxy to send the server certificate and required intermediate certificates. Adding a root CA on the client does not replace a missing intermediate in the server&amp;rsquo;s chain.&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;In most cases, the fix is trusting the right CA through &lt;code&gt;NODE_EXTRA_CA_CERTS&lt;/code&gt; or &lt;code&gt;cafile&lt;/code&gt;, not turning off &lt;code&gt;strict-ssl&lt;/code&gt;. For more on inspecting certificate chains, see our guide on &lt;a href="https://linuxize.com/post/how-to-use-openssl/"&gt;using OpenSSL&lt;/a&gt;
. The &lt;a href="https://linuxize.com/post/npm-command/"&gt;npm command reference&lt;/a&gt;
covers package installation and the other commands you will use after the connection is working.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/fix-npm-unable-to-get-local-issuer-certificate/featured_hu_1eeaaff0404deaca.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>How to Diagnose OOM Killer Events in Linux</title><link>https://linuxize.com/post/how-to-diagnose-oom-killer-events-in-linux/</link><pubDate>Wed, 30 Sep 2026 17:05:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/how-to-diagnose-oom-killer-events-in-linux/</guid><category>ram</category><category>kernel</category><category>linux commands</category><description>Diagnose Linux OOM kills with dmesg and journalctl, identify affected processes, check service memory limits, and investigate systemd-oomd events.</description><content:encoded>&lt;p&gt;A service disappears without warning. There is no crash report, just a process that was running and now is not, sometimes followed by systemd restarting it. Before assuming an application bug, check whether it was killed because of memory pressure.&lt;/p&gt;
&lt;p&gt;When the kernel cannot reclaim enough memory to satisfy an allocation, it may invoke the out-of-memory (OOM) killer. The killed process cannot catch the signal or write a final error message, so you need to look outside its own logs.&lt;/p&gt;
&lt;p&gt;This guide explains how to confirm an OOM event, read what the kernel recorded, and choose a fix based on the cause.&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;Search the kernel ring buffer&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo dmesg -T | grep -iE 'oom|out of memory|killed process'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search this boot&amp;rsquo;s kernel journal&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo journalctl -k -g 'oom|out of memory|killed process'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search the previous boot&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo journalctl -k -b -1 -g 'oom|out of memory|killed process'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check userspace OOM kills&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo journalctl -u systemd-oomd --since '2 hours ago'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Show a running process&amp;rsquo;s OOM score&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cat /proc/PID/oom_score&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Show current memory and swap use&lt;/td&gt;
&lt;td&gt;&lt;code&gt;free -h&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Replace &lt;code&gt;PID&lt;/code&gt; with a running process ID. Run the log checks on the affected host; a container may not have access to the host&amp;rsquo;s kernel log.&lt;/p&gt;
&lt;h2 id="what-the-oom-killer-does"&gt;What the OOM Killer Does &lt;a class="headline-link" href="#what-the-oom-killer-does" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Linux can allow applications to reserve more virtual memory than the system can back with RAM and swap. This behavior depends on the overcommit policy. Reserving an address range does not mean that the application is using that much physical memory.&lt;/p&gt;
&lt;p&gt;An OOM event occurs when the kernel cannot satisfy an allocation after attempting to reclaim memory. This can affect the whole host or be restricted to a service or container that has reached its memory limit. An OOM kill does not necessarily mean that every byte of RAM and swap on the host was in use.&lt;/p&gt;
&lt;p&gt;Under the usual selection policy, the kernel considers eligible processes within the affected memory domain and scores them using their memory use and &lt;code&gt;oom_score_adj&lt;/code&gt; settings. It does not know which application matters most to your business. The chosen process receives &lt;code&gt;SIGKILL&lt;/code&gt;, which it cannot handle or ignore.&lt;/p&gt;
&lt;h2 id="confirm-an-oom-event"&gt;Confirm an OOM Event &lt;a class="headline-link" href="#confirm-an-oom-event" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Start with the kernel log. The &lt;a href="https://linuxize.com/post/dmesg-command-in-linux/"&gt;&lt;code&gt;dmesg&lt;/code&gt; command&lt;/a&gt;
prints the kernel ring buffer, and &lt;code&gt;-T&lt;/code&gt; adds human-readable timestamps. Search for both OOM messages and the line identifying the killed 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 dmesg -T &lt;span class="p"&gt;|&lt;/span&gt; grep -iE &lt;span class="s1"&gt;&amp;#39;oom|out of memory|killed process&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;An abbreviated example 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;[Wed Sep 30 09:14:22 2026] node invoked oom-killer: gfp_mask=0x140cca(GFP_HIGHUSER_MOVABLE|__GFP_COMP), order=0, oom_score_adj=0
[Wed Sep 30 09:14:22 2026] Out of memory: Killed process 2417 (node) total-vm:4185672kB, anon-rss:3923116kB, file-rss:0kB, shmem-rss:0kB
[Wed Sep 30 09:14:22 2026] oom_reaper: reaped process 2417 (node), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The first line names the process whose allocation invoked the OOM killer. That process is not necessarily the victim or the main source of memory pressure. The &lt;code&gt;Killed process&lt;/code&gt; line identifies the victim, here PID &lt;code&gt;2417&lt;/code&gt; running &lt;code&gt;node&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;total-vm&lt;/code&gt; value is its virtual address space, not its RAM use. The &lt;code&gt;anon-rss&lt;/code&gt; value shows anonymous resident memory, about 3.7 GiB in this example. &lt;code&gt;file-rss&lt;/code&gt; and &lt;code&gt;shmem-rss&lt;/code&gt; report resident file-backed and shared memory. The final line records the OOM reaper&amp;rsquo;s progress reclaiming the victim&amp;rsquo;s memory.&lt;/p&gt;
&lt;p&gt;On systemd systems, you can also search the journal. This is useful when the ring buffer no longer contains the event:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 -k -g &lt;span class="s1"&gt;&amp;#39;oom|out of memory|killed process&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;The &lt;code&gt;-k&lt;/code&gt; option selects kernel messages from the current boot. The &lt;code&gt;-g&lt;/code&gt; option filters messages with a regular expression; an all-lowercase pattern matches without regard to case. The wider pattern also catches messages such as &lt;code&gt;Memory cgroup out of memory&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If the machine rebooted after the incident, search the previous 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 journalctl -k -b -1 -g &lt;span class="s1"&gt;&amp;#39;oom|out of memory|killed process&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;This works only if logs from that boot were retained. See the &lt;a href="https://linuxize.com/post/journalctl-command-in-linux/"&gt;&lt;code&gt;journalctl&lt;/code&gt; guide&lt;/a&gt;
for boot selection and persistent logging. The timestamps from &lt;code&gt;dmesg -T&lt;/code&gt; can be inaccurate after suspend and resume, so use journal timestamps when comparing events across logs.&lt;/p&gt;
&lt;p&gt;For a systemd service, check its journal around the same time. Replace &lt;code&gt;app.service&lt;/code&gt; with the affected 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 app.service --since &lt;span class="s1"&gt;&amp;#39;2 hours ago&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;Look for the process exit and any restart at the time of the OOM message. A &lt;code&gt;status=9/KILL&lt;/code&gt; entry records a &lt;code&gt;SIGKILL&lt;/code&gt; exit, and a shell exit status of &lt;code&gt;137&lt;/code&gt; is also consistent with that signal. Neither establishes who sent it. A manual kill or another supervisor can produce the same result.&lt;/p&gt;
&lt;h2 id="read-the-process-table-in-the-log"&gt;Read the Process Table in the Log &lt;a class="headline-link" href="#read-the-process-table-in-the-log" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The OOM report normally includes a process table when &lt;code&gt;vm.oom_dump_tasks&lt;/code&gt; is enabled. A cgroup OOM report is restricted to that cgroup&amp;rsquo;s eligible tasks. The filtered commands above hide most of this context, so read the full kernel journal around the event:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 -k --since &lt;span class="s1"&gt;&amp;#39;2026-09-30 09:13:00&amp;#39;&lt;/span&gt; --until &lt;span class="s1"&gt;&amp;#39;2026-09-30 09:16: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;Use the date and time of your incident, and add &lt;code&gt;-b -1&lt;/code&gt; if it happened during the previous boot. An abbreviated table might contain these rows:&lt;/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;[ pid ] uid tgid total_vm rss ... oom_score_adj name
[ 2417] 1000 2417 1046418 980779 ... 0 node
[ 1183] 999 1183 210334 45211 ... 0 mysqld&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;total_vm&lt;/code&gt; and &lt;code&gt;rss&lt;/code&gt; columns are in pages. On a system with 4 KiB pages, the Node.js process&amp;rsquo;s &lt;code&gt;rss&lt;/code&gt; of &lt;code&gt;980779&lt;/code&gt; corresponds to &lt;code&gt;3923116&lt;/code&gt; KiB, matching the kill message. Do not assume the page size is always 4 KiB; check it 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;getconf PAGESIZE&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The result is the page size in bytes. Use the table to identify large consumers, but do not treat the largest value as proof of a leak. Several processes may have exhausted the available memory together, or a legitimate workload may have exceeded a configured limit.&lt;/p&gt;
&lt;h2 id="check-service-and-container-memory-limits"&gt;Check Service and Container Memory Limits &lt;a class="headline-link" href="#check-service-and-container-memory-limits" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A service can hit a cgroup memory limit while the host still has available RAM. Look for &lt;code&gt;Memory cgroup out of memory&lt;/code&gt; or &lt;code&gt;constraint=CONSTRAINT_MEMCG&lt;/code&gt; in the kernel report, along with the affected cgroup path.&lt;/p&gt;
&lt;p&gt;For a systemd service, inspect its current memory use, configured limits, and cgroup 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;systemctl show app.service -p MemoryCurrent -p MemoryHigh -p MemoryMax -p ControlGroup&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Numeric memory values are in bytes. &lt;code&gt;MemoryMax=infinity&lt;/code&gt; means the unit has no explicit hard limit of its own; a parent slice can still impose one.&lt;/p&gt;
&lt;p&gt;On cgroup v2, use the reported &lt;code&gt;ControlGroup&lt;/code&gt; path to inspect event counters. For example, if it is &lt;code&gt;/system.slice/app.service&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 cat /sys/fs/cgroup/system.slice/app.service/memory.events&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The kernel&amp;rsquo;s &lt;a href="https://docs.kernel.org/admin-guide/cgroup-v2.html#memory-interface-files" target="_blank" rel="noopener noreferrer"&gt;cgroup documentation&lt;/a&gt;
explains these counters. &lt;code&gt;oom&lt;/code&gt; counts occasions when the cgroup reached its limit and an allocation was about to fail; &lt;code&gt;oom_kill&lt;/code&gt; counts processes killed by the kernel OOM killer. The counters include descendants and have no timestamps. An &lt;code&gt;oom_kill&lt;/code&gt; value alone does not distinguish a host OOM from a cgroup-limit OOM. Correlate it with the logs, and remember that counters disappear when the cgroup is removed.&lt;/p&gt;
&lt;h2 id="check-systemd-oomd"&gt;Check systemd-oomd &lt;a class="headline-link" href="#check-systemd-oomd" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Some systems also run &lt;code&gt;systemd-oomd&lt;/code&gt;, a userspace service that monitors memory pressure and swap use. It can kill processes in a selected cgroup before the kernel reaches an OOM condition, so there may be no kernel &lt;code&gt;Out of memory&lt;/code&gt; message.&lt;/p&gt;
&lt;p&gt;Check its journal around the time the application disappeared:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 systemd-oomd --since &lt;span class="s1"&gt;&amp;#39;2 hours ago&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;A kill entry identifies the cgroup selected by &lt;code&gt;systemd-oomd&lt;/code&gt;. Match that path and timestamp to the affected service or user session. If the service is not installed or enabled, this check does not apply. Its selection policy is separate from the kernel&amp;rsquo;s &lt;code&gt;oom_score_adj&lt;/code&gt; mechanism; see the &lt;a href="https://www.freedesktop.org/software/systemd/man/latest/systemd-oomd.service.html" target="_blank" rel="noopener noreferrer"&gt;systemd-oomd documentation&lt;/a&gt;
for details.&lt;/p&gt;
&lt;h2 id="inspect-and-adjust-oom-scores"&gt;Inspect and Adjust OOM Scores &lt;a class="headline-link" href="#inspect-and-adjust-oom-scores" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For a running process, &lt;code&gt;/proc&lt;/code&gt; exposes its current OOM score and adjustment. To inspect your current shell safely, use &lt;code&gt;$$&lt;/code&gt;, which expands to its PID:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 /proc/&lt;span class="nv"&gt;$$&lt;/span&gt;/oom_score
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cat /proc/&lt;span class="nv"&gt;$$&lt;/span&gt;/oom_score_adj&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For another process, replace &lt;code&gt;$$&lt;/code&gt; with its current PID. The killed process from the log is already gone, and its PID may later be reused. You cannot recover its old score by reading the same &lt;code&gt;/proc/PID&lt;/code&gt; path after the event.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://man7.org/linux/man-pages/man5/proc_pid_oom_score_adj.5.html" target="_blank" rel="noopener noreferrer"&gt;oom_score_adj setting&lt;/a&gt;
ranges from &lt;code&gt;-1000&lt;/code&gt; to &lt;code&gt;1000&lt;/code&gt;. Positive values make selection more likely, while negative values make it less likely. A value such as &lt;code&gt;-500&lt;/code&gt; does not guarantee that the process will survive. &lt;code&gt;-1000&lt;/code&gt; excludes the process from kernel OOM victim selection, which shifts pressure to other workloads and should not be a routine fix.&lt;/p&gt;
&lt;p&gt;If you need to lower a systemd service&amp;rsquo;s score, create a drop-in 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 edit app.service&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Add the adjustment under &lt;code&gt;[Service]&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;/etc/systemd/system/app.service.d/override.conf&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;[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;OOMScoreAdjust&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;-500&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 the file. &lt;code&gt;systemctl edit&lt;/code&gt; reloads systemd&amp;rsquo;s configuration, but the setting applies to newly started processes. Restart the service during a suitable maintenance window, since this interrupts 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 restart app.service&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This makes the preference persistent across service restarts. It does not reduce memory use or prevent &lt;code&gt;systemd-oomd&lt;/code&gt; from choosing the service&amp;rsquo;s cgroup.&lt;/p&gt;
&lt;h2 id="prevent-the-next-oom-event"&gt;Prevent the Next OOM Event &lt;a class="headline-link" href="#prevent-the-next-oom-event" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Start by checking current memory and swap 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;free -h&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Read the &lt;code&gt;available&lt;/code&gt; column, not the &lt;code&gt;free&lt;/code&gt; column. These are current values, so they may look healthy after a large process has been killed. The &lt;a href="https://linuxize.com/post/free-command-in-linux/"&gt;&lt;code&gt;free&lt;/code&gt; command guide&lt;/a&gt;
explains the columns.&lt;/p&gt;
&lt;p&gt;If one application&amp;rsquo;s memory use keeps growing under a steady workload, investigate a possible leak. If usage rises with traffic or job size, reduce worker concurrency, batch size, or cache limits before assuming a bug.&lt;/p&gt;
&lt;p&gt;For systemd services on cgroup v2, &lt;code&gt;MemoryHigh=&lt;/code&gt; applies reclaim pressure and throttling, while &lt;code&gt;MemoryMax=&lt;/code&gt; provides a hard limit. Exceeding &lt;code&gt;MemoryMax=&lt;/code&gt; can cause an OOM kill inside the service. Choose limits from measured usage and leave room for the rest of the host. The &lt;a href="https://www.freedesktop.org/software/systemd/man/latest/systemd.resource-control.html" target="_blank" rel="noopener noreferrer"&gt;systemd resource-control documentation&lt;/a&gt;
recommends &lt;code&gt;MemoryHigh=&lt;/code&gt; as the main control and &lt;code&gt;MemoryMax=&lt;/code&gt; as the last line of defense.&lt;/p&gt;
&lt;p&gt;If the host does not have enough capacity for its normal workload, add RAM or reduce the workload. &lt;a href="https://linuxize.com/post/create-a-linux-swap-file/"&gt;Adding swap space&lt;/a&gt;
can absorb temporary pressure from swappable memory, but it does not fix a leak or remove a service&amp;rsquo;s memory limit. Sustained swapping can also make the machine slow.&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;dmesg reports &amp;ldquo;Operation not permitted&amp;rdquo;&lt;/strong&gt;&lt;br&gt;
Run it with &lt;code&gt;sudo&lt;/code&gt; on the host. Kernel log access is often restricted, and root inside a container may still lack permission. Ask the host administrator for logs if you cannot access them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The previous boot has no journal entries&lt;/strong&gt;&lt;br&gt;
List retained boots with &lt;code&gt;sudo journalctl --list-boots&lt;/code&gt;. Logs may have been stored only in memory or removed by retention limits. Enabling persistent logging helps future investigations but cannot recover discarded entries.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The process was killed, but the search returns nothing&lt;/strong&gt;&lt;br&gt;
Check the right boot and time range, then inspect &lt;code&gt;systemd-oomd&lt;/code&gt; and supervisor logs. On systems using a syslog daemon, retained kernel messages may also be in &lt;code&gt;/var/log/kern.log&lt;/code&gt;, &lt;code&gt;/var/log/messages&lt;/code&gt;, or rotated copies. Missing logs do not prove that no OOM occurred, and a &lt;code&gt;SIGKILL&lt;/code&gt; exit alone does not prove that one did.&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;Save the full log around an OOM event before it rotates, including the process table and cgroup information. To watch memory use while reproducing the workload, see our guides on the &lt;a href="https://linuxize.com/post/top-command-in-linux/"&gt;&lt;code&gt;top&lt;/code&gt; command&lt;/a&gt;
and &lt;a href="https://linuxize.com/post/vmstat-command-in-linux/"&gt;&lt;code&gt;vmstat&lt;/code&gt;&lt;/a&gt;
.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/how-to-diagnose-oom-killer-events-in-linux/featured_hu_ee9fda9663af00c5.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>systemd Unit File Cheatsheet</title><link>https://linuxize.com/cheatsheet/systemd-unit-file/</link><pubDate>Wed, 30 Sep 2026 10:45:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/cheatsheet/systemd-unit-file/</guid><description>systemd unit file directives at a glance: [Unit] dependencies, service types, Exec commands, restart policy, users and environment, resource limits, hardening, timers, and drop-in overrides.</description><content:encoded>&lt;div class="card"&gt;
&lt;h3 id="file-locations"&gt;File Locations &lt;a class="headline-link" href="#file-locations" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;systemd reads units from several directories. &lt;code&gt;/etc&lt;/code&gt; overrides &lt;code&gt;/run&lt;/code&gt;, which overrides &lt;code&gt;/usr/lib&lt;/code&gt;.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Path&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;/etc/systemd/system/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Units you write or override as the administrator&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/run/systemd/system/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Runtime units, gone after a reboot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/usr/lib/systemd/system/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Units installed by packages; do not edit these&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/etc/systemd/system/name.service.d/*.conf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Drop-in overrides for one unit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;~/.config/systemd/user/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Per-user units, managed with &lt;code&gt;systemctl --user&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/etc/systemd/user/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;User units for every account on the system&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;systemctl cat name.service&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print the unit and every drop-in that applies to it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="unit-types"&gt;Unit Types &lt;a class="headline-link" href="#unit-types" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The file extension decides what kind of unit systemd creates.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Extension&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;.service&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A process or daemon&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.timer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Schedule that activates another unit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.socket&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Listening socket that starts a service on demand&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.target&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Group of units, used as a boot milestone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.path&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Watches a file or directory and activates a unit on change&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.mount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Filesystem mount point&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.slice&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Resource control group for other units&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="unit-section"&gt;[Unit] Section &lt;a class="headline-link" href="#unit-section" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Description, ordering, and dependencies.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive&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;Description=My API server&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Name shown by &lt;code&gt;systemctl status&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Documentation=man:nginx(8) https://nginx.org/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Documentation links&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;After=network-online.target&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start after the named unit; ordering only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Before=nginx.service&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start before the named unit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Wants=network-online.target&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pull in a unit, keep running if it fails&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Requires=postgresql.service&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pull in a unit, stop if it is explicitly stopped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BindsTo=dev-sdb1.device&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stop as soon as the named unit stops&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PartOf=app.target&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stop and restart together with the named unit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Conflicts=shutdown.target&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stop the named unit when this one starts, and the reverse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ConditionPathExists=/etc/app.conf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Skip the start quietly when the check fails&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AssertPathExists=/etc/app.conf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fail the start when the check fails&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;StartLimitIntervalSec=60&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Window for counting start attempts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;StartLimitBurst=5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Starts allowed inside that window&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;Wants=&lt;/code&gt; and &lt;code&gt;Requires=&lt;/code&gt; do not set start order. Pair them with &lt;code&gt;After=&lt;/code&gt; when one unit must be up first.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="service-types"&gt;Service Types &lt;a class="headline-link" href="#service-types" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Type=&lt;/code&gt; tells systemd when the service counts as started.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive&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;Type=simple&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Started as soon as the process is forked; the default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Type=exec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Started once the binary has been executed successfully&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Type=forking&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Started when the parent exits; the daemon forks itself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PIDFile=/run/app.pid&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PID file to track for a &lt;code&gt;forking&lt;/code&gt; daemon&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Type=oneshot&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Runs to completion; later units wait for it to exit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RemainAfterExit=yes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Keep a &lt;code&gt;oneshot&lt;/code&gt; unit shown as active after it exits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Type=notify&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Started when the process sends &lt;code&gt;READY=1&lt;/code&gt; via &lt;code&gt;sd_notify&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Type=notify-reload&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Like &lt;code&gt;notify&lt;/code&gt;, and reloads by sending &lt;code&gt;SIGHUP&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Type=dbus&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Started when the name in &lt;code&gt;BusName=&lt;/code&gt; appears on D-Bus&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Type=idle&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Like &lt;code&gt;simple&lt;/code&gt;, but waits until other boot jobs finish&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="exec-commands"&gt;Exec Commands &lt;a class="headline-link" href="#exec-commands" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Commands systemd runs at each stage of the service lifecycle.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive&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;ExecStart=/usr/bin/app --port 8080&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Main command; use an absolute path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ExecStartPre=/usr/bin/app --check-config&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run before &lt;code&gt;ExecStart=&lt;/code&gt;; a failure stops the start&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ExecStartPost=/usr/bin/notify-ready&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run after the service has started&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ExecReload=/bin/kill -HUP $MAINPID&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run by &lt;code&gt;systemctl reload&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ExecStop=/usr/bin/app --shutdown&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run by &lt;code&gt;systemctl stop&lt;/code&gt; before the signal is sent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ExecStopPost=/usr/bin/cleanup&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run after the service has stopped or failed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ExecStartPre=-/usr/bin/mkdir /tmp/app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-&lt;/code&gt; prefix: ignore a non-zero exit code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ExecStartPre=+/usr/bin/chown app /data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;+&lt;/code&gt; prefix: run with full privileges, ignoring &lt;code&gt;User=&lt;/code&gt; and sandboxing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ExecStart=:/usr/bin/echo $HOME&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:&lt;/code&gt; prefix: pass &lt;code&gt;$&lt;/code&gt; through without variable expansion&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Commands are not run through a shell, so pipes, &lt;code&gt;&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; do not work. Wrap them in &lt;code&gt;/bin/sh -c '...'&lt;/code&gt; or move them into a script.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="restart-and-timeouts"&gt;Restart and Timeouts &lt;a class="headline-link" href="#restart-and-timeouts" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Control what happens when the process exits or hangs.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive&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;Restart=on-failure&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Restart on non-zero exit, signal, timeout, or watchdog&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Restart=always&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Restart no matter how the process exited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Restart=on-abnormal&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Restart on signal, timeout, or watchdog, not on exit codes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Restart=no&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Never restart; the default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RestartSec=5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Wait five seconds before restarting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RestartSteps=5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Grow the delay over five restarts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RestartMaxDelaySec=60&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Upper limit for the growing delay&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TimeoutStartSec=30&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fail the start after 30 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TimeoutStopSec=30&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Escalate to &lt;code&gt;SIGKILL&lt;/code&gt; after 30 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SuccessExitStatus=143&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Treat an extra exit code as a clean exit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RestartPreventExitStatus=2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Never restart after this exit code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;KillMode=mixed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SIGTERM&lt;/code&gt; to the main process, &lt;code&gt;SIGKILL&lt;/code&gt; to the rest of the group&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WatchdogSec=30&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mark the service failed when keep-alive pings stop&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;A unit that restarts more than &lt;code&gt;StartLimitBurst=&lt;/code&gt; times inside &lt;code&gt;StartLimitIntervalSec=&lt;/code&gt; is refused further starts until the interval passes or you run &lt;code&gt;systemctl reset-failed&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="user-environment-and-directories"&gt;User, Environment, and Directories &lt;a class="headline-link" href="#user-environment-and-directories" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Who the process runs as and what it sees.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive&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;User=app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run as this user instead of root&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Group=app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run with this primary group&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DynamicUser=yes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allocate a temporary user for each run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WorkingDirectory=/opt/app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Directory the process starts in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Environment=&amp;quot;NODE_ENV=production&amp;quot; &amp;quot;PORT=8080&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set variables inline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EnvironmentFile=/etc/app/app.env&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read &lt;code&gt;KEY=value&lt;/code&gt; lines from a file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EnvironmentFile=-/etc/default/app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Same, but skip the file when it is missing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UMask=0027&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Default permission mask for new files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;StateDirectory=app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create &lt;code&gt;/var/lib/app&lt;/code&gt; owned by the service user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RuntimeDirectory=app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create &lt;code&gt;/run/app&lt;/code&gt;, removed when the service stops&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LogsDirectory=app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create &lt;code&gt;/var/log/app&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ConfigurationDirectory=app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create &lt;code&gt;/etc/app&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;StandardOutput=journal&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Send stdout to the journal; the default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SyslogIdentifier=app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tag used in &lt;code&gt;journalctl&lt;/code&gt; output&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="resource-limits"&gt;Resource Limits &lt;a class="headline-link" href="#resource-limits" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Cap what one service can consume.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive&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;LimitNOFILE=65536&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Maximum open files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LimitNPROC=4096&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Maximum processes for the service user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MemoryMax=512M&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hard memory limit; the OOM killer acts above it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MemoryHigh=400M&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Throttle and reclaim memory above this level&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CPUQuota=50%&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Half of one CPU; &lt;code&gt;200%&lt;/code&gt; means two CPUs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CPUWeight=50&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Relative CPU share; the default is 100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IOWeight=50&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Relative disk I/O share; the default is 100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TasksMax=100&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Maximum threads and processes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Nice=10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Scheduling priority, from -20 to 19&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;systemctl set-property app.service MemoryMax=1G&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change a limit at runtime and save it as a drop-in&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;ulimit&lt;/code&gt; values set in a shell do not apply to services. Use the &lt;code&gt;Limit*=&lt;/code&gt; directives instead.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="security-hardening"&gt;Security Hardening &lt;a class="headline-link" href="#security-hardening" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Sandboxing options that most services tolerate.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive&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;NoNewPrivileges=yes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Block privilege gain through setuid binaries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ProtectSystem=strict&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mount the whole file system read-only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ProtectSystem=full&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mount &lt;code&gt;/usr&lt;/code&gt;, &lt;code&gt;/boot&lt;/code&gt;, and &lt;code&gt;/etc&lt;/code&gt; read-only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ReadWritePaths=/var/lib/app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Keep one path writable under &lt;code&gt;ProtectSystem=&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ProtectHome=yes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hide &lt;code&gt;/home&lt;/code&gt;, &lt;code&gt;/root&lt;/code&gt;, and &lt;code&gt;/run/user&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ProtectHome=read-only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show home directories, but read-only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PrivateTmp=yes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Give the service its own &lt;code&gt;/tmp&lt;/code&gt; and &lt;code&gt;/var/tmp&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PrivateDevices=yes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hide physical devices under &lt;code&gt;/dev&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ProtectKernelTunables=yes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Make &lt;code&gt;/proc/sys&lt;/code&gt; and &lt;code&gt;/sys&lt;/code&gt; read-only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ProtectKernelModules=yes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Block kernel module loading&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AmbientCapabilities=CAP_NET_BIND_SERVICE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Let a non-root user bind ports below 1024&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CapabilityBoundingSet=CAP_NET_BIND_SERVICE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Drop every other capability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allow only IP and Unix sockets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SystemCallFilter=@system-service&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allow the system calls a typical service needs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;systemd-analyze security app.service&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Score the unit&amp;rsquo;s exposure and list what is missing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="install-section"&gt;[Install] Section &lt;a class="headline-link" href="#install-section" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Read only by &lt;code&gt;systemctl enable&lt;/code&gt; and &lt;code&gt;disable&lt;/code&gt;.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive&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;WantedBy=multi-user.target&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start at boot on servers and desktops&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WantedBy=graphical.target&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start only when the graphical session target is reached&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WantedBy=default.target&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start with the user session, for &lt;code&gt;--user&lt;/code&gt; units&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WantedBy=timers.target&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Activate a timer at boot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RequiredBy=app.target&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hard dependency created on enable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Alias=api.service&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Extra name created as a symlink on enable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Also=app.socket&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enable or disable another unit together with this one&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;A unit without an &lt;code&gt;[Install]&lt;/code&gt; section can be started by hand but not enabled.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="timer-units"&gt;Timer Units &lt;a class="headline-link" href="#timer-units" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A &lt;code&gt;name.timer&lt;/code&gt; activates &lt;code&gt;name.service&lt;/code&gt; by default.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive&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;OnCalendar=daily&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every day at midnight&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OnCalendar=*-*-* 02:30:00&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every day at 02:30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OnCalendar=Mon..Fri 09:00&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Weekdays at 09:00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OnCalendar=*:0/15&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every 15 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OnCalendar=*-*-01 00:00&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;First day of every month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OnBootSec=5min&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Five minutes after boot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OnUnitActiveSec=1h&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One hour after the unit last started&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Persistent=true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run a missed calendar event after downtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RandomizedDelaySec=10m&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Spread the start by up to 10 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AccuracySec=1s&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tighten the default one-minute accuracy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Unit=backup.service&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Activate a unit with a different name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;systemd-analyze calendar &amp;quot;Mon..Fri 09:00&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check an expression and show the next run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;systemctl list-timers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show active timers and their next run&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="overrides-and-drop-ins"&gt;Overrides and Drop-ins &lt;a class="headline-link" href="#overrides-and-drop-ins" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Change a packaged unit without editing the file under &lt;code&gt;/usr/lib&lt;/code&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 systemctl edit nginx.service&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create or edit a drop-in override&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl edit --full nginx.service&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Copy the whole unit to &lt;code&gt;/etc&lt;/code&gt; and edit it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl revert nginx.service&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove overrides and return to the vendor unit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;systemctl cat nginx.service&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the unit with every drop-in applied&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;systemd-delta&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List units that are overridden or extended&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ExecStart=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Empty line in a drop-in clears the list before a new &lt;code&gt;ExecStart=&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl daemon-reload&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reload unit files after a manual change&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;systemctl edit&lt;/code&gt; reloads the configuration on save. Edits made with a normal editor need &lt;code&gt;daemon-reload&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="specifiers"&gt;Specifiers &lt;a class="headline-link" href="#specifiers" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Placeholders systemd expands inside unit files.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Specifier&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;Full unit name, such as &lt;code&gt;app@web.service&lt;/code&gt;&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;Unit name without the type suffix&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;Instance name in a template unit, the part after &lt;code&gt;@&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;%H&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Host name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;%h&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Home directory of the user running the manager&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;%u&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;User name of the manager&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;Runtime directory: &lt;code&gt;/run&lt;/code&gt; or &lt;code&gt;$XDG_RUNTIME_DIR&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;%%&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A literal percent sign&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;A template file such as &lt;code&gt;app@.service&lt;/code&gt; is started as &lt;code&gt;app@web.service&lt;/code&gt;, with &lt;code&gt;%i&lt;/code&gt; set to &lt;code&gt;web&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="install-and-verify"&gt;Install and Verify &lt;a class="headline-link" href="#install-and-verify" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Put a new unit in place and confirm it loads.&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 cp app.service /etc/systemd/system/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install the unit file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;systemd-analyze verify /etc/systemd/system/app.service&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check syntax and unknown directives&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl daemon-reload&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Make systemd read the new file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl enable --now app.service&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enable at boot and start now&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/systemctl-command-in-linux/"&gt;&lt;code&gt;systemctl status app.service&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Show state and the latest log lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/journalctl-command-in-linux/"&gt;&lt;code&gt;journalctl -u app.service -f&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Follow the service log&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;systemctl show app.service -p Restart,User&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print the effective value of directives&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;systemctl list-dependencies app.service&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the dependency tree&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 units in &lt;code&gt;~/.config/systemd/user/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo loginctl enable-linger username&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Keep user units running after logout&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;Deeper reading on services and systemd.&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-create-a-systemd-service/"&gt;How to Create a systemd Service File&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Write, install, and manage a service step by step&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/tools/systemd-service-file-generator/"&gt;systemd Service File Generator&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Build a &lt;code&gt;.service&lt;/code&gt; file in the browser&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/systemctl-command-in-linux/"&gt;systemctl Command in Linux&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Start, stop, enable, and inspect units&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/systemctl-list/"&gt;How to List Services with systemctl&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Filter units by state and type&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/journalctl-command-in-linux/"&gt;journalctl Command in Linux&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Read and filter service logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/cheatsheet/systemctl/"&gt;systemctl cheatsheet&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Quick reference for &lt;code&gt;systemctl&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/cheatsheet/journalctl/"&gt;journalctl cheatsheet&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Quick reference for &lt;code&gt;journalctl&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;</content:encoded></item><item><title>Ubuntu Desktop vs Ubuntu Server: Which One Should You Use?</title><link>https://linuxize.com/post/ubuntu-desktop-vs-ubuntu-server/</link><pubDate>Sat, 26 Sep 2026 10:40:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/ubuntu-desktop-vs-ubuntu-server/</guid><category>linux-distros</category><category>ubuntu</category><description>Ubuntu Desktop and Ubuntu Server share the same base but differ in installers, software, resource needs, and workloads. See which edition fits your system.</description><content:encoded>&lt;p&gt;When you go to download Ubuntu, the site offers two separate images: Ubuntu Desktop and Ubuntu Server. They share the same name, the same release number, and the same six-month release schedule, so it is easy to assume the choice does not matter much. It does. The two editions are built around different defaults, ship different software, and expect to run on very different machines.&lt;/p&gt;
&lt;p&gt;This guide compares Ubuntu Desktop and Ubuntu Server where the differences actually show up: the installer, the default software set, the desktop environment, resource usage, and the kind of work each one is meant for.&lt;/p&gt;
&lt;h2 id="same-ubuntu-underneath"&gt;Same Ubuntu Underneath &lt;a class="headline-link" href="#same-ubuntu-underneath" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Both editions are the same Ubuntu. They draw from the same package archive, run the same &lt;code&gt;apt&lt;/code&gt; and &lt;code&gt;dpkg&lt;/code&gt; tools, use systemd as the init system, and follow the same release cadence and support windows. A long-term support (LTS) release gets five years of standard support on both Desktop and Server. Both are free to download and use, while the optional Ubuntu Pro subscription adds extended security coverage.&lt;/p&gt;
&lt;p&gt;Because the base is identical, anything you learn on one transfers to the other. The same &lt;code&gt;systemctl&lt;/code&gt; commands manage services, the same configuration files live in the same places, and the same packages are available from the archive. The differences are about what is installed and enabled out of the box, not about two separate operating systems.&lt;/p&gt;
&lt;h2 id="the-installer"&gt;The Installer &lt;a class="headline-link" href="#the-installer" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The first difference you meet is the installer.&lt;/p&gt;
&lt;p&gt;Ubuntu Desktop boots into a graphical live session. You can try the system before installing, and the installer walks you through language, keyboard, disk layout, and a user account with point-and-click screens. The usual installation path assumes that a monitor, keyboard, and mouse are attached.&lt;/p&gt;
&lt;p&gt;Ubuntu Server uses the text-based Subiquity installer. You can operate it from a local console, over a serial connection, or through an SSH connection to the installer. It puts server-focused options up front, including static network configuration, LVM or software RAID, SSH key imports from GitHub or Launchpad, and an optional OpenSSH server. Ubuntu Desktop also supports advanced disk layouts, but its guided path focuses on workstation setup.&lt;/p&gt;
&lt;h2 id="default-software-and-desktop-environment"&gt;Default Software and Desktop Environment &lt;a class="headline-link" href="#default-software-and-desktop-environment" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is the largest practical difference.&lt;/p&gt;
&lt;p&gt;Ubuntu Desktop installs the GNOME desktop environment, Firefox, the App Center, audio support, and the core utilities needed for a workstation. The current installer offers a smaller Default selection and an Extended selection with additional office tools and utilities. Third-party drivers and support for additional media formats are separate optional choices, so the exact software set depends on what you select during installation.&lt;/p&gt;
&lt;p&gt;Ubuntu Server installs none of that graphical stack. There is no desktop, display server, or browser in the standard installation. You get a command-line system, with the option to install OpenSSH and selected server-oriented snaps during setup. The expectation is that you administer the machine over SSH and run services on it, not sit in front of it.&lt;/p&gt;
&lt;p&gt;You are not locked in by this choice. You can install a full GNOME desktop on a server image with &lt;code&gt;apt&lt;/code&gt;, and you can install server packages on a desktop. The default set is a starting point, not a hard boundary.&lt;/p&gt;
&lt;h2 id="resource-usage"&gt;Resource Usage &lt;a class="headline-link" href="#resource-usage" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Because Ubuntu Server has no graphical stack, it generally uses less memory and disk at idle, leaving more resources for the services you run. Exact usage depends on the release, hardware, and installed packages. As a current reference, the &lt;a href="https://documentation.ubuntu.com/release-notes/26.04/#requirements-and-compatibility" target="_blank" rel="noopener noreferrer"&gt;Ubuntu 26.04 system requirements&lt;/a&gt;
list 6 GB of RAM and 25 GB of storage as the minimum for a comfortable Desktop experience, while Server requirements start at 1.5 GB of RAM and 4 GB of storage.&lt;/p&gt;
&lt;p&gt;Ubuntu Desktop needs more resources to keep GNOME and its background services responsive. On modern workstation hardware this is not a problem, but the lower Server baseline matters on small virtual machines and older systems. A smaller default package set also means fewer components to maintain, although the services you install and how you configure them matter more for security.&lt;/p&gt;
&lt;h2 id="the-kernel"&gt;The Kernel &lt;a class="headline-link" href="#the-kernel" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Both editions can run the same kernel, but the defaults differ in a way that matters for some hardware.&lt;/p&gt;
&lt;p&gt;On LTS releases, Ubuntu Desktop installations track the Hardware Enablement (HWE) stack, which brings newer kernels to support recent laptops and graphics hardware. Ubuntu Server defaults to the General Availability (GA) kernel, while the HWE kernel remains available when newer hardware support is required.&lt;/p&gt;
&lt;h2 id="when-to-choose-each"&gt;When to Choose Each &lt;a class="headline-link" href="#when-to-choose-each" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Pick Ubuntu Desktop when a person sits at the machine: a developer workstation, a laptop, a daily driver, or any system where you want a graphical environment, a browser, and desktop applications ready to go.&lt;/p&gt;
&lt;p&gt;Pick Ubuntu Server when the machine runs services and you reach it over the network: a web or database server, a VPS, a container host, a home lab box, or anything headless in a rack or a cloud. If the machine has no monitor attached and its job is to run software for other clients, the server edition is the right starting point. After the install, an &lt;a href="https://linuxize.com/post/initial-server-setup-on-ubuntu-26-04/"&gt;initial server setup&lt;/a&gt;
gets it hardened and ready.&lt;/p&gt;
&lt;p&gt;Here is a quick summary of where the two differ:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Ubuntu Desktop&lt;/th&gt;
&lt;th&gt;Ubuntu Server&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Installer&lt;/td&gt;
&lt;td&gt;Graphical live session&lt;/td&gt;
&lt;td&gt;Text-based (Subiquity)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Desktop environment&lt;/td&gt;
&lt;td&gt;GNOME, preinstalled&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Desktop applications&lt;/td&gt;
&lt;td&gt;Core apps included; more with Extended selection&lt;/td&gt;
&lt;td&gt;Not included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Default access&lt;/td&gt;
&lt;td&gt;Local graphical login&lt;/td&gt;
&lt;td&gt;Local console; SSH optional&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Baseline resource use&lt;/td&gt;
&lt;td&gt;Higher&lt;/td&gt;
&lt;td&gt;Lower&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LTS kernel track&lt;/td&gt;
&lt;td&gt;HWE&lt;/td&gt;
&lt;td&gt;GA by default; HWE optional&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Built for&lt;/td&gt;
&lt;td&gt;Workstations, laptops&lt;/td&gt;
&lt;td&gt;Servers, VPS, containers&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="converting-between-desktop-and-server"&gt;Converting Between Desktop and Server &lt;a class="headline-link" href="#converting-between-desktop-and-server" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can add either software profile because the package archive is shared, but the two directions are not equally simple.&lt;/p&gt;
&lt;p&gt;To turn a server into a desktop, install the desktop metapackage:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 ubuntu-desktop&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;ubuntu-desktop&lt;/code&gt; metapackage pulls in the full Ubuntu desktop software set. A lighter desktop environment is another option if you need only occasional graphical access.&lt;/p&gt;
&lt;p&gt;Going in the other direction is less tidy. Removing the &lt;code&gt;ubuntu-desktop&lt;/code&gt; metapackage does not automatically return the system to a minimal Server installation. If you only need to run server software on a Desktop system, install the required services directly. When a clean headless base matters, reinstalling from the Server image is usually safer than removing desktop packages one by one.&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 choice comes down to who, or what, uses the machine. A workstation with a screen wants Ubuntu Desktop; a headless box running services wants Ubuntu Server. If you are weighing the underlying distribution as well, our &lt;a href="https://linuxize.com/post/debian-vs-ubuntu-server/"&gt;Debian vs Ubuntu Server&lt;/a&gt;
comparison covers that side of the decision.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/ubuntu-desktop-vs-ubuntu-server/featured_hu_d83838f258c9f565.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>GitHub CLI Cheatsheet</title><link>https://linuxize.com/cheatsheet/github-cli/</link><pubDate>Wed, 23 Sep 2026 09:30:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/cheatsheet/github-cli/</guid><description>gh at a glance: authentication, repositories, pull requests, issues, GitHub Actions runs and secrets, releases, search, extensions, and the API.</description><content:encoded>&lt;div class="card"&gt;
&lt;h3 id="authentication"&gt;Authentication &lt;a class="headline-link" href="#authentication" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Log in once per host, then check what the stored token can do.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command or variable&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/github-cli-command/"&gt;&lt;code&gt;gh auth login&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Start the interactive login flow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh auth login --web&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Authenticate through the browser&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh auth login --hostname github.example.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Log in to a GitHub Enterprise Server host&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh auth status&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the active account, protocol, and token scopes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh auth refresh -s workflow&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add a scope to the existing token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh auth switch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change the active account&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh auth token&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print the raw token &lt;code&gt;gh&lt;/code&gt; is using&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh auth setup-git&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Configure &lt;code&gt;git&lt;/code&gt; to authenticate through &lt;code&gt;gh&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh auth logout&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove stored credentials for a host&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GH_TOKEN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Token environment variable for GitHub.com automation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GH_ENTERPRISE_TOKEN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Token environment variable for GitHub Enterprise Server automation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Treat the output of &lt;code&gt;gh auth token&lt;/code&gt; as a password. Store automation tokens in your CI secret manager, expose them through the appropriate environment variable, and never commit them to a repository.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="repositories"&gt;Repositories &lt;a class="headline-link" href="#repositories" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Create, clone, and inspect repositories without leaving the shell.&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/git-clone-command/"&gt;&lt;code&gt;gh repo clone OWNER/REPO&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Clone a repository using your configured protocol&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh repo view&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the description, default branch, and README&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh repo view --web&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Open the repository in a browser&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh repo create NAME --private --source=. --push&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Publish the current directory as a new repository&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh repo fork --clone&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fork a repository and clone the fork&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh repo sync&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Update a fork from its upstream&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh repo list OWNER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List repositories owned by a user or organization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh repo edit --default-branch main&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set &lt;code&gt;main&lt;/code&gt; as the default branch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh repo set-default OWNER/REPO&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pin the repository that commands target here&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh repo archive&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Archive a repository&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="pull-requests"&gt;Pull Requests &lt;a class="headline-link" href="#pull-requests" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The commands that replace most of the pull request web interface.&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;gh pr list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List open pull requests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh pr list --state all --author @me&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List your pull requests in every state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh pr status&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show pull requests relevant to you&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh pr create --fill&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Open a pull request using the commit title and body&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh pr create --draft --reviewer octocat&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a draft and request a review&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-create-and-list-git-branches/"&gt;&lt;code&gt;gh pr checkout NUMBER&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Check the pull request out as a local branch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/git-diff-command/"&gt;&lt;code&gt;gh pr diff NUMBER&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Show the patch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh pr checks NUMBER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the status checks and their conclusions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh pr review --approve&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Approve the pull request for the current branch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh pr merge NUMBER --squash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Squash the commits and merge the pull request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh pr ready NUMBER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mark a draft as ready for review&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh pr view NUMBER --web&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Open the pull request in a browser&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="issues"&gt;Issues &lt;a class="headline-link" href="#issues" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Triage and close issues from the same terminal you build in.&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;gh issue list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List open issues&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh issue list --label bug --assignee @me&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List open bugs assigned to you&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh issue status&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show issues assigned to or mentioning you&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh issue create --title &amp;quot;...&amp;quot; --body &amp;quot;...&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create an issue without the prompts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh issue view NUMBER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read an issue in the terminal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh issue comment NUMBER --body &amp;quot;...&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add a comment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh issue edit NUMBER --add-label bug&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add the &lt;code&gt;bug&lt;/code&gt; label to an issue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh issue close NUMBER --comment &amp;quot;...&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Close with a closing comment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh issue develop NUMBER --checkout&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create and check out a branch linked to the issue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh issue transfer NUMBER OWNER/REPO&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Move an issue to another repository&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="github-actions"&gt;GitHub Actions &lt;a class="headline-link" href="#github-actions" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Watch, debug, and re-run workflows from the command line.&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;gh run list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List recent workflow runs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh run list --workflow ci.yml --status failure&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List failed runs for &lt;code&gt;ci.yml&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh run view ID&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show a summary of one run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh run view ID --log-failed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print the logs for failed steps only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh run watch ID&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Follow a run until it finishes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh run rerun ID --failed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Re-run only the jobs that failed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh run download ID&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Download the artifacts a run produced&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh run cancel ID&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cancel a run in progress&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh workflow list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List the workflows in the repository&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh workflow run build.yml -f env=staging&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Trigger a &lt;code&gt;workflow_dispatch&lt;/code&gt; workflow with inputs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh workflow disable NAME&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Disable a workflow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh cache list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List the Actions caches for the repository&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh cache delete --all&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Immediately delete every Actions cache&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Deleting all Actions caches has no dry-run and can slow the next workflow runs while the caches rebuild. Confirm that the command targets the intended repository before running it.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="secrets-and-variables"&gt;Secrets and Variables &lt;a class="headline-link" href="#secrets-and-variables" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Secrets are write-only; variables stay readable.&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;gh secret set NAME&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prompt for a value and store it, hidden from shell history&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;echo &amp;quot;$VALUE&amp;quot; | gh secret set NAME&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read the value from standard input&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh secret set --env-file .env&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Load several secrets from a dotenv file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh secret set NAME --env production&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set a deployment environment secret&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh secret set NAME --org ORG --visibility selected --repos a,b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set an organization secret for named repositories&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh secret list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List secret names and update times, never values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh secret delete NAME&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Immediately remove a repository secret&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh variable set NAME --body value&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Store a non-sensitive value that workflows can read&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh variable get NAME&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print a variable value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh variable list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List variables with their values&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Keep any dotenv file used with &lt;code&gt;--env-file&lt;/code&gt; out of version control, and delete it once the values are uploaded. Before deleting a secret, confirm the repository and secret name because GitHub cannot return the stored value afterward.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="releases"&gt;Releases &lt;a class="headline-link" href="#releases" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Tag, publish, and fetch release assets.&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;gh release create TAG --generate-notes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a release with notes built from merged pull requests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh release create TAG --target main --title &amp;quot;...&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create the tag on a specific branch or commit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh release create TAG dist/app dist/checksums.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Attach build files while creating the release&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh release create TAG --draft --prerelease&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Publish as a draft or a prerelease&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh release list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List releases&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh release view TAG&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the notes and assets for one release&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh release download TAG&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Download every asset&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh release download TAG --pattern &amp;quot;*.tar.gz&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Download matching assets only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh release upload TAG file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add an asset to an existing release&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh release delete TAG&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete a release&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="search-and-browse"&gt;Search and Browse &lt;a class="headline-link" href="#search-and-browse" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Find work across GitHub, then jump to it.&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;gh search repos QUERY --language go&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search repositories&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh search issues QUERY --state open&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search issues&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh search prs --review-requested @me&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search pull requests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh search code QUERY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh search commits QUERY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search commit messages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh browse&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Open the current repository in a browser&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh browse NUMBER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Open an issue or pull request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh browse path/to/file.go&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Open a file at the current branch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh browse --actions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Open the Actions tab&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh browse -n path/to/file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print the URL instead of opening it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh status&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show issues, pull requests, and notifications across repositories&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="json-output-and-the-api"&gt;JSON Output and the API &lt;a class="headline-link" href="#json-output-and-the-api" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Structured output for scripts, and direct API access for everything else.&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;gh pr list --json number,title,author&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Return selected fields as JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh pr list --json number,title --jq '.[].title'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Filter JSON without a separate &lt;code&gt;jq&lt;/code&gt; process&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh run list --json conclusion --template ...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Format JSON with a Go template&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh help formatting&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;See every JSON formatting option&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh pr list --json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Passing no value lists the fields that command exposes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh api repos/{owner}/{repo}/releases&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Send an authenticated REST request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh api ENDPOINT --jq '.[].tag_name'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Filter the API response&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh api ENDPOINT -f title=&amp;quot;...&amp;quot; -f body=&amp;quot;...&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Send a POST with string fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh api ENDPOINT -X PATCH -F draft=false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Choose the method and send typed fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh api ENDPOINT --paginate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Follow pagination and return every page&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh api graphql -f query='...'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Send a GraphQL query&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The &lt;code&gt;{owner}&lt;/code&gt; and &lt;code&gt;{repo}&lt;/code&gt; placeholders are filled from the current repository.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="aliases-extensions-and-config"&gt;Aliases, Extensions, and Config &lt;a class="headline-link" href="#aliases-extensions-and-config" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Shorten what you type, and add commands &lt;code&gt;gh&lt;/code&gt; does not ship with.&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;gh alias set pv 'pr view --web'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a shortcut&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh alias set bugs 'issue list --label bug'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Alias a command with its flags&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh alias list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List configured aliases&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh alias delete NAME&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove an alias&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh extension search QUERY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Find extensions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh extension install OWNER/gh-NAME&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install an extension&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh extension list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List installed extensions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh extension upgrade --all&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Update every installed extension&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh extension remove NAME&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove an extension&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh config set editor vim&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set the editor &lt;code&gt;gh&lt;/code&gt; opens for bodies and prompts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh config set git_protocol ssh&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Choose HTTPS or SSH for Git operations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gh config list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show current configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Extensions are not reviewed or signed by GitHub, and they run with your token. Read the source before installing one.&lt;/p&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;What the common failures mean and where to look.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Message&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;To get started with GitHub CLI, please run: gh auth login&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No credentials for this host; run &lt;code&gt;gh auth login&lt;/code&gt;, or set &lt;code&gt;GH_TOKEN&lt;/code&gt; in CI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;HTTP 403: Resource not accessible by personal access token&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check with &lt;code&gt;gh auth status&lt;/code&gt;; refresh OAuth scopes, or fix fine-grained token access, SSO authorization, or Actions permissions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;error: your authentication token is missing required scopes [workflow]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Refresh with the scope named in the message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;none of the git remotes configured for this repository point to a known GitHub host&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run inside a GitHub clone, pass &lt;code&gt;--repo OWNER/REPO&lt;/code&gt;, or run &lt;code&gt;gh repo set-default&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;multiple remotes detected. please select which repo to use&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set the target once with &lt;code&gt;gh repo set-default&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pull request create failed: GraphQL: No commits between main and branch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Commit and push the branch before creating the pull request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;HTTP 404: Not Found&lt;/code&gt; on a private repository&lt;/td&gt;
&lt;td&gt;The account is authenticated but lacks access, or SSO is not authorized for the token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;X&lt;/code&gt; status with no logs in &lt;code&gt;gh run view&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Logs expire with the retention policy; check the run age&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;Deeper reading on GitHub CLI and the Git commands beside it.&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/github-cli-command/"&gt;GitHub CLI: Manage Repositories, Issues, and Pull Requests&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Full &lt;code&gt;gh&lt;/code&gt; walkthrough with examples and troubleshooting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/git-clone-command/"&gt;git clone Command&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Clone repositories directly with Git&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/git-diff-command/"&gt;git diff Command&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Read the patch format &lt;code&gt;gh pr diff&lt;/code&gt; prints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-add-git-remotes/"&gt;How to Add Git Remotes&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Manage the remotes &lt;code&gt;gh repo create&lt;/code&gt; configures&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/gitignore-ignoring-files-in-git/"&gt;gitignore: Ignoring Files in Git&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Keep secrets and build output out of a repository&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/cheatsheet/git/"&gt;Git cheatsheet&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Quick reference for the Git commands themselves&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;</content:encoded></item><item><title>Fix the NO_PUBKEY GPG Error on apt update</title><link>https://linuxize.com/post/fix-no-pubkey-gpg-error-apt/</link><pubDate>Mon, 21 Sep 2026 12:50:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/fix-no-pubkey-gpg-error-apt/</guid><category>apt</category><category>ubuntu</category><category>debian</category><description>Fix the apt NO_PUBKEY error on Ubuntu and Debian by installing a verified repository signing key under /etc/apt/keyrings and using signed-by.</description><content:encoded>&lt;p&gt;You run &lt;code&gt;sudo apt update&lt;/code&gt;, and one repository fails with a &lt;code&gt;GPG error&lt;/code&gt; ending in &lt;code&gt;NO_PUBKEY&lt;/code&gt; followed by a long hexadecimal key ID. APT can still update the other repositories, but it rejects the new package index from the affected source and may continue using an older cached copy.&lt;/p&gt;
&lt;p&gt;This usually happens after you add a third-party repository, or when its owner rotates the signing key. This guide explains how to identify the affected repository, install its verified key under &lt;code&gt;/etc/apt/keyrings&lt;/code&gt;, and limit that key to the correct source with &lt;code&gt;signed-by&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/apt/"&gt;APT 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;Identify the repository&lt;/td&gt;
&lt;td&gt;Read the URL on the &lt;code&gt;NO_PUBKEY&lt;/code&gt; line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Create the local keyring directory&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo install -m 0755 -d /etc/apt/keyrings&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inspect a downloaded key&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gpg --show-keys --with-fingerprint --with-subkey-fingerprint KEYRING&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Make a keyring readable by APT&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo chmod 0644 /etc/apt/keyrings/NAME.gpg&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Limit a key to one source&lt;/td&gt;
&lt;td&gt;Add &lt;code&gt;signed-by=/etc/apt/keyrings/NAME.gpg&lt;/code&gt; to a &lt;code&gt;.list&lt;/code&gt; entry, or &lt;code&gt;Signed-By:&lt;/code&gt; to a &lt;code&gt;.sources&lt;/code&gt; file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test the fix&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt update&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="what-the-no_pubkey-error-means"&gt;What the NO_PUBKEY Error Means &lt;a class="headline-link" href="#what-the-no_pubkey-error-means" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A typical failure 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;Err:7 https://repo.example.com/apt stable InRelease
The following signatures couldn&amp;#39;t be verified because the public key is not available: NO_PUBKEY 0123456789ABCDEF
Reading package lists... Done
W: GPG error: https://repo.example.com/apt stable InRelease: The following signatures couldn&amp;#39;t be verified because the public key is not available: NO_PUBKEY 0123456789ABCDEF
E: The repository &amp;#39;https://repo.example.com/apt stable InRelease&amp;#39; is not signed.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;APT checks the signature on a repository&amp;rsquo;s &lt;code&gt;InRelease&lt;/code&gt; file before it trusts the package index. When the matching public key is missing, unreadable, or no longer current, APT cannot confirm that the metadata came from the repository owner.&lt;/p&gt;
&lt;p&gt;The URL identifies the affected repository. The value after &lt;code&gt;NO_PUBKEY&lt;/code&gt;, here &lt;code&gt;0123456789ABCDEF&lt;/code&gt;, is the ID of the key that signed the metadata. You can use that ID to inspect a candidate key, but you should verify the key&amp;rsquo;s full fingerprint against the repository owner&amp;rsquo;s documentation before trusting it.&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;Older instructions use &lt;code&gt;sudo apt-key adv&lt;/code&gt; to fetch the missing key. The &lt;code&gt;apt-key&lt;/code&gt; command is deprecated and was last available in Debian 12 and Ubuntu 24.04. It also places imported keys in a global trust store unless you take additional steps. Use a dedicated keyring with &lt;code&gt;signed-by&lt;/code&gt; instead.&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="import-the-key-from-the-repository-owner"&gt;Import the Key From the Repository Owner &lt;a class="headline-link" href="#import-the-key-from-the-repository-owner" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The preferred fix is to download the key from the same organization that operates the repository. Do not substitute a key URL from an unrelated tutorial, even when the key ID looks correct.&lt;/p&gt;
&lt;p&gt;First, create the directory for locally managed APT keyrings:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 install -m &lt;span class="m"&gt;0755&lt;/span&gt; -d /etc/apt/keyrings&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Find the current signing-key URL and fingerprint in the repository&amp;rsquo;s official installation documentation. If the provider publishes an ASCII-armored key, download it and convert it to a binary OpenPGP keyring. Replace the example URL and filename with the provider&amp;rsquo;s values:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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://repo.example.com/apt/gpg.key &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; gpg --dearmor &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; sudo tee /etc/apt/keyrings/example.gpg &amp;gt; /dev/null
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo chmod &lt;span class="m"&gt;0644&lt;/span&gt; /etc/apt/keyrings/example.gpg&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;curl -f&lt;/code&gt; option makes the command fail when the server returns an HTTP error instead of quietly passing an error page to GnuPG. The final &lt;code&gt;chmod&lt;/code&gt; ensures that the &lt;code&gt;_apt&lt;/code&gt; system user can read the keyring.&lt;/p&gt;
&lt;p&gt;Some providers publish a ready-to-use binary &lt;code&gt;.gpg&lt;/code&gt; keyring. In that case, download it directly without &lt;code&gt;gpg --dearmor&lt;/code&gt;. A provider may also use an ASCII-armored &lt;code&gt;.asc&lt;/code&gt; file, which modern APT versions can reference directly. Keep the filename extension consistent with the key format.&lt;/p&gt;
&lt;p&gt;Before you connect the key to the repository, display its full fingerprint:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gpg --show-keys --with-fingerprint --with-subkey-fingerprint /etc/apt/keyrings/example.gpg&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Compare the primary fingerprint with the one in the provider&amp;rsquo;s documentation. The 16-character value from the APT error is a key ID, so it should match the last 16 characters of the primary fingerprint or of one of its signing subkeys. Stop if the documented fingerprint and downloaded key do not match.&lt;/p&gt;
&lt;h2 id="point-the-repository-to-the-keyring"&gt;Point the Repository to the Keyring &lt;a class="headline-link" href="#point-the-repository-to-the-keyring" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The key does not become active merely because it exists under &lt;code&gt;/etc/apt/keyrings&lt;/code&gt;. The affected repository entry must reference it with &lt;code&gt;signed-by&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Use the host from the error message to find the source file. Replace &lt;code&gt;repo.example.com&lt;/code&gt; with the actual repository 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;grep -R -n --include&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;*.list&amp;#39;&lt;/span&gt; --include&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;*.sources&amp;#39;&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; &lt;span class="s1"&gt;&amp;#39;repo.example.com&amp;#39;&lt;/span&gt; /etc/apt/sources.list /etc/apt/sources.list.d 2&amp;gt;/dev/null&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For a traditional &lt;code&gt;.list&lt;/code&gt; file, add &lt;code&gt;signed-by&lt;/code&gt; inside the option brackets. If the entry has no brackets, add them between &lt;code&gt;deb&lt;/code&gt; and the URL:&lt;/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;/etc/apt/sources.list.d/example.list&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="na"&gt;deb [signed-by&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/etc/apt/keyrings/example.gpg] https://repo.example.com/apt stable main&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 entry already has an option such as &lt;code&gt;arch=amd64&lt;/code&gt;, keep both options in the same brackets:&lt;/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;/etc/apt/sources.list.d/example.list&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="na"&gt;deb [arch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;amd64 signed-by=/etc/apt/keyrings/example.gpg] https://repo.example.com/apt stable main&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 the newer DEB822 format, add the &lt;code&gt;Signed-By&lt;/code&gt; field to the existing &lt;code&gt;.sources&lt;/code&gt; stanza:&lt;/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;/etc/apt/sources.list.d/example.sources&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="na"&gt;Types: deb&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;URIs: https://repo.example.com/apt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Suites: stable&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Components: main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Signed-By: /etc/apt/keyrings/example.gpg&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Preserve the repository&amp;rsquo;s existing URI, suite, components, and architecture settings. Only add or correct the keyring reference.&lt;/p&gt;
&lt;p&gt;Run the update again after saving the source 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 apt update&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_PUBKEY&lt;/code&gt; error for that repository should no longer appear.&lt;/p&gt;
&lt;h2 id="fetch-a-key-from-a-keyserver"&gt;Fetch a Key From a Keyserver &lt;a class="headline-link" href="#fetch-a-key-from-a-keyserver" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use a public keyserver only when the repository owner publishes the full fingerprint but does not provide a downloadable key. A keyserver can return a key for an ID, but it does not prove that the key belongs to the repository.&lt;/p&gt;
&lt;p&gt;Create an isolated temporary GnuPG home, then fetch the ID shown in the error. Replace the example ID with your missing key ID:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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;key_tmp&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;gpg --homedir &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$key_tmp&lt;/span&gt;&lt;span class="s2"&gt;&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; --keyserver hkps://keyserver.ubuntu.com &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --recv-keys 0123456789ABCDEF&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Display the complete fingerprint before exporting anything:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gpg --homedir &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$key_tmp&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; --fingerprint --with-subkey-fingerprint &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 0123456789ABCDEF&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Compare it with the fingerprint published by the repository owner. If it matches, export the key in the binary OpenPGP format that APT expects:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gpg --homedir &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$key_tmp&lt;/span&gt;&lt;span class="s2"&gt;&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; --export-options export-minimal &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --export 0123456789ABCDEF &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; sudo tee /etc/apt/keyrings/example.gpg &amp;gt; /dev/null
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo chmod &lt;span class="m"&gt;0644&lt;/span&gt; /etc/apt/keyrings/example.gpg
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rm -rf &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$key_tmp&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;Add the keyring to the repository&amp;rsquo;s &lt;code&gt;signed-by&lt;/code&gt; or &lt;code&gt;Signed-By&lt;/code&gt; setting as shown in the previous section, then run &lt;code&gt;sudo apt update&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If the key is already in your personal GnuPG keyring, run &lt;code&gt;gpg&lt;/code&gt; as your own user so that it reads that keyring, and keep &lt;code&gt;sudo&lt;/code&gt; on the &lt;code&gt;tee&lt;/code&gt; command that writes 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;gpg --export-options export-minimal --export 0123456789ABCDEF &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; sudo tee /etc/apt/keyrings/example.gpg &amp;gt; /dev/null
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo chmod &lt;span class="m"&gt;0644&lt;/span&gt; /etc/apt/keyrings/example.gpg&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="fix-keys-for-official-ubuntu-or-debian-repositories"&gt;Fix Keys for Official Ubuntu or Debian Repositories &lt;a class="headline-link" href="#fix-keys-for-official-ubuntu-or-debian-repositories" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A default Ubuntu or Debian installation already includes the keys for its official repositories. If the failing URL belongs to Ubuntu or Debian, do not replace the archive key with one copied from a third-party page.&lt;/p&gt;
&lt;p&gt;On Ubuntu, reinstall the package that provides the Ubuntu archive keys:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 --reinstall ubuntu-keyring&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On Debian, reinstall the Debian archive key 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 --reinstall debian-archive-keyring&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Then retry the update:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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;If the error remains, check that your source entries use the correct release codename and that the installed release still receives updates. An unsupported release or a repository entry copied from another distribution needs a source correction, not an extra trusted key.&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;The same NO_PUBKEY error appears after importing the key&lt;/strong&gt;&lt;br&gt;
Check the &lt;code&gt;signed-by&lt;/code&gt; or &lt;code&gt;Signed-By&lt;/code&gt; path in the affected source file. It must exactly match the keyring filename. Confirm that the file is readable with &lt;code&gt;ls -l /etc/apt/keyrings/NAME.gpg&lt;/code&gt;, then set safe permissions with &lt;code&gt;sudo chmod 0644 /etc/apt/keyrings/NAME.gpg&lt;/code&gt;. If the path and permissions are correct, compare the fingerprint again because the repository may have rotated its signing key.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;APT warns that the key is stored in the legacy trusted.gpg keyring&lt;/strong&gt;&lt;br&gt;
Debian 12 and Ubuntu 24.04 still fall back to the single &lt;code&gt;/etc/apt/trusted.gpg&lt;/code&gt; file and print &lt;code&gt;Key is stored in legacy trusted.gpg keyring (/etc/apt/trusted.gpg), see the DEPRECATION section in apt-key(8) for details.&lt;/code&gt; APT 3.0, which ships in Debian 13 and Ubuntu 25.04, dropped that fallback, so a repository whose key lives only in that file starts failing with &lt;code&gt;NO_PUBKEY&lt;/code&gt; after the upgrade. Export the key into its own keyring, then point the source at it with &lt;code&gt;signed-by&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;gpg --no-default-keyring --keyring /etc/apt/trusted.gpg &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --export 0123456789ABCDEF &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; sudo tee /etc/apt/keyrings/example.gpg &amp;gt; /dev/null
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo chmod &lt;span class="m"&gt;0644&lt;/span&gt; /etc/apt/keyrings/example.gpg&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;gpg reports no valid OpenPGP data&lt;/strong&gt;&lt;br&gt;
The key URL may be wrong, or the server may have returned an HTML error page. Open the repository owner&amp;rsquo;s current installation instructions and copy the key URL again. Keep &lt;code&gt;curl -f&lt;/code&gt; in the download command so an HTTP error stops the pipeline.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;gpg reports keyserver receive failed&lt;/strong&gt;&lt;br&gt;
The keyserver may be blocked or temporarily unavailable. The &lt;code&gt;hkps://keyserver.ubuntu.com&lt;/code&gt; address uses HTTPS, which normally works through networks that block the traditional HKP port. Prefer the repository owner&amp;rsquo;s direct key URL whenever one is available.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;gpg or curl is not installed&lt;/strong&gt;&lt;br&gt;
Install the required packages from the enabled Ubuntu or Debian repositories, then retry the key download:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 curl gnupg ca-certificates&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;APT reports conflicting values for Signed-By&lt;/strong&gt;&lt;br&gt;
The same repository is defined more than once with different keyring paths. Search &lt;code&gt;/etc/apt/sources.list.d/&lt;/code&gt; for the repository host, compare the matching &lt;code&gt;.list&lt;/code&gt; and &lt;code&gt;.sources&lt;/code&gt; files, and remove or disable the stale duplicate entry.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Several repositories report NO_PUBKEY&lt;/strong&gt;&lt;br&gt;
Handle each affected repository separately and verify every key against its owner. Repositories from the same provider may use one signing key, but unrelated repositories should not share a keyring merely because the errors appeared at the same time.&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 &lt;code&gt;NO_PUBKEY&lt;/code&gt; error should lead you back to the repository owner, not to an unverified key command. Once the full fingerprint matches, store the key under &lt;code&gt;/etc/apt/keyrings&lt;/code&gt; and limit it with &lt;code&gt;signed-by&lt;/code&gt;; for more on source configuration, see &lt;a href="https://linuxize.com/post/how-to-add-apt-repository-in-ubuntu/"&gt;adding an APT repository on Ubuntu&lt;/a&gt;
and the &lt;a href="https://linuxize.com/post/how-to-use-apt-command/"&gt;apt command guide&lt;/a&gt;
.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/fix-no-pubkey-gpg-error-apt/featured_hu_f05b8c0d2798e5a1.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>sudo Cheatsheet</title><link>https://linuxize.com/cheatsheet/sudo/</link><pubDate>Sun, 20 Sep 2026 09:25:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/cheatsheet/sudo/</guid><description>sudo at a glance: running commands as root, credential caching, environment handling, sudoers rule syntax, aliases, Defaults, and logging.</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;Run commands with elevated privileges.&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/sudo-command-in-linux/"&gt;&lt;code&gt;sudo command&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Run a command as root&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo -u username command&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run a command as another user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo -g groupname command&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run a command with another primary group&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo -u#1000 command&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run as a user ID instead of a name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo -i&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Open a root login shell that reads root&amp;rsquo;s profile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo -s&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Open a root shell that keeps the current directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo -e /etc/hosts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Edit a root-owned file with an editor running as your user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo -b command&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run the command in the background&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo -- command -l&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stop option parsing so the flags reach the command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo !!&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Re-run the previous command with &lt;code&gt;sudo&lt;/code&gt; in Bash or Zsh&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="password-caching"&gt;Password Caching &lt;a class="headline-link" href="#password-caching" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Control how often &lt;code&gt;sudo&lt;/code&gt; asks for a password.&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 -v&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Refresh the cached credentials without running a command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo -k&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Invalidate the cache for the current terminal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo -K&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove the cached credentials for every terminal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo -n command&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fail with an error instead of prompting, for cron and scripts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo -A command&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read the password from the program named in &lt;code&gt;SUDO_ASKPASS&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo -p &amp;quot;Password for %u: &amp;quot; command&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set a custom prompt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Defaults timestamp_timeout=10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cache the password for 10 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Defaults timestamp_timeout=0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prompt for every command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Defaults timestamp_type=global&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Share one timestamp across all terminals&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Debian and Ubuntu packages ship a 15 minute timeout, while Fedora and RHEL keep the upstream five minute default.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="listing-privileges"&gt;Listing Privileges &lt;a class="headline-link" href="#listing-privileges" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Check what a rule actually grants before you rely on it.&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 -l&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List the commands the current user may run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo -ll&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List the same rules in long form&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo -l -U username&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List another user&amp;rsquo;s privileges as root or an authorized user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo -l /usr/bin/systemctl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check whether one command is permitted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo -V&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print the version, and as root the full &lt;code&gt;Defaults&lt;/code&gt; list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;id -nG&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List group membership, which &lt;code&gt;%group&lt;/code&gt; rules match on&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;getent group sudo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the members of the &lt;code&gt;sudo&lt;/code&gt; group&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="environment-handling"&gt;Environment Handling &lt;a class="headline-link" href="#environment-handling" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;sudo&lt;/code&gt; builds a new environment rather than passing yours through.&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 -E command&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Request preservation of the current environment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo --preserve-env=http_proxy command&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Request preservation of only the named variables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo -H command&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set &lt;code&gt;HOME&lt;/code&gt; to the target user&amp;rsquo;s home directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo env&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print the environment &lt;code&gt;sudo&lt;/code&gt; actually builds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo env PATH=&amp;quot;/opt/tool/bin:/usr/bin&amp;quot; command&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run with an explicit trusted &lt;code&gt;PATH&lt;/code&gt; instead of &lt;code&gt;secure_path&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Defaults env_reset&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Strip the caller&amp;rsquo;s environment, the default on most distros&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Defaults env_keep += &amp;quot;http_proxy https_proxy&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Let named variables through &lt;code&gt;env_reset&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Defaults secure_path=&amp;quot;/usr/sbin:/usr/bin:/sbin:/bin&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Replace &lt;code&gt;PATH&lt;/code&gt; for every &lt;code&gt;sudo&lt;/code&gt; command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Defaults always_set_home&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set target &lt;code&gt;HOME&lt;/code&gt; when &lt;code&gt;env_reset&lt;/code&gt; does not already do so&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;sudo&lt;/code&gt; exports &lt;code&gt;SUDO_USER&lt;/code&gt;, &lt;code&gt;SUDO_UID&lt;/code&gt;, &lt;code&gt;SUDO_GID&lt;/code&gt;, and &lt;code&gt;SUDO_COMMAND&lt;/code&gt; into the command environment, so a privileged script can recover who called it.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="redirects-and-pipes"&gt;Redirects and Pipes &lt;a class="headline-link" href="#redirects-and-pipes" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The shell expands &lt;code&gt;&amp;gt;&lt;/code&gt; and &lt;code&gt;|&lt;/code&gt; before &lt;code&gt;sudo&lt;/code&gt; runs, so redirects need their own root process.&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 echo text &amp;gt; /root/file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Usually fails: the shell opens the file as your user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo sh -c 'echo text &amp;gt; /root/file'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run the whole line, redirect included, as root&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;echo text | sudo tee /root/file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Write the file as root&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;echo text | sudo tee -a /root/file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Append to the file as root&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;echo text | sudo tee /root/file &amp;gt; /dev/null&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Write without echoing the content back&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo sh -c 'cmd1 &amp;amp;&amp;amp; cmd2'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Chain several commands under one &lt;code&gt;sudo&lt;/code&gt; call&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo cat /root/file | grep pattern&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read as root, filter as your user&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="editing-the-sudoers-file"&gt;Editing the sudoers File &lt;a class="headline-link" href="#editing-the-sudoers-file" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Never open &lt;code&gt;/etc/sudoers&lt;/code&gt; in a plain editor. A syntax error there can disable &lt;code&gt;sudo&lt;/code&gt; access.&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 visudo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Edit &lt;code&gt;/etc/sudoers&lt;/code&gt; with a syntax check on save&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo visudo -c&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check every sudoers file without editing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo visudo -f /etc/sudoers.d/webops&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Edit a drop-in file with the same check&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo visudo -s&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Treat undefined aliases and alias cycles as errors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@includedir /etc/sudoers.d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Parse eligible files in the drop-in directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo chown root:root /etc/sudoers.d/webops&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set the required owner and group&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo chmod 0440 /etc/sudoers.d/webops&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set the permissions a drop-in file requires&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo ls /etc/sudoers.d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List the files in the drop-in directory&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;On &lt;code&gt;sudo&lt;/code&gt; older than 1.9.1 the directive is written &lt;code&gt;#includedir /etc/sudoers.d&lt;/code&gt;. Despite the leading &lt;code&gt;#&lt;/code&gt;, that line is not a comment. Drop-in files whose names contain a dot or end in &lt;code&gt;~&lt;/code&gt; are skipped.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;visudo&lt;/code&gt; reads &lt;code&gt;SUDO_EDITOR&lt;/code&gt;, &lt;code&gt;VISUAL&lt;/code&gt;, and &lt;code&gt;EDITOR&lt;/code&gt; when the policy lets those variables through. A command rule that matches &lt;code&gt;ALL&lt;/code&gt; implies &lt;code&gt;SETENV&lt;/code&gt;, while a rule granting only &lt;code&gt;/usr/sbin/visudo&lt;/code&gt; must add &lt;code&gt;SETENV&lt;/code&gt; or preserve the editor variables. To avoid depending on that policy, open a root shell with &lt;code&gt;sudo -i&lt;/code&gt;, then run &lt;code&gt;EDITOR=/usr/bin/vim visudo&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="sudoers-rule-syntax"&gt;sudoers Rule Syntax &lt;a class="headline-link" href="#sudoers-rule-syntax" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Each rule reads &lt;code&gt;user host=(runas:rungroup) tagged commands&lt;/code&gt;.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule&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;username ALL=(ALL:ALL) ALL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full access for one user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-add-user-to-sudoers-in-ubuntu/"&gt;&lt;code&gt;%sudo ALL=(ALL:ALL) ALL&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Full access for a group, Debian and Ubuntu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-add-user-to-sudoers-in-centos/"&gt;&lt;code&gt;%wheel ALL=(ALL) ALL&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Full access for a group, Fedora and RHEL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-run-sudo-command-without-password/"&gt;&lt;code&gt;username ALL=(ALL) NOPASSWD: ALL&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Skip the password prompt for every command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;username ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart nginx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Skip the prompt for one exact command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;username ALL=(www-data) /usr/bin/php&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run one command as a service account&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;username ALL=(ALL) NOEXEC: /usr/bin/less&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Block child commands where &lt;code&gt;NOEXEC&lt;/code&gt; is supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;username ALL=(ALL) PASSWD: /usr/bin/su&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Force a prompt for one command inside a &lt;code&gt;NOPASSWD&lt;/code&gt; rule&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Command paths generally must be absolute. A rule listing &lt;code&gt;apt&lt;/code&gt; never matches; write &lt;code&gt;/usr/bin/apt&lt;/code&gt;. Wildcards in command arguments can match whitespace, so exact arguments or anchored regular expressions are safer. Do not use an editor as a security boundary: &lt;code&gt;/usr/bin/vim&lt;/code&gt; can start a shell even without a wildcard.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="aliases"&gt;Aliases &lt;a class="headline-link" href="#aliases" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Aliases keep long rule sets readable and reduce repetition.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Declaration&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;User_Alias ADMINS = alice, bob, %ops&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Name a set of users or groups&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Runas_Alias SUPERUSER = root&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Name a set of target users&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Host_Alias WEB = web01, web02, 10.0.5.0/24&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Name a set of hosts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Cmnd_Alias NGINX = /usr/bin/systemctl restart nginx, /usr/bin/systemctl reload nginx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Name a set of exact commands&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ADMINS WEB=(SUPERUSER) NGINX&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Combine aliases in a rule&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ADMINS ALL=(root) NOPASSWD: NGINX&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Apply a tag to a command alias&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Alias names start with an uppercase letter and contain uppercase letters, digits, or underscores. The parser resolves aliases across the complete policy, so a definition may appear before or after a rule that uses it.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="defaults-directives"&gt;Defaults Directives &lt;a class="headline-link" href="#defaults-directives" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Policy settings that apply to every matching &lt;code&gt;sudo&lt;/code&gt; call.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive&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;Defaults env_reset&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run commands with a clean environment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Defaults secure_path=&amp;quot;/usr/sbin:/usr/bin:/sbin:/bin&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fixed &lt;code&gt;PATH&lt;/code&gt; for &lt;code&gt;sudo&lt;/code&gt; commands&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Defaults timestamp_timeout=15&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Minutes before the password is asked for again&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Defaults passwd_tries=3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Password attempts before &lt;code&gt;sudo&lt;/code&gt; gives up&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Defaults targetpw&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ask for the target user&amp;rsquo;s password, not the caller&amp;rsquo;s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Defaults lecture=never&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Skip the warning shown on first use&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Defaults mail_badpass&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mail the administrator after a failed password&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Defaults:username timestamp_timeout=30&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Apply a setting to one user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Defaults@web01 log_output&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Apply a setting on one host&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Defaults!/usr/sbin/visudo env_keep += &amp;quot;SUDO_EDITOR VISUAL EDITOR&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Preserve editor variables for &lt;code&gt;visudo&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="logging-and-auditing"&gt;Logging and Auditing &lt;a class="headline-link" href="#logging-and-auditing" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;By default, &lt;code&gt;sudoers&lt;/code&gt; logs allowed and denied commands as well as errors.&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 journalctl _COMM=sudo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show &lt;code&gt;sudo&lt;/code&gt; events from the systemd journal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo journalctl -t sudo -S today&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show today&amp;rsquo;s events by syslog tag&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo grep sudo /var/log/auth.log&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read the log on Debian and Ubuntu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo grep sudo /var/log/secure&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read the log on Fedora, RHEL, and derivatives&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Defaults logfile=&amp;quot;/var/log/sudo.log&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Write a dedicated &lt;code&gt;sudo&lt;/code&gt; log file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Defaults log_input, log_output&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Record full sessions for replay&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo sudoreplay -l&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List the recorded sessions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo sudoreplay ID&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Replay one recorded session&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;What the common failures mean and where to look.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Message&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;username is not in the sudoers file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add the user to &lt;code&gt;sudo&lt;/code&gt; on Debian and Ubuntu or &lt;code&gt;wheel&lt;/code&gt; on Fedora and RHEL, then log in again&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo: unable to resolve host name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Map the current hostname in &lt;code&gt;/etc/hosts&lt;/code&gt;, such as &lt;code&gt;127.0.1.1 name&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/fix-sudo-command-not-found/"&gt;&lt;code&gt;sudo: command not found&lt;/code&gt;&lt;/a&gt;
or &lt;code&gt;sudo: name: command not found&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The shell cannot find &lt;code&gt;sudo&lt;/code&gt;, or the target command is missing or outside &lt;code&gt;secure_path&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sorry, you are not allowed to set the following environment variables&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Preserve the variable or grant &lt;code&gt;SETENV&lt;/code&gt; narrowly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sorry, you are not allowed to preserve the environment&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Grant &lt;code&gt;SETENV&lt;/code&gt;; matching &lt;code&gt;ALL&lt;/code&gt; implies it unless &lt;code&gt;NOSETENV&lt;/code&gt; overrides it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; /etc/sudoers: syntax error near line N &amp;lt;&amp;lt;&amp;lt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Press &lt;code&gt;e&lt;/code&gt; to re-edit; &lt;code&gt;Q&lt;/code&gt; force-saves the broken file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo: no tty present and no askpass program specified&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;-n&lt;/code&gt; to fail fast, configure &lt;code&gt;-A&lt;/code&gt;, or grant &lt;code&gt;NOPASSWD&lt;/code&gt; narrowly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo: effective uid is not 0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check root ownership, setuid, &lt;code&gt;nosuid&lt;/code&gt;, and NFS mounts; repair from root or recovery&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;Deeper reading on privilege escalation and user administration.&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/sudo-command-in-linux/"&gt;sudo Command in Linux&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Full &lt;code&gt;sudo&lt;/code&gt; guide with examples, sudoedit, and credential caching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-add-user-to-sudoers-in-ubuntu/"&gt;How to Add User to Sudoers in Ubuntu&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Grant sudo access on Debian and Ubuntu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-add-user-to-sudoers-in-centos/"&gt;How to Add User to Sudoers in CentOS&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Grant sudo access on RHEL and derivatives&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-run-sudo-command-without-password/"&gt;How to Run sudo Command Without Password&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Set up a &lt;code&gt;NOPASSWD&lt;/code&gt; rule safely&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/su-command-in-linux/"&gt;su Command in Linux&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Switch user accounts instead of running one command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/cheatsheet/su/"&gt;su cheatsheet&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Quick reference for &lt;code&gt;su&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;</content:encoded></item><item><title>nice and renice Commands in Linux: Set Process Priority</title><link>https://linuxize.com/post/nice-and-renice-command-in-linux/</link><pubDate>Fri, 18 Sep 2026 18:00:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/nice-and-renice-command-in-linux/</guid><category>linux commands</category><description>Control Linux process priority with nice and renice. See how nice values work, start lower-priority jobs, and change the priority of running processes.</description><content:encoded>&lt;p&gt;When you run a CPU-heavy task on a shared server, a long compile job or a background backup can slow down everything else. Linux lets you give that process a lower priority, so it receives a smaller share of CPU time when it competes with other work. Use &lt;code&gt;nice&lt;/code&gt; to adjust the priority when you start a command and &lt;code&gt;renice&lt;/code&gt; to change the priority of a process that is already running.&lt;/p&gt;
&lt;p&gt;This guide explains how nice values work in Linux and how to use &lt;code&gt;nice&lt;/code&gt; and &lt;code&gt;renice&lt;/code&gt; to start and adjust processes at different priority levels.&lt;/p&gt;
&lt;h2 id="how-nice-values-work"&gt;How Nice Values Work &lt;a class="headline-link" href="#how-nice-values-work" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For processes using normal time-sharing or batch scheduling, the kernel uses a nice value to decide how much CPU time they should receive relative to competing processes. The range goes from &lt;code&gt;-20&lt;/code&gt; (highest priority) to &lt;code&gt;19&lt;/code&gt; (lowest priority). New processes inherit their parent&amp;rsquo;s nice value, which is usually &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A higher nice value means the process is &amp;ldquo;nicer&amp;rdquo; to other work and receives a smaller CPU share under contention. A lower nice value gives it a larger share. Without contention, even a process at nice &lt;code&gt;19&lt;/code&gt; can use a full CPU core. Niceness does not set a CPU usage limit.&lt;/p&gt;
&lt;p&gt;On most systems, regular users can only increase the nice value of their own processes (lower their priority). Decreasing the value normally requires root privileges, even when changing it from &lt;code&gt;10&lt;/code&gt; back to &lt;code&gt;0&lt;/code&gt;. An administrator can allow exceptions through the &lt;code&gt;RLIMIT_NICE&lt;/code&gt; resource limit.&lt;/p&gt;
&lt;p&gt;You can check the current nice value of a process in the &lt;code&gt;NI&lt;/code&gt; column of &lt;a href="https://linuxize.com/post/ps-command-in-linux/"&gt;&lt;code&gt;ps&lt;/code&gt;&lt;/a&gt;
or &lt;a href="https://linuxize.com/post/top-command-in-linux/"&gt;&lt;code&gt;top&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;ps -eo pid,ni,comm&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For example, the output may look 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; PID NI COMMAND
1 0 systemd
842 0 sshd
1024 10 rsync&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;rsync&lt;/code&gt; process in the example above is running with a nice value of &lt;code&gt;10&lt;/code&gt;, which is lower priority than the default.&lt;/p&gt;
&lt;h2 id="nice-command-syntax"&gt;nice Command Syntax &lt;a class="headline-link" href="#nice-command-syntax" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;nice&lt;/code&gt; command runs a command with an adjustment to the inherited nice 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="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;nice [OPTION] [COMMAND [ARGS]...]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;With no command, &lt;code&gt;nice&lt;/code&gt; prints its current nice value, inherited from your 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;nice&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;In a shell with the usual nice value of &lt;code&gt;0&lt;/code&gt;, the output 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="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&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This does not change the shell&amp;rsquo;s priority. When you pass a command, &lt;code&gt;nice&lt;/code&gt; adds &lt;code&gt;10&lt;/code&gt; to the inherited nice value unless you choose a different adjustment with &lt;code&gt;-n&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The examples below assume a starting nice value of &lt;code&gt;0&lt;/code&gt; and use GNU &lt;code&gt;nice&lt;/code&gt; from coreutils and &lt;code&gt;renice&lt;/code&gt; from util-linux, the implementations commonly found on Linux.&lt;/p&gt;
&lt;h2 id="running-a-command-with-lower-priority"&gt;Running a Command With Lower Priority &lt;a class="headline-link" href="#running-a-command-with-lower-priority" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To try the default adjustment without creating files or running a heavy workload, use &lt;code&gt;nice&lt;/code&gt; to run another copy of 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;nice nice&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The second &lt;code&gt;nice&lt;/code&gt; prints the adjusted 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="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;10&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The outer command adds &lt;code&gt;10&lt;/code&gt; to the inherited value of &lt;code&gt;0&lt;/code&gt;. The inner command only prints that value and exits.&lt;/p&gt;
&lt;p&gt;For a practical example, you can run an archive job at a lower CPU priority. Choose an unused archive filename and replace &lt;code&gt;/var/www&lt;/code&gt; with a directory you can 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;nice tar -czf backup.tar.gz /var/www&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;tar&lt;/code&gt; process runs with a nice value of &lt;code&gt;10&lt;/code&gt; instead of &lt;code&gt;0&lt;/code&gt;. This can help interactive programs stay responsive when they compete with the archive job for CPU time.&lt;/p&gt;
&lt;p&gt;You can choose a different adjustment with the &lt;code&gt;-n&lt;/code&gt; option. For example, to add &lt;code&gt;15&lt;/code&gt; when running an existing build 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;nice -n &lt;span class="m"&gt;15&lt;/span&gt; ./build.sh&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;From a starting value of &lt;code&gt;0&lt;/code&gt;, the build script runs at nice &lt;code&gt;15&lt;/code&gt;. If your shell already has a nice value of &lt;code&gt;10&lt;/code&gt;, the result is &lt;code&gt;19&lt;/code&gt;, the maximum allowed value, rather than &lt;code&gt;15&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="running-a-command-with-higher-priority"&gt;Running a Command With Higher Priority &lt;a class="headline-link" href="#running-a-command-with-higher-priority" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To raise a command&amp;rsquo;s priority, use a negative adjustment. This normally requires root privileges. For example, a regular user without a suitable nice resource limit can try:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nice -n -5 nice&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;From a starting value of &lt;code&gt;0&lt;/code&gt;, GNU &lt;code&gt;nice&lt;/code&gt; reports the failed adjustment and the inner command prints the unchanged 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="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;nice: cannot set niceness: Permission denied
0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;GNU &lt;code&gt;nice&lt;/code&gt; still runs the command after this permission error, so the warning does not mean the workload was stopped.&lt;/p&gt;
&lt;p&gt;With &lt;a href="https://linuxize.com/post/sudo-command-in-linux/"&gt;&lt;code&gt;sudo&lt;/code&gt;&lt;/a&gt;
, you can normally apply the adjustment:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 nice -n -5 nice&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;When the starting value is &lt;code&gt;0&lt;/code&gt;, the inner command prints:&lt;/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;-5&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This command runs as root. To raise the priority of a job you already started as your own user, use &lt;code&gt;sudo renice&lt;/code&gt; as shown below. Changing its nice value does not change its owner.&lt;/p&gt;
&lt;p&gt;Use negative values carefully. A CPU-heavy process at nice &lt;code&gt;-20&lt;/code&gt; can leave competing work with little CPU time.&lt;/p&gt;
&lt;h2 id="renice-command-syntax"&gt;renice Command Syntax &lt;a class="headline-link" href="#renice-command-syntax" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;renice&lt;/code&gt; command changes the nice value of a process that is already 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="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;renice PRIORITY [-p|-g|-u] IDENTIFIER...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;By default, &lt;code&gt;renice&lt;/code&gt; interprets the identifier as a process ID. The &lt;code&gt;-p&lt;/code&gt; option makes that explicit, &lt;code&gt;-g&lt;/code&gt; targets a process group, and &lt;code&gt;-u&lt;/code&gt; targets processes owned by a given user.&lt;/p&gt;
&lt;p&gt;Unlike &lt;code&gt;nice -n&lt;/code&gt;, the priority in this form is an absolute nice value. For example, &lt;code&gt;renice 10 -p PID&lt;/code&gt; sets the value to &lt;code&gt;10&lt;/code&gt;; it does not add &lt;code&gt;10&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In util-linux, &lt;code&gt;renice -n 10&lt;/code&gt; also sets an absolute value by default, but it becomes a relative adjustment when the &lt;code&gt;POSIXLY_CORRECT&lt;/code&gt; environment variable is set. We use the form without &lt;code&gt;-n&lt;/code&gt; to keep the examples unambiguous. See the &lt;a href="https://man7.org/linux/man-pages/man1/renice.1.html" target="_blank" rel="noopener noreferrer"&gt;renice manual&lt;/a&gt;
for details.&lt;/p&gt;
&lt;h2 id="changing-the-priority-of-a-running-process"&gt;Changing the Priority of a Running Process &lt;a class="headline-link" href="#changing-the-priority-of-a-running-process" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To lower the priority of a running process, find its PID with &lt;code&gt;ps&lt;/code&gt; or &lt;a href="https://linuxize.com/post/pgrep-command-in-linux/"&gt;&lt;code&gt;pgrep&lt;/code&gt;&lt;/a&gt;
, then pass it to &lt;code&gt;renice&lt;/code&gt;. Replace &lt;code&gt;4821&lt;/code&gt; with the PID of your 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;renice &lt;span class="m"&gt;10&lt;/span&gt; -p &lt;span class="m"&gt;4821&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 process starts at nice &lt;code&gt;0&lt;/code&gt;, the output 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="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;4821 (process ID) old priority 0, new priority 10&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The output confirms the change from &lt;code&gt;0&lt;/code&gt; to &lt;code&gt;10&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To try this on a harmless process, start &lt;a href="https://linuxize.com/post/how-to-use-linux-sleep-command-to-pause-a-bash-script/"&gt;&lt;code&gt;sleep&lt;/code&gt;&lt;/a&gt;
in the background, capture its PID, and change its nice 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;sleep &lt;span class="m"&gt;60&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;job_pid&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;renice &lt;span class="m"&gt;10&lt;/span&gt; -p &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$job_pid&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;ps -p &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$job_pid&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -o pid,ni,comm&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; holds the PID of the last background job. The &lt;code&gt;NI&lt;/code&gt; column from &lt;code&gt;ps&lt;/code&gt; should show &lt;code&gt;10&lt;/code&gt;. The process exits on its own after 60 seconds, so run the following examples before it finishes.&lt;/p&gt;
&lt;p&gt;To change the value again, pass the new absolute 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;renice &lt;span class="m"&gt;15&lt;/span&gt; -p &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$job_pid&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;This changes the nice value from &lt;code&gt;10&lt;/code&gt; to &lt;code&gt;15&lt;/code&gt;, rather than adding &lt;code&gt;15&lt;/code&gt; to it.&lt;/p&gt;
&lt;p&gt;Restoring the value to &lt;code&gt;0&lt;/code&gt; raises the process&amp;rsquo;s priority and normally requires &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;sudo renice &lt;span class="m"&gt;0&lt;/span&gt; -p &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$job_pid&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;On Linux, nice values are per thread. Renicing a PID does not automatically update every worker thread or an existing child process. Newly created children inherit the calling thread&amp;rsquo;s nice value.&lt;/p&gt;
&lt;h2 id="renicing-all-processes-for-a-user"&gt;Renicing All Processes for a User &lt;a class="headline-link" href="#renicing-all-processes-for-a-user" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To adjust the current processes owned by a user, pass the username with &lt;code&gt;-u&lt;/code&gt;. For example, if your backup jobs run under an account named &lt;code&gt;backup&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 renice &lt;span class="m"&gt;15&lt;/span&gt; -u backup&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This sets the account&amp;rsquo;s current processes to nice &lt;code&gt;15&lt;/code&gt;. It does not configure a permanent default for the account or change jobs that will start independently later.&lt;/p&gt;
&lt;h2 id="renicing-a-process-group"&gt;Renicing a Process Group &lt;a class="headline-link" href="#renicing-a-process-group" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To target a process group, use &lt;code&gt;-g&lt;/code&gt; with the process group ID (PGID). First, list process IDs and their groups:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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,pgid,ni,comm&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If the jobs you want to adjust belong to group &lt;code&gt;1820&lt;/code&gt;, set their nice value to &lt;code&gt;5&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 renice &lt;span class="m"&gt;5&lt;/span&gt; -g &lt;span class="m"&gt;1820&lt;/span&gt;&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;1820&lt;/code&gt; with the PGID you found. A process group can contain several commands from the same job, such as the stages of a shell pipeline.&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;Show the nice value inherited from your shell&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nice&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Add &lt;code&gt;10&lt;/code&gt; when starting a command&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nice command&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Add &lt;code&gt;15&lt;/code&gt; when starting a command&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nice -n 15 command&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subtract &lt;code&gt;5&lt;/code&gt; when starting a command as root&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo nice -n -5 command&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Set a running process to nice &lt;code&gt;10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;renice 10 -p PID&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restore a running process to nice &lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo renice 0 -p PID&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Set a user&amp;rsquo;s current processes to nice &lt;code&gt;15&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo renice 15 -u USER&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Set processes in a group to nice &lt;code&gt;5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo renice 5 -g PGID&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;nice: cannot set niceness: Permission denied&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
You tried to apply a negative adjustment without permission to raise priority. Use &lt;code&gt;sudo&lt;/code&gt; when appropriate, or choose a nonnegative adjustment. GNU &lt;code&gt;nice&lt;/code&gt; still runs the command at its inherited nice value after this warning.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;renice reports permission denied or operation not permitted&lt;/strong&gt;&lt;br&gt;
You tried to change a process you do not own, or to decrease a nice value without the required privileges. On most systems, even restoring your own process from nice &lt;code&gt;10&lt;/code&gt; to &lt;code&gt;0&lt;/code&gt; requires &lt;code&gt;sudo&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;renice reports no such process&lt;/strong&gt;&lt;br&gt;
The process has exited or the PID is wrong. Find the current PID with &lt;code&gt;ps&lt;/code&gt; or &lt;code&gt;pgrep&lt;/code&gt; and try again. In the &lt;code&gt;sleep&lt;/code&gt; example above, the process disappears after 60 seconds.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Nice value does not seem to affect performance&lt;/strong&gt;&lt;br&gt;
Nice values matter when processes compete for CPU time. On an idle system, a niced process can still use a full CPU core. Use &lt;a href="https://linuxize.com/post/htop-command-in-linux/"&gt;&lt;code&gt;htop&lt;/code&gt;&lt;/a&gt;
to check whether the workload is CPU-bound.&lt;/p&gt;
&lt;p&gt;Autogrouping and cgroup CPU scheduling also affect how CPU time is shared. With autogrouping enabled, changing niceness in one terminal session may have little effect on a job in another session. Process nice values apply within the same scheduling group; see the &lt;a href="https://man7.org/linux/man-pages/man7/sched.7.html" target="_blank" rel="noopener noreferrer"&gt;Linux scheduling documentation&lt;/a&gt;
for the details.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A backup still slows down disk access&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;nice&lt;/code&gt; and &lt;code&gt;renice&lt;/code&gt; adjust CPU scheduling, so they do not directly set disk I/O priority. Some I/O schedulers derive a default I/O priority from CPU niceness, but the behavior depends on the scheduler. Use &lt;a href="https://man7.org/linux/man-pages/man1/ionice.1.html" target="_blank" rel="noopener noreferrer"&gt;ionice&lt;/a&gt;
when you need to set I/O priority explicitly, and check whether your active I/O scheduler 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;For a long job that can wait, start with the default &lt;code&gt;nice&lt;/code&gt; adjustment and check its &lt;code&gt;NI&lt;/code&gt; value with &lt;code&gt;ps&lt;/code&gt;. If you prefer to adjust priorities interactively, see how to change nice values in &lt;a href="https://linuxize.com/post/htop-command-in-linux/"&gt;htop&lt;/a&gt;
.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/nice-and-renice-command-in-linux/featured_hu_a9d7c9374bf33d76.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>Nginx Cheatsheet</title><link>https://linuxize.com/cheatsheet/nginx/</link><pubDate>Wed, 16 Sep 2026 17:45:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/cheatsheet/nginx/</guid><description>Nginx directives at a glance: server blocks, location matching, reverse proxy headers, TLS, redirects, rate limits, caching, and log formats.</description><content:encoded>&lt;div class="card wrap-commands"&gt;
&lt;h3 id="service-and-cli-commands"&gt;Service and CLI Commands &lt;a class="headline-link" href="#service-and-cli-commands" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Control the service and check the configuration before it goes live.&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/start-stop-restart-nginx/"&gt;&lt;code&gt;sudo systemctl reload nginx&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Apply a new configuration without dropping connections&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl start nginx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start the service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl stop nginx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stop the service immediately&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl restart nginx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stop and start the service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl status nginx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the service state and recent log lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl enable --now nginx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start now and at every boot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nginx -t&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Test the configuration for syntax errors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nginx -T&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Test and print the full merged configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nginx -s reload&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reload through the master process signal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nginx -s quit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Shut down gracefully after current requests finish&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nginx -s reopen&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reopen the log files after rotation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nginx -V&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print the version and the configure arguments&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Always run &lt;code&gt;nginx -t&lt;/code&gt; before a reload. A failed reload leaves the old configuration running, but a restart with a broken file leaves the service down.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card wrap-commands"&gt;
&lt;h3 id="configuration-layout"&gt;Configuration Layout &lt;a class="headline-link" href="#configuration-layout" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Where the files live and how a site is switched on.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Path&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-nginx-on-ubuntu-26-04/"&gt;&lt;code&gt;/etc/nginx/nginx.conf&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Main configuration file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/etc/nginx/conf.d/*.conf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Common include pattern; verify it in &lt;code&gt;nginx.conf&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/etc/nginx/sites-available/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Site definitions on Ubuntu and Debian&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/etc/nginx/sites-enabled/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Symlinks to the active sites&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/etc/nginx/snippets/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reusable fragments on Ubuntu and Debian&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/var/www/html&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Default document root on Ubuntu and Debian&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/usr/share/nginx/html&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Default document root on Fedora and RHEL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/var/log/nginx/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Access and error logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enable a site on Ubuntu and Debian&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo unlink /etc/nginx/sites-enabled/default&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Disable the default site&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Fedora and RHEL have no &lt;code&gt;sites-available&lt;/code&gt; directory. Put each site in its own file under &lt;code&gt;/etc/nginx/conf.d/&lt;/code&gt; instead.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card wrap-commands"&gt;
&lt;h3 id="server-blocks"&gt;Server Blocks &lt;a class="headline-link" href="#server-blocks" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Directives that decide which server block answers a request.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive&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-set-up-nginx-server-blocks-on-ubuntu-22-04/"&gt;&lt;code&gt;listen 80;&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Listen on IPv4 port 80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;listen [::]:80;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Listen on IPv6 port 80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;listen 80 default_server;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Serve requests matching no other server name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;server_name example.com www.example.com;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Match these host names&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;server_name *.example.com;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Match any subdomain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;server_name _;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Invalid name used as a catch-all placeholder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;root /var/www/example.com;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set the document root&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;index index.html index.htm;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;File served when a directory is requested&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;include /etc/nginx/snippets/ssl.conf;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pull in a shared fragment&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Nginx matches the exact name first, then the longest wildcard starting with an asterisk, then the longest wildcard ending with one, and finally the first matching regular expression.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card wrap-commands"&gt;
&lt;h3 id="location-matching"&gt;Location Matching &lt;a class="headline-link" href="#location-matching" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Modifiers that set both the match rule and its priority.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive&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/nginx-location-block/"&gt;&lt;code&gt;location = /health { ... }&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Exact match, checked first and wins immediately&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;location ^~ /static/ { ... }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prefix match that suppresses the regex pass&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;location ~ \.php$ { ... }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Case-sensitive regular expression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;location ~* \.css$ { ... }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Case-insensitive regular expression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;location /images/ { ... }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Plain prefix match&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;location / { ... }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fallback for every request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;location @fallback { ... }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Named location, reachable only from &lt;code&gt;error_page&lt;/code&gt; or &lt;code&gt;try_files&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Nginx checks the exact match first, then stores the longest matching prefix. If that prefix uses &lt;code&gt;^~&lt;/code&gt;, it is used right away. Otherwise the regular expressions are tried in file order and the first match wins. The stored prefix is used only when no regular expression matches.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card wrap-commands"&gt;
&lt;h3 id="serving-files"&gt;Serving Files &lt;a class="headline-link" href="#serving-files" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Map a request to a file on disk and decide what happens when it is missing.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive&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;root /var/www/example.com;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Append the current normalized URI path, without the query string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;alias /srv/media/;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Replace the matched location prefix with this path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;try_files $uri $uri/ =404;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Try the file, then the directory, then return 404&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;try_files $uri $uri/ /index.html;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Single-page application fallback&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;try_files $uri $uri/ /index.php?$query_string;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;WordPress and PHP framework fallback&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;autoindex on;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generate a directory listing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;error_page 404 /404.html;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Serve a custom error page&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;error_page 502 503 504 /5xx.html;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One page for several statuses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sendfile on;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Copy files to the socket in the kernel&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;With &lt;code&gt;root&lt;/code&gt;, nginx appends the current normalized URI path without the query string, so &lt;code&gt;location /images/&lt;/code&gt; with &lt;code&gt;root /data&lt;/code&gt; serves &lt;code&gt;/data/images/cat.png&lt;/code&gt;. With &lt;code&gt;alias&lt;/code&gt;, the matched prefix is replaced instead, so the same location with &lt;code&gt;alias /data/pictures/&lt;/code&gt; serves &lt;code&gt;/data/pictures/cat.png&lt;/code&gt;. Keep the trailing slash on both the location and the alias.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card wrap-commands"&gt;
&lt;h3 id="reverse-proxy"&gt;Reverse Proxy &lt;a class="headline-link" href="#reverse-proxy" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Forward requests to an application and pass on the client details.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive&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/nginx-reverse-proxy/"&gt;&lt;code&gt;proxy_pass http://127.0.0.1:3000;&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Forward to a local application&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;proxy_pass http://backend;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Forward to a named upstream group&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;proxy_set_header Host $host;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pass the original host name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;proxy_set_header X-Real-IP $remote_addr;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pass the client address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Append the client to the forwarding chain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;proxy_set_header X-Forwarded-Proto $scheme;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tell the application whether TLS was used&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;proxy_http_version 1.1;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use HTTP/1.1 for upstream keepalive and WebSocket upgrades&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;proxy_set_header Upgrade $http_upgrade;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pass the WebSocket upgrade request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;proxy_set_header Connection &amp;quot;upgrade&amp;quot;;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Complete the WebSocket handshake&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;proxy_read_timeout 300s;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Wait longer for a slow response&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;proxy_buffering off;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stream the response as it arrives&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The trailing slash changes the result. &lt;code&gt;proxy_pass http://127.0.0.1:3000;&lt;/code&gt; forwards the full request URI, while &lt;code&gt;proxy_pass http://127.0.0.1:3000/;&lt;/code&gt; replaces the matched location prefix with &lt;code&gt;/&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card wrap-commands"&gt;
&lt;h3 id="load-balancing"&gt;Load Balancing &lt;a class="headline-link" href="#load-balancing" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Spread traffic across a pool of backends. Define the pool in &lt;code&gt;http&lt;/code&gt; and put the other directives inside &lt;code&gt;upstream&lt;/code&gt;.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive&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;upstream backend { server 10.0.0.1:8080; server 10.0.0.2:8080; }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Define a pool, round-robin by default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;least_conn;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Send each request to the least busy server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ip_hash;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pin a client address to one server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hash $request_uri consistent;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Distribute by key with minimal reshuffling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;server 10.0.0.1:8080 weight=3;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Take three times the usual share&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;server 10.0.0.2:8080 max_fails=3 fail_timeout=30s;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;After three qualifying failures within 30 seconds, mark unavailable for 30 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;server 10.0.0.3:8080 backup;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use only when the others are down&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;server 10.0.0.4:8080 down;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Take a server out of rotation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;server unix:/run/app.sock;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Proxy to a Unix socket&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;keepalive 32;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cache up to 32 idle upstream connections per worker&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Choose only one of &lt;code&gt;least_conn&lt;/code&gt;, &lt;code&gt;ip_hash&lt;/code&gt;, or &lt;code&gt;hash&lt;/code&gt;; round-robin is the default. The &lt;code&gt;backup&lt;/code&gt; parameter cannot be combined with &lt;code&gt;hash&lt;/code&gt; or &lt;code&gt;ip_hash&lt;/code&gt;. On nginx versions older than 1.29.7, set &lt;code&gt;proxy_http_version 1.1;&lt;/code&gt; and &lt;code&gt;proxy_set_header Connection &amp;quot;&amp;quot;;&lt;/code&gt; in the proxy location to use HTTP/1.1 upstream keepalive. Since 1.29.7, HTTP/1.1 and upstream keepalive are enabled by default, and the default proxy configuration no longer sends &lt;code&gt;Connection: close&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card wrap-commands"&gt;
&lt;h3 id="https-and-tls"&gt;HTTPS and TLS &lt;a class="headline-link" href="#https-and-tls" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Terminate TLS and keep the protocol settings current.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive&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/secure-nginx-with-let-s-encrypt-on-ubuntu-20-04/"&gt;&lt;code&gt;listen 443 ssl;&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Accept TLS connections&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;http2 on;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enable HTTP/2 on nginx 1.25.1 and later&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;listen 443 ssl http2;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enable HTTP/2 on older releases&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Certificate and intermediate chain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Private key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ssl_protocols TLSv1.2 TLSv1.3;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allow only modern protocol versions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ssl_prefer_server_ciphers off;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Let the client pick from the allowed ciphers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ssl_session_cache shared:SSL:10m;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Share the session cache between workers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ssl_session_timeout 1d;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Keep sessions resumable for a day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;add_header Strict-Transport-Security &amp;quot;max-age=63072000&amp;quot; always;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Send HSTS on every response&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo certbot --nginx -d example.com -d www.example.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Issue and install a certificate&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Keep the private key readable by root only. Never copy a key into a repository or a document root, and add &lt;code&gt;*.pem&lt;/code&gt; to &lt;code&gt;.gitignore&lt;/code&gt; when the configuration lives in version control.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card wrap-commands"&gt;
&lt;h3 id="redirects-and-rewrites"&gt;Redirects and Rewrites &lt;a class="headline-link" href="#redirects-and-rewrites" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Move URLs without losing the original path.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive&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/redirect-http-to-https-in-nginx/"&gt;&lt;code&gt;return 301 https://$host$request_uri;&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Redirect every request to HTTPS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;return 301 https://www.example.com$request_uri;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Redirect to the www host&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;return 302 /maintenance.html;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Temporary redirect&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;return 444;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Close the connection without a response&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rewrite ^/old/(.*)$ /new/$1 permanent;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;301 rewrite that keeps the path tail&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rewrite ^/old/(.*)$ /new/$1 redirect;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The same rewrite as a 302&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rewrite ^/blog/(.*)$ /$1 last;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rewrite internally and restart location matching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rewrite ^/api/(.*)$ /$1 break;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rewrite internally and stop processing rewrites&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Prefer &lt;code&gt;return&lt;/code&gt; over &lt;code&gt;rewrite&lt;/code&gt; for plain redirects. It is faster, easier to read, and it skips the regular expression evaluation that &lt;code&gt;rewrite&lt;/code&gt; performs on every request.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card wrap-commands"&gt;
&lt;h3 id="access-control-and-limits"&gt;Access Control and Limits &lt;a class="headline-link" href="#access-control-and-limits" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Guard the upstream against oversized uploads and traffic spikes. Use &lt;code&gt;htpasswd -c&lt;/code&gt; only for a new password file, because it overwrites an existing one.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive&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;client_max_body_size 64m;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Raise the upload size limit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;allow 10.0.0.0/8;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Permit a network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;deny all;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Block everything else&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;auth_basic &amp;quot;Restricted&amp;quot;;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Turn on HTTP basic authentication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;auth_basic_user_file /etc/nginx/.htpasswd;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Point to the password file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo htpasswd -c /etc/nginx/.htpasswd admin&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a new password file, first creation only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo htpasswd /etc/nginx/.htpasswd editor&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add or update a user in the existing file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;limit_req_zone $binary_remote_addr zone=req:10m rate=10r/s;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Define a rate limit zone in &lt;code&gt;http&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;limit_req zone=req burst=20 nodelay;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Apply the zone with a burst allowance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;limit_conn_zone $binary_remote_addr zone=conn:10m;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Define a connection limit zone in &lt;code&gt;http&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;limit_conn conn 10;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Limit active connections to ten per address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;server_tokens off;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hide the version number in responses and error pages&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The &lt;code&gt;.htpasswd&lt;/code&gt; file holds hashed credentials. Keep it outside the document root and out of version control. In HTTP/2 and HTTP/3, &lt;code&gt;limit_conn&lt;/code&gt; counts each concurrent request as a separate connection.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card wrap-commands"&gt;
&lt;h3 id="compression-and-caching"&gt;Compression and Caching &lt;a class="headline-link" href="#compression-and-caching" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Cut response size and avoid repeat trips to the backend.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive&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;gzip on;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Compress responses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gzip_types text/css application/javascript application/json;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Compress these types beyond &lt;code&gt;text/html&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gzip_min_length 256;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Skip responses too small to benefit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gzip_comp_level 5;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Balance CPU time against size&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gzip_vary on;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add &lt;code&gt;Vary: Accept-Encoding&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;expires 30d;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set a far-future expiry inside a static location&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;add_header Cache-Control &amp;quot;public, immutable&amp;quot;;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mark fingerprinted assets as cacheable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;proxy_cache_path /var/cache/nginx keys_zone=cache:10m max_size=1g;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Define a proxy cache in &lt;code&gt;http&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;proxy_cache cache;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Turn the cache on for a location&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;proxy_cache_valid 200 10m;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cache successful responses for ten minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;add_header X-Cache-Status $upstream_cache_status;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Expose cache hits and misses while debugging&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;By default, a block with its own &lt;code&gt;add_header&lt;/code&gt; directives does not inherit its parent&amp;rsquo;s &lt;code&gt;add_header&lt;/code&gt; directives. Repeat the ones you still need, or use &lt;code&gt;add_header_inherit merge;&lt;/code&gt; on nginx 1.29.3 and later.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card wrap-commands"&gt;
&lt;h3 id="logging"&gt;Logging &lt;a class="headline-link" href="#logging" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Choose what gets recorded and where to watch it.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Directive&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/nginx-log-files/"&gt;&lt;code&gt;access_log /var/log/nginx/access.log combined;&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Write access logs in the default format&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;access_log off;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Disable access logging for a location&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;error_log /var/log/nginx/error.log warn;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set the error log file and level&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;log_format main '$remote_addr $status &amp;quot;$request&amp;quot; $request_time';&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Define a custom format in &lt;code&gt;http&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;access_log /var/log/nginx/api.log main;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use that custom format&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo tail -f /var/log/nginx/error.log&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Follow errors live&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo journalctl -u nginx -f&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Follow service-level messages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nginx -s reopen&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reopen log files after rotation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Error log levels run &lt;code&gt;debug&lt;/code&gt;, &lt;code&gt;info&lt;/code&gt;, &lt;code&gt;notice&lt;/code&gt;, &lt;code&gt;warn&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt;, &lt;code&gt;crit&lt;/code&gt;, &lt;code&gt;alert&lt;/code&gt;, and &lt;code&gt;emerg&lt;/code&gt;, from most to least verbose, and each level includes everything more severe. The &lt;code&gt;debug&lt;/code&gt; level needs a build configured with &lt;code&gt;--with-debug&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card wrap-commands"&gt;
&lt;h3 id="common-variables"&gt;Common Variables &lt;a class="headline-link" href="#common-variables" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Values available in log formats, proxy headers, redirects, and conditions.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&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;$host&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Host from the request line, then the Host header, then the matching server name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$remote_addr&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Client IP address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$request_uri&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full original URI including the query string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$uri&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Current URI after rewrites, without the query string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$args&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Query string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$scheme&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;http&lt;/code&gt; or &lt;code&gt;https&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$request_method&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;GET, POST, and so on&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$status&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Response status code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$body_bytes_sent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Size of the response body&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$request_time&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Request duration in seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$http_user_agent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;User-Agent header&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$proxy_add_x_forwarded_for&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Existing X-Forwarded-For plus the client address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$upstream_addr&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Backend that served the request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$upstream_response_time&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Backend response time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$document_root&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Document root for the current request&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card wrap-commands"&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 the longer explanations behind these directives.&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/nginx-commands-you-should-know/"&gt;&lt;code&gt;Nginx Commands You Should Know&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Service, testing, and reload commands in detail&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/nginx-location-block/"&gt;&lt;code&gt;Nginx Location Blocks&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Match rules and the full priority order&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/nginx-reverse-proxy/"&gt;&lt;code&gt;Nginx Reverse Proxy&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;proxy_pass, headers, and WebSocket support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/nginx-log-files/"&gt;&lt;code&gt;Configuring the Nginx Error and Access Logs&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Log formats, levels, and rotation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/redirect-http-to-https-in-nginx/"&gt;&lt;code&gt;Redirect HTTP to HTTPS in Nginx&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Redirect patterns and the pitfalls to avoid&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/start-stop-restart-nginx/"&gt;&lt;code&gt;How to Start, Stop, or Restart Nginx&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Reload versus restart and what each one does&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-set-up-nginx-server-blocks-on-ubuntu-22-04/"&gt;&lt;code&gt;Nginx Server Blocks on Ubuntu&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Hosting several sites on one server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-install-nginx-on-ubuntu-26-04/"&gt;&lt;code&gt;How to Install Nginx on Ubuntu 26.04&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Installation, firewall rules, and first steps&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;</content:encoded></item><item><title>Fix "No Space Left on Device" When df Shows Free Space</title><link>https://linuxize.com/post/fix-no-space-left-on-device/</link><pubDate>Mon, 14 Sep 2026 18:10:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/fix-no-space-left-on-device/</guid><category>disk</category><category>linux commands</category><description>Fix Linux no-space errors when df shows free space by checking the target mount, inode usage, tmpfs limits, container storage, Btrfs, and inotify watches.</description><content:encoded>&lt;p&gt;A command can fail with &lt;code&gt;No space left on device&lt;/code&gt; even when &lt;code&gt;df -h&lt;/code&gt; reports gigabytes of free space:&lt;/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;touch: cannot touch &amp;#39;/var/lib/app/cache/item&amp;#39;: No space left on device&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The message does not always mean that the filesystem has run out of data blocks. Linux also needs a free inode for every new file, and the affected path may be on a different mount or a size-limited memory filesystem. Btrfs can run out of usable data or metadata space while &lt;code&gt;df&lt;/code&gt; still reports free capacity, and the kernel returns the same error when a program exhausts its inotify watch limit.&lt;/p&gt;
&lt;p&gt;This guide shows how to check each cause in a practical order, identify the exhausted resource, and free space safely.&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;Check&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;Space on the affected path&lt;/td&gt;
&lt;td&gt;&lt;code&gt;df -h /affected/path&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inodes on the affected path&lt;/td&gt;
&lt;td&gt;&lt;code&gt;df -ih /affected/path&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Filesystem and source device&lt;/td&gt;
&lt;td&gt;&lt;code&gt;findmnt -T /affected/path&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inode-heavy directories&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo du --inodes -x -d 2 /path | sort -n | tail -20&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deleted files still open&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo lsof +L1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Btrfs allocation details&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo btrfs filesystem usage /affected/path&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;inotify watch limit&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sysctl fs.inotify.max_user_watches&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker storage usage&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker system df&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Journal size&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo journalctl --disk-usage&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;APT cache size&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo du -sh /var/cache/apt/archives&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="check-the-exact-path-that-failed"&gt;Check the Exact Path That Failed &lt;a class="headline-link" href="#check-the-exact-path-that-failed" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Start by passing the affected path to &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;
, not only the root 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;df -h /var/lib/app/cache&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;Filesystem Size Used Avail Use% Mounted on
/dev/nvme0n1p5 40G 17G 22G 44% /var&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;Mounted on&lt;/code&gt; column shows which filesystem actually holds the path. Here the failing directory lives on a separate &lt;code&gt;/var&lt;/code&gt; filesystem, not on the root filesystem that &lt;code&gt;df -h /&lt;/code&gt; would have reported. A server may have separate filesystems for &lt;code&gt;/&lt;/code&gt;, &lt;code&gt;/var&lt;/code&gt;, &lt;code&gt;/home&lt;/code&gt;, &lt;code&gt;/tmp&lt;/code&gt;, container storage, or application data, and each one fills up independently.&lt;/p&gt;
&lt;p&gt;Display the source device, filesystem type, and mount 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;findmnt -T /var/lib/app/cache&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;TARGET SOURCE FSTYPE OPTIONS
/var /dev/nvme0n1p5 ext4 rw,relatime&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If &lt;code&gt;Use%&lt;/code&gt; is 100%, the filesystem is genuinely out of data blocks. Use &lt;a href="https://linuxize.com/post/du-command-in-linux/"&gt;&lt;code&gt;du&lt;/code&gt;&lt;/a&gt;
or the guide on &lt;a href="https://linuxize.com/post/find-large-files-in-linux/"&gt;finding large files in Linux&lt;/a&gt;
to locate the largest directories and files.&lt;/p&gt;
&lt;h2 id="check-inode-usage"&gt;Check Inode Usage &lt;a class="headline-link" href="#check-inode-usage" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When block usage looks normal, check inodes on the same 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;df -ih /var/lib/app/cache&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;Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/nvme0n1p5 2.6M 2.6M 0 100% /var&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Each file and directory consumes &lt;a href="https://linuxize.com/post/what-is-an-inode-in-linux/"&gt;an inode&lt;/a&gt;
. A filesystem containing millions of tiny cache, session, queue, or temporary files can run out of inodes while still having substantial free capacity in bytes.&lt;/p&gt;
&lt;p&gt;If &lt;code&gt;IFree&lt;/code&gt; is &lt;code&gt;0&lt;/code&gt; or &lt;code&gt;IUse%&lt;/code&gt; is &lt;code&gt;100%&lt;/code&gt;, inode exhaustion is the cause.&lt;/p&gt;
&lt;h3 id="find-directories-using-the-most-inodes"&gt;Find Directories Using the Most Inodes &lt;a class="headline-link" href="#find-directories-using-the-most-inodes" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The GNU &lt;code&gt;du&lt;/code&gt; command can count inodes instead of bytes. To inspect &lt;code&gt;/var&lt;/code&gt; without crossing into other mounted filesystems:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 du --inodes -x -d &lt;span class="m"&gt;2&lt;/span&gt; /var 2&amp;gt;/dev/null &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; sort -n &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; tail -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;12540 /var/lib/systemd
48201 /var/lib/docker
73122 /var/cache
2310884 /var/lib/app/cache
2485119 /var/lib
2621402 /var&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The largest counts appear at the bottom. In this example, &lt;code&gt;/var/lib/app/cache&lt;/code&gt; alone accounts for almost every inode on the filesystem. Repeat the command on the highest directory with a greater depth:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 du --inodes -x -d &lt;span class="m"&gt;3&lt;/span&gt; /var/lib/app/cache 2&amp;gt;/dev/null &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; sort -n &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; tail -20&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This narrows the search before you remove anything.&lt;/p&gt;
&lt;p&gt;For a file-count view grouped by parent directory, 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 find /var -xdev -type f -printf &lt;span class="s1"&gt;&amp;#39;%h\n&amp;#39;&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; sort &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; uniq -c &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; sort -n &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; tail -20&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This command may take time on a filesystem containing millions of files. It does not delete anything.&lt;/p&gt;
&lt;h3 id="remove-excess-small-files-safely"&gt;Remove Excess Small Files Safely &lt;a class="headline-link" href="#remove-excess-small-files-safely" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Once you identify the directory, determine which application owns it and whether the files are disposable. Common sources include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Application caches that do not expire old entries.&lt;/li&gt;
&lt;li&gt;PHP session directories.&lt;/li&gt;
&lt;li&gt;Mail queues.&lt;/li&gt;
&lt;li&gt;Container image layers and build caches.&lt;/li&gt;
&lt;li&gt;Monitoring data and rotated logs.&lt;/li&gt;
&lt;li&gt;Temporary files created by failed jobs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Preview old temporary files before deleting 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;sudo find /tmp -xdev -type f -mtime +7 -print&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If the list contains only files that are safe to remove, rerun it with &lt;code&gt;-delete&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 find /tmp -xdev -type f -mtime +7 -delete&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Do not run broad &lt;code&gt;find ... -delete&lt;/code&gt; commands against application directories until you know their retention rules. Prefer the application&amp;rsquo;s own cleanup command when one exists.&lt;/p&gt;
&lt;p&gt;On Ubuntu and Debian, clear downloaded package archives 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 apt clean&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Check the system journal&amp;rsquo;s current 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;sudo journalctl --disk-usage&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To remove archived journal data older than seven days:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 --vacuum-time&lt;span class="o"&gt;=&lt;/span&gt;7d&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;After cleanup, confirm that inodes are available 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;df -ih /var/lib/app/cache&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="check-for-deleted-files-still-held-open"&gt;Check for Deleted Files Still Held Open &lt;a class="headline-link" href="#check-for-deleted-files-still-held-open" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This check covers a different version of the problem: &lt;code&gt;df&lt;/code&gt; reports a full filesystem, but &lt;code&gt;du&lt;/code&gt; cannot account for the usage. A process may still have a deleted file open. The directory entry is gone, but the kernel does not release its blocks until the process closes the file descriptor.&lt;/p&gt;
&lt;p&gt;List deleted files that remain open:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 lsof +L1&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;COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME
java 4217 app 7w REG 259,3 8589934592 0 8123 /var/log/app.log (deleted)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The example shows an 8 GB deleted log still held by a Java process. Restart the owning service during an appropriate maintenance 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;sudo systemctl restart app.service&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Use the actual service name for your process. Confirm the file is gone from the &lt;code&gt;lsof +L1&lt;/code&gt; output and check space again with &lt;code&gt;df -h&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For a detailed explanation of deleted open files, see the &lt;a href="https://linuxize.com/post/lsof-command-in-linux/"&gt;&lt;code&gt;lsof&lt;/code&gt; command guide&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="check-tmpfs-and-container-filesystems"&gt;Check tmpfs and Container Filesystems &lt;a class="headline-link" href="#check-tmpfs-and-container-filesystems" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Paths such as &lt;code&gt;/run&lt;/code&gt;, &lt;code&gt;/dev/shm&lt;/code&gt;, and sometimes &lt;code&gt;/tmp&lt;/code&gt; may use &lt;code&gt;tmpfs&lt;/code&gt;, which is backed by memory and has its own size limit. Check the affected path 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;df -h /run /dev/shm /tmp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If a tmpfs mount is full, remove stale files owned by the relevant application or increase the mount limit after checking available memory.&lt;/p&gt;
&lt;p&gt;Containers also write through overlay filesystems and mounted volumes. A host filesystem may have free space while a container-specific mount or storage pool is full. From the host, identify the path and mount with &lt;code&gt;findmnt -T&lt;/code&gt;. Inside the container, run &lt;code&gt;df -h&lt;/code&gt; and &lt;code&gt;df -i&lt;/code&gt; to inspect its view.&lt;/p&gt;
&lt;p&gt;For Docker, display the space used by images, containers, local volumes, and the build cache 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;docker system df&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This command reports Docker&amp;rsquo;s storage usage without removing anything. Review the detailed output before deciding which objects are safe to clean up.&lt;/p&gt;
&lt;h2 id="check-btrfs-data-and-metadata-allocation"&gt;Check Btrfs Data and Metadata Allocation &lt;a class="headline-link" href="#check-btrfs-data-and-metadata-allocation" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Btrfs manages data and metadata in separate block groups. Its copy-on-write allocation can return &lt;code&gt;No space left on device&lt;/code&gt; even when the free-space total from &lt;code&gt;df&lt;/code&gt; looks sufficient.&lt;/p&gt;
&lt;p&gt;If &lt;code&gt;findmnt&lt;/code&gt; reports &lt;code&gt;btrfs&lt;/code&gt; for the affected path, inspect its allocation details 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 btrfs filesystem usage /affected/path&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Review the &lt;code&gt;Device unallocated&lt;/code&gt;, &lt;code&gt;Free (estimated)&lt;/code&gt;, &lt;code&gt;Data&lt;/code&gt;, and &lt;code&gt;Metadata&lt;/code&gt; values together. A filesystem may have free data space but too little unallocated capacity to create another metadata block group.&lt;/p&gt;
&lt;p&gt;First, remove unneeded files or snapshots according to your retention policy. You can then reclaim completely unused data and metadata block groups 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 btrfs balance start -dusage&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt; -musage&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt; /mountpoint&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;/mountpoint&lt;/code&gt; with the Btrfs mount point shown by &lt;code&gt;findmnt&lt;/code&gt;. The &lt;code&gt;usage=0&lt;/code&gt; filters select only empty block groups and do not need extra workspace. Do not run an unfiltered balance as a general disk-full fix because it rewrites all selected block groups, can take a long time, and needs temporary free space.&lt;/p&gt;
&lt;h2 id="check-the-inotify-watch-limit"&gt;Check the inotify Watch Limit &lt;a class="headline-link" href="#check-the-inotify-watch-limit" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Editors, build tools, log shippers, and file sync daemons watch files through inotify. Every watched file or directory consumes one watch, and the kernel enforces a per-user limit. When a program exceeds that limit, &lt;code&gt;inotify_add_watch()&lt;/code&gt; fails with the same &lt;code&gt;No space left on device&lt;/code&gt; error even though the filesystem has free blocks and free inodes. Applications often report it in their own wording:&lt;/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: ENOSPC: System limit for number of file watchers reached, watch &amp;#39;/srv/app/src&amp;#39;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Display the current 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;sysctl fs.inotify.max_user_watches&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;fs.inotify.max_user_watches = 65536&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The value varies by distribution and kernel version. To find which process is consuming the watches, count them per file descriptor:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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;grep -c &amp;#39;^inotify&amp;#39; /proc/*/fdinfo/* 2&amp;gt;/dev/null&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; awk -F: &lt;span class="s1"&gt;&amp;#39;$2 &amp;gt; 0 {print $2, $1}&amp;#39;&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; sort -rn &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&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;62184 /proc/2811/fdinfo/23
1204 /proc/1533/fdinfo/9
118 /proc/994/fdinfo/12&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The PID appears in the path, so &lt;code&gt;ps -p 2811 -o comm=&lt;/code&gt; names the program holding the watches. If that program is watching a directory it does not need, such as a dependency or build output directory, exclude the directory in the program&amp;rsquo;s own configuration instead of raising the limit.&lt;/p&gt;
&lt;p&gt;When the workload genuinely needs more watches, raise the limit for the running 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;sudo sysctl fs.inotify.max_user_watches&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;524288&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Make the change persistent across reboots:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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="s1"&gt;&amp;#39;fs.inotify.max_user_watches=524288&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; sudo tee /etc/sysctl.d/90-inotify.conf&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Each watch consumes a small amount of kernel memory that cannot be swapped out, so set the limit to match the workload rather than to the largest value the system accepts.&lt;/p&gt;
&lt;h2 id="check-filesystem-and-kernel-errors"&gt;Check Filesystem and Kernel Errors &lt;a class="headline-link" href="#check-filesystem-and-kernel-errors" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If space and inodes are available but writes still fail, inspect recent kernel messages:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 dmesg --level&lt;span class="o"&gt;=&lt;/span&gt;err,warn &lt;span class="p"&gt;|&lt;/span&gt; tail -50&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Also check the system journal:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 -k -p warning --since &lt;span class="s2"&gt;&amp;#34;1 hour ago&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;Look for Btrfs allocation warnings, I/O errors, filesystem corruption, a read-only remount, or storage device failures. Errors such as &lt;code&gt;Input/output error&lt;/code&gt; and &lt;code&gt;Read-only file system&lt;/code&gt; need a different fix than &lt;code&gt;No space left on device&lt;/code&gt;. A filesystem check may be required, but do not run &lt;code&gt;fsck&lt;/code&gt; against a mounted filesystem. Schedule downtime, unmount the filesystem, and follow the appropriate recovery procedure.&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;df -h shows space but df -i shows 100 percent&lt;/strong&gt;&lt;br&gt;
The filesystem has exhausted its inode table. Find directories containing very large numbers of files, then remove or archive files according to the owning application&amp;rsquo;s retention policy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;df shows more usage than du&lt;/strong&gt;&lt;br&gt;
A process probably holds a deleted file open. Run &lt;code&gt;sudo lsof +L1&lt;/code&gt;, identify the process, and restart the owning service to release the blocks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The error occurs only in /tmp, /run, or /dev/shm&lt;/strong&gt;&lt;br&gt;
The path may be a size-limited tmpfs mount. Run &lt;code&gt;df -h&lt;/code&gt; and &lt;code&gt;df -i&lt;/code&gt; against that exact path rather than checking &lt;code&gt;/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Docker reports no space while the host has free capacity&lt;/strong&gt;&lt;br&gt;
Check Docker&amp;rsquo;s storage path and filesystem with &lt;code&gt;docker info&lt;/code&gt;, &lt;code&gt;docker system df&lt;/code&gt;, and &lt;code&gt;findmnt -T /var/lib/docker&lt;/code&gt;. Review unused images, containers, build cache, and volumes before removing them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Btrfs reports no space while df shows free capacity&lt;/strong&gt;&lt;br&gt;
Run &lt;code&gt;sudo btrfs filesystem usage /affected/path&lt;/code&gt; and compare data, metadata, and unallocated space. After removing unneeded files or snapshots, a balance limited to &lt;code&gt;-dusage=0 -musage=0&lt;/code&gt; can reclaim empty block groups without starting a full balance.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;An editor, build tool, or sync daemon reports no space while df and df -i look fine&lt;/strong&gt;&lt;br&gt;
The process reached the inotify watch limit. Check &lt;code&gt;fs.inotify.max_user_watches&lt;/code&gt;, exclude directories the program does not need to watch, and raise the limit in &lt;code&gt;/etc/sysctl.d/&lt;/code&gt; when the workload requires it.&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 an inode?&lt;/strong&gt;&lt;br&gt;
An inode stores filesystem metadata for a file or directory, such as its size, ownership, permissions, timestamps, and the location of its data blocks. Each filesystem has a finite inode supply, and each file normally consumes one inode regardless of its size.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Can I add more inodes to an existing ext4 filesystem?&lt;/strong&gt;&lt;br&gt;
The inode count is chosen when the filesystem is created and cannot be increased with an online setting change. The practical fixes are to remove excess files, move the workload to another filesystem, or recreate the filesystem with a smaller bytes-per-inode ratio suited to many small files.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why does deleting a large file not free space immediately?&lt;/strong&gt;&lt;br&gt;
A running process may still have the deleted file open. Linux releases the blocks only when the last file descriptor closes, so &lt;code&gt;df&lt;/code&gt; keeps reporting the space as used. Run &lt;code&gt;sudo lsof +L1&lt;/code&gt; to find the process and restart the owning service.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why does the error appear on a system with terabytes of free space?&lt;/strong&gt;&lt;br&gt;
The failing write is limited by something other than total capacity, most often the inode count of the specific mount, a size-limited tmpfs, container storage, Btrfs allocation, or the inotify watch limit. Always check the exact path that failed rather than the root filesystem.&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;When &lt;code&gt;df -h&lt;/code&gt; shows free space, check the affected path with &lt;code&gt;df -ih&lt;/code&gt; and use &lt;code&gt;findmnt&lt;/code&gt; to confirm its mount and filesystem type. Inode exhaustion, a full nested mount or tmpfs, container storage, Btrfs allocation, and the inotify watch limit each need a different fix, while &lt;code&gt;lsof +L1&lt;/code&gt; solves the separate case where &lt;code&gt;df&lt;/code&gt; is full but &lt;code&gt;du&lt;/code&gt; cannot explain the usage.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/fix-no-space-left-on-device/featured_hu_b710fcb3d23cc03a.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>ffmpeg Cheatsheet</title><link>https://linuxize.com/cheatsheet/ffmpeg/</link><pubDate>Sun, 13 Sep 2026 10:30:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/cheatsheet/ffmpeg/</guid><description>Keep essential ffmpeg commands close at hand, from stream inspection and lossless remuxing to CRF encoding, filters, subtitles, and GPU acceleration.</description><content:encoded>&lt;div class="card"&gt;
&lt;h3 id="install-ffmpeg"&gt;Install ffmpeg &lt;a class="headline-link" href="#install-ffmpeg" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Get the ffmpeg binaries and confirm the build.&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/ffmpeg-command-in-linux/"&gt;&lt;code&gt;sudo apt install ffmpeg&lt;/code&gt;&lt;/a&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 ffmpeg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install the full build from RPM Fusion on Fedora or RHEL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo dnf install ffmpeg-free&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install the codec-limited build from Fedora or EPEL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print the version and build configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -encoders&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List every available encoder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -decoders&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List every available decoder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -formats&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List supported container formats&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="inspect-media-files"&gt;Inspect Media Files &lt;a class="headline-link" href="#inspect-media-files" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Read stream details before deciding how to re-encode.&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/ffmpeg-command-in-linux/"&gt;&lt;code&gt;ffprobe -hide_banner in.mp4&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Show streams, codecs, and duration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffprobe -v error -show_format in.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print container metadata only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffprobe -v error -show_streams in.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print every stream property&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffprobe -v error -print_format json -show_format -show_streams in.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Machine-readable output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=p=0 in.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print the video resolution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffprobe -v error -show_entries format=duration -of csv=p=0 in.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print the duration in seconds&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;Options that control inputs, outputs, and command behavior.&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;-i input&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set an input file (repeat for multiple inputs)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-y&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Overwrite the output file without asking&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;Never overwrite an existing output file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-hide_banner&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Suppress the build and library banner&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-loglevel error&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print errors only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-stats&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Keep the progress line while quiet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-threads:v 4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Request four threads for the video encoder when supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-f format&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Force an input or output format&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="convert-between-formats"&gt;Convert Between Formats &lt;a class="headline-link" href="#convert-between-formats" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Change the container, with or without re-encoding.&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/ffmpeg-command-in-linux/"&gt;&lt;code&gt;ffmpeg -i in.mkv -c copy out.mp4&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Repackage compatible streams without re-encoding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.avi out.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Convert using the default encoders&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mov -c:v libx264 -c:a aac out.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Convert to H.264 and AAC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mp4 -c:v libvpx-vp9 -c:a libopus out.webm&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Convert to WebM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mp4 -c:v copy -c:a aac out.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Re-encode the audio, keep the video&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.wav out.flac&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Convert between audio formats&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="compress-video"&gt;Compress Video &lt;a class="headline-link" href="#compress-video" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Lower the bitrate with constant quality or a controlled bitrate.&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/ffmpeg-command-in-linux/"&gt;&lt;code&gt;ffmpeg -i in.mp4 -c:v libx264 -crf 23 -preset slow -c:a aac -b:a 128k out.mp4&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Compress with H.264&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mp4 -c:v libx265 -crf 28 -c:a copy out.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Compress with H.265 at a similar quality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mp4 -c:v libx264 -crf 18 out.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Keep near-source quality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mp4 -c:v libx264 -b:v 2M -maxrate 2M -bufsize 4M -c:a copy out.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Target 2 Mb/s with a 2 Mb/s VBV ceiling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mp4 -c:v h264_nvenc -cq 23 -c:a copy out.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Encode on an NVIDIA GPU&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -vaapi_device /dev/dri/renderD128 -i in.mp4 -vf 'format=nv12,hwupload' -c:v h264_vaapi -c:a copy out.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Encode with VA-API&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;CRF runs from 0 (lossless) to 51 (worst). Lower values mean larger files, and 18 to 28 covers most work. Slower presets such as &lt;code&gt;slow&lt;/code&gt; or &lt;code&gt;veryslow&lt;/code&gt; shrink the file further at the same CRF.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="audio"&gt;Audio &lt;a class="headline-link" href="#audio" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Extract, convert, and adjust audio tracks.&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/convert-mp4-to-mp3-with-ffmpeg/"&gt;&lt;code&gt;ffmpeg -i in.mp4 -vn -c:a libmp3lame -q:a 2 out.mp3&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Extract audio as MP3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mp4 -vn -c:a copy out.m4a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Copy compatible audio without re-encoding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mp4 -vn -c:a aac -b:a 192k out.aac&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Extract audio as AAC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mp3 -ac 1 -ar 16000 out.wav&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Downmix to mono at 16 kHz&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mp4 -c:v copy -af 'volume=1.5' out.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Raise the volume by 50 percent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mp4 -c:v copy -af 'loudnorm=I=-23' -ar 48000 out.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Apply one-pass EBU R128 normalization at -23 LUFS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mp4 -an -c:v copy out.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Strip the audio track&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The &lt;code&gt;-q:a&lt;/code&gt; scale for libmp3lame runs from 0 (best) to 9 (worst), and 2 is a good default for speech and music alike.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="resize-crop-and-rotate"&gt;Resize, Crop, and Rotate &lt;a class="headline-link" href="#resize-crop-and-rotate" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Reshape the picture with video filters.&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/ffmpeg-command-in-linux/"&gt;&lt;code&gt;ffmpeg -i in.mp4 -vf scale=1280:-2 -c:a copy out.mp4&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Scale to 1280 wide, keep the aspect ratio&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mp4 -vf scale=-2:720 -c:a copy out.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Scale to 720 high&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mp4 -vf scale=1920:1080 -c:a copy out.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Force an exact resolution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mp4 -vf 'crop=640:480:100:50' -c:a copy out.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Crop 640x480 starting at x=100, y=50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mp4 -vf 'transpose=1' -c:a copy out.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rotate 90 degrees clockwise&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mp4 -vf 'hflip' -c:a copy out.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mirror horizontally&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mp4 -vf 'pad=1920:1080:(ow-iw)/2:(oh-ih)/2' -c:a copy out.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pad to 1080p with centered bars&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Use &lt;code&gt;-2&lt;/code&gt; rather than &lt;code&gt;-1&lt;/code&gt; for the free dimension so the calculated size stays even, as required by common 4:2:0 output formats.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="trim-and-concatenate"&gt;Trim and Concatenate &lt;a class="headline-link" href="#trim-and-concatenate" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Cut clips and join files back together.&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/ffmpeg-command-in-linux/"&gt;&lt;code&gt;ffmpeg -ss 00:01:00 -to 00:02:30 -i in.mp4 -c copy clip.mp4&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Fast cut at the nearest keyframe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -ss 00:01:00 -to 00:02:30 -i in.mp4 -c:v libx264 -c:a aac clip.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Frame-accurate cut by re-encoding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -ss 00:00:30 -t 15 -i in.mp4 -c copy clip.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cut 15 seconds starting at 30 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mp4 -t 60 -c copy first-minute.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Keep the first minute&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -f concat -safe 0 -i list.txt -c copy out.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Join files listed in list.txt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mp4 -c copy -f segment -segment_time 600 part%03d.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Split near ten-minute boundaries at keyframes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Each line of &lt;code&gt;list.txt&lt;/code&gt; takes the form &lt;code&gt;file '/path/to/clip.mp4'&lt;/code&gt;. The concat demuxer requires matching stream layouts and parameters, including codecs and time bases.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="images-thumbnails-and-gifs"&gt;Images, Thumbnails, and GIFs &lt;a class="headline-link" href="#images-thumbnails-and-gifs" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Move between video and still images.&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/ffmpeg-command-in-linux/"&gt;&lt;code&gt;ffmpeg -ss 00:00:10 -i in.mp4 -frames:v 1 thumb.jpg&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Grab a single frame at ten seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mp4 -vf fps=1 frame%04d.png&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Export one frame per second&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mp4 -vf 'fps=1/10' frame%04d.jpg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Export one frame every ten seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -framerate 30 -i frame%04d.png -c:v libx264 -pix_fmt yuv420p out.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build a video from an image sequence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mp4 -vf 'fps=10,scale=480:-1:flags=lanczos' out.gif&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a GIF&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mp4 -vf 'fps=10,scale=480:-1:flags=lanczos,palettegen' palette.png&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build a GIF color palette&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mp4 -i palette.png -lavfi 'fps=10,scale=480:-1:flags=lanczos [x]; [x][1:v] paletteuse' out.gif&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a GIF with that palette&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="subtitles"&gt;Subtitles &lt;a class="headline-link" href="#subtitles" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Attach, burn in, or pull out subtitle tracks.&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;ffmpeg -i in.mp4 -i subs.srt -map 0 -map 1:0 -c copy -c:s mov_text out.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add soft subtitles to MP4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mkv -i subs.srt -map 0 -map 1:0 -c copy out.mkv&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add soft subtitles to MKV&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mp4 -vf subtitles=subs.srt -c:a copy out.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Burn subtitles into the picture&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mkv -vf 'subtitles=in.mkv:si=0' -c:a copy out.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Burn in an embedded subtitle stream&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mkv -map 0:s:0 subs.srt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Convert the first subtitle track to SRT when it is text based&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mkv -map 0 -sn -c copy out.mkv&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove every subtitle track and keep other streams&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="stream-mapping-and-metadata"&gt;Stream Mapping and Metadata &lt;a class="headline-link" href="#stream-mapping-and-metadata" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Choose which streams reach the 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;ffmpeg -i in.mkv -map 0:v:0 -map 0:a:1 -c copy out.mkv&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Keep the first video and second audio stream&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mkv -map 0 -c copy out.mkv&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Keep every stream from the input&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i video.mp4 -i audio.mp3 -map 0:v -map 1:a -c:v copy -shortest out.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Combine separate video and audio files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mp4 -map_metadata -1 -c copy out.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Strip global metadata&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mp3 -metadata title='Track name' -c copy out.mp3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set a metadata tag&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ffmpeg -i in.mp4 -c copy -movflags +faststart out.mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Move the MP4 index to the front for streaming&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;for f in *.mkv; do ffmpeg -i &amp;quot;$f&amp;quot; -c:v libx264 -crf 23 -c:a aac &amp;quot;${f%.mkv}.mp4&amp;quot;; done&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Batch convert a directory&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 the longer explanations behind these commands.&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/ffmpeg-command-in-linux/"&gt;&lt;code&gt;ffmpeg Command in Linux&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Full ffmpeg tutorial with worked examples&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/convert-mp4-to-mp3-with-ffmpeg/"&gt;&lt;code&gt;Convert MP4 to MP3 with ffmpeg&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Audio extraction and quality settings in detail&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;</content:encoded></item><item><title>pnpm Cheatsheet</title><link>https://linuxize.com/cheatsheet/pnpm/</link><pubDate>Fri, 11 Sep 2026 08:45:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/cheatsheet/pnpm/</guid><description>Quick reference for pnpm commands covering installation, dependencies, lockfiles, scripts, workspace filtering, the package store, and Node.js runtimes</description><content:encoded>&lt;div class="card"&gt;
&lt;h3 id="install-and-update-pnpm"&gt;Install and Update pnpm &lt;a class="headline-link" href="#install-and-update-pnpm" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Set up the pnpm executable and keep it current.&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/pnpm-command/"&gt;&lt;code&gt;curl -fsSL https://get.pnpm.io/install.sh | sh -&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Install with the standalone script&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wget -qO- https://get.pnpm.io/install.sh | sh -&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install without curl&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npx get-pnpm&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install through npm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm self-update&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Update pnpm to the current release&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm self-update 12.3.4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Move to a specific pnpm version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm setup&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create PNPM_HOME and update the shell profile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm --version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print the installed pnpm version&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="nodejs-runtimes"&gt;Node.js Runtimes &lt;a class="headline-link" href="#nodejs-runtimes" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Install language runtimes with pnpm itself.&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/pnpm-command/"&gt;&lt;code&gt;pnpm runtime set node lts -g&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Install the Node.js LTS release&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm runtime set node 22 -g&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install a Node.js major version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm runtime set node latest -g&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install the newest Node.js release&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm runtime set node 22.23.2 -g&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install an exact Node.js version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm runtime set deno 2 -g&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install Deno&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm runtime set bun latest -g&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install Bun&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&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 edit 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/pnpm-command/"&gt;&lt;code&gt;pnpm init&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Create package.json&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm init --init-package-manager&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create package.json and pin pnpm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm init --bare&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create package.json with required fields only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm import&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build pnpm-lock.yaml from another lockfile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm pkg get name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print a package.json field&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm pkg set scripts.start=&amp;quot;node index.js&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add or update a field&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm 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="add-dependencies"&gt;Add Dependencies &lt;a class="headline-link" href="#add-dependencies" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Record packages in package.json and the lockfile.&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/pnpm-command/"&gt;&lt;code&gt;pnpm add express&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Add a runtime dependency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm add -D eslint&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;pnpm add -O sharp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add an optional dependency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm add -E express&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add without a version range&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm add express@5.1.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add a specific version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm add -g http-server&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add a global package&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm add -w typescript&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add to the workspace root&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;Reproduce a dependency tree from the lockfile.&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;pnpm install&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install everything package.json declares&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/pnpm-command/"&gt;&lt;code&gt;pnpm install --frozen-lockfile&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Install without changing the lockfile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm ci&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Clean install for CI pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm install --prod&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;pnpm install --offline&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install from the store only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm install --ignore-scripts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install without lifecycle scripts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm fetch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fetch the lockfile contents into the store&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="update-and-inspect"&gt;Update and Inspect &lt;a class="headline-link" href="#update-and-inspect" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Check versions and trace where packages come from.&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/pnpm-command/"&gt;&lt;code&gt;pnpm outdated&lt;/code&gt;&lt;/a&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;pnpm 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;pnpm update express --latest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Update one package and its range&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm update -i&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Choose updates interactively&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm list --depth 0&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;pnpm why express&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Explain why a package is installed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm licenses list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List dependency licenses&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;Drop packages and reclaim project space.&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/pnpm-command/"&gt;&lt;code&gt;pnpm remove express&lt;/code&gt;&lt;/a&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;pnpm remove -g http-server&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove a global package&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm 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;pnpm prune --prod&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove development dependencies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm dedupe&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Collapse duplicate versions in the lockfile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm dedupe --check&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Report duplicates without writing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm pm clean&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove node_modules from every workspace project&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;a href="https://linuxize.com/post/pnpm-command/"&gt;&lt;code&gt;pnpm run build&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;pnpm 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;pnpm 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;pnpm run build -- --watch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pass arguments to a script&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm exec eslint .&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run a local package binary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm dlx create-vite@latest app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run a package without installing it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm pm &amp;lt;command&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Force a built-in command over a same-named script&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="workspaces-and-filtering"&gt;Workspaces and Filtering &lt;a class="headline-link" href="#workspaces-and-filtering" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Target one package or a slice of a monorepo.&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/pnpm-command/"&gt;&lt;code&gt;pnpm -r run build&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Run a script in every workspace package&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm --filter web-app add zod&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add a dependency to one package&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm --filter web-app run test&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run a script in one package&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm --filter &amp;quot;@scope/*&amp;quot; test&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Select packages by pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm --filter web-app... build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Select a package and its dependencies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm --filter ...web-app build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Select a package and its dependents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm --filter &amp;quot;...[origin/main]&amp;quot; build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Select packages changed since a branch&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="store-and-cache"&gt;Store and Cache &lt;a class="headline-link" href="#store-and-cache" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Maintain the shared content-addressable store.&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;pnpm store path&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print the store location&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm store status&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check the store for modified packages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm store prune&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove orphaned packages from the store&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm store add express@5.1.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add a package to the store&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm cache path&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print the metadata cache directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm root -g&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print the global node_modules path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm bin -g&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print the global binary directory&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-diagnostics"&gt;Security and Diagnostics &lt;a class="headline-link" href="#security-and-diagnostics" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Audit dependencies and check the installation.&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;pnpm 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;pnpm audit --prod&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Audit production dependencies only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm audit --fix&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add overrides for non-vulnerable versions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm doctor&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run diagnostics on the pnpm environment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm sbom --sbom-format spdx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generate a software bill of materials&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm approve-builds&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Approve dependency build scripts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pnpm ignored-builds&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List packages with blocked build scripts&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 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/pnpm-command/"&gt;&lt;code&gt;pnpm Command&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Full pnpm tutorial with examples&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 Command&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;The npm equivalents of these commands&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;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;</content:encoded></item><item><title>How to Fix APT and dpkg Lock Errors on Ubuntu and Debian</title><link>https://linuxize.com/post/fix-dpkg-lock-error/</link><pubDate>Wed, 09 Sep 2026 10:20:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/fix-dpkg-lock-error/</guid><category>apt</category><category>ubuntu</category><category>debian</category><description>Resolve APT and dpkg lock errors safely on Ubuntu and Debian by finding the process holding the lock and repairing interrupted package operations.</description><content:encoded>&lt;p&gt;When you install or update packages on Ubuntu or Debian, you may see an error saying that APT or dpkg could not obtain a lock:&lt;/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;E: Could not get lock /var/lib/dpkg/lock-frontend. It is held by process 2481 (apt)
N: Be aware that removing the lock file is not a solution and may break your system.
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), is another process using it?&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The lock prevents two package managers from changing the package database at the same time. In most cases, another update is still running and the correct fix is to let it finish. If the process is stuck, you need to identify and stop it before repairing the package state.&lt;/p&gt;
&lt;p&gt;This guide explains how to resolve APT and dpkg lock errors without deleting lock files or damaging the package database.&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/apt/"&gt;apt 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 the process from the error&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ps -p PID -o pid,etime,cmd&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check APT lock holders&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo lsof /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock /var/cache/apt/archives/lock /var/lib/apt/lists/lock&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check with fuser&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo fuser -v /var/lib/dpkg/lock-frontend&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check automatic update services&lt;/td&gt;
&lt;td&gt;&lt;code&gt;systemctl status apt-daily.service apt-daily-upgrade.service unattended-upgrades.service&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ask a stuck process to stop&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo kill PID&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Finish pending package configuration&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo dpkg --configure -a&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Repair broken dependencies&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt --fix-broken install&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Refresh the package index&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt update&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="why-the-lock-error-occurs"&gt;Why the Lock Error Occurs &lt;a class="headline-link" href="#why-the-lock-error-occurs" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;APT and dpkg use lock files to coordinate access to package data. A lock error commonly appears when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Another terminal is running &lt;code&gt;apt&lt;/code&gt;, &lt;code&gt;apt-get&lt;/code&gt;, &lt;code&gt;dpkg&lt;/code&gt;, or a graphical software manager.&lt;/li&gt;
&lt;li&gt;Ubuntu&amp;rsquo;s automatic update services are checking for or installing updates.&lt;/li&gt;
&lt;li&gt;A package operation was interrupted and left a process running.&lt;/li&gt;
&lt;li&gt;A package manager process stopped responding.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The error often includes the process ID that holds the lock. That PID is the safest place to start because it tells you which process currently owns package management.&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 delete &lt;code&gt;/var/lib/dpkg/lock&lt;/code&gt;, &lt;code&gt;/var/lib/dpkg/lock-frontend&lt;/code&gt;, &lt;code&gt;/var/cache/apt/archives/lock&lt;/code&gt;, or &lt;code&gt;/var/lib/apt/lists/lock&lt;/code&gt; as a first response. Removing a lock file does not stop the process using the package database, and two concurrent writers can leave packages in an inconsistent state.&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="step-1-check-the-process-holding-the-lock"&gt;Step 1: Check the Process Holding the Lock &lt;a class="headline-link" href="#step-1-check-the-process-holding-the-lock" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Replace &lt;code&gt;2481&lt;/code&gt; with the PID shown in your error:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 -p &lt;span class="m"&gt;2481&lt;/span&gt; -o pid,etime,cmd&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 ELAPSED CMD
2481 01:42 apt upgrade&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;ELAPSED&lt;/code&gt; column shows how long the process has been running. If it is an expected &lt;code&gt;apt&lt;/code&gt;, &lt;code&gt;dpkg&lt;/code&gt;, or update process and its runtime is reasonable, wait for it to finish.&lt;/p&gt;
&lt;p&gt;When the error does not show a PID, check the common lock files with &lt;code&gt;lsof&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 lsof &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; /var/lib/dpkg/lock-frontend &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; /var/lib/dpkg/lock &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; /var/cache/apt/archives/lock &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; /var/lib/apt/lists/lock&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;COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
apt 2481 root 4uW REG 8,2 0 812 /var/lib/dpkg/lock-frontend&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;COMMAND&lt;/code&gt; and &lt;code&gt;PID&lt;/code&gt; columns identify the process. If &lt;code&gt;lsof&lt;/code&gt; is unavailable, use &lt;code&gt;fuser&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 fuser -v /var/lib/dpkg/lock-frontend&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; USER PID ACCESS COMMAND
/var/lib/dpkg/lock-frontend:
root 2481 F.... apt&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Do not start another package command until the listed process has finished or been stopped safely.&lt;/p&gt;
&lt;h2 id="step-2-check-automatic-updates"&gt;Step 2: Check Automatic Updates &lt;a class="headline-link" href="#step-2-check-automatic-updates" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Ubuntu and Debian systems may run background package operations through systemd. Check the APT update services 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;systemctl status apt-daily.service apt-daily-upgrade.service unattended-upgrades.service&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If any of these services is active, wait several minutes and check 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;systemctl is-active apt-daily.service apt-daily-upgrade.service unattended-upgrades.service&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;inactive
inactive
inactive&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once all three services are inactive, retry your original package command.&lt;/p&gt;
&lt;p&gt;You can also inspect recent service messages:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 apt-daily.service -u apt-daily-upgrade.service -u unattended-upgrades.service --since &lt;span class="s2"&gt;&amp;#34;30 minutes ago&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 journal shows whether the updater is downloading packages, installing them, or failing repeatedly.&lt;/p&gt;
&lt;h2 id="step-3-stop-a-stuck-package-process"&gt;Step 3: Stop a Stuck Package Process &lt;a class="headline-link" href="#step-3-stop-a-stuck-package-process" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Only stop the process when it has clearly stopped making progress. Check the terminal output or service journal first, then confirm its command, state, and elapsed 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;ps -p &lt;span class="m"&gt;2481&lt;/span&gt; -o pid,stat,etime,cmd&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;STAT&lt;/code&gt; column shows the current process state, but it does not prove that a process is stuck. A sleeping package process may still be waiting for network or disk activity, so use the command output and logs as your main evidence.&lt;/p&gt;
&lt;p&gt;First, ask the process to terminate normally:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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; &lt;span class="m"&gt;2481&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Wait a few seconds, then check whether it still exists:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 -p &lt;span class="m"&gt;2481&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 no process row, it has exited. Avoid sending &lt;code&gt;SIGKILL&lt;/code&gt; unless a normal termination fails, because an immediate kill gives the package manager no opportunity to clean up.&lt;/p&gt;
&lt;p&gt;If an automatic update service owns the process, stop the service instead of killing a child 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 systemctl stop apt-daily.service apt-daily-upgrade.service unattended-upgrades.service&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Check the lock again before continuing:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 fuser -v /var/lib/dpkg/lock-frontend&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;No output means no visible process is accessing that lock file.&lt;/p&gt;
&lt;h2 id="step-4-repair-interrupted-package-configuration"&gt;Step 4: Repair Interrupted Package Configuration &lt;a class="headline-link" href="#step-4-repair-interrupted-package-configuration" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Stopping a package process may leave unpacked packages waiting to be configured. Complete those pending operations 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 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 command configures packages that were unpacked but not fully configured. Our &lt;a href="https://linuxize.com/post/dpkg-command-in-linux/"&gt;dpkg command guide&lt;/a&gt;
explains package states and other useful recovery options.&lt;/p&gt;
&lt;p&gt;Next, repair missing or broken dependencies:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 --fix-broken install&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Review the proposed package changes before confirming. After the repair completes, refresh 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;You can then retry the original install or upgrade command.&lt;/p&gt;
&lt;h2 id="lock-file-locations"&gt;Lock File Locations &lt;a class="headline-link" href="#lock-file-locations" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The path in the error depends on which package-management layer is busy:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/var/lib/dpkg/lock-frontend&lt;/code&gt; - Frontend lock used by APT before it calls dpkg.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/var/lib/dpkg/lock&lt;/code&gt; - Lock for the dpkg package database.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/var/cache/apt/archives/lock&lt;/code&gt; - Lock for downloaded package archives.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/var/lib/apt/lists/lock&lt;/code&gt; - Lock for repository index files.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The diagnostic process is the same for each path: identify the process using the file, let it finish or stop it safely, and repair interrupted package configuration when necessary.&lt;/p&gt;
&lt;h2 id="if-no-process-holds-the-lock"&gt;If No Process Holds the Lock &lt;a class="headline-link" href="#if-no-process-holds-the-lock" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If &lt;code&gt;lsof&lt;/code&gt; and &lt;code&gt;fuser&lt;/code&gt; show no holder, do not remove the lock file. The dpkg tools keep these files in place, and the presence of a file does not mean that its lock is active.&lt;/p&gt;
&lt;p&gt;First, confirm that no package manager 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;ps aux &lt;span class="p"&gt;|&lt;/span&gt; grep -E &lt;span class="s1"&gt;&amp;#39;[a]pt|[d]pkg|unattended-upgrade&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;Retry the original package command. If the lock error returns with a PID, another process acquired the lock between your checks. Inspect that new process before taking any other action.&lt;/p&gt;
&lt;p&gt;Run the recovery commands only if an earlier package operation was interrupted:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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;span class="line"&gt;&lt;span class="cl"&gt;sudo apt --fix-broken install&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;The lock is held by unattended-upgrade&lt;/strong&gt;&lt;br&gt;
Wait for the automatic update to finish and monitor it with &lt;code&gt;systemctl status apt-daily-upgrade.service unattended-upgrades.service&lt;/code&gt; or &lt;code&gt;sudo journalctl -u apt-daily-upgrade.service -u unattended-upgrades.service&lt;/code&gt;. Stop it only when it has clearly failed or remained stuck for an unreasonable time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;dpkg &amp;ndash;configure -a reports dependency problems&lt;/strong&gt;&lt;br&gt;
Run &lt;code&gt;sudo apt --fix-broken install&lt;/code&gt; to install missing dependencies and finish incomplete package operations. Then run &lt;code&gt;sudo dpkg --configure -a&lt;/code&gt; again if the error requests it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The lock returns immediately&lt;/strong&gt;&lt;br&gt;
Check the PID in the new error instead of assuming that the previous process restarted. An APT timer may have launched a new one-shot package operation between your checks. Inspect the services and their next scheduled runs 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;systemctl status apt-daily.service apt-daily-upgrade.service
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl list-timers apt-daily.timer apt-daily-upgrade.timer&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Do not repeatedly kill package processes. Wait for the active service to finish or stop that service only after confirming that it is stuck.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The lock error appears inside a script&lt;/strong&gt;&lt;br&gt;
Another package process may overlap with the script. For an &lt;code&gt;apt-get install&lt;/code&gt; command, set a bounded dpkg lock timeout so the script waits instead of failing immediately:&lt;/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;sudo apt-get -o DPkg::Lock::Timeout&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;60&lt;/span&gt; install -y curl&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This example waits up to 60 seconds for the dpkg lock. If the timeout expires, log the failure and stop the script. Do not add &lt;code&gt;rm&lt;/code&gt; commands for APT or dpkg lock files.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A graphical software manager is open&lt;/strong&gt;&lt;br&gt;
Close applications such as App Center, Software Updater, Synaptic, or other package frontends. Wait for their background process to exit, then retry the terminal command.&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 it safe to delete /var/lib/dpkg/lock?&lt;/strong&gt;&lt;br&gt;
Deleting the file is not the correct first fix. The lock is associated with a running process, so removing the path does not stop that process. Identify the holder with &lt;code&gt;lsof&lt;/code&gt;, &lt;code&gt;fuser&lt;/code&gt;, or the PID in the error and deal with the process instead.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How long should I wait for apt-daily?&lt;/strong&gt;&lt;br&gt;
Most background checks finish within a few minutes, but upgrades can take longer depending on package downloads and installation work. Check its status and journal rather than relying on a fixed timeout.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What does dpkg &amp;ndash;configure -a do?&lt;/strong&gt;&lt;br&gt;
It configures all packages that were unpacked but not fully configured. This is the standard recovery step after a package operation is interrupted.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why does the error mention lock-frontend instead of lock?&lt;/strong&gt;&lt;br&gt;
APT uses a frontend lock to ensure that only one high-level package manager controls dpkg at a time. The lower-level dpkg database has its own lock as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Can I prevent automatic updates from taking the lock?&lt;/strong&gt;&lt;br&gt;
You can change the system&amp;rsquo;s automatic update schedule or policy, but disabling security updates only to avoid occasional lock contention is usually a poor tradeoff. It is safer to schedule maintenance commands outside the automatic update window.&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;An APT or dpkg lock error usually means package management is already active, not that the lock file is broken. Identify the holder, wait or stop it safely, then use &lt;code&gt;dpkg --configure -a&lt;/code&gt; and &lt;code&gt;apt --fix-broken install&lt;/code&gt; to repair any interrupted work. For more package maintenance examples, see our &lt;a href="https://linuxize.com/post/how-to-use-apt-command/"&gt;APT command guide&lt;/a&gt;
.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/fix-dpkg-lock-error/featured_hu_142dac0e3ad9fbd3.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>vmstat Command in Linux: Memory, CPU, and I/O Statistics</title><link>https://linuxize.com/post/vmstat-command-in-linux/</link><pubDate>Mon, 07 Sep 2026 17:10:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/vmstat-command-in-linux/</guid><category>linux commands</category><description>Use vmstat to monitor Linux CPU, memory, swap, processes, and disk I/O. This guide explains each column, live sampling, timestamps, and disk statistics.</description><content:encoded>&lt;p&gt;When a server slows down, the first question is what it is waiting on. Is the CPU saturated? Is memory exhausted and the system is swapping? Is disk I/O holding up processes? The &lt;code&gt;vmstat&lt;/code&gt; command puts clues about each resource in one compact table, making it a useful first check before you move to a more specialized monitoring tool.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;vmstat&lt;/code&gt; stands for virtual memory statistics and is provided by procps-ng. Most full Linux installations include it through a package named &lt;code&gt;procps&lt;/code&gt; or &lt;code&gt;procps-ng&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This guide explains how to read &lt;code&gt;vmstat&lt;/code&gt; output and use its most useful options.&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;vmstat [OPTIONS] [delay [count]]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;delay&lt;/code&gt; - Seconds between reports. Without it, &lt;code&gt;vmstat&lt;/code&gt; prints a single report and exits.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;count&lt;/code&gt; - Number of reports to print. When you set a delay without a count, &lt;code&gt;vmstat&lt;/code&gt; runs until you press &lt;code&gt;Ctrl+C&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="reading-the-default-output"&gt;Reading the Default Output &lt;a class="headline-link" href="#reading-the-default-output" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Running &lt;code&gt;vmstat&lt;/code&gt; without arguments prints one report:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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&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 gu
1 0 0 362824 94368 1207160 0 0 12 5 48 112 1 0 99 0 0 0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The process and memory columns show values from the moment the command runs. In the first report, swap, I/O, system, and CPU activity is averaged since the last boot. The output has six sections.&lt;/p&gt;
&lt;h3 id="procs"&gt;procs &lt;a class="headline-link" href="#procs" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;r&lt;/code&gt; - Runnable processes, including processes running or waiting for CPU time. Compare a sustained value with the logical CPU count from &lt;code&gt;nproc&lt;/code&gt;; a busy run queue together with little idle CPU suggests CPU contention.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;b&lt;/code&gt; - Processes blocked while waiting for I/O to complete.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="memory-default-kib"&gt;memory (default KiB) &lt;a class="headline-link" href="#memory-default-kib" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;swpd&lt;/code&gt; - Swap space currently in use.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;free&lt;/code&gt; - Idle memory that is not currently in use.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;buff&lt;/code&gt; - Memory used as buffers.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cache&lt;/code&gt; - Memory used as cache. Linux uses otherwise idle RAM to cache data, so a large value is normal.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;free&lt;/code&gt; column alone does not show how much memory applications can readily use. Check the &lt;code&gt;available&lt;/code&gt; value from the &lt;a href="https://linuxize.com/post/free-command-in-linux/"&gt;&lt;code&gt;free&lt;/code&gt; command&lt;/a&gt;
when you need that figure.&lt;/p&gt;
&lt;h3 id="swap-kibs"&gt;swap (KiB/s) &lt;a class="headline-link" href="#swap-kibs" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;si&lt;/code&gt; - Memory swapped in from disk per second.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;so&lt;/code&gt; - Memory swapped out to disk per second.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Occasional swap activity is not enough to diagnose a problem. Sustained &lt;code&gt;si&lt;/code&gt; or &lt;code&gt;so&lt;/code&gt; activity together with falling free and cache memory is a stronger sign of memory pressure.&lt;/p&gt;
&lt;h3 id="io-kibs"&gt;io (KiB/s) &lt;a class="headline-link" href="#io-kibs" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;bi&lt;/code&gt; - Kibibytes received from block devices per second.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bo&lt;/code&gt; - Kibibytes sent to block devices per second.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="system"&gt;system &lt;a class="headline-link" href="#system" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;in&lt;/code&gt; - Interrupts per second, including the clock interrupt.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cs&lt;/code&gt; - Context switches per second. Compare this value with the system&amp;rsquo;s normal workload rather than using a fixed threshold.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="cpu-percentage-of-total-cpu-time"&gt;cpu (percentage of total CPU time) &lt;a class="headline-link" href="#cpu-percentage-of-total-cpu-time" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;us&lt;/code&gt; - Time spent running user-space code, including nice time.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sy&lt;/code&gt; - Time spent running kernel code.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;id&lt;/code&gt; - Idle time.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wa&lt;/code&gt; - Time the CPU was idle while waiting for I/O. A sustained increase is a clue to investigate I/O, but it does not identify the device or prove that storage is the bottleneck.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;st&lt;/code&gt; - Time taken from a virtual machine by the hypervisor.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gu&lt;/code&gt; - Time spent running KVM guest code, including guest nice time.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Older procps-ng versions may end the CPU columns at &lt;code&gt;st&lt;/code&gt;. The meaning of the preceding columns is unchanged.&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;A single report gives limited information about a live problem. Pass a delay to watch activity over consecutive intervals:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 &lt;span class="m"&gt;2&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 a new line every two seconds until you press &lt;code&gt;Ctrl+C&lt;/code&gt;. The first line still contains activity averages since boot, while later lines cover each two-second interval. Add &lt;code&gt;-y&lt;/code&gt; when you want to omit that first report:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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;2&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 collect ten interval reports and then stop, add a count:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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;2&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;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 gu
0 0 0 360988 94504 1208068 0 0 0 0 92 198 0 0 100 0 0 0
1 0 0 360744 94504 1208068 0 0 0 16 104 231 1 0 99 0 0 0
0 0 0 360720 94504 1208080 0 0 0 0 89 190 0 0 100 0 0 0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Only the first three rows are shown here. Because &lt;code&gt;-y&lt;/code&gt; skips the immediate boot-average report, the command waits for and records ten two-second intervals, taking about 20 seconds.&lt;/p&gt;
&lt;p&gt;Add timestamps and redirect the reports to a file when you want to review them 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;vmstat -y -t &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt; &amp;gt; vmstat.log&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="active-and-inactive-memory"&gt;Active and Inactive Memory &lt;a class="headline-link" href="#active-and-inactive-memory" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;-a&lt;/code&gt; flag replaces the &lt;code&gt;buff&lt;/code&gt; and &lt;code&gt;cache&lt;/code&gt; columns with &lt;code&gt;inact&lt;/code&gt; (inactive memory) and &lt;code&gt;active&lt;/code&gt; (active memory):&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 -a&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 inact active si so bi bo in cs us sy id wa st gu
0 0 0 358900 921048 490836 0 0 12 5 48 112 1 0 99 0 0 0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Active memory contains pages used recently and likely to be referenced again. Inactive memory has not been accessed recently and contains candidates for reclaim when the system needs space. This view is useful when several workloads compete for memory.&lt;/p&gt;
&lt;h2 id="adding-timestamps"&gt;Adding Timestamps &lt;a class="headline-link" href="#adding-timestamps" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;-t&lt;/code&gt; flag appends a timestamp column to each line, which is useful when capturing output to a log 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;vmstat -y -t &lt;span class="m"&gt;2&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------- -----timestamp-----
r b swpd free buff cache si so bi bo in cs us sy id wa st gu EEST
0 0 0 362824 94368 1207160 0 0 0 0 88 195 0 0 100 0 0 0 2026-09-07 10:30:02
0 0 0 362700 94368 1207160 0 0 0 12 94 207 1 0 99 0 0 0 2026-09-07 10:30:04&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The timestamp uses the system&amp;rsquo;s local time zone. The example shows only the first two of the five requested reports.&lt;/p&gt;
&lt;h2 id="wide-output-mode"&gt;Wide Output Mode &lt;a class="headline-link" href="#wide-output-mode" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;On systems with many CPU cores or high counter values, the default column widths can cause numbers to truncate or columns to overlap. The &lt;code&gt;-w&lt;/code&gt; flag switches to a wider format that avoids truncation:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 -w &lt;span class="m"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="memory-event-counters"&gt;Memory Event Counters &lt;a class="headline-link" href="#memory-event-counters" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;-s&lt;/code&gt; flag prints current memory totals followed by CPU and event counters accumulated since 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;vmstat -s&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; 2023560 K total memory
417148 K used memory
493280 K active memory
921048 K inactive memory
362824 K free memory
94368 K buffer memory
1207160 K swap cache
2097148 K total swap
0 K used swap
2097148 K free swap
48712 non-nice user cpu ticks
952 nice user cpu ticks
12836 system cpu ticks
5181148 idle cpu ticks
5984 IO-wait cpu ticks
0 IRQ cpu ticks
3268 softirq cpu ticks
0 stolen cpu ticks
0 non-nice guest cpu ticks
0 nice guest cpu ticks
621264 K paged in
247908 K paged out
0 pages swapped in
0 pages swapped out
2518704 interrupts
5884112 CPU context switches
1788713671 boot time
52364 forks&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The exact list varies with the kernel and procps-ng version. This view is useful for checking current swap totals and cumulative paging activity. The event counters and CPU ticks start again after a reboot.&lt;/p&gt;
&lt;h2 id="disk-statistics"&gt;Disk Statistics &lt;a class="headline-link" href="#disk-statistics" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;-d&lt;/code&gt; flag shows cumulative read and write statistics broken down by disk device:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 -d&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;disk- ------------reads------------ ------------writes----------- -----IO------
total merged sectors ms total merged sectors ms cur sec
sda 12486 983 956342 34208 18402 4682 920612 142104 0 42
sdb 142 0 9168 252 0 0 0 0 0 0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;total&lt;/code&gt; - Total number of completed read or write operations.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;merged&lt;/code&gt; - Adjacent requests grouped into a single I/O operation.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sectors&lt;/code&gt; - Total sectors read or written.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ms&lt;/code&gt; - Total milliseconds spent reading or writing.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cur&lt;/code&gt; - I/O operations currently in progress.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sec&lt;/code&gt; - Total seconds spent doing I/O.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To see statistics for a specific partition rather than the whole disk:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 -p /dev/sda1&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;sda1 reads read sectors writes requested writes
12384 948812 18320 910484&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="changing-output-units"&gt;Changing Output Units &lt;a class="headline-link" href="#changing-output-units" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;By default, memory and swap values use 1024-byte kibibytes. The &lt;code&gt;-S&lt;/code&gt; flag changes their display 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;vmstat -S M &lt;span class="m"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Accepted values are &lt;code&gt;k&lt;/code&gt; (1000 bytes), &lt;code&gt;K&lt;/code&gt; (1024 bytes), &lt;code&gt;m&lt;/code&gt; (1,000,000 bytes), and &lt;code&gt;M&lt;/code&gt; (1,048,576 bytes). This option does not change the swap (&lt;code&gt;si&lt;/code&gt;, &lt;code&gt;so&lt;/code&gt;) or I/O (&lt;code&gt;bi&lt;/code&gt;, &lt;code&gt;bo&lt;/code&gt;) fields, which stay in KiB/s.&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;-a&lt;/code&gt; - Show active and inactive memory instead of buffer and cache values.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-d&lt;/code&gt; - Display disk statistics per device.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-D&lt;/code&gt; - Display summary disk statistics.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-f&lt;/code&gt; - Show the number of forks since boot.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-m&lt;/code&gt; - Display slab memory information.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-n&lt;/code&gt; - Print the header only once.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-p device&lt;/code&gt; - Display partition-level disk statistics.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-s&lt;/code&gt; - Display memory statistics and event counters.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-S unit&lt;/code&gt; - Set the memory and swap output unit to &lt;code&gt;k&lt;/code&gt;, &lt;code&gt;K&lt;/code&gt;, &lt;code&gt;m&lt;/code&gt;, or &lt;code&gt;M&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-t&lt;/code&gt; - Append a timestamp to each line.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-w&lt;/code&gt; - Use wider columns to prevent unwanted column breaks.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-y&lt;/code&gt; - Omit the first report containing activity averages since boot.&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;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;Single snapshot&lt;/td&gt;
&lt;td&gt;&lt;code&gt;vmstat&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Update every 2 seconds&lt;/td&gt;
&lt;td&gt;&lt;code&gt;vmstat 2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Skip the boot-average report&lt;/td&gt;
&lt;td&gt;&lt;code&gt;vmstat -y 2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10 interval reports, 2 seconds apart&lt;/td&gt;
&lt;td&gt;&lt;code&gt;vmstat -y 2 10&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Active/inactive memory view&lt;/td&gt;
&lt;td&gt;&lt;code&gt;vmstat -a 2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Timestamped output&lt;/td&gt;
&lt;td&gt;&lt;code&gt;vmstat -t 2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wide output&lt;/td&gt;
&lt;td&gt;&lt;code&gt;vmstat -w 2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory event counters&lt;/td&gt;
&lt;td&gt;&lt;code&gt;vmstat -s&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Disk statistics&lt;/td&gt;
&lt;td&gt;&lt;code&gt;vmstat -d&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Partition statistics&lt;/td&gt;
&lt;td&gt;&lt;code&gt;vmstat -p /dev/sda1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output in megabytes&lt;/td&gt;
&lt;td&gt;&lt;code&gt;vmstat -S M 2&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;All values look like averages, not live activity&lt;/strong&gt;&lt;br&gt;
The first line contains activity averages since boot, although its process and memory fields are current values. Run &lt;code&gt;vmstat -y 2&lt;/code&gt; to skip that line and show only two-second interval reports.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The shell reports vmstat: command not found&lt;/strong&gt;&lt;br&gt;
Install the &lt;code&gt;procps&lt;/code&gt; package on Ubuntu and Debian with &lt;code&gt;sudo apt install procps&lt;/code&gt;. Fedora and RHEL use &lt;code&gt;sudo dnf install procps-ng&lt;/code&gt;, while Arch Linux uses &lt;code&gt;sudo pacman -S procps-ng&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;vmstat cannot read data under /proc&lt;/strong&gt;&lt;br&gt;
The command needs access to files under &lt;code&gt;/proc&lt;/code&gt;. Containers and systems mounted with restrictive &lt;code&gt;/proc&lt;/code&gt; options may hide some statistics. The &lt;code&gt;vmstat -m&lt;/code&gt; view also requires access to &lt;code&gt;/proc/slabinfo&lt;/code&gt;, which may be unavailable to a standard user.&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;Run &lt;code&gt;vmstat -y 2&lt;/code&gt; as a first check when a system slows down, then follow the signal: use &lt;a href="https://linuxize.com/post/top-command-in-linux/"&gt;&lt;code&gt;top&lt;/code&gt;&lt;/a&gt;
or &lt;a href="https://linuxize.com/post/htop-command-in-linux/"&gt;&lt;code&gt;htop&lt;/code&gt;&lt;/a&gt;
for per-process CPU and memory usage, or &lt;code&gt;iostat -xz 2&lt;/code&gt; for device latency and queues. For per-core and historical CPU data, see the guide on &lt;a href="https://linuxize.com/post/how-to-check-cpu-usage-in-linux/"&gt;checking CPU usage in Linux&lt;/a&gt;
.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/vmstat-command-in-linux/featured_hu_b116ea65517c393a.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>btop Command in Linux: Monitor System Resources</title><link>https://linuxize.com/post/btop-command-in-linux/</link><pubDate>Sat, 05 Sep 2026 14:35:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/btop-command-in-linux/</guid><category>linux commands</category><description>Monitor CPU, memory, disks, network traffic, and processes with btop. This guide covers installation, keyboard shortcuts, process actions, and configuration.</description><content:encoded>&lt;p&gt;When a server feels slow, the first minutes are often spent juggling tools: &lt;code&gt;top&lt;/code&gt; for processes, &lt;code&gt;free&lt;/code&gt; for memory, and separate commands for network and disk activity. &lt;code&gt;btop&lt;/code&gt; puts these metrics on one screen, with live graphs for CPU, memory, disks, and network traffic alongside a filterable process list. It is the C++ continuation of the bashtop and bpytop resource monitors.&lt;/p&gt;
&lt;p&gt;This guide explains how to install &lt;code&gt;btop&lt;/code&gt;, read its dashboard, manage processes from it, and adjust its configuration.&lt;/p&gt;
&lt;h2 id="installing-btop"&gt;Installing btop &lt;a class="headline-link" href="#installing-btop" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;btop&lt;/code&gt; is available from the package repositories of current Ubuntu, Debian, Fedora, RHEL-based, and Arch Linux releases.&lt;/p&gt;
&lt;p&gt;On Ubuntu, Debian, and Derivatives, install it 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 btop&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;. Fedora provides the package 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 dnf install btop&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On RHEL, Rocky Linux, and AlmaLinux, enable the EPEL repository before installing &lt;code&gt;btop&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 epel-release
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo dnf install btop&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 the package with &lt;code&gt;pacman&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 pacman -S btop&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 the version 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;btop --version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="btop-syntax"&gt;btop Syntax &lt;a class="headline-link" href="#btop-syntax" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;btop&lt;/code&gt; command accepts the following general 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="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;btop [OPTIONS]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Run it without options to open the interactive dashboard:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;btop&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Use the built-in help to see the options supported by your 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="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;btop --help&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The options you will use most often are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--low-color&lt;/code&gt; - Disable truecolor and convert to 256 colors.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-t&lt;/code&gt; - Force TTY mode with 16 colors and tty-friendly graph symbols.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-p &amp;lt;id&amp;gt;&lt;/code&gt; - Start with one of the saved presets, 0 through 9.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--version&lt;/code&gt; - Print the installed version.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Short option names changed in btop 1.4.2, so prefer the long forms above when you write scripts or aliases that must work across distributions.&lt;/p&gt;
&lt;h2 id="reading-the-dashboard"&gt;Reading the Dashboard &lt;a class="headline-link" href="#reading-the-dashboard" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The screen is divided into four boxes. The top box graphs per-core CPU usage along with frequency, temperature (when sensors are available), load average, and uptime. The left box covers memory and disks: used, available, and cached memory, plus usage and I/O activity for each mounted filesystem. The right box graphs network download and upload per interface. The bottom box is the process list.&lt;/p&gt;
&lt;p&gt;Everything is live and mouse-aware: you can click a process to select it, click column headers to change sorting, and scroll the list with the wheel. The same navigation works from the keyboard. Press &lt;code&gt;1&lt;/code&gt; to toggle the CPU box, &lt;code&gt;2&lt;/code&gt; for memory and disks, &lt;code&gt;3&lt;/code&gt; for network, and &lt;code&gt;4&lt;/code&gt; for processes.&lt;/p&gt;
&lt;p&gt;In the process list, memory usage is based on resident memory. By default, CPU usage is shown as a share of the machine&amp;rsquo;s total CPU capacity. Press &lt;code&gt;c&lt;/code&gt; to switch to per-core scaling, where 100% represents one fully used core and a multithreaded process can exceed 100%. For more detail on the underlying numbers, see the &lt;a href="https://linuxize.com/post/free-command-in-linux/"&gt;&lt;code&gt;free&lt;/code&gt;&lt;/a&gt;
command for memory and the load discussion in our &lt;a href="https://linuxize.com/post/top-command-in-linux/"&gt;top command guide&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="working-with-the-process-list"&gt;Working with the Process List &lt;a class="headline-link" href="#working-with-the-process-list" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The process list supports keyboard and mouse navigation. These keys cover the most common tasks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Up and Down arrows select a process; Enter opens a detail view with its own CPU and memory graphs for the selected process.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;f&lt;/code&gt; or &lt;code&gt;/&lt;/code&gt; starts a filter. Type part of a process name and press Enter to keep only matching rows. Press Delete after leaving the filter editor to clear the active filter; Esc cancels an edit and restores the previous filter.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;e&lt;/code&gt; toggles tree view, showing parent-child relationships so you can see what started a process.&lt;/li&gt;
&lt;li&gt;Left and Right arrows change the sort column (CPU, memory, PID, and so on), and &lt;code&gt;r&lt;/code&gt; reverses the order.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Filtering plus the detail view covers the &amp;ldquo;what is eating this machine&amp;rdquo; workflow: filter to the suspect, Enter to watch its graphs, and you have a per-process monitor without leaving the dashboard.&lt;/p&gt;
&lt;h2 id="managing-processes"&gt;Managing Processes &lt;a class="headline-link" href="#managing-processes" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;With a process selected, three keys open signal actions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;t&lt;/code&gt; opens a confirmation prompt for &lt;code&gt;SIGTERM&lt;/code&gt;, the normal termination request.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;k&lt;/code&gt; opens a confirmation prompt for &lt;code&gt;SIGKILL&lt;/code&gt;, which stops the process immediately.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s&lt;/code&gt; opens a menu of all signals, for cases like &lt;code&gt;SIGHUP&lt;/code&gt; or &lt;code&gt;SIGSTOP&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&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;Try &lt;code&gt;SIGTERM&lt;/code&gt; first and wait for the process to exit. &lt;code&gt;SIGKILL&lt;/code&gt; does not let the process save data or run cleanup handlers, so use it only when the process ignores &lt;code&gt;SIGTERM&lt;/code&gt;.&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;You can signal processes owned by your user. Run &lt;code&gt;sudo btop&lt;/code&gt; only when you need to manage a process owned by another account, and confirm the selected PID before sending a signal.&lt;/p&gt;
&lt;h2 id="options-and-themes"&gt;Options and Themes &lt;a class="headline-link" href="#options-and-themes" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Press &lt;code&gt;F2&lt;/code&gt; or &lt;code&gt;o&lt;/code&gt; to open the options screen directly. You can change the update interval, visible boxes, temperature units, network interface, and color theme from this menu. Press Esc for the main menu or &lt;code&gt;q&lt;/code&gt; to quit.&lt;/p&gt;
&lt;p&gt;When &lt;code&gt;$XDG_CONFIG_HOME&lt;/code&gt; is set, &lt;code&gt;btop&lt;/code&gt; stores its settings in &lt;code&gt;$XDG_CONFIG_HOME/btop/btop.conf&lt;/code&gt;. Otherwise, it uses &lt;code&gt;~/.config/btop/btop.conf&lt;/code&gt;. The file is plain text and can also be edited 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="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;~/.config/btop/btop.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;color_theme = &amp;#34;tokyo-night&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;theme_background = False
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;update_ms = 2000
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;shown_boxes = &amp;#34;cpu mem net proc&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Setting &lt;code&gt;theme_background = False&lt;/code&gt; makes &lt;code&gt;btop&lt;/code&gt; use your terminal&amp;rsquo;s background. The 2000 ms update interval is the upstream default and provides steadier graph samples than a very short interval.&lt;/p&gt;
&lt;p&gt;Distribution packages usually place system themes in &lt;code&gt;/usr/share/btop/themes&lt;/code&gt;, while source installations may use &lt;code&gt;/usr/local/share/btop/themes&lt;/code&gt;. Put additional themes in &lt;code&gt;$XDG_CONFIG_HOME/btop/themes&lt;/code&gt; or &lt;code&gt;~/.config/btop/themes&lt;/code&gt; to make them available in the picker.&lt;/p&gt;
&lt;p&gt;GPU panels appear only when the &lt;code&gt;btop&lt;/code&gt; binary was built with GPU support and the required driver libraries are available. On Linux x86_64, upstream supports NVIDIA and AMD GPUs and Intel integrated GPUs, but the exact support depends on how your distribution packaged &lt;code&gt;btop&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;Key or Command&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;btop&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Open the resource dashboard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;btop --help&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show options supported by the installed version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;btop --version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the installed version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;btop -p 1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start with preset 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;btop -t&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start in 16-color TTY mode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;btop --low-color&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start in 256-color mode&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;code&gt;3&lt;/code&gt; / &lt;code&gt;4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Toggle the CPU, memory, network, or process box&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Up / Down&lt;/td&gt;
&lt;td&gt;Select a process&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enter&lt;/td&gt;
&lt;td&gt;Open or close details for the selected process&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Space&lt;/td&gt;
&lt;td&gt;Expand or collapse the selected process in tree view&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;f&lt;/code&gt; or &lt;code&gt;/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Edit the process filter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete&lt;/td&gt;
&lt;td&gt;Clear the active process filter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Left / Right&lt;/td&gt;
&lt;td&gt;Change the process sort column&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;r&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reverse the process sort order&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;e&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Toggle process tree view&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;c&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Toggle per-core process CPU percentages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;t&lt;/code&gt; / &lt;code&gt;k&lt;/code&gt; / &lt;code&gt;s&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Open SIGTERM, SIGKILL, or signal selection actions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;+&lt;/code&gt; / &lt;code&gt;-&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add or subtract 100 ms from the update timer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;F2&lt;/code&gt; or &lt;code&gt;o&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Open options&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;h&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the help window&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Esc&lt;/td&gt;
&lt;td&gt;Toggle the main menu&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 &lt;code&gt;btop&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;The interface renders as broken boxes or stray characters&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;btop&lt;/code&gt; needs a UTF-8 locale and a font with Braille, geometric shape, and box-drawing characters. Fix the locale or font first. The flag that bypasses locale detection was renamed in btop 1.4.2. Version 1.4.2 and newer accept &lt;code&gt;btop --force-utf&lt;/code&gt;, while 1.4.1 and earlier, including the packages in Ubuntu 24.04 and Debian 13, accept &lt;code&gt;btop --utf-force&lt;/code&gt;. Run &lt;code&gt;btop --help&lt;/code&gt; to confirm which form your version accepts. TTY mode is another fallback for limited terminals:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;btop -t&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;No temperatures are shown&lt;/strong&gt;&lt;br&gt;
On Linux, &lt;code&gt;btop&lt;/code&gt; reads sensor data through the kernel&amp;rsquo;s hwmon interface. Check whether the &lt;code&gt;sensors&lt;/code&gt; command reports a CPU temperature, then select the correct sensor under Options &amp;gt; CPU if automatic detection chose the wrong one. If the kernel module for your hardware is missing, install &lt;code&gt;lm-sensors&lt;/code&gt; and run &lt;code&gt;sudo sensors-detect&lt;/code&gt;. Virtual machines usually do not expose hardware temperature sensors.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Colors look wrong or washed out&lt;/strong&gt;&lt;br&gt;
Try 256-color mode when the terminal does not render truecolor correctly:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;btop --low-color&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For a basic console or a terminal with limited character support, use &lt;code&gt;btop -t&lt;/code&gt; instead.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sending a signal returns an insufficient permissions error&lt;/strong&gt;&lt;br&gt;
Your user can normally signal only its own processes. Exit and restart with &lt;code&gt;sudo btop&lt;/code&gt; if you must manage a system process, then verify the process name and PID before confirming the signal.&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;btop&lt;/code&gt; combines CPU, memory, disk, network, and process monitoring in one interactive terminal view. Keep &lt;a href="https://linuxize.com/post/top-command-in-linux/"&gt;&lt;code&gt;top&lt;/code&gt;&lt;/a&gt;
as the widely available fallback, or use &lt;a href="https://linuxize.com/post/htop-command-in-linux/"&gt;&lt;code&gt;htop&lt;/code&gt;&lt;/a&gt;
when you want a process-focused interface with fewer system-wide graphs.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/btop-command-in-linux/featured_hu_cbe34142fa413278.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>How to Open a Port in the Firewall on Linux</title><link>https://linuxize.com/post/how-to-open-a-port-in-the-firewall-on-linux/</link><pubDate>Wed, 02 Sep 2026 09:35:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/how-to-open-a-port-in-the-firewall-on-linux/</guid><category>firewall</category><category>security</category><description>Open a port in a Linux firewall with ufw, firewalld, nftables, or iptables, then make the rule persistent and test remote access.</description><content:encoded>&lt;p&gt;When a service works locally but cannot be reached from another machine, the host firewall is one of the first places to check. Opening the port tells the firewall to accept matching traffic, but it does not start the service or change the address on which it listens.&lt;/p&gt;
&lt;p&gt;This guide shows how to open a port with &lt;code&gt;ufw&lt;/code&gt;, &lt;code&gt;firewalld&lt;/code&gt;, &lt;code&gt;nftables&lt;/code&gt;, and legacy &lt;code&gt;iptables&lt;/code&gt; rules, make the change persistent, and test the connection from another host.&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/ufw/"&gt;ufw cheatsheet&lt;/a&gt;
, &lt;a href="https://linuxize.com/cheatsheet/firewalld/"&gt;firewalld cheatsheet&lt;/a&gt;
, and &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;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 ufw&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo ufw status&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check firewalld&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo firewall-cmd --state&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Find the active firewalld zone&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo firewall-cmd --get-active-zones&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Open TCP port 80 with ufw&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo ufw allow 80/tcp&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Open TCP port 80 with firewalld&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo firewall-cmd --zone=public --add-port=80/tcp&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Add a permanent firewalld rule&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo firewall-cmd --permanent --zone=public --add-port=80/tcp&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inspect nftables rules&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo nft -a list ruleset&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Open TCP port 80 with iptables&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo iptables -I INPUT 1 -p tcp --dport 80 -j ACCEPT&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check the local listener&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo ss -ltnp 'sport = :80'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test from another host&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nc -zv server.example.com 80&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The firewalld example uses the &lt;code&gt;public&lt;/code&gt; zone. Replace it with the zone attached to your incoming network interface. Raw nftables rules use administrator-defined table and chain names, so inspect the ruleset before changing it.&lt;/p&gt;
&lt;h2 id="check-whether-the-service-is-listening"&gt;Check Whether the Service Is Listening &lt;a class="headline-link" href="#check-whether-the-service-is-listening" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before changing the firewall, confirm that the service is listening on the expected TCP port. This example checks port 80:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 ss -ltnp &lt;span class="s1"&gt;&amp;#39;sport = :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 returns no output, nothing is listening on that port. Start or reconfigure the service before changing the firewall. If the local address is &lt;code&gt;127.0.0.1:80&lt;/code&gt; or &lt;code&gt;[::1]:80&lt;/code&gt;, the service accepts only local connections. Configure it to listen on the server&amp;rsquo;s network address, such as &lt;code&gt;0.0.0.0:80&lt;/code&gt; or &lt;code&gt;[::]:80&lt;/code&gt;, when remote access is required.&lt;/p&gt;
&lt;p&gt;For a UDP service, replace &lt;code&gt;-ltnp&lt;/code&gt; with &lt;code&gt;-lunp&lt;/code&gt;. The &lt;a href="https://linuxize.com/post/ss-command-in-linux/"&gt;&lt;code&gt;ss&lt;/code&gt; command guide&lt;/a&gt;
covers socket filters and listener output in more detail.&lt;/p&gt;
&lt;h2 id="identify-the-active-firewall"&gt;Identify the Active Firewall &lt;a class="headline-link" href="#identify-the-active-firewall" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use the firewall manager that is already active on the system. Do not add raw nftables or iptables rules alongside &lt;code&gt;ufw&lt;/code&gt; or &lt;code&gt;firewalld&lt;/code&gt;, because the manager may replace or reorder them during a reload.&lt;/p&gt;
&lt;p&gt;On Ubuntu and systems configured with &lt;code&gt;ufw&lt;/code&gt;, check its 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 ufw status&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Use the &lt;code&gt;ufw&lt;/code&gt; section when the output begins with &lt;code&gt;Status: active&lt;/code&gt;. An inactive &lt;code&gt;ufw&lt;/code&gt; configuration is not enforcing its saved rules.&lt;/p&gt;
&lt;p&gt;On Fedora, RHEL, and derivatives, check &lt;code&gt;firewalld&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 firewall-cmd --state&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 &lt;code&gt;running&lt;/code&gt;, use the firewalld section. When neither manager is active, check whether the system loads a raw nftables 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 systemctl is-active nftables&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;An &lt;code&gt;active&lt;/code&gt; result means the nftables service is running. If all three checks are inactive or unavailable, the machine may not have an active host firewall. A cloud firewall, security group, or router can still block incoming traffic before it reaches the server.&lt;/p&gt;
&lt;h2 id="opening-a-port-with-ufw"&gt;Opening a Port with ufw &lt;a class="headline-link" href="#opening-a-port-with-ufw" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Ubuntu uses &lt;code&gt;ufw&lt;/code&gt; as its default firewall configuration tool, and it is also available on Debian. To allow TCP traffic on port 80, 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 ufw allow 80/tcp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The rule takes effect immediately when &lt;code&gt;ufw&lt;/code&gt; is active and remains in place after a reboot. Replace &lt;code&gt;tcp&lt;/code&gt; with &lt;code&gt;udp&lt;/code&gt; when the application uses UDP.&lt;/p&gt;
&lt;p&gt;You can also use a service name from &lt;code&gt;/etc/services&lt;/code&gt;. The following rule opens the standard HTTP port:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 http&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Do not expose a database or an administration port to every source unless the service is meant to be public. To allow one address to reach PostgreSQL on port 5432, 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 ufw allow proto tcp from 203.0.113.10 to any port &lt;span class="m"&gt;5432&lt;/span&gt;&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;203.0.113.10&lt;/code&gt; with the client address. You can also provide a CIDR range, such as &lt;code&gt;192.168.1.0/24&lt;/code&gt;, when a trusted network needs access.&lt;/p&gt;
&lt;p&gt;For a TCP port range, separate the first and last ports with a colon:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 6000:6007/tcp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;List the rules with their numbers to confirm 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 ufw status numbered&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
-- ------ ----
[ 1] 80/tcp ALLOW IN Anywhere
[ 2] 80/tcp (v6) ALLOW IN Anywhere (v6)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The two entries allow port 80 over IPv4 and IPv6. For more examples, including deleting a rule, see the &lt;a href="https://linuxize.com/post/ufw-command-in-linux/"&gt;ufw command guide&lt;/a&gt;
.&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;If you are enabling &lt;code&gt;ufw&lt;/code&gt; over SSH, allow the SSH service or its custom port first. Otherwise, &lt;code&gt;sudo ufw enable&lt;/code&gt; can block the connection and leave you dependent on console access.&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="opening-a-port-with-firewalld"&gt;Opening a Port with firewalld &lt;a class="headline-link" href="#opening-a-port-with-firewalld" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;firewalld&lt;/code&gt; organizes rules into zones, and each network interface or source is assigned to a zone. Find the zone that receives the incoming connection before adding the 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 firewall-cmd --get-active-zones&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;public
interfaces: enp1s0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This server uses the &lt;code&gt;public&lt;/code&gt; zone for traffic arriving on &lt;code&gt;enp1s0&lt;/code&gt;. Open TCP port 80 in the runtime configuration so the change takes effect immediately:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 firewall-cmd --zone&lt;span class="o"&gt;=&lt;/span&gt;public --add-port&lt;span class="o"&gt;=&lt;/span&gt;80/tcp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Add the same rule to the permanent configuration so it survives a reload or reboot:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 firewall-cmd --permanent --zone&lt;span class="o"&gt;=&lt;/span&gt;public --add-port&lt;span class="o"&gt;=&lt;/span&gt;80/tcp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Running both commands avoids reloading the entire firewall and does not disturb unrelated runtime-only rules. If your interface belongs to another zone, replace &lt;code&gt;public&lt;/code&gt; in both commands.&lt;/p&gt;
&lt;p&gt;For standard services, a named service is easier to read than a port number. These commands allow HTTP immediately and permanently:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 firewall-cmd --zone&lt;span class="o"&gt;=&lt;/span&gt;public --add-service&lt;span class="o"&gt;=&lt;/span&gt;http
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo firewall-cmd --permanent --zone&lt;span class="o"&gt;=&lt;/span&gt;public --add-service&lt;span class="o"&gt;=&lt;/span&gt;http&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;A port range works the same way, except that &lt;code&gt;firewalld&lt;/code&gt; separates the two ports with a hyphen rather than the colon &lt;code&gt;ufw&lt;/code&gt; expects. These commands open TCP ports 6000 through 6007:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 firewall-cmd --zone&lt;span class="o"&gt;=&lt;/span&gt;public --add-port&lt;span class="o"&gt;=&lt;/span&gt;6000-6007/tcp
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo firewall-cmd --permanent --zone&lt;span class="o"&gt;=&lt;/span&gt;public --add-port&lt;span class="o"&gt;=&lt;/span&gt;6000-6007/tcp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Query the active zone to confirm that the port rule is present:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 firewall-cmd --zone&lt;span class="o"&gt;=&lt;/span&gt;public --query-port&lt;span class="o"&gt;=&lt;/span&gt;80/tcp&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;yes&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When you add &lt;code&gt;http&lt;/code&gt; as a named service, verify it with &lt;code&gt;sudo firewall-cmd --zone=public --query-service=http&lt;/code&gt; instead.&lt;/p&gt;
&lt;h2 id="opening-a-port-with-nftables"&gt;Opening a Port with nftables &lt;a class="headline-link" href="#opening-a-port-with-nftables" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use raw &lt;code&gt;nft&lt;/code&gt; commands only when nftables itself manages the firewall. &lt;code&gt;ufw&lt;/code&gt; and &lt;code&gt;firewalld&lt;/code&gt; can also use nftables as a backend, but their rules should still be changed through &lt;code&gt;ufw&lt;/code&gt; or &lt;code&gt;firewall-cmd&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Unlike iptables, nftables does not create standard table and chain names. Display the current ruleset, including rule handles, before adding anything:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 nft -a list ruleset&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The following example assumes the ruleset contains an &lt;code&gt;inet&lt;/code&gt; table named &lt;code&gt;filter&lt;/code&gt; and an input chain named &lt;code&gt;input&lt;/code&gt;. It inserts an allow rule at the beginning of that chain so an existing catch-all drop rule cannot match 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;sudo nft insert rule inet filter input tcp dport &lt;span class="m"&gt;80&lt;/span&gt; accept&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Substitute the family, table, and chain names shown on your system. The command changes the running ruleset immediately, but it does not update the configuration loaded during boot.&lt;/p&gt;
&lt;p&gt;On Debian, persistent nftables rules are normally stored in &lt;code&gt;/etc/nftables.conf&lt;/code&gt;. Add the equivalent rule inside the existing input chain, before its final &lt;code&gt;drop&lt;/code&gt; or &lt;code&gt;reject&lt;/code&gt; 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="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/nftables.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;tcp dport 80 accept&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Validate the complete file before loading 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 nft --check --file /etc/nftables.conf&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If the check produces no error, apply the file and enable the service 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 nft --file /etc/nftables.conf
&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; nftables&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Some systems split the rules across files included from &lt;code&gt;/etc/nftables.conf&lt;/code&gt;. In that case, edit the file that defines the input chain rather than adding a second copy of the chain.&lt;/p&gt;
&lt;h2 id="opening-a-port-with-iptables"&gt;Opening a Port with iptables &lt;a class="headline-link" href="#opening-a-port-with-iptables" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;iptables&lt;/code&gt; is the legacy interface and is often a compatibility front end for nftables on current distributions. Prefer &lt;code&gt;ufw&lt;/code&gt;, &lt;code&gt;firewalld&lt;/code&gt;, or native nftables for a new firewall, but use iptables syntax when maintaining an existing iptables ruleset.&lt;/p&gt;
&lt;p&gt;Insert an IPv4 rule at the top of the &lt;code&gt;INPUT&lt;/code&gt; chain to allow TCP port 80:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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;80&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;Using &lt;code&gt;-I INPUT 1&lt;/code&gt; places the rule before a later rule that drops unmatched traffic. List the chain with rule numbers to verify its position:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 --line-numbers&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The running rule disappears after a reboot unless the system restores it. On Ubuntu and Debian, install the persistence package and save the current ruleset:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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;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;The &lt;code&gt;iptables&lt;/code&gt; command affects IPv4 only. If the service also accepts IPv6 connections, add the equivalent rule with &lt;code&gt;ip6tables&lt;/code&gt; and save 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 ip6tables -I INPUT &lt;span class="m"&gt;1&lt;/span&gt; -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 netfilter-persistent save&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;See the &lt;a href="https://linuxize.com/post/iptables-command-in-linux/"&gt;iptables command guide&lt;/a&gt;
when you need to inspect, remove, or reorder existing rules.&lt;/p&gt;
&lt;h2 id="verifying-the-port-is-reachable"&gt;Verifying the Port Is Reachable &lt;a class="headline-link" href="#verifying-the-port-is-reachable" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;After applying the firewall rule, check the local listener 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 ss -ltnp &lt;span class="s1"&gt;&amp;#39;sport = :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;Then test from another machine that should be allowed through the firewall. Replace the hostname and port with your server details:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nc -zv server.example.com &lt;span class="m"&gt;80&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;A successful connection confirms that the service is listening and the network path permits the traffic. A timeout usually points to a firewall, routing, or upstream network filter. &lt;code&gt;Connection refused&lt;/code&gt; usually means the host is reachable but nothing accepts the connection on that address and port.&lt;/p&gt;
&lt;p&gt;For additional remote tests with &lt;code&gt;nmap&lt;/code&gt;, netcat, and Bash, see &lt;a href="https://linuxize.com/post/check-open-ports-linux/"&gt;how to check open ports in Linux&lt;/a&gt;
.&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;The firewall rule exists, but the port is still unreachable&lt;/strong&gt;&lt;br&gt;
Run &lt;code&gt;sudo ss -ltnp 'sport = :80'&lt;/code&gt; and inspect the local address. A service bound only to &lt;code&gt;127.0.0.1&lt;/code&gt; or &lt;code&gt;::1&lt;/code&gt; cannot accept remote connections.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The ufw rule does not filter any traffic&lt;/strong&gt;&lt;br&gt;
Check &lt;code&gt;sudo ufw status&lt;/code&gt;. Rules are saved while &lt;code&gt;ufw&lt;/code&gt; is inactive, but they are not enforced until you enable the firewall.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The firewalld rule is in the wrong zone&lt;/strong&gt;&lt;br&gt;
Compare &lt;code&gt;sudo firewall-cmd --get-active-zones&lt;/code&gt; with the zone used in the command. Add the rule to the zone attached to the incoming interface or source.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The rule uses the wrong protocol&lt;/strong&gt;&lt;br&gt;
TCP and UDP rules are separate. Check the application&amp;rsquo;s documentation or use &lt;code&gt;ss -ltnp&lt;/code&gt; for TCP and &lt;code&gt;ss -lunp&lt;/code&gt; for UDP before adding the rule.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A cloud server still blocks the port&lt;/strong&gt;&lt;br&gt;
Cloud security groups and provider firewalls operate outside the Linux host. Allow the same port and source in the provider control panel. A server behind a router may also need a port-forwarding rule before internet clients can reach 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;Open only the port, protocol, and source range the service needs. Check both the local listener and a remote connection after every change, and remove temporary rules when the service no longer requires them.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/how-to-open-a-port-in-the-firewall-on-linux/featured_hu_c0bffd4c1adacec1.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>PHP Cheatsheet</title><link>https://linuxize.com/cheatsheet/php/</link><pubDate>Tue, 01 Sep 2026 10:55:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/cheatsheet/php/</guid><description>PHP 8 quick reference for syntax, arrays, functions, classes, request data, files, JSON, exceptions, CLI commands, Composer, configuration, and PHP-FPM.</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;Every PHP file starts with an opening tag. Closing tags are omitted in pure PHP files to avoid stray output.&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;&amp;lt;?php ... ?&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Standard PHP tags&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;?= $name ?&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Short echo tag (always available)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;declare(strict_types=1);&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use strict scalar checks for calls and returns in this file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;// comment&lt;/code&gt;, &lt;code&gt;# comment&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Single-line comment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/* comment */&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Multi-line comment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;echo &amp;quot;text&amp;quot;;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Output one or more strings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;print_r($var)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Human-readable dump of an array or object&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;var_dump($var)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Dump value with type and length&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;require 'file.php'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Include file, fatal error if missing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;include 'file.php'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Include file, warning if missing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;require_once 'file.php'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Include only once&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="variables-and-types"&gt;Variables and Types &lt;a class="headline-link" href="#variables-and-types" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;PHP variables start with &lt;code&gt;$&lt;/code&gt; and are dynamically typed. Type declarations are optional but recommended.&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;$name = &amp;quot;Alice&amp;quot;;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Assign a value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;int&lt;/code&gt;, &lt;code&gt;float&lt;/code&gt;, &lt;code&gt;bool&lt;/code&gt;, &lt;code&gt;string&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Scalar types&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;array&lt;/code&gt;, &lt;code&gt;object&lt;/code&gt;, &lt;code&gt;callable&lt;/code&gt;, &lt;code&gt;iterable&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Compound types&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Absence of a value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;const MAX = 10;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Compile-time constant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;define('MAX', 10);&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Runtime constant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gettype($x)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Return the type name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;is_int($x)&lt;/code&gt;, &lt;code&gt;is_string($x)&lt;/code&gt;, &lt;code&gt;is_array($x)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Type checks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;(int) $x&lt;/code&gt;, &lt;code&gt;(string) $x&lt;/code&gt;, &lt;code&gt;(bool) $x&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Explicit casts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;intval($x)&lt;/code&gt;, &lt;code&gt;floatval($x)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Convert to int or float&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;isset($x)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt; if set and not &lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;empty($x)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt; when unset or equal to &lt;code&gt;&amp;quot;&amp;quot;&lt;/code&gt;, &lt;code&gt;&amp;quot;0&amp;quot;&lt;/code&gt;, &lt;code&gt;0&lt;/code&gt;, &lt;code&gt;0.0&lt;/code&gt;, &lt;code&gt;[]&lt;/code&gt;, &lt;code&gt;null&lt;/code&gt;, or &lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;unset($x)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Destroy a variable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;?int $x&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Nullable type (int or null)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;int|string $x&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Union type&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="operators"&gt;Operators &lt;a class="headline-link" href="#operators" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Arithmetic, comparison, logical, and null-handling operators.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operator&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;+&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;/td&gt;
&lt;td&gt;Arithmetic and modulo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;**&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exponentiation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;String concatenation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&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;/td&gt;
&lt;td&gt;Compound assignment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;==&lt;/code&gt;, &lt;code&gt;!=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Loose comparison (type juggling)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;===&lt;/code&gt;, &lt;code&gt;!==&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Strict comparison (value and type)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;=&lt;/code&gt;, &lt;code&gt;&amp;gt;=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Relational comparison&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;=&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Spaceship, returns -1, 0, or 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;, &lt;code&gt;||&lt;/code&gt;, &lt;code&gt;!&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Logical AND, OR, NOT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;and&lt;/code&gt;, &lt;code&gt;or&lt;/code&gt;, &lt;code&gt;xor&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Low-precedence logical operators&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;? :&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ternary conditional&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;?:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Elvis, returns left side if truthy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;??&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Null coalescing, returns right side if left is null&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;??=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Null coalescing assignment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;?-&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Nullsafe method or property access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;|&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pipe operator, PHP 8.5 and later&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="strings"&gt;Strings &lt;a class="headline-link" href="#strings" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Double-quoted strings interpolate variables; single-quoted strings do not.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&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;strlen($s)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;String length in bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mb_strlen($s)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Character length (requires the mbstring extension)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;strtolower($s)&lt;/code&gt; / &lt;code&gt;strtoupper($s)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change case&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ucfirst($s)&lt;/code&gt; / &lt;code&gt;ucwords($s)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Capitalize first letter / each word&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;trim($s)&lt;/code&gt;, &lt;code&gt;ltrim($s)&lt;/code&gt;, &lt;code&gt;rtrim($s)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Strip whitespace&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;str_contains($s, &amp;quot;x&amp;quot;)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt; if substring is present&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;str_starts_with($s, &amp;quot;x&amp;quot;)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt; if string starts with prefix&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;str_ends_with($s, &amp;quot;x&amp;quot;)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt; if string ends with suffix&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;strpos($s, &amp;quot;x&amp;quot;)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;First index of substring, &lt;code&gt;false&lt;/code&gt; if absent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;substr($s, 0, 5)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Extract part of a string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;str_replace(&amp;quot;a&amp;quot;, &amp;quot;b&amp;quot;, $s)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Replace all occurrences&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;explode(&amp;quot;,&amp;quot;, $s)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Split into an array&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;implode(&amp;quot;, &amp;quot;, $arr)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Join array elements&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sprintf(&amp;quot;%s has %d&amp;quot;, $a, $b)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Format into a string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;number_format(1234.5, 2)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Format a number with separators&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;str_pad($s, 10, &amp;quot;0&amp;quot;, STR_PAD_LEFT)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pad to a fixed width&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;str_repeat($s, 3)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Repeat a string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;htmlspecialchars($s)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Escape HTML before output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nl2br($s)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Convert newlines to &lt;code&gt;&amp;lt;br&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;quot;Hello $name&amp;quot;&lt;/code&gt; / &lt;code&gt;&amp;quot;Sum: {$a['b']}&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Interpolation, braces for complex expressions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;&amp;lt;&amp;lt;EOT ... EOT;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Heredoc (interpolates) and nowdoc &lt;code&gt;&amp;lt;&amp;lt;&amp;lt;'EOT'&lt;/code&gt; (does not)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="arrays"&gt;Arrays &lt;a class="headline-link" href="#arrays" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;PHP arrays are ordered maps and cover both lists and dictionaries.&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;$a = [1, 2, 3];&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Indexed array&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$a = [&amp;quot;k&amp;quot; =&amp;gt; &amp;quot;v&amp;quot;];&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Associative array&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$a[] = 4;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Append an element&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$a[&amp;quot;k&amp;quot;]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Access by key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$a[0][1]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Nested access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;count($a)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Number of elements&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;array_key_exists(&amp;quot;k&amp;quot;, $a)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt; if the key exists, even when null&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;in_array(4, $a)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt; if the value exists&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;array_search(4, $a)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Return the key of a value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[$x, $y] = $a;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Destructuring assignment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[&amp;quot;k&amp;quot; =&amp;gt; $v] = $a;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Destructure by key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[...$a, ...$b]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Spread into a new array&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;foreach ($a as $k =&amp;gt; $v)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Iterate keys and values&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="array-functions"&gt;Array Functions &lt;a class="headline-link" href="#array-functions" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Transform, filter, and sort arrays without writing loops.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&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;array_push($a, $v)&lt;/code&gt; / &lt;code&gt;array_pop($a)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add or remove at the end&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;array_unshift($a, $v)&lt;/code&gt; / &lt;code&gt;array_shift($a)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add or remove at the start&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;array_merge($a, $b)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Merge arrays, reindex numeric keys&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;array_keys($a)&lt;/code&gt; / &lt;code&gt;array_values($a)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Extract keys or values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;array_slice($a, 1, 3)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Extract a portion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;array_splice($a, 1, 2)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove or replace a portion in place&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;array_map(fn($x) =&amp;gt; $x * 2, $a)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Apply a callback to each element&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;array_filter($a, fn($x) =&amp;gt; $x &amp;gt; 2)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Keep elements passing a test&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;array_reduce($a, fn($c, $x) =&amp;gt; $c + $x, 0)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reduce to a single value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;array_column($rows, &amp;quot;name&amp;quot;, &amp;quot;id&amp;quot;)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pull one column, optionally keyed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;array_unique($a)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove duplicate values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;array_combine($keys, $vals)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build an array from two arrays&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;array_flip($a)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Swap keys and values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;array_sum($a)&lt;/code&gt; / &lt;code&gt;array_product($a)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sum or product of values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;min($a)&lt;/code&gt; / &lt;code&gt;max($a)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Smallest or largest value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;range(1, 10)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build a sequence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sort($a)&lt;/code&gt; / &lt;code&gt;rsort($a)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sort values ascending / descending&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;asort($a)&lt;/code&gt; / &lt;code&gt;ksort($a)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sort by value / key, keep keys&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;usort($a, fn($x, $y) =&amp;gt; $x &amp;lt;=&amp;gt; $y)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sort with a custom comparator&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;array_find($a, $fn)&lt;/code&gt;, &lt;code&gt;array_any($a, $fn)&lt;/code&gt;, &lt;code&gt;array_all($a, $fn)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search and test, PHP 8.4 and later&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;array_first($a)&lt;/code&gt; / &lt;code&gt;array_last($a)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;First or last value, PHP 8.5 and later&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="control-flow"&gt;Control Flow &lt;a class="headline-link" href="#control-flow" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Conditionals and loops. The alternative syntax with &lt;code&gt;endif&lt;/code&gt; and &lt;code&gt;endforeach&lt;/code&gt; reads better inside HTML templates.&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;if (...) { } elseif (...) { } else { }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Standard branching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;if (...): ... endif;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Alternative syntax for templates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;switch ($x) { case 1: ...; break; default: ...; }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Multi-branch on loose comparison&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;match($x) { 1 =&amp;gt; &amp;quot;a&amp;quot;, default =&amp;gt; &amp;quot;b&amp;quot; }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Strict comparison, returns a value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;for ($i = 0; $i &amp;lt; 10; $i++)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Counter loop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;foreach ($arr as $value)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Iterate values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;foreach ($arr as $key =&amp;gt; $value)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Iterate keys and values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;foreach ($arr as &amp;amp;$value)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Iterate by reference (unset after)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;while (cond)&lt;/code&gt; / &lt;code&gt;do { } while (cond);&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Conditional loops&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;break;&lt;/code&gt; / &lt;code&gt;break 2;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exit the loop, or two levels of loops&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;continue;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Skip to the next iteration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;return $x;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Return from a function&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="functions"&gt;Functions &lt;a class="headline-link" href="#functions" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Functions support default values, type declarations, named arguments, and variadics.&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;function name($a, $b) { }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Function declaration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;function name(int $a): string { }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Typed parameters and return type&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;function name($a = 10) { }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Default parameter value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;function name(...$args) { }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Variadic parameters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;name(b: 2, a: 1)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Named arguments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;function name(&amp;amp;$a) { }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pass by reference&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;function (): void { }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No return value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fn($x) =&amp;gt; $x * 2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Arrow function, captures scope automatically&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;function ($x) use ($y) { }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Closure with an explicit captured variable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$fn = strlen(...);&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;First-class callable syntax&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;call_user_func($fn, $arg)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Call a callable dynamically&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;function gen() { yield $x; }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generator function&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;static function () { }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Closure without &lt;code&gt;$this&lt;/code&gt; binding&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="classes-and-objects"&gt;Classes and Objects &lt;a class="headline-link" href="#classes-and-objects" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;PHP 8 adds constructor promotion, enums, readonly properties, and property hooks.&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;class User { }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Class declaration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;new User()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Instantiate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;public&lt;/code&gt;, &lt;code&gt;protected&lt;/code&gt;, &lt;code&gt;private&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Visibility modifiers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;public function __construct(private string $name) { }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Constructor property promotion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;public readonly int $id;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Write once, then immutable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;private(set) string $name;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Asymmetric visibility, PHP 8.4 and later&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;public string $full { get =&amp;gt; &amp;quot;$this-&amp;gt;a $this-&amp;gt;b&amp;quot;; }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Property hook, PHP 8.4 and later&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$this-&amp;gt;name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Access a property on the instance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;self::CONST&lt;/code&gt;, &lt;code&gt;static::method()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Class and late static binding access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;parent::__construct()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Call the parent constructor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;class Admin extends User { }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Inheritance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;interface Jsonable { }&lt;/code&gt; / &lt;code&gt;implements Jsonable&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Interfaces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;abstract class Base { }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Abstract class&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;trait Loggable { }&lt;/code&gt; / &lt;code&gt;use Loggable;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Trait reuse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;enum Status: string { case Active = 'active'; }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Backed enum&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Status::from('active')&lt;/code&gt;, &lt;code&gt;Status::tryFrom($x)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enum lookup, &lt;code&gt;tryFrom&lt;/code&gt; returns null&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$obj instanceof User&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Type check&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;User::class&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fully qualified class name as a string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;__get&lt;/code&gt;, &lt;code&gt;__set&lt;/code&gt;, &lt;code&gt;__call&lt;/code&gt;, &lt;code&gt;__toString&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Magic methods&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;namespace App\Models;&lt;/code&gt; / &lt;code&gt;use App\Models\User;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Namespaces and imports&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="superglobals-and-request-data"&gt;Superglobals and Request Data &lt;a class="headline-link" href="#superglobals-and-request-data" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Superglobals are available in every scope. Treat all of them as untrusted input.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&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&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Query string parameters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$_POST&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Form body parameters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$_REQUEST&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Merge of GET, POST, and cookies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$_SERVER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Request and server metadata&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$_SERVER['REQUEST_METHOD']&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;HTTP method&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$_SERVER['REMOTE_ADDR']&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Client IP address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$_FILES&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Uploaded file metadata&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$_COOKIE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cookies sent by the client&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$_SESSION&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Session data, after &lt;code&gt;session_start()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$_ENV&lt;/code&gt;, &lt;code&gt;getenv('NAME')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Environment variables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read and validate in one call&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;filter_var($email, FILTER_VALIDATE_EMAIL)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Validate a value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;htmlspecialchars($v, ENT_QUOTES)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Escape before printing to HTML&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;header('Location: /home'); exit;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Redirect and stop further execution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;http_response_code(404)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set the response status&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;password_hash($p, PASSWORD_DEFAULT)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hash a password&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;password_verify($p, $hash)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Verify a password against a hash&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="files-and-json"&gt;Files and JSON &lt;a class="headline-link" href="#files-and-json" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;File helpers for small payloads, plus JSON encoding and decoding.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&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;file_get_contents($path)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read a whole file into a string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;file_put_contents($path, $data)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Write a string to a file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;file($path, FILE_IGNORE_NEW_LINES)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read a file into an array of lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fopen($path, 'r')&lt;/code&gt;, &lt;code&gt;fgets($fh)&lt;/code&gt;, &lt;code&gt;fclose($fh)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Streamed reads for large files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fwrite($fh, $data)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Write to an open handle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;file_exists($p)&lt;/code&gt;, &lt;code&gt;is_file($p)&lt;/code&gt;, &lt;code&gt;is_dir($p)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Existence and type checks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;is_readable($p)&lt;/code&gt;, &lt;code&gt;is_writable($p)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Permission checks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;unlink($p)&lt;/code&gt;, &lt;code&gt;rename($a, $b)&lt;/code&gt;, &lt;code&gt;copy($a, $b)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete, move, copy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mkdir($p, 0755, true)&lt;/code&gt; / &lt;code&gt;rmdir($p)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create or remove directories&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;scandir($p)&lt;/code&gt; / &lt;code&gt;glob(&amp;quot;*.log&amp;quot;)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List directory entries or match a pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dirname($p)&lt;/code&gt;, &lt;code&gt;basename($p)&lt;/code&gt;, &lt;code&gt;pathinfo($p)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Split a path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;__DIR__&lt;/code&gt;, &lt;code&gt;__FILE__&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Directory and path of the current file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;realpath($p)&lt;/code&gt;, &lt;code&gt;filesize($p)&lt;/code&gt;, &lt;code&gt;filemtime($p)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Resolve path, size, modification time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;json_encode($data, JSON_PRETTY_PRINT)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Encode to JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;json_decode($s, true)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Decode to an associative array&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;json_encode($d, JSON_THROW_ON_ERROR)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Throw &lt;code&gt;JsonException&lt;/code&gt; on failure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;json_validate($s)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check validity without decoding, PHP 8.3 and later&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="dates-and-times"&gt;Dates and Times &lt;a class="headline-link" href="#dates-and-times" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;DateTimeImmutable&lt;/code&gt; is the safer default because arithmetic returns a new object instead of mutating the original.&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;time()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Current Unix timestamp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;date('Y-m-d H:i:s')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Format the current time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;date('Y-m-d', $ts)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Format a given timestamp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;strtotime('+1 day')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Parse a relative or absolute string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mktime($h, $m, $s, $mo, $d, $y)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build a timestamp from parts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;new DateTimeImmutable('2026-09-01')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create an immutable date object&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$d-&amp;gt;format('D, d M Y')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Format a date object&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$d-&amp;gt;modify('+2 weeks')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Return a shifted copy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$d-&amp;gt;add(new DateInterval('P1M'))&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add an interval&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$a-&amp;gt;diff($b)-&amp;gt;days&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Difference in days&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;new DateTimeZone('Europe/Berlin')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Time zone object&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;date_default_timezone_set('UTC')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set the script time zone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;checkdate($m, $d, $y)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Validate a calendar date&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Y m d H i s&lt;/code&gt;, &lt;code&gt;D M&lt;/code&gt;, &lt;code&gt;N&lt;/code&gt;, &lt;code&gt;U&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Common format characters&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="errors-and-exceptions"&gt;Errors and Exceptions &lt;a class="headline-link" href="#errors-and-exceptions" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Error&lt;/code&gt; covers engine failures and &lt;code&gt;Exception&lt;/code&gt; covers application failures; both implement &lt;code&gt;Throwable&lt;/code&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;try { } catch (Exception $e) { } finally { }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Handle and clean up&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;catch (TypeError | ValueError $e)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Catch multiple types&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;catch (Exception)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Catch without capturing the object&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;throw new RuntimeException(&amp;quot;msg&amp;quot;, 500);&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Throw an exception&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$e-&amp;gt;getMessage()&lt;/code&gt;, &lt;code&gt;$e-&amp;gt;getCode()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read the message and code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$e-&amp;gt;getFile()&lt;/code&gt;, &lt;code&gt;$e-&amp;gt;getLine()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Where the exception was thrown&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$e-&amp;gt;getTraceAsString()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stack trace as text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$e-&amp;gt;getPrevious()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Chained exception&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;class MyException extends Exception { }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Custom exception&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;error_reporting(E_ALL)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Report every error level&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ini_set('display_errors', '1')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show errors, development only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ini_set('log_errors', '1')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Write errors to the log&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;set_error_handler($fn)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Register a user-defined error handler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;set_exception_handler($fn)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Catch uncaught exceptions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;trigger_error(&amp;quot;msg&amp;quot;, E_USER_WARNING)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Raise a user-level error&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@$value&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Error suppression operator, avoid it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="regular-expressions"&gt;Regular Expressions &lt;a class="headline-link" href="#regular-expressions" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;PHP uses PCRE. Patterns need delimiters, usually &lt;code&gt;/&lt;/code&gt; or &lt;code&gt;#&lt;/code&gt;.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&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;preg_match('/^a/', $s, $m)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Match once, fill &lt;code&gt;$m&lt;/code&gt; with captures&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;preg_match_all('/\d+/', $s, $m)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Match every occurrence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;preg_replace('/\s+/', ' ', $s)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Replace matches&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;preg_replace_callback('/\d/', $fn, $s)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Replace using a callback&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;preg_split('/[\s,]+/', $s)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Split on a pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;preg_quote($s, '/')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Escape user input used in a pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;preg_grep('/^a/', $arr)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Filter array entries by pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/pattern/i&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Case-insensitive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/pattern/m&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Multiline, &lt;code&gt;^&lt;/code&gt; and &lt;code&gt;$&lt;/code&gt; match each line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/pattern/s&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Dot matches newlines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/pattern/u&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Treat pattern and subject as UTF-8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;(?&amp;lt;name&amp;gt;...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Named capture group, read as &lt;code&gt;$m['name']&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="php-cli"&gt;PHP CLI &lt;a class="headline-link" href="#php-cli" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;php&lt;/code&gt; binary runs scripts, checks syntax, and starts a development server without a web server in front of it.&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;php script.php&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run a script&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;php -v&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the PHP version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;php -m&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List compiled and loaded modules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;php -i&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print the full &lt;code&gt;phpinfo()&lt;/code&gt; output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;php --ini&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show which &lt;code&gt;php.ini&lt;/code&gt; files are loaded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;php -l script.php&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Syntax check without executing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;php -r 'echo PHP_VERSION;'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run inline code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;php -a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Interactive shell&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;php -S localhost:8000&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Built-in development server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;php -S localhost:8000 -t public&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Serve a specific document root&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;php -d memory_limit=512M script.php&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Override an &lt;code&gt;ini&lt;/code&gt; setting for one run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;php -c /path/to/php.ini script.php&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use a specific config file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;php --rf str_replace&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reflect on a function signature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;php --rc DateTimeImmutable&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reflect on a class&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;php --re json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reflect on an extension&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;php -n script.php&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run without loading any &lt;code&gt;php.ini&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="configuration-and-extensions"&gt;Configuration and Extensions &lt;a class="headline-link" href="#configuration-and-extensions" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Configuration paths depend on the SAPI and distribution. Use &lt;code&gt;php --ini&lt;/code&gt; for the CLI or &lt;code&gt;phpinfo()&lt;/code&gt; through the web server to confirm which files are active.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Path or 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;php --ini&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Locate the CLI configuration files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/etc/php/&amp;lt;version&amp;gt;/cli/php.ini&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CLI config on Debian and Ubuntu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/etc/php/&amp;lt;version&amp;gt;/fpm/php.ini&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PHP-FPM config on Debian and Ubuntu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/etc/php/&amp;lt;version&amp;gt;/apache2/php.ini&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Apache module config on Debian and Ubuntu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/etc/php.ini&lt;/code&gt;, &lt;code&gt;/etc/php.d/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Main config and snippets on Fedora and RHEL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/etc/php/&amp;lt;version&amp;gt;/mods-available/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Extension snippets on Debian and Ubuntu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;memory_limit&lt;/code&gt;, &lt;code&gt;max_execution_time&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Per-script resource limits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;upload_max_filesize&lt;/code&gt;, &lt;code&gt;post_max_size&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Upload limits, raise both together&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;display_errors&lt;/code&gt;, &lt;code&gt;error_log&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Error output and log destination&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;date.timezone&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Default time zone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;opcache.enable&lt;/code&gt;, &lt;code&gt;opcache.memory_consumption&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bytecode cache settings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo apt install php-gd php-curl php-mbstring&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install default-version extensions on Ubuntu or Debian&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo dnf install php-gd php-curl php-mbstring&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install extensions on Fedora or RHEL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;php -m | grep -E 'curl|gd|mbstring'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Confirm extensions are loaded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo phpenmod curl&lt;/code&gt; / &lt;code&gt;sudo phpdismod curl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enable or disable an extension on Debian or Ubuntu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo update-alternatives --config php&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Select an installed CLI version on Debian or Ubuntu&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="php-fpm"&gt;PHP-FPM &lt;a class="headline-link" href="#php-fpm" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;PHP-FPM is the process manager that Nginx and Apache hand PHP requests to. The versioned examples below use PHP 8.5 on Ubuntu 26.04; Debian 13 uses 8.4, while Fedora and RHEL use unversioned &lt;code&gt;php-fpm&lt;/code&gt; names. Reload or restart FPM after config or extension changes.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command or directive&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 status php8.5-fpm&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check the Ubuntu 26.04 service state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl status php-fpm&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check the Fedora or RHEL service state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl restart php8.5-fpm&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Restart the versioned service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl reload php8.5-fpm&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Gracefully reload FPM workers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo php-fpm8.5 -t&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Test Ubuntu 26.04 configuration before reloading&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/etc/php/8.5/fpm/pool.d/www.conf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ubuntu 26.04 default pool configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;listen = /run/php/php8.5-fpm.sock&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unix socket the web server connects to&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;user&lt;/code&gt; / &lt;code&gt;group&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;System account the workers run as&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pm = dynamic&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Process manager mode: &lt;code&gt;static&lt;/code&gt;, &lt;code&gt;dynamic&lt;/code&gt;, or &lt;code&gt;ondemand&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pm.max_children&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hard cap on worker processes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pm.start_servers&lt;/code&gt;, &lt;code&gt;pm.min_spare_servers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Warm pool sizing for &lt;code&gt;dynamic&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pm.max_requests&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Recycle a worker after N requests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;php_admin_value[memory_limit] = 256M&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Override an &lt;code&gt;ini&lt;/code&gt; value per pool&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;slowlog&lt;/code&gt;, &lt;code&gt;request_slowlog_timeout&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Log requests that run too long&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo journalctl -u php8.5-fpm -f&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Follow the versioned service log&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="composer"&gt;Composer &lt;a class="headline-link" href="#composer" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Composer manages dependencies and the autoloader for almost every modern PHP project.&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;composer init&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a &lt;code&gt;composer.json&lt;/code&gt; interactively&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;composer require vendor/package&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add a dependency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;composer require --dev phpunit/phpunit&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;composer install&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install from &lt;code&gt;composer.lock&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;composer update&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Update dependencies and the lock file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;composer update vendor/package&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Update a single package&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;composer remove vendor/package&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;composer install --no-dev --optimize-autoloader&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Production install&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;composer dump-autoload -o&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Regenerate an optimized autoloader&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;composer show&lt;/code&gt; / &lt;code&gt;composer show -t&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List packages, or show the dependency tree&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;composer outdated&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List packages with newer versions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;composer why vendor/package&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Explain why a package is installed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;composer audit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check dependencies for known vulnerabilities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;composer create-project vendor/skeleton app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start a project from a skeleton&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;require 'vendor/autoload.php';&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Load the autoloader in your entry script&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 to install PHP, check the running version, and debug errors on a server.&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-install-php-on-ubuntu-26-04/"&gt;How to Install PHP on Ubuntu 26.04&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Install PHP 8.5 with Apache or Nginx and PHP-FPM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-check-php-version/"&gt;How to Check the PHP Version&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Find the CLI and web server PHP versions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/php-error-reporting/"&gt;PHP Error Reporting&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Show, log, and control PHP errors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-install-lamp-stack-on-ubuntu-26-04/"&gt;How to Install a LAMP Stack on Ubuntu 26.04&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Apache, MySQL, and PHP on one server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/cheatsheet/mysql-mariadb/"&gt;MySQL and MariaDB Cheatsheet&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Database commands for the data layer behind PHP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;</content:encoded></item><item><title>What Is .bashrc? When It Runs and What to Put in It</title><link>https://linuxize.com/post/what-is-bashrc/</link><pubDate>Mon, 31 Aug 2026 14:35:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/what-is-bashrc/</guid><category>bash</category><description>~/.bashrc controls aliases, functions, prompts, and other interactive Bash settings. See when it runs, what belongs inside it, and how to reload changes.</description><content:encoded>&lt;p&gt;You define an alias in your terminal, use it happily for an hour, then open a new tab and it is gone. Shell settings typed at the prompt live only as long as that shell does. To make them permanent, they have to go in a file that Bash reads every time it starts a new session. On most Linux systems that file is &lt;code&gt;~/.bashrc&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This guide covers where &lt;code&gt;.bashrc&lt;/code&gt; lives, when Bash reads it, what belongs inside it, and how to apply your changes without opening a new terminal.&lt;/p&gt;
&lt;h2 id="what-bashrc-does"&gt;What .bashrc Does &lt;a class="headline-link" href="#what-bashrc-does" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;.bashrc&lt;/code&gt; is a per-user Bash startup script, not a generic Linux configuration file. When Bash loads it, each command runs in the current shell. That is why aliases, functions, prompt settings, and shell options become part of the session without a separate import step. Other shells use their own files, such as &lt;code&gt;~/.zshrc&lt;/code&gt; for zsh.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;rc&lt;/code&gt; suffix comes from RUNCOM, an early CTSS program that executed commands stored in a file. The name survives in Unix configuration files such as &lt;code&gt;.vimrc&lt;/code&gt;, &lt;code&gt;.screenrc&lt;/code&gt;, and &lt;code&gt;.inputrc&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="where-the-bashrc-file-is-located"&gt;Where the .bashrc File Is Located &lt;a class="headline-link" href="#where-the-bashrc-file-is-located" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;.bashrc&lt;/code&gt; sits in your home directory. The leading dot makes it a hidden file, so a plain &lt;code&gt;ls&lt;/code&gt; will not show 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;ls -la ~/.bashrc&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;-rw-r--r-- 1 dejan dejan 3771 Aug 14 09:12 /home/dejan/.bashrc&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Every user account has its own copy, and editing yours has no effect on anyone else on the system. The file is plain text, so any editor works:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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;If the file does not exist, nothing is broken. Bash simply skips it. Most distributions ship a template in &lt;code&gt;/etc/skel/&lt;/code&gt; that is copied into each new home directory, so you can restore the default 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;cp /etc/skel/.bashrc ~/.bashrc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You can also &lt;a href="https://linuxize.com/post/create-a-file-in-linux/"&gt;create the file&lt;/a&gt;
from scratch and start with an empty one.&lt;/p&gt;
&lt;h2 id="when-bash-reads-bashrc"&gt;When Bash Reads .bashrc &lt;a class="headline-link" href="#when-bash-reads-bashrc" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Bash reads &lt;code&gt;~/.bashrc&lt;/code&gt; when it starts an interactive shell that is not a login shell. In everyday terms, that means opening a new terminal window or tab in your desktop environment, or typing &lt;code&gt;bash&lt;/code&gt; inside a shell you already have open.&lt;/p&gt;
&lt;p&gt;Two common cases do not fit that description. Logging in over &lt;a href="https://linuxize.com/post/ssh-command-in-linux/"&gt;SSH&lt;/a&gt;
or at a console starts a login shell. Bash reads &lt;code&gt;/etc/profile&lt;/code&gt;, then the first readable file it finds from &lt;code&gt;~/.bash_profile&lt;/code&gt;, &lt;code&gt;~/.bash_login&lt;/code&gt;, and &lt;code&gt;~/.profile&lt;/code&gt;. Running a local script starts a non-interactive shell, which normally reads none of those files. If &lt;code&gt;BASH_ENV&lt;/code&gt; is set, Bash reads the file named by that variable before running the script.&lt;/p&gt;
&lt;p&gt;Because most people want the same interactive settings in both login and non-login shells, the usual arrangement is to keep those settings in &lt;code&gt;~/.bashrc&lt;/code&gt; and have the login file source it. For the full startup order and the reasoning behind that setup, see &lt;a href="https://linuxize.com/post/bashrc-vs-bash-profile/"&gt;.bashrc vs .bash_profile&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="the-guard-at-the-top-of-the-file"&gt;The Guard at the Top of the File &lt;a class="headline-link" href="#the-guard-at-the-top-of-the-file" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Open the default &lt;code&gt;~/.bashrc&lt;/code&gt; on Debian or Ubuntu and the first real line is 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="c1"&gt;# If not running interactively, don&amp;#39;t do anything&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;$-&lt;/span&gt; in
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; *i*&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 class="k"&gt;return&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;$-&lt;/code&gt; variable holds the current shell option flags, and it contains &lt;code&gt;i&lt;/code&gt; only in an interactive shell. If the &lt;code&gt;i&lt;/code&gt; flag is missing, &lt;code&gt;return&lt;/code&gt; stops reading the file right there.&lt;/p&gt;
&lt;p&gt;That guard exists because of one specific Bash behavior: when Bash detects that its standard input is connected to a network connection, as happens with &lt;code&gt;ssh user@host 'command'&lt;/code&gt;, it reads &lt;code&gt;~/.bashrc&lt;/code&gt; even though the shell is not interactive. Without the guard, everything below it could run during remote commands and file transfers that start Bash on the server.&lt;/p&gt;
&lt;p&gt;Keep the guard where it is, and add interactive settings below it. A setting required by a non-interactive remote command is an exception, but anything placed above the guard must stay silent.&lt;/p&gt;
&lt;h2 id="what-to-put-in-bashrc"&gt;What to Put in .bashrc &lt;a class="headline-link" href="#what-to-put-in-bashrc" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Anything that shapes how an interactive shell behaves belongs here. The most common additions follow.&lt;/p&gt;
&lt;h3 id="aliases"&gt;Aliases &lt;a class="headline-link" href="#aliases" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://linuxize.com/post/how-to-create-bash-aliases/"&gt;Aliases&lt;/a&gt;
are short names for longer 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;~/.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;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 -alF&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 class="nb"&gt;alias&lt;/span&gt; ..&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cd ..&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id="functions"&gt;Functions &lt;a class="headline-link" href="#functions" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When a shortcut needs arguments or more than one command, use a &lt;a href="https://linuxize.com/post/bash-functions/"&gt;function&lt;/a&gt;
instead of an alias:&lt;/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;mkcd &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; mkdir -p &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 class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&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="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;Running &lt;code&gt;mkcd projects/api&lt;/code&gt; now creates the directory and moves into it.&lt;/p&gt;
&lt;h3 id="interactive-path-additions"&gt;Interactive PATH Additions &lt;a class="headline-link" href="#interactive-path-additions" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To make Bash find executables in a directory of your own at the interactive prompt, &lt;a href="https://linuxize.com/post/how-to-add-directory-to-path-in-linux/"&gt;add it to &lt;code&gt;$PATH&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="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;/.local/bin:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&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;Putting &lt;code&gt;$PATH&lt;/code&gt; at the end preserves the existing directories. Placing your directory first means your version of a command wins over a system one with the same name.&lt;/p&gt;
&lt;p&gt;This change applies to shells that read &lt;code&gt;.bashrc&lt;/code&gt;. If the path must also reach GUI programs or other shells, set it in &lt;code&gt;~/.profile&lt;/code&gt; or &lt;code&gt;~/.bash_profile&lt;/code&gt; instead.&lt;/p&gt;
&lt;h3 id="environment-variables"&gt;Environment Variables &lt;a class="headline-link" href="#environment-variables" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://linuxize.com/post/how-to-set-and-list-environment-variables-in-linux/"&gt;Environment variables&lt;/a&gt;
needed by programs you start from interactive Bash can go here. Export them so child processes inherit their values:&lt;/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;EDITOR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nano
&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;LESS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;-R&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 variables that must exist across the entire login session, including applications not launched from a terminal, use a login or desktop environment file instead.&lt;/p&gt;
&lt;p&gt;Do not put API keys, tokens, or passwords in &lt;code&gt;.bashrc&lt;/code&gt;. The file often ends up in a dotfiles repository. Keep secrets in a separate file that is excluded from version control and protected with &lt;code&gt;chmod 600&lt;/code&gt;, or use a dedicated secret manager.&lt;/p&gt;
&lt;h3 id="the-shell-prompt"&gt;The Shell Prompt &lt;a class="headline-link" href="#the-shell-prompt" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;PS1&lt;/code&gt; defines the prompt string. The following example shows the username, host, and current directory in color:&lt;/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="nv"&gt;PS1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;\[\e[32m\]\u@\h\[\e[0m\]:\[\e[34m\]\w\[\e[0m\]\$ &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;The &lt;code&gt;\[&lt;/code&gt; and &lt;code&gt;\]&lt;/code&gt; markers tell Bash that the enclosed escape sequences take up no screen width. Leaving them out makes long command lines wrap incorrectly.&lt;/p&gt;
&lt;h3 id="shell-options"&gt;Shell Options &lt;a class="headline-link" href="#shell-options" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;shopt&lt;/code&gt; toggles Bash behaviors that are off by default:&lt;/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;shopt&lt;/span&gt; -s autocd &lt;span class="c1"&gt;# type a directory name to cd into it&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;shopt&lt;/span&gt; -s cdspell &lt;span class="c1"&gt;# fix minor typos in cd arguments&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;shopt&lt;/span&gt; -s globstar &lt;span class="c1"&gt;# ** matches files across subdirectories&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;shopt&lt;/span&gt; -s histappend &lt;span class="c1"&gt;# append to the history file instead of overwriting&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;History settings such as &lt;code&gt;HISTSIZE&lt;/code&gt; and &lt;code&gt;HISTCONTROL&lt;/code&gt; also live in this file. See the &lt;a href="https://linuxize.com/post/history-command-in-linux/"&gt;history command guide&lt;/a&gt;
for working with the entries themselves.&lt;/p&gt;
&lt;h2 id="apply-changes-without-restarting-the-terminal"&gt;Apply Changes Without Restarting the Terminal &lt;a class="headline-link" href="#apply-changes-without-restarting-the-terminal" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Bash reads &lt;code&gt;~/.bashrc&lt;/code&gt; at startup, so an edit has no effect on shells that are already running. Before loading an edited file, check its 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;bash -n ~/.bashrc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;No output means Bash found no syntax errors. You can then &lt;a href="https://linuxize.com/post/bash-source-command/"&gt;source&lt;/a&gt;
the file instead of closing 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;&lt;span class="nb"&gt;source&lt;/span&gt; ~/.bashrc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The dot command is the POSIX spelling of &lt;code&gt;source&lt;/code&gt; and does the same thing in Bash:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;. ~/.bashrc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Either command runs the file in your current shell, so aliases, functions, and variables become available immediately. Sourcing does not undo anything: if you deleted an alias from the file, it stays defined in the current session until you run &lt;code&gt;unalias&lt;/code&gt; or open a new terminal.&lt;/p&gt;
&lt;h2 id="split-bashrc-into-separate-files"&gt;Split .bashrc Into Separate Files &lt;a class="headline-link" href="#split-bashrc-into-separate-files" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A &lt;code&gt;.bashrc&lt;/code&gt; that has grown past a couple of hundred lines is easier to manage in pieces. Debian and Ubuntu already use this pattern for aliases, and their default file includes:&lt;/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="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -f ~/.bash_aliases &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; . ~/.bash_aliases
&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;For a more general split, create a directory for the extra 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;mkdir -p ~/.bashrc.d&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Then add a loop to &lt;code&gt;~/.bashrc&lt;/code&gt; that sources every readable shell file in the 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="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="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -d ~/.bashrc.d &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;for&lt;/span&gt; rc in ~/.bashrc.d/*.sh&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; -r &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$rc&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]&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;$rc&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;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;unset&lt;/span&gt; rc
&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;Files are sourced in alphabetical order, so a &lt;code&gt;10-path.sh&lt;/code&gt; runs before a &lt;code&gt;20-aliases.sh&lt;/code&gt;. This keeps work-specific settings in one file that you can drop in or remove without editing &lt;code&gt;.bashrc&lt;/code&gt; itself. A &lt;code&gt;bash -n ~/.bashrc&lt;/code&gt; check does not read the sourced files, so check each of them separately after an edit.&lt;/p&gt;
&lt;p&gt;Fedora and RHEL already source files from &lt;code&gt;~/.bashrc.d/&lt;/code&gt; in their default configuration, so check your existing file before adding another loop.&lt;/p&gt;
&lt;h2 id="what-not-to-put-in-bashrc"&gt;What Not to Put in .bashrc &lt;a class="headline-link" href="#what-not-to-put-in-bashrc" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Commands that print output are the main thing to avoid. A &lt;code&gt;neofetch&lt;/code&gt; call or an &lt;code&gt;echo &amp;quot;Welcome back&amp;quot;&lt;/code&gt; line placed above the interactivity guard looks harmless in a terminal, but it also runs during &lt;code&gt;scp&lt;/code&gt;, &lt;code&gt;rsync&lt;/code&gt;, and &lt;code&gt;ssh host 'command'&lt;/code&gt; sessions. The extra text can corrupt the protocol stream and make those tools fail. Keep printed output below the guard, or move a login banner to the login profile.&lt;/p&gt;
&lt;p&gt;Long-running commands are worth avoiding too. Everything in &lt;code&gt;.bashrc&lt;/code&gt; runs before you get a prompt, so a network call or a version-manager initialization that takes half a second adds that delay to every terminal you open.&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;Setting&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Alias&lt;/td&gt;
&lt;td&gt;&lt;code&gt;alias ll='ls -alF'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Short name for a longer command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Function&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mkcd () { mkdir -p &amp;quot;$1&amp;quot; &amp;amp;&amp;amp; cd &amp;quot;$1&amp;quot;; }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Shortcut that takes arguments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interactive PATH&lt;/td&gt;
&lt;td&gt;&lt;code&gt;export PATH=&amp;quot;$HOME/.local/bin:$PATH&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add an executable directory to interactive Bash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Environment variable&lt;/td&gt;
&lt;td&gt;&lt;code&gt;export EDITOR=nano&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Setting inherited by programs launched from Bash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prompt&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PS1='\u@\h:\w\$ '&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Format of the shell prompt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shell option&lt;/td&gt;
&lt;td&gt;&lt;code&gt;shopt -s autocd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Toggle a Bash behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source a file&lt;/td&gt;
&lt;td&gt;&lt;code&gt;. ~/.bash_aliases&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Load settings from another file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Syntax check&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bash -n ~/.bashrc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check the file without running it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reload&lt;/td&gt;
&lt;td&gt;&lt;code&gt;source ~/.bashrc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Apply edits to the current shell&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;Changes do not take effect&lt;/strong&gt;&lt;br&gt;
Bash reads &lt;code&gt;~/.bashrc&lt;/code&gt; only when a shell starts. Run &lt;code&gt;bash -n ~/.bashrc&lt;/code&gt;, then &lt;code&gt;source ~/.bashrc&lt;/code&gt; in the current terminal, or open a new one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Settings work in a new terminal but not over SSH&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;ssh user@host&lt;/code&gt; opens a login shell, which reads the first available file from &lt;code&gt;~/.bash_profile&lt;/code&gt;, &lt;code&gt;~/.bash_login&lt;/code&gt;, and &lt;code&gt;~/.profile&lt;/code&gt; rather than &lt;code&gt;~/.bashrc&lt;/code&gt;. Debian and Ubuntu ship a &lt;code&gt;~/.profile&lt;/code&gt; that sources &lt;code&gt;~/.bashrc&lt;/code&gt; already, so the default setup works. On other systems, or after replacing the login file, add &lt;code&gt;. ~/.bashrc&lt;/code&gt; to it so both shell types load the same interactive configuration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;scp or rsync fails after editing .bashrc&lt;/strong&gt;&lt;br&gt;
Something in the file may be printing output during the remote session. Move any &lt;code&gt;echo&lt;/code&gt;, &lt;code&gt;neofetch&lt;/code&gt;, or banner command below the interactivity guard, then test with &lt;code&gt;ssh user@host 'true'&lt;/code&gt;, which should print nothing at all.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A syntax error appears in every new terminal&lt;/strong&gt;&lt;br&gt;
Bash reports the offending line number. Open a shell that skips the file with &lt;code&gt;bash --norc&lt;/code&gt;, fix the line, then run &lt;code&gt;bash -n ~/.bashrc&lt;/code&gt; before reloading. A missing &lt;code&gt;fi&lt;/code&gt; or an unclosed quote is the usual cause.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The file was deleted or emptied&lt;/strong&gt;&lt;br&gt;
Copy the distribution default back with &lt;code&gt;cp /etc/skel/.bashrc ~/.bashrc&lt;/code&gt; and reload it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A command is found in one terminal but not another&lt;/strong&gt;&lt;br&gt;
The two shells may read different startup files, or the &lt;code&gt;$PATH&lt;/code&gt; addition may be below an early &lt;code&gt;return&lt;/code&gt; or inside a conditional that does not match. Run &lt;code&gt;echo &amp;quot;$PATH&amp;quot;&lt;/code&gt; in both shells to compare.&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;Does .bashrc run when I execute a script?&lt;/strong&gt;&lt;br&gt;
Normally, no. A local script runs in a non-interactive shell, which does not read &lt;code&gt;~/.bashrc&lt;/code&gt; unless the script sources it or &lt;code&gt;BASH_ENV&lt;/code&gt; points to it. Bash may also read &lt;code&gt;.bashrc&lt;/code&gt; when a remote shell daemon starts a non-interactive command, but the interactivity guard usually stops the file immediately.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I use zsh. Where do these settings go?&lt;/strong&gt;&lt;br&gt;
Use &lt;code&gt;~/.zshrc&lt;/code&gt;, which serves the same role for zsh. Aliases, functions, and &lt;code&gt;PATH&lt;/code&gt; changes carry over unchanged, but prompt escapes and some &lt;code&gt;shopt&lt;/code&gt; options do not.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Should I put .bashrc in version control?&lt;/strong&gt;&lt;br&gt;
Yes, as long as it holds no secrets. A dotfiles repository makes it easy to set up a new machine. Keep tokens and passwords in a separate file that is listed in &lt;code&gt;.gitignore&lt;/code&gt; and sourced from &lt;code&gt;.bashrc&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why is my .bashrc not read on macOS?&lt;/strong&gt;&lt;br&gt;
Terminal and iTerm2 normally open a login shell. If you configured them to use Bash, Bash reads the first available login file instead of &lt;code&gt;~/.bashrc&lt;/code&gt;. Current macOS releases use zsh by default, and its interactive configuration belongs in &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;Treat &lt;code&gt;~/.bashrc&lt;/code&gt; as code that runs every time you open an interactive Bash shell. Keep it fast, keep secrets out of it, and check each edit with &lt;code&gt;bash -n ~/.bashrc&lt;/code&gt; before loading the change.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/what-is-bashrc/featured_hu_dc8aabe3f4f9449f.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>Regex Cheatsheet</title><link>https://linuxize.com/cheatsheet/regex/</link><pubDate>Fri, 28 Aug 2026 17:40:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/cheatsheet/regex/</guid><description>Regex syntax and examples for Linux tools, including metacharacters, quantifiers, character classes, anchors, groups, BRE, ERE, and PCRE</description><content:encoded>&lt;div class="card"&gt;
&lt;h3 id="metacharacters"&gt;Metacharacters &lt;a class="headline-link" href="#metacharacters" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Characters that mean something other than themselves.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&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;.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Any single character except a newline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Zero or more of the item before it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[...]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Any one character from the set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[^...]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Any one character not in the set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;^&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start of the line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;End of the line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Escape a character or begin a special sequence, depending on the regex flavor&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Most other characters match themselves. Line-based tools such as grep, sed, and awk read one line at a time, so &lt;code&gt;.&lt;/code&gt; never matches the line ending.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="anchors-and-word-boundaries"&gt;Anchors and Word Boundaries &lt;a class="headline-link" href="#anchors-and-word-boundaries" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Tie a pattern to a position instead of a character.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&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;^error&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Line starts with &lt;code&gt;error&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;done$&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Line ends with &lt;code&gt;done&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;^exact$&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Match the whole line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;^$&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Match an empty line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\&amp;lt;word&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start of a word (GNU tools and Vim)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;word\&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;End of a word&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\bword\b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Word boundary on both sides (GNU and PCRE)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\B&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Any position that is not a word boundary&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Anchors match a position of zero width, so they never consume a character. In awk, &lt;code&gt;\b&lt;/code&gt; means a backspace rather than a word boundary; gawk provides &lt;code&gt;\y&lt;/code&gt; instead.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="bracket-expressions"&gt;Bracket Expressions &lt;a class="headline-link" href="#bracket-expressions" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Match one character out of a set you define.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&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;[abc]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;b&lt;/code&gt;, or &lt;code&gt;c&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[^abc]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Any character except &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;b&lt;/code&gt;, or &lt;code&gt;c&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[a-z]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One lowercase letter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[0-9a-fA-F]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One hexadecimal digit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[]abc]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Include a literal &lt;code&gt;]&lt;/code&gt; by putting it first&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[abc-]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Include a literal &lt;code&gt;-&lt;/code&gt; by putting it last&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[a^]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;^&lt;/code&gt; is literal when it is not first&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Most metacharacters lose their meaning inside brackets, so &lt;code&gt;[.*]&lt;/code&gt; matches a dot or an asterisk. Ranges follow the current locale, so use &lt;code&gt;LC_ALL=C&lt;/code&gt; or a POSIX class when you need predictable results.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="posix-character-classes"&gt;POSIX Character Classes &lt;a class="headline-link" href="#posix-character-classes" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Portable, locale-aware sets that go inside a bracket expression.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Class&lt;/th&gt;
&lt;th&gt;Matches&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[[:digit:]]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Digits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[[:alpha:]]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Letters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[[:alnum:]]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Letters and digits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[[:lower:]]&lt;/code&gt; &lt;code&gt;[[:upper:]]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lowercase and uppercase letters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[[:space:]]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Whitespace, including tabs and newlines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[[:blank:]]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Spaces and tabs only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[[:punct:]]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Punctuation characters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[[:xdigit:]]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hexadecimal digits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[[:print:]]&lt;/code&gt; &lt;code&gt;[[:graph:]]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Printable, and printable except space&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[[:cntrl:]]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Control characters&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The double brackets are not a typo. The class itself is &lt;code&gt;[:digit:]&lt;/code&gt;, and the outer brackets are the bracket expression that holds it, so &lt;code&gt;[[:digit:]_]&lt;/code&gt; matches a digit or an underscore.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="quantifiers"&gt;Quantifiers &lt;a class="headline-link" href="#quantifiers" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Say how many times the preceding item repeats.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&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;a*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Zero or more &lt;code&gt;a&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;a\+&lt;/code&gt; / &lt;code&gt;a+&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One or more, BRE and ERE forms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;a\?&lt;/code&gt; / &lt;code&gt;a?&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Zero or one&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;a\{3\}&lt;/code&gt; / &lt;code&gt;a{3}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exactly three&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;a{3,}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Three or more&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;a{,3}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Up to three (GNU extension for &lt;code&gt;{0,3}&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;a{2,4}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Between two and four&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Any run of characters, including none&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Quantifiers are greedy and take the longest match available. In a basic regular expression, a &lt;code&gt;*&lt;/code&gt; at the very start of the pattern is a literal asterisk because there is nothing for it to repeat.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="groups-alternation-and-backreferences"&gt;Groups, Alternation, and Backreferences &lt;a class="headline-link" href="#groups-alternation-and-backreferences" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Treat several characters as one unit and reuse what they matched.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&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;(ab)+&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ERE group repeated one or more times&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\(ab\)\+&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The same group in BRE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cat|dog&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ERE alternation, either side matches&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cat\|dog&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The same alternation in GNU BRE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;^(a|b)c$&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Alternation limited to the group&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Whatever group 1 matched&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\(.\)\1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Any character repeated twice&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Groups are numbered from left to right by their opening parenthesis. Backreferences are part of POSIX BRE, and GNU grep and GNU sed also accept them in extended patterns. POSIX ERE and awk do not support backreferences. In a &lt;code&gt;sed&lt;/code&gt; replacement, &lt;code&gt;\1&lt;/code&gt; inserts group 1 and &lt;code&gt;&amp;amp;&lt;/code&gt; inserts the whole match.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="escaping-special-characters"&gt;Escaping Special Characters &lt;a class="headline-link" href="#escaping-special-characters" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Match a metacharacter as an ordinary character.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&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;\.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A literal dot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\\&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A literal backslash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&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;/td&gt;
&lt;td&gt;The literal symbol&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[.]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A dot, escaped by a bracket expression instead&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep -F 'a.b'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Treat the whole pattern as a fixed string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\Q...\E&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Quote a run of characters in PCRE&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Quote patterns with single quotes so the shell passes the backslashes through untouched. Inside double quotes, the shell expands &lt;code&gt;$&lt;/code&gt; and consumes some backslashes before the tool ever sees the pattern.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="shorthand-classes"&gt;Shorthand Classes &lt;a class="headline-link" href="#shorthand-classes" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Short names for common sets, provided as extensions rather than by POSIX.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Matches and Availability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\w&lt;/code&gt; &lt;code&gt;\W&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Word and non-word characters in GNU grep, GNU sed, gawk, PCRE, and Vim; the exact character set varies by engine and locale&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\s&lt;/code&gt; &lt;code&gt;\S&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Whitespace and non-whitespace in GNU grep, GNU sed, gawk, PCRE, and Vim&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\d&lt;/code&gt; &lt;code&gt;\D&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Digits and non-digits in PCRE and Vim; not defined by POSIX BRE or ERE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\h&lt;/code&gt; &lt;code&gt;\v&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Horizontal and vertical whitespace in PCRE; Vim assigns different meanings to both sequences&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Do not use &lt;code&gt;\d&lt;/code&gt; with grep&amp;rsquo;s basic or extended syntax. GNU grep documents an escaped ordinary character such as &lt;code&gt;\d&lt;/code&gt; as unspecified, and other grep implementations may interpret it differently. Use &lt;code&gt;[[:digit:]]&lt;/code&gt;, &lt;code&gt;[0-9]&lt;/code&gt;, or &lt;code&gt;grep -P&lt;/code&gt; instead.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="pcre-extras"&gt;PCRE Extras &lt;a class="headline-link" href="#pcre-extras" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Available with &lt;code&gt;grep -P&lt;/code&gt; and other Perl-compatible engines.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&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;.*?&lt;/code&gt; &lt;code&gt;+?&lt;/code&gt; &lt;code&gt;??&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lazy quantifiers that take the shortest match&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;(?:...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Group without capturing it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;(?&amp;lt;name&amp;gt;...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Named capture group&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;(?=...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lookahead, text must follow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;(?!...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Negative lookahead&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;(?&amp;lt;=...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lookbehind, text must precede&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;(?&amp;lt;!...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Negative lookbehind&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;Case-insensitive from this point on&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\K&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Drop everything matched so far from the result&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Lookarounds check their surroundings without adding them to the match, which pairs well with &lt;code&gt;grep -oP&lt;/code&gt;. None of this works in sed or awk, and &lt;code&gt;grep -P&lt;/code&gt; needs a build with PCRE support.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="bre-ere-and-pcre"&gt;BRE, ERE, and PCRE &lt;a class="headline-link" href="#bre-ere-and-pcre" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The same idea written three ways.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Syntax by Flavor&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Grouping&lt;/td&gt;
&lt;td&gt;BRE &lt;code&gt;\(ab\)&lt;/code&gt;; ERE and PCRE &lt;code&gt;(ab)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alternation&lt;/td&gt;
&lt;td&gt;GNU BRE &lt;code&gt;a\|b&lt;/code&gt;; ERE and PCRE &lt;code&gt;a|b&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One or more&lt;/td&gt;
&lt;td&gt;GNU BRE &lt;code&gt;a\+&lt;/code&gt;; ERE and PCRE &lt;code&gt;a+&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optional&lt;/td&gt;
&lt;td&gt;GNU BRE &lt;code&gt;a\?&lt;/code&gt;; ERE and PCRE &lt;code&gt;a?&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interval&lt;/td&gt;
&lt;td&gt;BRE &lt;code&gt;a\{2,4\}&lt;/code&gt;; ERE and PCRE &lt;code&gt;a{2,4}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backreference&lt;/td&gt;
&lt;td&gt;BRE and PCRE &lt;code&gt;\1&lt;/code&gt;; GNU grep and GNU sed also accept &lt;code&gt;\1&lt;/code&gt; in ERE, but POSIX ERE and awk do not&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\d&lt;/code&gt; and lookaround&lt;/td&gt;
&lt;td&gt;PCRE supports both; BRE and ERE do not&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Default in&lt;/td&gt;
&lt;td&gt;BRE: &lt;code&gt;grep&lt;/code&gt;, &lt;code&gt;sed&lt;/code&gt;; ERE: &lt;code&gt;grep -E&lt;/code&gt;, &lt;code&gt;sed -E&lt;/code&gt;, awk; PCRE: &lt;code&gt;grep -P&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;In GNU grep, BRE and ERE provide the same pattern-matching functionality with different notation. The characters &lt;code&gt;?&lt;/code&gt;, &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;()&lt;/code&gt;, &lt;code&gt;{}&lt;/code&gt;, and &lt;code&gt;|&lt;/code&gt; are special without backslashes in ERE and with backslashes in GNU BRE. This equivalence does not extend to POSIX ERE backreferences or PCRE-only features.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="regex-in-grep"&gt;Regex in grep &lt;a class="headline-link" href="#regex-in-grep" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Pick the flavor with a flag.&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-grep-command-to-search-files-in-linux/"&gt;&lt;code&gt;grep 'pattern' file&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Basic regular expression, the default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/regular-expressions-in-grep/"&gt;&lt;code&gt;grep -E 'pattern' file&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Extended regular expression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep -P 'pattern' file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Perl-compatible regular expression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep -F 'pattern' file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No regex at all, fixed string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep -o 'pattern' file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print only the matched text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep -w 'pattern' file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Require word boundaries around the match&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep -i 'pattern' file&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;grep -v 'pattern' file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print the lines that do not match&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Use &lt;code&gt;-o&lt;/code&gt; while building a pattern to see exactly what it captures. GNU grep 3.8 and later print a warning for &lt;code&gt;egrep&lt;/code&gt; and &lt;code&gt;fgrep&lt;/code&gt;, so write &lt;code&gt;grep -E&lt;/code&gt; and &lt;code&gt;grep -F&lt;/code&gt; instead.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="regex-in-sed"&gt;Regex in sed &lt;a class="headline-link" href="#regex-in-sed" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Patterns select lines, and the same syntax drives substitutions.&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-sed-to-find-and-replace-string-in-files/"&gt;&lt;code&gt;sed 's/old/new/' file&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Substitute using a basic regular expression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sed -E 's/[0-9]+/N/g' file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Extended syntax, with &lt;code&gt;-r&lt;/code&gt; as a synonym&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sed -n '/error/p' file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print only matching lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/sed-delete-lines/"&gt;&lt;code&gt;sed '/^$/d' file&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Delete every empty line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sed 's/\(a\)\(b\)/\2\1/' file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Swap two captured groups&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sed 's/word/[&amp;amp;]/' file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Wrap the whole match in brackets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sed 's/error/ERROR/gI' file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Replace every match, ignoring case&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sed -E 's/(\w+)/\U\1/' file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Uppercase group 1 with a GNU escape&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The delimiter does not have to be a slash. Writing &lt;code&gt;sed 's|/usr/bin|/usr/local/bin|'&lt;/code&gt; avoids escaping every slash in a path.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="regex-in-awk"&gt;Regex in awk &lt;a class="headline-link" href="#regex-in-awk" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Patterns are extended regular expressions and sit between slashes.&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/awk-command/"&gt;&lt;code&gt;awk '/error/' file&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Print lines matching the pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;awk '$1 ~ /^web/' file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Match a single field&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;awk '$3 !~ /ok/' file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Match fields that fail the pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;awk '/start/,/stop/' file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print an inclusive range of lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;awk '{ gsub(/[0-9]+/, &amp;quot;N&amp;quot;); print }' file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Replace every match on the line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;awk '{ sub(/^ +/, &amp;quot;&amp;quot;); print }' file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Replace the first match only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;awk 'match($0, /[0-9]+/) { print substr($0, RSTART, RLENGTH) }' file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Locate a match and extract it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gawk '{ gsub(/\yroot\y/, &amp;quot;USER&amp;quot;); print }' file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Word boundaries in gawk&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Awk always uses extended syntax, so &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;?&lt;/code&gt;, and &lt;code&gt;{n,m}&lt;/code&gt; work without a backslash. When the pattern is a string rather than a &lt;code&gt;/.../&lt;/code&gt; literal, every backslash needs doubling, so &lt;code&gt;gsub(/\./, &amp;quot;-&amp;quot;)&lt;/code&gt; becomes &lt;code&gt;gsub(&amp;quot;\\.&amp;quot;, &amp;quot;-&amp;quot;)&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="regex-in-vim"&gt;Regex in Vim &lt;a class="headline-link" href="#regex-in-vim" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Vim uses its own flavor, close to ERE once you turn on very magic mode.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&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/vim-search/"&gt;&lt;code&gt;/pattern&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Search forward in the file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/vim-find-replace/"&gt;&lt;code&gt;:%s/old/new/g&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Replace every match in the file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;a\+&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One or more, since &lt;code&gt;+&lt;/code&gt; needs a backslash by default&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;Very magic mode, so &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;?&lt;/code&gt;, &lt;code&gt;(&lt;/code&gt;, and &lt;code&gt;|&lt;/code&gt; work unescaped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\v\d+&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Digits in very magic mode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\&amp;lt;word\&amp;gt;&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;price: \zs\d\+&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start the match after &lt;code&gt;\zs&lt;/code&gt;, end it at &lt;code&gt;\ze&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\cerror&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ignore case for this pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Very magic mode is the shortcut worth remembering. Writing &lt;code&gt;:%s/\v(\w+), (\w+)/\2 \1/g&lt;/code&gt; to swap two fields keeps a pattern readable instead of filling it with backslashes.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="regex-in-bash"&gt;Regex in Bash &lt;a class="headline-link" href="#regex-in-bash" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;=~&lt;/code&gt; operator inside &lt;code&gt;[[ ]]&lt;/code&gt; takes an extended regular expression.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Snippet&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;[[ $ip =~ ^[0-9.]+$ ]]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Test a string against a pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[[ ! $name =~ ^[a-z] ]]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Negate the test&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;re='^v([0-9]+)\.([0-9]+)$'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Keep the pattern in a variable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[[ $tag =~ $re ]]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use the variable unquoted so it stays a regex&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;${BASH_REMATCH[0]}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The whole match&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;${BASH_REMATCH[1]}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The first capture group&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/bash-case-statement/"&gt;&lt;code&gt;case $file in *.txt) ;; esac&lt;/code&gt;&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;A reminder that &lt;code&gt;case&lt;/code&gt; uses globs, not regex&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Quoting the right-hand side turns the pattern into a literal string, so &lt;code&gt;[[ abc =~ &amp;quot;a.c&amp;quot; ]]&lt;/code&gt; fails while &lt;code&gt;[[ abc =~ a.c ]]&lt;/code&gt; succeeds. Store the pattern in a variable when it contains spaces or quotes.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="common-patterns"&gt;Common Patterns &lt;a class="headline-link" href="#common-patterns" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Working starting points to copy and adjust.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Matches&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep -E '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Anything shaped like an IPv4 address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep -P '\b((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\b'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;An IPv4 candidate with each octet under 256&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep -E '[[:alnum:]._%+-]+@[[:alnum:].-]+\.[[:alpha:]]{2,}'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;An email-shaped address&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep -E '[0-9]{4}-[0-9]{2}-[0-9]{2}'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;An ISO date such as &lt;code&gt;2026-08-28&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep -E '#[0-9a-fA-F]{6}\b'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A six-digit hex color&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep -nE '[[:space:]]+$'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Trailing whitespace, with line numbers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep -cE '^[[:space:]]*$'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Count blank lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep -E '^[[:space:]]*#'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Commented-out configuration lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep -vE '^[[:space:]]*(#|$)'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Everything except comments and blank lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep -E '\b([[:alpha:]]+) \1\b'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A word accidentally repeated twice&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep -oE '&amp;quot;[^&amp;quot;]*&amp;quot;'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A double-quoted string, quotes included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep -oP 'user=\K\S+'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The value after a &lt;code&gt;user=&lt;/code&gt; key, key excluded&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The address patterns describe a shape rather than validate it. Use them to pull candidates out of logs, and check the results with a real parser when correctness matters.&lt;/p&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 regex problems and what to check first.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&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;\d&lt;/code&gt; fails or behaves unexpectedly&lt;/td&gt;
&lt;td&gt;BRE and ERE do not define &lt;code&gt;\d&lt;/code&gt;; use &lt;code&gt;[[:digit:]]&lt;/code&gt;, &lt;code&gt;[0-9]&lt;/code&gt;, or &lt;code&gt;grep -P&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;+&lt;/code&gt; or &lt;code&gt;?&lt;/code&gt; matched literally&lt;/td&gt;
&lt;td&gt;The pattern is basic syntax; escape them as &lt;code&gt;\+&lt;/code&gt; and &lt;code&gt;\?&lt;/code&gt;, or switch to &lt;code&gt;grep -E&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alternation found nothing&lt;/td&gt;
&lt;td&gt;ERE takes a bare pipe, BRE takes an escaped one; &lt;code&gt;grep -E&lt;/code&gt; is the simpler fix&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The shell changed the pattern&lt;/td&gt;
&lt;td&gt;Wrap the pattern in single quotes so backslashes and &lt;code&gt;$&lt;/code&gt; survive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[a-z]&lt;/code&gt; also matched uppercase&lt;/td&gt;
&lt;td&gt;Range order follows the locale; use &lt;code&gt;LC_ALL=C&lt;/code&gt; or &lt;code&gt;[[:lower:]]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lookahead or lookbehind rejected&lt;/td&gt;
&lt;td&gt;BRE and ERE do not support them; use &lt;code&gt;grep -P&lt;/code&gt;, or the engine&amp;rsquo;s own syntax such as Vim&amp;rsquo;s &lt;code&gt;\@=&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep -P&lt;/code&gt; is not supported&lt;/td&gt;
&lt;td&gt;The build lacks PCRE; use &lt;code&gt;pcre2grep&lt;/code&gt;, &lt;code&gt;perl -ne&lt;/code&gt;, or rewrite the pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\b&lt;/code&gt; did nothing in awk&lt;/td&gt;
&lt;td&gt;Awk reads &lt;code&gt;\b&lt;/code&gt; as a backspace; use &lt;code&gt;\y&lt;/code&gt; in gawk or &lt;code&gt;\&amp;lt;&lt;/code&gt; and &lt;code&gt;\&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The match ran too far&lt;/td&gt;
&lt;td&gt;Quantifiers are greedy; use a negated class such as &lt;code&gt;[^&amp;quot;]*&lt;/code&gt;, or a lazy &lt;code&gt;.*?&lt;/code&gt; with &lt;code&gt;-P&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Intervals matched literally in Vim&lt;/td&gt;
&lt;td&gt;Vim needs &lt;code&gt;\{2,4}&lt;/code&gt; in magic mode, or &lt;code&gt;\v&lt;/code&gt; first&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;Longer walkthroughs and per-tool references.&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/regular-expressions-basics/"&gt;Regular Expressions Basics&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Learn the building blocks from the ground up&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/regular-expressions-in-grep/"&gt;Regular Expressions in Grep&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Every grep flavor with worked examples&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/cheatsheet/grep/"&gt;grep Cheatsheet&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Search options, recursion, and context output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/cheatsheet/sed/"&gt;sed Cheatsheet&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Substitution, addresses, and in-place editing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/cheatsheet/awk/"&gt;awk Cheatsheet&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Fields, patterns, actions, and built-in variables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/cheatsheet/vim/"&gt;Vim Cheatsheet&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Motions, editing, search, and replace&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;</content:encoded></item><item><title>parted Cheatsheet</title><link>https://linuxize.com/cheatsheet/parted/</link><pubDate>Tue, 25 Aug 2026 18:50:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/cheatsheet/parted/</guid><description>Quick reference for parted commands: inspect disks, create GPT and MBR tables, add aligned partitions, manage flags, resize boundaries, and remove partitions 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;Core command forms and help options.&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;parted [OPTIONS] DEVICE [COMMAND [ARGUMENTS]]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;General syntax&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/parted-command-in-linux/"&gt;&lt;code&gt;sudo parted /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 parted /dev/sdX -- print&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print one disk&amp;rsquo;s partition table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo parted --list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List partition tables on all detected disks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;parted --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;parted --version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the installed version&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Always pass the whole-disk path explicitly. Most &lt;code&gt;parted&lt;/code&gt; changes take effect immediately, even in interactive mode.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="identify-the-target-disk"&gt;Identify the Target Disk &lt;a class="headline-link" href="#identify-the-target-disk" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Confirm the device name, size, model, and current use before changing it.&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,TRAN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List whole disks with identifying details&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS,MODEL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show disks, partitions, filesystems, and mounts&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 filesystem labels and UUIDs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;findmnt -S /dev/sdX1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check whether a partition is mounted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo parted /dev/sdX -- print&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Review the current partition table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo parted /dev/sdX -- unit MiB print free&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show partitions and unallocated space&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Use &lt;code&gt;/dev/sdX&lt;/code&gt; for the whole disk and &lt;code&gt;/dev/sdX1&lt;/code&gt; for its first partition. NVMe and eMMC disks insert &lt;code&gt;p&lt;/code&gt; before the number, so partition 1 of &lt;code&gt;/dev/nvme0n1&lt;/code&gt; is &lt;code&gt;/dev/nvme0n1p1&lt;/code&gt;. The &lt;code&gt;MOUNTPOINTS&lt;/code&gt; column needs util-linux 2.37 or later; use &lt;code&gt;MOUNTPOINT&lt;/code&gt; on older releases. Replace every placeholder with a verified device path.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="command-line-options"&gt;Command-Line Options &lt;a class="headline-link" href="#command-line-options" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Options must appear before the device and session command.&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;-l&lt;/code&gt;, &lt;code&gt;--list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List partition layouts on all block devices&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-s&lt;/code&gt;, &lt;code&gt;--script&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Never prompt for input&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-m&lt;/code&gt;, &lt;code&gt;--machine&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print colon-separated, machine-readable output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-j&lt;/code&gt;, &lt;code&gt;--json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print JSON output on supported versions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-f&lt;/code&gt;, &lt;code&gt;--fix&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Automatically choose &lt;code&gt;fix&lt;/code&gt; for repairable exceptions in script mode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-a optimal&lt;/code&gt;, &lt;code&gt;--align optimal&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use optimal alignment for new partitions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;End option parsing before session commands and negative positions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Script mode is not a dry run. Inspect the disk separately before using &lt;code&gt;--script&lt;/code&gt; or &lt;code&gt;--fix&lt;/code&gt;.&lt;/p&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;Enter these commands at the &lt;code&gt;(parted)&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;help&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List available session commands&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;help mkpart&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show help for one command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;print&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;print free&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Include unallocated space&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;unit MiB&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set the default display and input unit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;select /dev/sdY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Switch to another disk&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;Exit Parted&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The &lt;code&gt;quit&lt;/code&gt; command does not undo earlier changes because Parted writes most changes as each command runs.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="create-a-partition-table"&gt;Create a Partition Table &lt;a class="headline-link" href="#create-a-partition-table" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Create a disk label on a new or intentionally cleared 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;sudo parted --script /dev/sdX -- mklabel gpt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a GPT partition table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo parted --script /dev/sdX -- mklabel msdos&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create an MBR partition table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo parted /dev/sdX -- print&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Confirm the new table type&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;mklabel&lt;/code&gt; replaces the existing partition table and makes its partitions inaccessible. Use GPT for current systems; use MBR only when legacy compatibility requires it.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="create-gpt-partitions"&gt;Create GPT Partitions &lt;a class="headline-link" href="#create-gpt-partitions" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Give each GPT partition a name, filesystem type hint, start, and end.&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 parted --script /dev/sdX -- mkpart data ext4 1MiB 100%&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create one partition using the available disk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo parted --script /dev/sdX -- mkpart data ext4 1MiB 50GiB&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a partition ending at 50 GiB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo parted --script /dev/sdX -- mkpart backup ext4 50GiB 100%&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use the remaining space for a second partition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo parted /dev/sdX -- print&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Review the finished layout&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo parted /dev/sdX -- align-check optimal 1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check partition 1 alignment&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The filesystem type is a partition-table hint. &lt;code&gt;mkpart&lt;/code&gt; does not create a filesystem.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="create-mbr-partitions"&gt;Create MBR Partitions &lt;a class="headline-link" href="#create-mbr-partitions" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;An MBR table takes a partition type where GPT takes a 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;sudo parted --script /dev/sdX -- mkpart primary ext4 1MiB 100%&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create one primary partition using the available disk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo parted --script /dev/sdX -- mkpart primary ext4 1MiB 50GiB&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a primary partition ending at 50 GiB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo parted --script /dev/sdX -- mkpart primary ext4 50GiB 100%&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add a second primary partition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo parted --script /dev/sdX -- mkpart extended 50GiB 100%&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create an extended partition to hold logical partitions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo parted --script /dev/sdX -- mkpart logical ext4 51GiB 100%&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a logical partition inside the extended partition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo parted /dev/sdX -- print&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Review the finished layout&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;An MBR table holds four primary partitions, or three primary partitions plus one extended partition that contains the logical ones. Start the first logical partition after the extended partition&amp;rsquo;s start so Parted can write the extended boot record. The &lt;code&gt;name&lt;/code&gt; command does not work on MBR partitions.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="units-and-positions"&gt;Units and Positions &lt;a class="headline-link" href="#units-and-positions" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Suffix boundary values so Parted does not have to infer the unit.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;s&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sectors, used for exact sector positions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MiB&lt;/code&gt;, &lt;code&gt;GiB&lt;/code&gt;, &lt;code&gt;TiB&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exact IEC binary positions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MB&lt;/code&gt;, &lt;code&gt;GB&lt;/code&gt;, &lt;code&gt;TB&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Decimal positions that may allow a nearby boundary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;1MiB&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Common aligned start for a new partition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;100%&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;End at the last usable part of the disk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-1s&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Last sector of the disk; put &lt;code&gt;--&lt;/code&gt; before the command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo parted /dev/sdX -- unit s print&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Display all boundaries in sectors&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Use &lt;code&gt;MiB&lt;/code&gt; or &lt;code&gt;GiB&lt;/code&gt; for repeatable commands and &lt;code&gt;unit MiB print free&lt;/code&gt; when comparing boundaries.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="partition-names-and-flags"&gt;Partition Names and Flags &lt;a class="headline-link" href="#partition-names-and-flags" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Names and available flags depend on the partition table type.&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 parted --script /dev/sdX -- name 1 data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rename GPT partition 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo parted --script /dev/sdX -- set 1 esp on&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mark an EFI System Partition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo parted --script /dev/sdX -- set 1 bios_grub on&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mark a BIOS GRUB partition on GPT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo parted --script /dev/sdX -- set 1 swap on&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mark a swap partition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo parted --script /dev/sdX -- set 1 lvm on&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mark an LVM physical volume where supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo parted --script /dev/sdX -- set 1 raid on&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mark a software RAID member where supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo parted --script /dev/sdX -- set 1 FLAG off&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Disable a flag&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Run &lt;code&gt;help set&lt;/code&gt; interactively to see the flags supported by the current table. Flags record a partition&amp;rsquo;s purpose but do not create an EFI filesystem, swap area, LVM volume, or RAID array.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="resize-a-partition-boundary"&gt;Resize a Partition Boundary &lt;a class="headline-link" href="#resize-a-partition-boundary" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Confirm that free space is adjacent to the partition before extending it.&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 parted /dev/sdX -- unit MiB print free&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check the partition and adjacent free space&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo parted --script /dev/sdX -- resizepart 1 100%&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Move partition 1&amp;rsquo;s end to the end of the disk&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 reread the partition table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo resize2fs /dev/sdX1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Grow an ext2, ext3, or ext4 filesystem afterward&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo xfs_growfs /mount/point&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Grow a mounted XFS filesystem afterward&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 the resulting partition and filesystem size&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;resizepart&lt;/code&gt; changes only the partition boundary. When shrinking, shrink the filesystem first and use a filesystem-specific procedure.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="remove-or-rescue-a-partition"&gt;Remove or Rescue a Partition &lt;a class="headline-link" href="#remove-or-rescue-a-partition" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Keep inspection and removal as separate 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;sudo parted /dev/sdX -- print&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Note the target partition number and boundaries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo parted --script /dev/sdX -- rm 2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove partition 2 immediately&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo parted /dev/sdX&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Open interactive mode for recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rescue START END&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search near the old boundaries for a lost partition&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 reread the changed table&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;After an accidental removal, stop writing to the disk and try &lt;code&gt;rescue&lt;/code&gt; before creating another partition. Recovery is not guaranteed.&lt;/p&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;Create a filesystem only after checking the new partition path.&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 -f /dev/sdX&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Confirm the new partition and existing filesystems&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo mkfs.ext4 -L data /dev/sdX1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create an ext4 filesystem with label &lt;code&gt;data&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo mkfs.xfs -L data /dev/sdX1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create an XFS filesystem with label &lt;code&gt;data&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo mkswap -L swap /dev/sdX2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create swap space on partition 2&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 new filesystem&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;p&gt;Formatting destroys data on the selected partition. A partition name and a filesystem label are separate values.&lt;/p&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 Parted problems and the next check to run.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&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;Partition is not aligned&lt;/td&gt;
&lt;td&gt;Run &lt;code&gt;align-check optimal NUMBER&lt;/code&gt;; recreate an empty partition with a suitable start&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kernel still shows the old table&lt;/td&gt;
&lt;td&gt;Unmount filesystems, disable swap, then run &lt;code&gt;sudo partprobe /dev/sdX&lt;/code&gt; or reboot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Device or resource is busy&lt;/td&gt;
&lt;td&gt;Check &lt;code&gt;lsblk&lt;/code&gt;, &lt;code&gt;findmnt&lt;/code&gt;, swap, LVM, RAID, and encrypted mappings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;New partition does not appear&lt;/td&gt;
&lt;td&gt;Run &lt;code&gt;sudo partprobe /dev/sdX&lt;/code&gt;, then check &lt;code&gt;lsblk&lt;/code&gt;; reboot if the disk remains busy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;unrecognised disk label&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The disk has no supported table; verify it is the correct empty disk before using &lt;code&gt;mklabel&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mkpart&lt;/code&gt; rejects the partition name&lt;/td&gt;
&lt;td&gt;The disk uses an MBR table; pass &lt;code&gt;primary&lt;/code&gt;, &lt;code&gt;extended&lt;/code&gt;, or &lt;code&gt;logical&lt;/code&gt; instead of a name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Filesystem size did not change&lt;/td&gt;
&lt;td&gt;Run the correct filesystem grow command after &lt;code&gt;resizepart&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 and cheatsheets for the complete storage workflow.&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/parted-command-in-linux/"&gt;parted Command in Linux&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Full walkthrough for inspecting and changing partitions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/linux-block-devices-partitions-filesystems/"&gt;Linux Block Devices, Partitions, Filesystems, and Mount Points&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Understand where partitioning fits in the storage stack&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/cheatsheet/fdisk/"&gt;fdisk Cheatsheet&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Menu-driven partitioning command reference&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/cheatsheet/mount/"&gt;mount Cheatsheet&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Mount options, UUIDs, labels, and &lt;code&gt;/etc/fstab&lt;/code&gt; entries&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;</content:encoded></item><item><title>parted Command in Linux: Manage Disk Partitions</title><link>https://linuxize.com/post/parted-command-in-linux/</link><pubDate>Mon, 24 Aug 2026 20:40:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/parted-command-in-linux/</guid><category>disk</category><category>linux commands</category><description>Use the parted command to inspect disks, create GPT partition tables, add aligned partitions, resize boundaries, and remove partitions safely on Linux.</description><content:encoded>&lt;p&gt;When a new disk appears in &lt;code&gt;lsblk&lt;/code&gt;, it is visible to Linux but still has nowhere to store files. The disk needs a partition table and at least one partition before you create a filesystem and mount it. The &lt;code&gt;parted&lt;/code&gt; command manages those boundaries from the terminal, works with both GPT and MBR tables, and can run interactively or from a script.&lt;/p&gt;
&lt;p&gt;Unlike &lt;code&gt;fdisk&lt;/code&gt;, which stages changes until you write the table, &lt;code&gt;parted&lt;/code&gt; applies most commands immediately. This guide explains how to inspect a disk, create a GPT partition table, add aligned partitions, resize a partition boundary, and remove a partition without losing track of which storage layer you are changing.&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;Partitioning the wrong disk can make its data inaccessible. Before every write operation, confirm the device with &lt;a href="https://linuxize.com/post/lsblk-command-in-linux/" target="_blank" rel="noopener noreferrer"&gt;&lt;code&gt;lsblk&lt;/code&gt;&lt;/a&gt;
, use the whole-disk name such as &lt;code&gt;/dev/sdb&lt;/code&gt; rather than a partition such as &lt;code&gt;/dev/sdb1&lt;/code&gt;, and keep a current backup of any disk that holds data.&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="parted-command-syntax"&gt;parted Command Syntax &lt;a class="headline-link" href="#parted-command-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;parted [OPTIONS] [DEVICE [COMMAND [ARGUMENTS]]]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Always pass the device explicitly. If you omit it, &lt;code&gt;parted&lt;/code&gt; tries to choose a device, which is not a risk worth taking on a machine with several disks.&lt;/p&gt;
&lt;p&gt;The options you will use most often are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-l&lt;/code&gt;, &lt;code&gt;--list&lt;/code&gt; - List partition layouts on all detected block devices.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-s&lt;/code&gt;, &lt;code&gt;--script&lt;/code&gt; - Never prompt for input. Use this only after validating the device and command.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-m&lt;/code&gt;, &lt;code&gt;--machine&lt;/code&gt; - Produce colon-separated output for scripts.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-j&lt;/code&gt;, &lt;code&gt;--json&lt;/code&gt; - Produce JSON output on versions that support it.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-a&lt;/code&gt;, &lt;code&gt;--align&lt;/code&gt; - Choose the alignment type for newly created partitions. The default is &lt;code&gt;optimal&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The examples place options before the device and put &lt;code&gt;--&lt;/code&gt; before the parted command. The separator matters when an argument begins with a dash, such as the &lt;code&gt;-1s&lt;/code&gt; end position that means the last sector of the disk. Without it, &lt;code&gt;parted&lt;/code&gt; reads &lt;code&gt;-1s&lt;/code&gt; as an invalid option instead of a position.&lt;/p&gt;
&lt;h2 id="installing-parted"&gt;Installing parted &lt;a class="headline-link" href="#installing-parted" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Most distributions include &lt;code&gt;parted&lt;/code&gt;. If the command is missing, install it with your distribution 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 update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install parted&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 parted&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Confirm the installed version before continuing:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;parted --version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="listing-disks-and-free-space"&gt;Listing Disks and Free Space &lt;a class="headline-link" href="#listing-disks-and-free-space" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Start with &lt;code&gt;lsblk&lt;/code&gt; so you can match each device name to its size, model, filesystem, and mount points:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS,MODEL&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;A row with &lt;code&gt;TYPE&lt;/code&gt; set to &lt;code&gt;disk&lt;/code&gt; is a whole device, while rows marked &lt;code&gt;part&lt;/code&gt; are partitions. Confirm the size and model instead of relying on a name such as &lt;code&gt;/dev/sdb&lt;/code&gt;, since device names can change between boots.&lt;/p&gt;
&lt;p&gt;Next, list the partition table on every detected disk:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 parted --list&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;Model: ATA Samsung SSD 870 (scsi)
Disk /dev/sda: 500GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:
Number Start End Size File system Name Flags
1 1049kB 538MB 537MB fat32 EFI System Partition boot, esp
2 538MB 500GB 500GB ext4&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;Partition Table&lt;/code&gt; line identifies the layout as &lt;code&gt;gpt&lt;/code&gt; or &lt;code&gt;msdos&lt;/code&gt; (MBR). The partition rows show the boundaries, detected filesystem, GPT name, and any flags.&lt;/p&gt;
&lt;p&gt;To inspect one disk in MiB and include its unallocated regions, use &lt;code&gt;unit MiB&lt;/code&gt; followed by &lt;code&gt;print free&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 parted /dev/sdb -- unit MiB print free&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;Model: QEMU QEMU HARDDISK (scsi)
Disk /dev/sdb: 102400MiB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:
Number Start End Size File system Name Flags
0.02MiB 1.00MiB 0.98MiB Free Space
1 1.00MiB 51200MiB 51199MiB ext4 data
51200MiB 102400MiB 51200MiB Free Space&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Rows without a number are unallocated regions. The small gap at the start of the disk holds the GPT header, while the 51200MiB region after partition 1 is space available for a new partition. This read-only command is the one to run before creating or extending a partition, since it shows whether free space exists and on which side of the existing partitions it sits.&lt;/p&gt;
&lt;p&gt;On parted 3.4 and later, &lt;code&gt;--json&lt;/code&gt; returns the same layout as structured data for 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;sudo parted --json /dev/sdb -- unit MiB print&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;#34;disk&amp;#34;: {
&amp;#34;path&amp;#34;: &amp;#34;/dev/sdb&amp;#34;,
&amp;#34;size&amp;#34;: &amp;#34;102400MiB&amp;#34;,
&amp;#34;model&amp;#34;: &amp;#34;QEMU QEMU HARDDISK (scsi)&amp;#34;,
&amp;#34;transport&amp;#34;: &amp;#34;scsi&amp;#34;,
&amp;#34;logical-sector-size&amp;#34;: 512,
&amp;#34;physical-sector-size&amp;#34;: 512,
&amp;#34;label&amp;#34;: &amp;#34;gpt&amp;#34;,
&amp;#34;max-partitions&amp;#34;: 128,
&amp;#34;partitions&amp;#34;: [
{
&amp;#34;number&amp;#34;: 1,
&amp;#34;start&amp;#34;: &amp;#34;1.00MiB&amp;#34;,
&amp;#34;end&amp;#34;: &amp;#34;51200MiB&amp;#34;,
&amp;#34;size&amp;#34;: &amp;#34;51199MiB&amp;#34;,
&amp;#34;name&amp;#34;: &amp;#34;data&amp;#34;,
&amp;#34;filesystem&amp;#34;: &amp;#34;ext4&amp;#34;
}
]
}
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Pipe that into &lt;code&gt;jq&lt;/code&gt; when a script needs a single value, such as &lt;code&gt;jq -r '.disk.partitions[].size'&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can also open an interactive session by passing only the device:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 parted /dev/sdb&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;GNU Parted 3.7
Using /dev/sdb
Welcome to GNU Parted! Type &amp;#39;help&amp;#39; to view a list of commands.
(parted)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;At the &lt;code&gt;(parted)&lt;/code&gt; prompt, enter commands such as &lt;code&gt;print&lt;/code&gt;, &lt;code&gt;mkpart&lt;/code&gt;, and &lt;code&gt;quit&lt;/code&gt;. The remaining examples use command-line mode so each operation and its target are visible in one copy-pasteable command.&lt;/p&gt;
&lt;h2 id="creating-a-gpt-partition-table"&gt;Creating a GPT Partition Table &lt;a class="headline-link" href="#creating-a-gpt-partition-table" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A new disk needs a partition table before it can hold partitions. GPT is the standard choice for current systems and is required when an MBR table with 512-byte sectors would exceed its 2 TiB addressing limit.&lt;/p&gt;
&lt;p&gt;Run the inspection command again immediately before creating the 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;lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS,MODEL /dev/sdb&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Once &lt;code&gt;/dev/sdb&lt;/code&gt; is confirmed as the intended disk, create the GPT label:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 parted --script /dev/sdb -- mklabel gpt&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This replaces the existing partition table and makes its old partitions inaccessible. The &lt;code&gt;--script&lt;/code&gt; option suppresses prompts; it is not a dry run and does not make the operation safer. Use &lt;code&gt;mklabel msdos&lt;/code&gt; only when you specifically need an MBR table for legacy compatibility.&lt;/p&gt;
&lt;h2 id="creating-an-aligned-partition"&gt;Creating an Aligned Partition &lt;a class="headline-link" href="#creating-an-aligned-partition" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;mkpart&lt;/code&gt; command takes a GPT partition name, a filesystem type hint, and the start and end positions. To create one data partition that fills the usable disk space, 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 parted --script /dev/sdb -- mkpart data ext4 1MiB 100%&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The name is &lt;code&gt;data&lt;/code&gt;, and &lt;code&gt;ext4&lt;/code&gt; sets the intended partition type. It does not create an ext4 filesystem. Starting at &lt;code&gt;1MiB&lt;/code&gt; leaves room for the GPT metadata and gives the partition a common aligned starting point; &lt;code&gt;100%&lt;/code&gt; extends it to the last usable part of the disk.&lt;/p&gt;
&lt;p&gt;For two partitions, give each one its own range. This example creates a 50 GiB data partition and uses the remaining space for backups:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 parted --script /dev/sdb -- mkpart data ext4 1MiB 50GiB
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo parted --script /dev/sdb -- mkpart backup ext4 50GiB 100%&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;IEC units such as &lt;code&gt;MiB&lt;/code&gt; and &lt;code&gt;GiB&lt;/code&gt; identify exact positions. Decimal units such as &lt;code&gt;MB&lt;/code&gt; and &lt;code&gt;GB&lt;/code&gt; allow &lt;code&gt;parted&lt;/code&gt; to choose a nearby position, so explicit IEC units are easier to reason about in repeatable commands.&lt;/p&gt;
&lt;p&gt;Print the finished layout:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 parted /dev/sdb -- unit MiB print free&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Then check the alignment of partition 1:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 parted /dev/sdb -- align-check optimal &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;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 aligned&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The alignment check confirms that the partition starts on a boundary suited to the device. If it reports &lt;code&gt;not aligned&lt;/code&gt;, recreate an empty partition with a suitable start position before writing data to it.&lt;/p&gt;
&lt;p&gt;The new partition still has no filesystem. After verifying that &lt;code&gt;/dev/sdb1&lt;/code&gt; is the new empty partition, format it separately:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 mkfs.ext4 -L data /dev/sdb1&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;-L data&lt;/code&gt; option assigns a filesystem label, which is separate from the GPT partition name. Follow the &lt;a href="https://linuxize.com/post/how-to-mount-and-unmount-file-systems-in-linux/"&gt;mount and unmount guide&lt;/a&gt;
when you are ready to attach the filesystem to the directory tree.&lt;/p&gt;
&lt;h2 id="renaming-a-gpt-partition"&gt;Renaming a GPT Partition &lt;a class="headline-link" href="#renaming-a-gpt-partition" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;GPT stores a human-readable name for each partition. To rename partition 1 without changing its filesystem label, use the &lt;code&gt;name&lt;/code&gt; 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;sudo parted --script /dev/sdb -- name &lt;span class="m"&gt;1&lt;/span&gt; archive&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Run &lt;code&gt;print&lt;/code&gt; afterward to confirm the new value in the &lt;code&gt;Name&lt;/code&gt; column. Partition names are available with GPT and several other table formats, but not with an MBR table.&lt;/p&gt;
&lt;h2 id="setting-partition-flags"&gt;Setting Partition Flags &lt;a class="headline-link" href="#setting-partition-flags" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Flags mark what a partition is for so that firmware and other tools treat it correctly. The &lt;code&gt;set&lt;/code&gt; command takes the partition number, the flag name, and &lt;code&gt;on&lt;/code&gt; or &lt;code&gt;off&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 parted --script /dev/sdb -- &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; esp on&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The flags you will use most often on a GPT disk are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;esp&lt;/code&gt; - Mark the partition as an EFI System Partition. On GPT, &lt;code&gt;boot&lt;/code&gt; and &lt;code&gt;esp&lt;/code&gt; are the same flag.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bios_grub&lt;/code&gt; - Mark the small unformatted partition that GRUB needs to boot a GPT disk on BIOS firmware.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lvm&lt;/code&gt; - Mark the partition as an LVM physical volume.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;raid&lt;/code&gt; - Mark the partition as a software RAID member.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;msftdata&lt;/code&gt; - Mark the partition as Microsoft basic data, which is the type Windows expects.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Turn a flag off by passing &lt;code&gt;off&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;sudo parted --script /dev/sdb -- &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; lvm off&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Flags record intent and set the GPT partition type, but they create nothing. Setting &lt;code&gt;lvm&lt;/code&gt; does not run &lt;code&gt;pvcreate&lt;/code&gt;, and setting &lt;code&gt;esp&lt;/code&gt; does not put a FAT32 filesystem on the partition. To see which flags the current table supports, open an interactive session and run &lt;code&gt;help set&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="resizing-a-partition"&gt;Resizing a Partition &lt;a class="headline-link" href="#resizing-a-partition" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before extending a partition, use &lt;code&gt;print free&lt;/code&gt; to confirm that unallocated space begins directly after 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 parted /dev/sdb -- unit MiB print free&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If partition 1 is followed by free space, move its end boundary to the end of the disk:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 parted --script /dev/sdb -- resizepart &lt;span class="m"&gt;1&lt;/span&gt; 100%&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;resizepart&lt;/code&gt; command changes only the partition boundary. It does not resize the filesystem inside. For an ext4 filesystem, grow the filesystem 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 resize2fs /dev/sdb1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Use &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;
to confirm the filesystem now sees the extra capacity. Other filesystems need their own resizing tools; XFS, for example, grows with &lt;code&gt;xfs_growfs&lt;/code&gt; and cannot be shrunk. For the full grow workflow, including XFS, Btrfs, and LVM, see &lt;a href="https://linuxize.com/post/how-to-resize-and-extend-a-linux-partition/"&gt;how to resize and extend a Linux partition&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;Shrinking is a different workflow. You must unmount and shrink a shrinkable filesystem first, check it for errors, and only then move the partition boundary. Do not use &lt;code&gt;resizepart&lt;/code&gt; alone to shrink a partition, since it can cut off live filesystem data.&lt;/p&gt;
&lt;h2 id="removing-a-partition"&gt;Removing a Partition &lt;a class="headline-link" href="#removing-a-partition" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;List the table one final time and note the number of the partition you intend to remove:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 parted /dev/sdb -- print&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;When the number is confirmed, remove partition 2:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 parted --script /dev/sdb -- rm &lt;span class="m"&gt;2&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 partition disappears from the table immediately, and its former space becomes unallocated. Script mode provides no confirmation, so keep the inspection and removal as separate commands rather than chaining them.&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/parted/"&gt;parted 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;List all partition tables&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo parted --list&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Show one disk and its free space&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo parted /dev/sdb -- unit MiB print free&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Print the layout as JSON&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo parted --json /dev/sdb -- unit MiB print&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Open interactive mode&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo parted /dev/sdb&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Create a GPT table&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo parted --script /dev/sdb -- mklabel gpt&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Create an ext4 data partition&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo parted --script /dev/sdb -- mkpart data ext4 1MiB 100%&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check optimal alignment&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo parted /dev/sdb -- align-check optimal 1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rename GPT partition 1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo parted --script /dev/sdb -- name 1 archive&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Set the EFI system partition flag&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo parted --script /dev/sdb -- set 1 esp on&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extend partition 1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo parted --script /dev/sdb -- resizepart 1 100%&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remove partition 2&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo parted --script /dev/sdb -- rm 2&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;The kernel still uses the old partition table&lt;/strong&gt;&lt;br&gt;
Unmount filesystems on the disk, disable any swap partition it contains, and run &lt;code&gt;sudo partprobe /dev/sdb&lt;/code&gt; to ask the kernel to reread the table. If the device is still busy, schedule a reboot instead of forcing another partition change.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;parted reports that a partition is not properly aligned&lt;/strong&gt;&lt;br&gt;
Use &lt;code&gt;sudo parted /dev/sdb -- align-check optimal NUMBER&lt;/code&gt; to verify the start boundary. If the partition is empty, recreate it with a &lt;code&gt;1MiB&lt;/code&gt; start or another boundary that satisfies the device&amp;rsquo;s reported alignment requirements.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A partition was removed by mistake&lt;/strong&gt;&lt;br&gt;
Stop writing to the disk. Open &lt;code&gt;sudo parted /dev/sdb&lt;/code&gt;, run &lt;code&gt;rescue START END&lt;/code&gt; with the approximate old boundaries, and confirm the discovered partition only when its location and filesystem match. Recovery is not guaranteed, especially after new data has been written.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;parted says a partition is in use&lt;/strong&gt;&lt;br&gt;
Check &lt;code&gt;lsblk&lt;/code&gt; and &lt;code&gt;findmnt&lt;/code&gt;, then unmount the filesystem before changing its boundaries. A disk can also remain busy because it contains active swap, an LVM physical volume, software RAID, or an encrypted mapping.&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 parted or fdisk?&lt;/strong&gt;&lt;br&gt;
Both tools support GPT and MBR tables. &lt;a href="https://linuxize.com/post/fdisk-command-in-linux/"&gt;&lt;code&gt;fdisk&lt;/code&gt;&lt;/a&gt;
provides an interactive workflow that stages changes until you write them, while &lt;code&gt;parted&lt;/code&gt; is convenient for one-line commands, scripts, alignment checks, and changing a partition&amp;rsquo;s end boundary.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Does parted format partitions?&lt;/strong&gt;&lt;br&gt;
No. &lt;code&gt;parted&lt;/code&gt; creates the partition entry and can record an intended filesystem type, but it does not create the filesystem. Run the appropriate &lt;code&gt;mkfs&lt;/code&gt; command on the new partition after checking the device name.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Does resizepart resize the filesystem too?&lt;/strong&gt;&lt;br&gt;
No. &lt;code&gt;resizepart&lt;/code&gt; only moves the partition&amp;rsquo;s end boundary. Grow or shrink the filesystem separately with the tool designed for that filesystem, and always shrink the filesystem before shrinking its partition.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Should I use GPT or MBR?&lt;/strong&gt;&lt;br&gt;
Use GPT for current Linux systems, UEFI boot disks, and disks larger than 2 TiB. Use MBR only when an older operating system or firmware requires 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;&lt;code&gt;parted&lt;/code&gt; is most useful when you need precise, repeatable control over partition boundaries, especially on GPT disks. Keep inspection and write commands separate, use explicit units, and stop after unexpected output instead of pushing through with &lt;code&gt;--script&lt;/code&gt;.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/parted-command-in-linux/featured_hu_c93b99cd1ae66e4c.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>DNS Record Types Explained: A, AAAA, CNAME, MX, TXT, and More</title><link>https://linuxize.com/post/dns-record-types-explained/</link><pubDate>Fri, 21 Aug 2026 16:00:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/dns-record-types-explained/</guid><category>dns</category><category>networking</category><description>DNS record types map domains to servers, route email, publish verification data, and control certificate issuance. See zone-file examples and dig queries.</description><content:encoded>&lt;p&gt;A website can load normally while mail delivery, domain verification, or certificate issuance fails. Each service asks DNS a different question, and the answer comes from a specific record type.&lt;/p&gt;
&lt;p&gt;A and AAAA records provide server addresses, MX records route email, TXT records publish verification and policy data, and several other types handle aliases, delegation, service discovery, and security. This guide explains the DNS record types you will encounter most often, shows their zone-file syntax, and demonstrates how to inspect them with &lt;code&gt;dig&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="what-a-dns-record-contains"&gt;What a DNS Record Contains &lt;a class="headline-link" href="#what-a-dns-record-contains" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;DNS is a distributed database that associates names with typed data. A resource record contains a name, time to live (TTL), class, type, and value. The class is almost always &lt;code&gt;IN&lt;/code&gt;, which means Internet.&lt;/p&gt;
&lt;p&gt;Here are three records in zone-file 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="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;example.com. 3600 IN A 203.0.113.10
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;www.example.com. 3600 IN CNAME example.com.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;example.com. 3600 IN MX 10 mail.example.com.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The final dot marks each name as fully qualified. DNS control panels often hide this detail and let you enter &lt;code&gt;@&lt;/code&gt; for the zone apex or only the subdomain label, such as &lt;code&gt;www&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The TTL in this example is 3600 seconds, so a resolver may reuse a cached answer for up to one hour. A shorter TTL lets resolvers fetch a changed record sooner, but it does not shorten the lifetime of copies already cached with the old value. Before a planned migration, lower the TTL at least one old-TTL interval in advance, wait for existing caches to expire, and then make the change.&lt;/p&gt;
&lt;h2 id="a-records-ipv4-addresses"&gt;A Records: IPv4 Addresses &lt;a class="headline-link" href="#a-records-ipv4-addresses" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;An A record maps a hostname to an IPv4 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="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;example.com. 3600 IN A 203.0.113.10&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 record for pointing a website or another network service at an IPv4 server. The value must be an IPv4 address, not a URL, port number, or hostname.&lt;/p&gt;
&lt;p&gt;A name can have several A records. Resolvers usually return the complete set, and clients may try the addresses in different orders. This provides basic round-robin traffic distribution, but DNS does not check whether each server is healthy. An unavailable address can remain in the answer until you remove it and its cached TTL expires.&lt;/p&gt;
&lt;h2 id="aaaa-records-ipv6-addresses"&gt;AAAA Records: IPv6 Addresses &lt;a class="headline-link" href="#aaaa-records-ipv6-addresses" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;An AAAA record maps a hostname to an IPv6 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="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;example.com. 3600 IN AAAA 2001:db8::10&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;A dual-stack service commonly publishes both A and AAAA records. Current clients usually request both types and use a connection strategy such as Happy Eyeballs to try IPv6 and IPv4 without waiting through a long failure.&lt;/p&gt;
&lt;p&gt;Publish an AAAA record only when the service is reachable at that IPv6 address. IPv4-only clients will continue using the A record, but an incorrect AAAA record can delay or break connections for visitors whose systems prefer IPv6.&lt;/p&gt;
&lt;h2 id="cname-records-aliases"&gt;CNAME Records: Aliases &lt;a class="headline-link" href="#cname-records-aliases" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A CNAME record makes one name an alias of another 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="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;www.example.com. 3600 IN CNAME example.com.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;When a resolver receives this answer, it continues the lookup at &lt;code&gt;example.com&lt;/code&gt;. CNAME records are useful when a subdomain must follow a hostname managed by a CDN, application host, or other provider whose IP addresses may change.&lt;/p&gt;
&lt;p&gt;The owner of a CNAME cannot also hold ordinary A, AAAA, MX, or TXT data. The zone apex (&lt;code&gt;example.com&lt;/code&gt;) must hold SOA and NS records, so it cannot be a CNAME. Providers implement apex aliasing through non-standard features named ALIAS, ANAME, or CNAME flattening, which resolve the target and publish A or AAAA answers on your behalf.&lt;/p&gt;
&lt;h2 id="mx-records-mail-routing"&gt;MX Records: Mail Routing &lt;a class="headline-link" href="#mx-records-mail-routing" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;MX records identify the servers that accept email for a domain. Each value starts with a preference number, and lower numbers are preferred:&lt;/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;example.com. 3600 IN MX 10 mail1.example.com.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;example.com. 3600 IN MX 20 mail2.example.com.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;A sending server tries &lt;code&gt;mail1.example.com&lt;/code&gt; first and falls back to &lt;code&gt;mail2.example.com&lt;/code&gt; if the preferred server is unavailable. Records with equal preference can share delivery attempts.&lt;/p&gt;
&lt;p&gt;An MX target must be a hostname that resolves directly through A or AAAA records. It cannot be an IP address or a CNAME alias. The trailing dot is also important in a zone file because omitting it may cause the DNS server to append the zone name.&lt;/p&gt;
&lt;h2 id="txt-records-verification-and-policy-data"&gt;TXT Records: Verification and Policy Data &lt;a class="headline-link" href="#txt-records-verification-and-policy-data" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;TXT records store text associated with a name. Providers use them for domain-ownership challenges, while email systems use them for SPF, DKIM, and DMARC:&lt;/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;example.com. 3600 IN TXT &amp;#34;v=spf1 mx -all&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;_dmarc.example.com. 3600 IN TXT &amp;#34;v=DMARC1; p=quarantine&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The SPF value lists which systems may send mail for the domain. A domain must not publish multiple TXT records beginning with &lt;code&gt;v=spf1&lt;/code&gt; at the same name. DMARC lives at &lt;code&gt;_dmarc&lt;/code&gt;, while DKIM keys use a selector name such as &lt;code&gt;default._domainkey.example.com&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Long TXT data can appear as several quoted strings because each individual character-string has a size limit. DNS clients concatenate those strings into one record value. Our guide to &lt;a href="https://linuxize.com/post/email-authentication-spf-dkim-dmarc/"&gt;SPF, DKIM, and DMARC&lt;/a&gt;
explains how the three email checks work together.&lt;/p&gt;
&lt;h2 id="ns-records-zone-delegation"&gt;NS Records: Zone Delegation &lt;a class="headline-link" href="#ns-records-zone-delegation" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;NS records name the authoritative servers for a DNS zone:&lt;/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;example.com. 86400 IN NS ns1.dns-provider.net.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;example.com. 86400 IN NS ns2.dns-provider.net.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The parent zone publishes a delegation that directs resolvers to these servers, and the delegated zone publishes its own NS set at the apex. Changing name servers at a registrar updates the parent delegation. The parent and authoritative-zone sets should agree, or different resolution paths may produce inconsistent results.&lt;/p&gt;
&lt;p&gt;DNS zones should have at least two authoritative servers for availability, and most registries require it. Managed providers normally create the apex NS records automatically, although you still select or confirm the delegation through the registrar.&lt;/p&gt;
&lt;h2 id="soa-records-zone-metadata"&gt;SOA Records: Zone Metadata &lt;a class="headline-link" href="#soa-records-zone-metadata" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Every zone contains one SOA (start of authority) record. It identifies the primary server, encodes the administrator contact, and stores a serial number and timers used by secondary servers:&lt;/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;example.com. 86400 IN SOA ns1.dns-provider.net. admin.example.com. (
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 2026082101 ; serial
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 7200 ; refresh
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 3600 ; retry
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 1209600 ; expire
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 3600 ) ; minimum&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;In this example, &lt;code&gt;admin.example.com.&lt;/code&gt; represents the email address &lt;code&gt;admin@example.com&lt;/code&gt;. Secondary servers compare the serial number with their copy and transfer the zone when the serial increases.&lt;/p&gt;
&lt;p&gt;The final MINIMUM field participates in negative caching. A resolver may cache a response stating that a name or record does not exist for the lower of this value and the SOA record&amp;rsquo;s own TTL. Managed DNS services maintain the SOA fields for you, but the serial and timers remain useful when diagnosing stale or inconsistent authoritative servers.&lt;/p&gt;
&lt;h2 id="ptr-records-reverse-dns"&gt;PTR Records: Reverse DNS &lt;a class="headline-link" href="#ptr-records-reverse-dns" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A PTR record maps an IP address to a hostname. IPv4 records live below &lt;code&gt;in-addr.arpa&lt;/code&gt;, with the address octets reversed, while IPv6 uses &lt;code&gt;ip6.arpa&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="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;10.113.0.203.in-addr.arpa. 3600 IN PTR mail.example.com.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The organization that controls the IP block also controls its reverse zone. For a server address, this is usually your hosting provider or ISP, so you configure PTR data through its control panel rather than your domain&amp;rsquo;s normal DNS zone.&lt;/p&gt;
&lt;p&gt;Forward and reverse records are independent. Mail systems often check that the PTR hostname resolves forward to the sending address, so a mail server should have matching PTR and A or AAAA data.&lt;/p&gt;
&lt;h2 id="srv-records-service-discovery"&gt;SRV Records: Service Discovery &lt;a class="headline-link" href="#srv-records-service-discovery" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;An SRV record advertises the hostname and port for a service. Its owner name begins with underscore-prefixed service and protocol labels:&lt;/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;_sip._tcp.example.com. 3600 IN SRV 10 60 5060 sipserver.example.com.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The four values are priority, weight, port, and target. Clients prefer lower priorities. When several records share a priority, weight helps distribute requests among their targets.&lt;/p&gt;
&lt;p&gt;SRV records are common with SIP, XMPP, LDAP, Active Directory, and some game servers. Web browsers did not adopt SRV for ordinary HTTP and HTTPS navigation, so websites still depend on address, alias, and newer HTTPS records.&lt;/p&gt;
&lt;h2 id="caa-records-certificate-issuance"&gt;CAA Records: Certificate Issuance &lt;a class="headline-link" href="#caa-records-certificate-issuance" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A CAA record authorizes a certificate authority to issue TLS certificates for 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="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;example.com. 3600 IN CAA 0 issue &amp;#34;letsencrypt.org&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;A public certificate authority checks the relevant CAA set before issuance. If a name has no CAA record, the search continues up its DNS name hierarchy, so a policy at &lt;code&gt;example.com&lt;/code&gt; also applies to subdomains that do not publish their own CAA set.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;issue&lt;/code&gt; property covers standard certificates, &lt;code&gt;issuewild&lt;/code&gt; can set a separate wildcard policy, and &lt;code&gt;iodef&lt;/code&gt; supplies a contact for policy reports. Check the issuer identifier required by your certificate authority before adding CAA records. An incorrect policy can block both initial issuance and automated renewal.&lt;/p&gt;
&lt;h2 id="other-dns-record-types"&gt;Other DNS Record Types &lt;a class="headline-link" href="#other-dns-record-types" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;DNSSEC uses DNSKEY, DS, RRSIG, NSEC, and NSEC3 records so validating resolvers can authenticate DNS answers. The DNS provider creates signatures and DNSKEY records in the zone, while a DS record in the parent zone completes the chain of trust. Enabling DNSSEC only at the provider without publishing the required DS record does not establish that chain.&lt;/p&gt;
&lt;p&gt;SVCB and HTTPS records publish connection parameters and alternative service endpoints. HTTPS records can advertise supported application protocols, including HTTP/3, and provide address hints before a client opens its connection. DNS providers and content delivery networks may create these records automatically.&lt;/p&gt;
&lt;p&gt;You may also encounter TLSA for DANE certificate association, SSHFP for SSH host-key fingerprints, and NAPTR for rule-based service discovery. These specialized types are not needed for most domain setups.&lt;/p&gt;
&lt;h2 id="querying-dns-records-with-dig"&gt;Querying DNS Records with dig &lt;a class="headline-link" href="#querying-dns-records-with-dig" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The general short-answer form of a &lt;code&gt;dig&lt;/code&gt; query 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;dig +short NAME TYPE&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For a stable public example, query the A records for Cloudflare&amp;rsquo;s resolver hostname:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dig +short one.one.one.one A&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.0.0.1
1.1.1.1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The order may vary because both addresses belong to the same A record set. Remove &lt;code&gt;+short&lt;/code&gt; when you need the response status, authoritative flag, TTL, and server details.&lt;/p&gt;
&lt;p&gt;To inspect a domain&amp;rsquo;s mail routing, request its MX records:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dig +short gmail.com MX&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 gmail-smtp-in.l.google.com.
10 alt1.gmail-smtp-in.l.google.com.
20 alt2.gmail-smtp-in.l.google.com.
30 alt3.gmail-smtp-in.l.google.com.
40 alt4.gmail-smtp-in.l.google.com.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Each answer starts with its preference number, followed by the mail-server hostname. A sending server tries &lt;code&gt;gmail-smtp-in.l.google.com&lt;/code&gt; first because its preference of 5 is the lowest. The returned servers and their order can change as the domain operator updates its mail infrastructure.&lt;/p&gt;
&lt;p&gt;TXT, CAA, NS, and other records use the same 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;dig +short example.com TXT
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dig +short example.com CAA
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dig +short example.com NS&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For reverse DNS, pass an address to &lt;code&gt;-x&lt;/code&gt; instead of building the reverse-zone name manually:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dig +short -x 1.1.1.1&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;one.one.one.one.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;a href="https://linuxize.com/post/how-to-use-dig-command-to-query-dns-in-linux/"&gt;dig command guide&lt;/a&gt;
covers server selection, trace queries, and DNSSEC checks. The &lt;a href="https://linuxize.com/post/host-command-in-linux/"&gt;host&lt;/a&gt;
and &lt;a href="https://linuxize.com/post/nslookup-command-in-linux/"&gt;nslookup&lt;/a&gt;
commands provide simpler alternatives. After changing a record, remember that your resolver or operating system may keep the previous answer until its TTL expires, although you can &lt;a href="https://linuxize.com/post/how-to-clear-the-dns-cache/"&gt;clear the local DNS cache&lt;/a&gt;
during testing.&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;Record type&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;Point a name to an IPv4 address&lt;/td&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;&lt;code&gt;203.0.113.10&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Point a name to an IPv6 address&lt;/td&gt;
&lt;td&gt;AAAA&lt;/td&gt;
&lt;td&gt;&lt;code&gt;2001:db8::10&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Make a name follow another hostname&lt;/td&gt;
&lt;td&gt;CNAME&lt;/td&gt;
&lt;td&gt;&lt;code&gt;example.com.&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Route incoming email&lt;/td&gt;
&lt;td&gt;MX&lt;/td&gt;
&lt;td&gt;&lt;code&gt;10 mail.example.com.&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Publish verification or policy text&lt;/td&gt;
&lt;td&gt;TXT&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;quot;v=spf1 mx -all&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delegate a zone to authoritative servers&lt;/td&gt;
&lt;td&gt;NS&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ns1.dns-provider.net.&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Store zone authority and transfer data&lt;/td&gt;
&lt;td&gt;SOA&lt;/td&gt;
&lt;td&gt;Primary server, serial, and timers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Map an IP address back to a hostname&lt;/td&gt;
&lt;td&gt;PTR&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mail.example.com.&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Advertise a service hostname and port&lt;/td&gt;
&lt;td&gt;SRV&lt;/td&gt;
&lt;td&gt;&lt;code&gt;10 60 5060 sip.example.com.&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restrict certificate authorities&lt;/td&gt;
&lt;td&gt;CAA&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0 issue &amp;quot;letsencrypt.org&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;Before saving a DNS change, confirm the record owner, value format, and whether the target must be an address or a hostname. Lower the TTL ahead of planned migrations, keep the old service available while caches expire, and use &lt;code&gt;dig&lt;/code&gt; to verify the answers that resolvers actually receive.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/dns-record-types-explained/featured_hu_bcf152475aceaab9.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>Block Devices, Partitions, and Filesystems in Linux Explained</title><link>https://linuxize.com/post/linux-block-devices-partitions-filesystems/</link><pubDate>Sat, 15 Aug 2026 10:35:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/linux-block-devices-partitions-filesystems/</guid><category>disk</category><description>Linux storage works in layers: block device, partition, filesystem, mount point. This guide explains what each layer does and which command inspects it.</description><content:encoded>&lt;p&gt;You attach a new disk to a server, run &lt;a href="https://linuxize.com/post/lsblk-command-in-linux/"&gt;&lt;code&gt;lsblk&lt;/code&gt;&lt;/a&gt;
, and the drive is there. Then you try to copy a file onto it and nothing works, because there is no path to copy to. The disk is visible to the kernel, but it has no partition, no filesystem, and no place in the directory tree.&lt;/p&gt;
&lt;p&gt;Linux storage is built from four layers stacked on top of each other, and most confusion comes from treating them as one thing. This guide explains what a block device, a partition, a filesystem, and a mount point each are, how they connect, and which command shows you the state of each layer.&lt;/p&gt;
&lt;h2 id="what-a-block-device-is"&gt;What a Block Device Is &lt;a class="headline-link" href="#what-a-block-device-is" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A block device is how the kernel represents a piece of storage that can be read and written in fixed-size chunks called blocks, rather than one byte at a time. Hard drives, SSDs, USB sticks, SD cards, and the virtual disks of a cloud instance are all block devices.&lt;/p&gt;
&lt;p&gt;Every block device gets a file under &lt;code&gt;/dev&lt;/code&gt;. The name depends on the driver that claims the hardware:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/dev/sda&lt;/code&gt;, &lt;code&gt;/dev/sdb&lt;/code&gt; - SATA, SAS, and USB storage&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/dev/nvme0n1&lt;/code&gt; - the first namespace of the first NVMe drive&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/dev/vda&lt;/code&gt; - a virtual disk on a KVM or cloud guest&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/dev/mmcblk0&lt;/code&gt; - an SD card or eMMC device&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These files are not ordinary files. Running &lt;code&gt;ls -l&lt;/code&gt; on one shows what they really 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;ls -l /dev/sda /dev/null&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;brw-rw---- 1 root disk 8, 0 Aug 14 09:12 /dev/sda
crw-rw-rw- 1 root root 1, 3 Aug 14 09:12 /dev/null&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The first character of the permission string is the giveaway. &lt;code&gt;b&lt;/code&gt; marks a block device and &lt;code&gt;c&lt;/code&gt; marks a character device such as &lt;code&gt;/dev/null&lt;/code&gt;. The pair of numbers where a file size would normally appear, &lt;code&gt;8, 0&lt;/code&gt;, is the major and minor number that identifies the driver and the specific device it handles.&lt;/p&gt;
&lt;p&gt;The kernel also publishes every block device under &lt;code&gt;/sys/block&lt;/code&gt;, which is where tools such as &lt;code&gt;lsblk&lt;/code&gt; read their information from. A block device at this stage is just an addressable range of storage. Nothing on it means anything yet.&lt;/p&gt;
&lt;h2 id="partitions-divide-a-disk"&gt;Partitions Divide a Disk &lt;a class="headline-link" href="#partitions-divide-a-disk" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A partition is a labeled region of a block device. The disk keeps a small table near its start that records where each region begins and ends, and the kernel exposes each region as its own block device: &lt;code&gt;/dev/sda1&lt;/code&gt;, &lt;code&gt;/dev/sda2&lt;/code&gt;, and so on. NVMe and SD devices insert a &lt;code&gt;p&lt;/code&gt; before the number, so the first partition of &lt;code&gt;/dev/nvme0n1&lt;/code&gt; is &lt;code&gt;/dev/nvme0n1p1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Two partition table formats are in common use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MBR&lt;/strong&gt; (also called msdos) is the older format. It supports four primary partitions, works around that limit with extended partitions, and cannot address a disk larger than 2 TiB with standard 512-byte sectors.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GPT&lt;/strong&gt; is the modern format. It commonly provides 128 partition entries, supports disks far larger than MBR, and stores a backup copy of the table at the end of the disk. UEFI firmware supports both GPT and legacy MBR, though GPT is the standard choice for current UEFI installations.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To see which format a disk uses, run &lt;a href="https://linuxize.com/post/fdisk-command-in-linux/"&gt;&lt;code&gt;fdisk&lt;/code&gt;&lt;/a&gt;
with the &lt;code&gt;-l&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 fdisk -l /dev/sda&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 the disk size and model, then prints a &lt;code&gt;Disklabel type&lt;/code&gt; line that reads either &lt;code&gt;gpt&lt;/code&gt; or &lt;code&gt;dos&lt;/code&gt;, followed by the partition list.&lt;/p&gt;
&lt;p&gt;Partitioning is a convention rather than a hard requirement. You can create a filesystem directly on a whole disk, and some storage appliances do exactly that. On a system that boots from the disk, though, partitions are what separate the EFI system partition, the boot files, and the root filesystem from each other.&lt;/p&gt;
&lt;p&gt;A partition still holds no structure. It marks out space and stops there.&lt;/p&gt;
&lt;h2 id="filesystems-give-a-partition-structure"&gt;Filesystems Give a Partition Structure &lt;a class="headline-link" href="#filesystems-give-a-partition-structure" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A filesystem is the format written inside a partition that turns raw space into directories, filenames, permissions, and timestamps. Creating one is called formatting, and the &lt;code&gt;mkfs&lt;/code&gt; family of commands does it:&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;Creating a filesystem destroys any existing filesystem and data on the target. Before running &lt;code&gt;mkfs&lt;/code&gt;, confirm the device name with &lt;code&gt;lsblk -f&lt;/code&gt;, verify that it is the intended empty partition, and make sure it is not mounted.&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 mkfs.ext4 /dev/sdb1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This writes ext4 metadata across &lt;code&gt;/dev/sdb1&lt;/code&gt;, including the superblock, the inode tables, and the free-space maps. From that moment the partition can hold files, and each file gets an &lt;a href="https://linuxize.com/post/what-is-an-inode-in-linux/"&gt;inode&lt;/a&gt;
that stores its metadata.&lt;/p&gt;
&lt;p&gt;Linux supports many filesystem types, and the choice matters:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ext4&lt;/code&gt; - the default on most Debian and Ubuntu installations, stable and well understood&lt;/li&gt;
&lt;li&gt;&lt;code&gt;xfs&lt;/code&gt; - the default on RHEL and derivatives, strong with large files and parallel writes&lt;/li&gt;
&lt;li&gt;&lt;code&gt;btrfs&lt;/code&gt; - supports snapshots, checksums, and built-in volume management&lt;/li&gt;
&lt;li&gt;&lt;code&gt;vfat&lt;/code&gt; - used for EFI System Partitions and broadly compatible removable drives&lt;/li&gt;
&lt;li&gt;&lt;code&gt;exfat&lt;/code&gt; - suited to large removable drives shared with Windows and macOS&lt;/li&gt;
&lt;li&gt;&lt;code&gt;swap&lt;/code&gt; - not a filesystem for files at all, but a formatted area the kernel uses as &lt;a href="https://linuxize.com/post/create-a-linux-swap-file/"&gt;swap space&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Formatting also assigns the filesystem a UUID, and optionally a label. Both identify the filesystem itself rather than the device it happens to sit on. This distinction matters more than it first appears: device names are assigned in detection order, so a disk that is &lt;code&gt;/dev/sdb&lt;/code&gt; today can come up as &lt;code&gt;/dev/sdc&lt;/code&gt; after you add another drive or reboot a cloud instance. The UUID does not move. That is why configuration files should reference &lt;code&gt;UUID=&lt;/code&gt; instead of &lt;code&gt;/dev/sdb1&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="mount-points-attach-a-filesystem-to-the-tree"&gt;Mount Points Attach a Filesystem to the Tree &lt;a class="headline-link" href="#mount-points-attach-a-filesystem-to-the-tree" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Linux has no drive letters. Every filesystem on the machine appears somewhere inside a single tree that starts at &lt;code&gt;/&lt;/code&gt;. A mount point is the directory where a filesystem is attached, and &lt;a href="https://linuxize.com/post/how-to-mount-and-unmount-file-systems-in-linux/"&gt;mounting&lt;/a&gt;
is the act of attaching it. Create the mount point, then attach the filesystem:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 /srv/data
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo mount /dev/sdb1 /srv/data&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The first command creates the directory if it does not already exist. After the second command, everything written under &lt;code&gt;/srv/data&lt;/code&gt; lands on &lt;code&gt;/dev/sdb1&lt;/code&gt;, while the rest of the tree stays on whatever filesystem holds &lt;code&gt;/&lt;/code&gt;. The reader cannot tell from the path alone which disk a file lives on, which is the point of the design.&lt;/p&gt;
&lt;p&gt;A mount point is an ordinary directory. There is no special flag that makes &lt;code&gt;/srv/data&lt;/code&gt; mountable, and any empty directory works.&lt;/p&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;If the directory already contains files when you mount over it, those files are hidden rather than deleted. They are still on the underlying filesystem and reappear once you unmount.&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;A manual &lt;code&gt;mount&lt;/code&gt; command lasts until reboot. To make it persistent, the filesystem needs an entry in &lt;a href="https://linuxize.com/post/etc-fstab-file/"&gt;&lt;code&gt;/etc/fstab&lt;/code&gt;&lt;/a&gt;
, which the system reads at boot and mounts automatically.&lt;/p&gt;
&lt;h2 id="walking-through-one-real-layout"&gt;Walking Through One Real Layout &lt;a class="headline-link" href="#walking-through-one-real-layout" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Putting the four layers side by side makes the stack easier to read. The &lt;code&gt;-o&lt;/code&gt; flag selects the columns, and adding &lt;code&gt;TYPE&lt;/code&gt; shows what each row actually 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;lsblk -o NAME,TYPE,FSTYPE,LABEL,MOUNTPOINTS&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 TYPE FSTYPE LABEL MOUNTPOINTS
nvme0n1 disk
├─nvme0n1p1 part vfat /boot/efi
├─nvme0n1p2 part ext4 boot /boot
└─nvme0n1p3 part LVM2_member
├─vg0-root lvm ext4 root /
└─vg0-swap lvm swap [SWAP]
sdb disk
└─sdb1 part xfs data /srv/data&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Read it from the outside in. &lt;code&gt;nvme0n1&lt;/code&gt; and &lt;code&gt;sdb&lt;/code&gt; have &lt;code&gt;TYPE&lt;/code&gt; of &lt;code&gt;disk&lt;/code&gt; and no filesystem of their own, because they are the block devices. The indented rows marked &lt;code&gt;part&lt;/code&gt; are partitions. The &lt;code&gt;FSTYPE&lt;/code&gt; column tells you which of those partitions were formatted, and with what. The &lt;code&gt;MOUNTPOINTS&lt;/code&gt; column tells you where each formatted filesystem was attached.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;nvme0n1p3&lt;/code&gt; shows all four layers coming apart cleanly. It is a partition, it has a format, but that format is &lt;code&gt;LVM2_member&lt;/code&gt; rather than a filesystem you can browse, and it has no mount point. Its two children are the layer that carries the actual filesystems.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;sdb&lt;/code&gt; disk is the simple case: one disk, one partition, one XFS filesystem labeled &lt;code&gt;data&lt;/code&gt;, mounted at &lt;code&gt;/srv/data&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="where-lvm-raid-and-encryption-fit"&gt;Where LVM, RAID, and Encryption Fit &lt;a class="headline-link" href="#where-lvm-raid-and-encryption-fit" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The four layers describe the common path, but Linux lets you insert extra layers between the partition and the filesystem. Each one consumes a block device and produces a new one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;LVM&lt;/strong&gt; groups one or more physical volumes into a volume group, then carves logical volumes out of it. The logical volume is a block device at &lt;code&gt;/dev/mapper/vg0-root&lt;/code&gt;, and you format that instead of the partition. Logical volumes can be resized and moved between disks without repartitioning, which is why servers so often use them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Software RAID&lt;/strong&gt; combines several devices into one &lt;code&gt;/dev/md0&lt;/code&gt; device with mirroring or striping across the members. The filesystem sits on &lt;code&gt;/dev/md0&lt;/code&gt; and never sees the individual disks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;LUKS encryption&lt;/strong&gt; wraps a device in an encrypted container. Unlocking it produces a decrypted block device under &lt;code&gt;/dev/mapper&lt;/code&gt;, and the filesystem goes there. &lt;code&gt;lsblk&lt;/code&gt; shows the container with &lt;code&gt;TYPE&lt;/code&gt; of &lt;code&gt;crypt&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;These stack in any sensible order, and a common server layout runs all of them at once: partition, then LUKS, then LVM, then ext4. Each layer only needs to know about the block device directly beneath it.&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;Layer&lt;/th&gt;
&lt;th&gt;What it is&lt;/th&gt;
&lt;th&gt;Typical name&lt;/th&gt;
&lt;th&gt;Inspect with&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Block device&lt;/td&gt;
&lt;td&gt;Raw addressable storage exposed by the kernel&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/dev/sda&lt;/code&gt;, &lt;code&gt;/dev/nvme0n1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;lsblk -d&lt;/code&gt;, &lt;code&gt;ls -l /dev/sd*&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Partition&lt;/td&gt;
&lt;td&gt;A labeled region of a block device&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/dev/sda1&lt;/code&gt;, &lt;code&gt;/dev/nvme0n1p1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo fdisk -l&lt;/code&gt;, &lt;code&gt;lsblk&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Filesystem&lt;/td&gt;
&lt;td&gt;The format written inside a partition&lt;/td&gt;
&lt;td&gt;ext4, xfs, btrfs, vfat&lt;/td&gt;
&lt;td&gt;&lt;code&gt;lsblk -f&lt;/code&gt;, &lt;code&gt;blkid&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mount point&lt;/td&gt;
&lt;td&gt;The directory where a filesystem is attached&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/&lt;/code&gt;, &lt;code&gt;/boot&lt;/code&gt;, &lt;code&gt;/srv/data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;findmnt&lt;/code&gt;, &lt;code&gt;df -h&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 a partition and a filesystem?&lt;/strong&gt;&lt;br&gt;
A partition reserves a region of a disk and records its boundaries in the partition table. A filesystem is the structure written inside that region so it can store files. A freshly created partition has no filesystem, which is why &lt;code&gt;mkfs&lt;/code&gt; is a separate step after &lt;code&gt;fdisk&lt;/code&gt; or &lt;code&gt;parted&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Is /dev/sda a file or a device?&lt;/strong&gt;&lt;br&gt;
Both, in a sense. &lt;code&gt;/dev/sda&lt;/code&gt; is a special file that acts as the interface to the device, so tools can open, read, and write it with ordinary file operations. The leading &lt;code&gt;b&lt;/code&gt; in &lt;code&gt;ls -l&lt;/code&gt; output marks it as a block device rather than a regular file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why does my disk name change between reboots?&lt;/strong&gt;&lt;br&gt;
Kernel device names are assigned in detection order, not fixed to the hardware. Adding a drive or rebooting a virtual machine can shift &lt;code&gt;/dev/sdb&lt;/code&gt; to &lt;code&gt;/dev/sdc&lt;/code&gt;. Refer to filesystems by &lt;code&gt;UUID=&lt;/code&gt; or &lt;code&gt;LABEL=&lt;/code&gt; in &lt;code&gt;/etc/fstab&lt;/code&gt; and scripts so the reference survives reordering.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How do I tell which disk a directory lives on?&lt;/strong&gt;&lt;br&gt;
Pass the path to &lt;code&gt;findmnt --target&lt;/code&gt;, for example &lt;code&gt;findmnt --target /srv/data&lt;/code&gt;, and it prints the source device, filesystem type, and mount options for the filesystem containing that path. Running &lt;a href="https://linuxize.com/post/how-to-check-disk-space-in-linux-using-the-df-command/"&gt;&lt;code&gt;df -h /srv/data&lt;/code&gt;&lt;/a&gt;
shows the source device, capacity, usage, and mount point instead.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Can I create a filesystem without partitioning first?&lt;/strong&gt;&lt;br&gt;
Yes. Running &lt;code&gt;mkfs.ext4 /dev/sdb&lt;/code&gt; creates an ext4 filesystem directly on the whole disk with no partition table. Filesystem-aware tools such as &lt;code&gt;lsblk -f&lt;/code&gt; and &lt;code&gt;blkid&lt;/code&gt; still detect it, while partitioning tools report that no partition table is present. This is destructive and should only be done intentionally on a verified empty data disk. Use partitions for boot disks and workflows that expect a partition table.&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;Once you can name which layer a problem sits on, the next check becomes clearer: a missing device is a kernel or cabling question, an unknown partition or filesystem needs inspection before you change it, and a known unmounted filesystem calls for &lt;code&gt;mount&lt;/code&gt;. Start with &lt;code&gt;lsblk -f&lt;/code&gt; on any unfamiliar machine, since it shows three of the four layers in a single screen.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/linux-block-devices-partitions-filesystems/featured_hu_3bf81da214777401.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>nmap Cheatsheet</title><link>https://linuxize.com/cheatsheet/nmap/</link><pubDate>Fri, 14 Aug 2026 15:15:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/cheatsheet/nmap/</guid><description>Quick reference for host discovery, port scanning, and service detection with nmap 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 &lt;code&gt;nmap&lt;/code&gt; command forms.&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;nmap 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Scan the 1000 most common TCP ports on one host&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmap 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Same scan as root, which enables the faster SYN scan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap -v 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Increase verbosity, repeat as &lt;code&gt;-vv&lt;/code&gt; for more detail&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap --reason 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show why each port is in its reported state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap --open 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Report only ports that are open&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="target-specification"&gt;Target Specification &lt;a class="headline-link" href="#target-specification" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Point a scan at one host, a range, a subnet, or a list.&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;nmap 192.168.1.10 10.0.0.5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Scan several hosts in one run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap 192.168.1.0/24&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Scan a whole subnet in CIDR notation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap 192.168.1.1-50&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Scan an address range&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap -iL targets.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read targets from a file, one per line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap --exclude 192.168.1.1 192.168.1.0/24&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Skip specific hosts in a larger scan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap -6 2001:db8::1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Scan an IPv6 target&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="host-discovery"&gt;Host Discovery &lt;a class="headline-link" href="#host-discovery" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Find which hosts are up before spending time on port scans.&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 nmap -sn 192.168.1.0/24&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ping scan: list live hosts without scanning ports&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap -Pn 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Skip discovery and treat the host as online&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap -PS22,80,443 192.168.1.0/24&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Discover hosts with TCP SYN probes to those ports&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap -PA80 192.168.1.0/24&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Discover hosts with TCP ACK probes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmap -PE 192.168.1.0/24&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Discover hosts with ICMP echo requests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap -n 192.168.1.0/24&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Skip reverse DNS lookups to speed up the scan&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="port-selection"&gt;Port Selection &lt;a class="headline-link" href="#port-selection" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Control which ports the scan covers.&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;nmap -p 22 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Scan a single port&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap -p 22,80,443 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Scan a list of ports&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap -p 1-1024 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Scan a port range&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap -p- 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Scan all 65535 TCP ports&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap -F 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fast scan of the top 100 ports&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap --top-ports 20 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Scan the 20 most common ports&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmap -sU -sS -p U:53,T:80,443 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mix UDP and TCP ports, which needs both scan types&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="scan-types"&gt;Scan Types &lt;a class="headline-link" href="#scan-types" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Pick how nmap probes each port. The raw-packet scans need root.&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 nmap -sS 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;TCP SYN scan: fast, the default when running as root&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap -sT 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;TCP connect scan: the fallback for an unprivileged user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmap -sU 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;UDP scan: slow, so pair it with &lt;code&gt;--top-ports&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmap -sA 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ACK scan: map which ports a firewall filters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmap -sn 192.168.1.0/24&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No port scan at all, discovery only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap -sL 192.168.1.0/24&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List scan: show the targets without sending probes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="service-and-os-detection"&gt;Service and OS Detection &lt;a class="headline-link" href="#service-and-os-detection" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Identify what is listening and what the host is running.&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;nmap -sV 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Detect service names and version numbers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap -sV --version-intensity 9 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Probe harder for versions, from 0 (light) to 9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmap -O 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Guess the operating system from TCP/IP fingerprints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmap -A 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Aggressive scan: &lt;code&gt;-sV&lt;/code&gt;, &lt;code&gt;-O&lt;/code&gt;, default scripts, and traceroute&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmap --traceroute 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Trace the route to each target&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="timing-and-performance"&gt;Timing and Performance &lt;a class="headline-link" href="#timing-and-performance" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Trade speed against accuracy and network load.&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;nmap -T4 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Faster timing, a good default on a local network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap -T2 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Slower and gentler on fragile or busy networks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap --min-rate 1000 192.168.1.0/24&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Send at least 1000 packets per second&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap --max-retries 1 192.168.1.0/24&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Give up sooner on unanswered probes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap --host-timeout 5m 192.168.1.0/24&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Abandon any host still scanning after 5 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="output-formats"&gt;Output Formats &lt;a class="headline-link" href="#output-formats" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Save results for review, diffing, or scripting.&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;nmap -oN scan.txt 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Write human readable output to a file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap -oX scan.xml 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Write XML for other tools to parse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap -oG scan.gnmap 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Write grepable output for &lt;code&gt;grep&lt;/code&gt; and &lt;code&gt;awk&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap -oA scan 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Write all three formats using one base name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap --append-output -oN scan.txt 192.168.1.11&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Append to an existing output file&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="nse-scripts"&gt;NSE Scripts &lt;a class="headline-link" href="#nse-scripts" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Run the Nmap Scripting Engine for deeper checks.&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;nmap -sC 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run the default script set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap --script http-title 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run one named script&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap --script &amp;quot;http-*&amp;quot; 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run every script matching a pattern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap --script vuln 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run the vulnerability script category&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap --script ssl-enum-ciphers -p 443 example.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List the TLS ciphers a server accepts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap --script-help ssh-auth-methods&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read what a script does before running it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="common-use-cases"&gt;Common Use Cases &lt;a class="headline-link" href="#common-use-cases" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Practical scans for day-to-day network work.&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 nmap -sn 192.168.1.0/24&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Inventory the hosts on a local network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmap -p- -T4 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Find every open TCP port on one server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap -sV --open 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List running services and their versions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nmap -p 80,443 --open 192.168.1.0/24&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Locate the web servers on a subnet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmap -sU --top-ports 20 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check the most common UDP services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo nmap -A -oA audit 192.168.1.10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full audit of one host, saved in every format&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;nmap&lt;/code&gt; problems.&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;You requested a scan type which requires root privileges&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run the command with &lt;code&gt;sudo&lt;/code&gt;, or use &lt;code&gt;-sT&lt;/code&gt; instead of &lt;code&gt;-sS&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Note: Host seems down&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add &lt;code&gt;-Pn&lt;/code&gt; when ICMP is blocked but the host is reachable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scan takes far too long&lt;/td&gt;
&lt;td&gt;Add &lt;code&gt;-T4&lt;/code&gt;, narrow the ports with &lt;code&gt;-F&lt;/code&gt; or &lt;code&gt;--top-ports&lt;/code&gt;, and add &lt;code&gt;-n&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Every UDP port shows &lt;code&gt;open|filtered&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;UDP has no handshake, so add &lt;code&gt;-sV&lt;/code&gt; or scan fewer ports for a clearer answer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Results differ from a local port check&lt;/td&gt;
&lt;td&gt;The firewall may filter the port; compare with &lt;code&gt;ss -tulpn&lt;/code&gt; on the host itself&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 broader port and network troubleshooting.&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/nmap-command/"&gt;nmap Command in Linux&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Full &lt;code&gt;nmap&lt;/code&gt; guide with detailed examples&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/check-open-ports-linux/"&gt;How to Check Open Ports in Linux&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Scan a host for open ports with nmap, netcat, or telnet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/ss-command-in-linux/"&gt;ss Command in Linux&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Inspect sockets and listening services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/cheatsheet/netcat/"&gt;netcat cheatsheet&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Test single ports and move data between hosts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/cheatsheet/tcpdump/"&gt;tcpdump cheatsheet&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Capture the packets behind a scan result&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;</content:encoded></item><item><title>lsblk Command in Linux: List Block Devices</title><link>https://linuxize.com/post/lsblk-command-in-linux/</link><pubDate>Thu, 13 Aug 2026 11:20:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/lsblk-command-in-linux/</guid><category>disk</category><category>linux commands</category><description>Use lsblk to list disks, partitions, filesystems, UUIDs, and mount points in Linux, then customize columns and produce JSON output for scripts.</description><content:encoded>&lt;p&gt;When you plug in a new drive, set up a fresh partition, or try to remember which disk is mounted where, you need a quick way to see every block device attached to the system. The &lt;code&gt;lsblk&lt;/code&gt; command reads information from &lt;code&gt;sysfs&lt;/code&gt; and the &lt;code&gt;udev&lt;/code&gt; database and prints a tidy tree of disks, partitions, loop devices, and LVM volumes, together with their sizes and mount points.&lt;/p&gt;
&lt;p&gt;This guide explains how to use &lt;code&gt;lsblk&lt;/code&gt; to inspect block devices, pick the columns you need, and format the output for scripts.&lt;/p&gt;
&lt;h2 id="lsblk-syntax"&gt;lsblk Syntax &lt;a class="headline-link" href="#lsblk-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;lsblk [OPTIONS] [DEVICE...]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;With no arguments, &lt;code&gt;lsblk&lt;/code&gt; lists every block device except RAM disks and empty devices. You can pass one or more device paths to limit the output, for example &lt;code&gt;lsblk /dev/sda&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Unlike &lt;a href="https://linuxize.com/post/fdisk-command-in-linux/"&gt;&lt;code&gt;fdisk&lt;/code&gt;&lt;/a&gt;
, &lt;code&gt;lsblk&lt;/code&gt; does not need root privileges for most queries, which makes it safer for quick checks.&lt;/p&gt;
&lt;h2 id="basic-usage"&gt;Basic Usage &lt;a class="headline-link" href="#basic-usage" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Run &lt;code&gt;lsblk&lt;/code&gt; with no options to see a tree of all devices 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;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 16G 0 part [SWAP]
└─sda3 8:3 0 449.3G 0 part /
sdb 8:16 1 14.3G 0 disk
└─sdb1 8:17 1 14.3G 0 part /media/john/usb
sr0 11:0 1 1024M 0 rom&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Each row describes one device. The tree characters on the left show which partitions belong to which disk. The &lt;code&gt;TYPE&lt;/code&gt; column tells you whether the entry is a whole &lt;code&gt;disk&lt;/code&gt;, a &lt;code&gt;part&lt;/code&gt;ition, an optical &lt;code&gt;rom&lt;/code&gt;, an LVM &lt;code&gt;lvm&lt;/code&gt; volume, a &lt;code&gt;crypt&lt;/code&gt; container, and so on. The &lt;code&gt;MOUNTPOINTS&lt;/code&gt; column is empty when the partition is not mounted.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;RM&lt;/code&gt; column is &lt;code&gt;1&lt;/code&gt; when the kernel marks a device as removable, and &lt;code&gt;RO&lt;/code&gt; is &lt;code&gt;1&lt;/code&gt; for read-only devices. Some USB drives report &lt;code&gt;RM&lt;/code&gt; as &lt;code&gt;0&lt;/code&gt;, so use the &lt;code&gt;TRAN&lt;/code&gt; column when you specifically need to identify USB storage.&lt;/p&gt;
&lt;h2 id="hide-partitions-and-child-devices"&gt;Hide Partitions and Child Devices &lt;a class="headline-link" href="#hide-partitions-and-child-devices" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When you only care about top-level devices, pass the &lt;code&gt;-d&lt;/code&gt; (or &lt;code&gt;--nodeps&lt;/code&gt;) flag to hide their partitions, holders, and slaves:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 -d&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
sdb 8:16 1 14.3G 0 disk
sr0 11:0 1 1024M 0 rom&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is handy when you do not want partition noise. It does not filter by device type, so entries such as the &lt;code&gt;sr0&lt;/code&gt; optical drive can still appear.&lt;/p&gt;
&lt;p&gt;Snap packages mount as loop devices, so on an Ubuntu desktop the list can fill with &lt;code&gt;loop0&lt;/code&gt; through &lt;code&gt;loop20&lt;/code&gt; before you reach the real disks. Exclude them by major number with &lt;code&gt;-e&lt;/code&gt; (or &lt;code&gt;--exclude&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 -e &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;Major number 7 is the loop driver. You can pass several numbers as a comma-separated list, for example &lt;code&gt;lsblk -e 7,11&lt;/code&gt; to drop optical drives as well.&lt;/p&gt;
&lt;h2 id="show-filesystem-information"&gt;Show Filesystem Information &lt;a class="headline-link" href="#show-filesystem-information" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Passing &lt;code&gt;-f&lt;/code&gt; (or &lt;code&gt;--fs&lt;/code&gt;) replaces the default columns with filesystem details such as the type, label, UUID, and how full each mounted filesystem 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;lsblk -f&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 FSTYPE FSVER LABEL UUID FSAVAIL FSUSE% MOUNTPOINTS
sda
├─sda1 vfat FAT32 4F3A-91BC 470.1M 8% /boot/efi
├─sda2 swap 1 9f4b5a44-6d1e-44a8-a0ae-2c3d3e4f1234 [SWAP]
└─sda3 ext4 1.0 root be2e1f8e-7a0b-4ab1-9f1d-5c6d7e8f9012 312.5G 25% /
sdb
└─sdb1 vfat FAT32 USB 1A2B-3C4D 12.8G 10% /media/john/usb&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is usually the fastest way to find a UUID for &lt;code&gt;/etc/fstab&lt;/code&gt; or to confirm which filesystem type sits on a given partition.&lt;/p&gt;
&lt;h2 id="pick-specific-columns"&gt;Pick Specific Columns &lt;a class="headline-link" href="#pick-specific-columns" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The default columns cover the common cases, but you can choose exactly what to show with the &lt;code&gt;-o&lt;/code&gt; (or &lt;code&gt;--output&lt;/code&gt;) option. Pass a comma-separated list of column names:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 -o NAME,SIZE,TYPE,MOUNTPOINTS&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 SIZE TYPE MOUNTPOINTS
sda 465.8G disk
├─sda1 512M part /boot/efi
├─sda2 16G part [SWAP]
└─sda3 449.3G part /
sdb 14.3G disk
└─sdb1 14.3G part /media/john/usb
sr0 1024M rom&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To see the full list of columns &lt;code&gt;lsblk&lt;/code&gt; understands, 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;lsblk --list-columns&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On util-linux versions older than 2.41, that option does not exist yet, and &lt;code&gt;lsblk --help&lt;/code&gt; prints the column list instead.&lt;/p&gt;
&lt;p&gt;Useful columns include &lt;code&gt;UUID&lt;/code&gt;, &lt;code&gt;LABEL&lt;/code&gt;, &lt;code&gt;MODEL&lt;/code&gt;, &lt;code&gt;SERIAL&lt;/code&gt;, &lt;code&gt;VENDOR&lt;/code&gt;, &lt;code&gt;FSTYPE&lt;/code&gt;, &lt;code&gt;PARTTYPE&lt;/code&gt;, &lt;code&gt;HOTPLUG&lt;/code&gt;, and &lt;code&gt;TRAN&lt;/code&gt; (transport, for example &lt;code&gt;sata&lt;/code&gt;, &lt;code&gt;nvme&lt;/code&gt;, or &lt;code&gt;usb&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;To add columns to the default set rather than replace it, prefix the list with &lt;code&gt;+&lt;/code&gt;. For example, this command adds the model and serial 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;lsblk -o +MODEL,SERIAL&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;-O&lt;/code&gt; (or &lt;code&gt;--output-all&lt;/code&gt;) flag has a different purpose: it displays every available column.&lt;/p&gt;
&lt;h2 id="show-device-paths"&gt;Show Device Paths &lt;a class="headline-link" href="#show-device-paths" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;By default, &lt;code&gt;lsblk&lt;/code&gt; prints short names such as &lt;code&gt;sda1&lt;/code&gt;. If you want the full &lt;code&gt;/dev&lt;/code&gt; path in the &lt;code&gt;NAME&lt;/code&gt; column, add &lt;code&gt;-p&lt;/code&gt; (or &lt;code&gt;--paths&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 -p&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
/dev/sda 8:0 0 465.8G 0 disk
├─/dev/sda1 8:1 0 512M 0 part /boot/efi
├─/dev/sda2 8:2 0 16G 0 part [SWAP]
└─/dev/sda3 8:3 0 449.3G 0 part /&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Full paths are easier to copy into a script or a mount command.&lt;/p&gt;
&lt;h2 id="list-a-single-device"&gt;List a Single Device &lt;a class="headline-link" href="#list-a-single-device" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can pass a device path to inspect only that device and its partitions:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 /dev/sda&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This narrows the output to one disk, which is useful on servers with many drives.&lt;/p&gt;
&lt;h2 id="flat-and-ascii-output"&gt;Flat and ASCII Output &lt;a class="headline-link" href="#flat-and-ascii-output" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The tree layout is readable in a terminal but awkward to parse in scripts. Use &lt;code&gt;-l&lt;/code&gt; (or &lt;code&gt;--list&lt;/code&gt;) to print one device per line with no tree 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;lsblk -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;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 16G 0 part [SWAP]
sda3 8:3 0 449.3G 0 part /&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If your terminal does not render Unicode box-drawing characters correctly, swap them for plain ASCII with &lt;code&gt;-i&lt;/code&gt; (or &lt;code&gt;--ascii&lt;/code&gt;).&lt;/p&gt;
&lt;h2 id="machine-readable-output"&gt;Machine-Readable Output &lt;a class="headline-link" href="#machine-readable-output" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For scripts, the &lt;code&gt;-J&lt;/code&gt; (or &lt;code&gt;--json&lt;/code&gt;) flag prints the same information as structured JSON that is easy to parse with tools such as &lt;code&gt;jq&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 -J -o NAME,SIZE,FSTYPE /dev/sda&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;#34;blockdevices&amp;#34;: [
{
&amp;#34;name&amp;#34;: &amp;#34;sda&amp;#34;,
&amp;#34;size&amp;#34;: &amp;#34;465.8G&amp;#34;,
&amp;#34;fstype&amp;#34;: null,
&amp;#34;children&amp;#34;: [
{
&amp;#34;name&amp;#34;: &amp;#34;sda1&amp;#34;,
&amp;#34;size&amp;#34;: &amp;#34;512M&amp;#34;,
&amp;#34;fstype&amp;#34;: &amp;#34;vfat&amp;#34;
},
{
&amp;#34;name&amp;#34;: &amp;#34;sda2&amp;#34;,
&amp;#34;size&amp;#34;: &amp;#34;16G&amp;#34;,
&amp;#34;fstype&amp;#34;: &amp;#34;swap&amp;#34;
},
{
&amp;#34;name&amp;#34;: &amp;#34;sda3&amp;#34;,
&amp;#34;size&amp;#34;: &amp;#34;449.3G&amp;#34;,
&amp;#34;fstype&amp;#34;: &amp;#34;ext4&amp;#34;
}
]
}
]
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Each disk becomes an object, and its partitions appear in the &lt;code&gt;children&lt;/code&gt; array, so the tree relationship survives the conversion. Fields that do not apply are &lt;code&gt;null&lt;/code&gt; rather than empty strings, which keeps the types stable for &lt;code&gt;jq&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can also use &lt;code&gt;-P&lt;/code&gt; (pairs) for line-oriented &lt;code&gt;KEY=&amp;quot;value&amp;quot;&lt;/code&gt; output, or &lt;code&gt;-r&lt;/code&gt; (raw) for space-separated fields with no padding.&lt;/p&gt;
&lt;p&gt;The default columns can change between util-linux releases. In scripts, always select the columns you expect with &lt;code&gt;-o&lt;/code&gt;, then choose a predictable format such as &lt;code&gt;-J&lt;/code&gt;, &lt;code&gt;-P&lt;/code&gt;, or &lt;code&gt;-l&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For example, to list only mounted ext4 partitions in a 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;lsblk -lnp -o NAME,FSTYPE,MOUNTPOINTS &lt;span class="p"&gt;|&lt;/span&gt; awk &lt;span class="s1"&gt;&amp;#39;$2 == &amp;#34;ext4&amp;#34; &amp;amp;&amp;amp; $3 != &amp;#34;&amp;#34; {print $1, $3}&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;The &lt;code&gt;-n&lt;/code&gt; (or &lt;code&gt;--noheadings&lt;/code&gt;) flag drops the header row so that the output is pure data.&lt;/p&gt;
&lt;h2 id="show-sizes-in-bytes"&gt;Show Sizes in Bytes &lt;a class="headline-link" href="#show-sizes-in-bytes" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Sizes are printed in human-readable units by default, for example &lt;code&gt;465.8G&lt;/code&gt;. For exact values, use &lt;code&gt;-b&lt;/code&gt; (or &lt;code&gt;--bytes&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 -b -o NAME,SIZE&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This is the form you want when comparing sizes in a script, since &lt;code&gt;465.8G&lt;/code&gt; is not directly comparable with &lt;code&gt;449.3G&lt;/code&gt; as a 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;Common &lt;code&gt;lsblk&lt;/code&gt; flags and what they do:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-a&lt;/code&gt; - Include empty devices and RAM disks that are normally hidden.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-b&lt;/code&gt; - Print sizes in bytes instead of human-readable units.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-d&lt;/code&gt; - Hide partitions, holders, and slaves.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-e LIST&lt;/code&gt; - Exclude devices by major number (7 is loop).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-f&lt;/code&gt; - Show filesystem information (type, label, UUID, usage).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-i&lt;/code&gt; - Use plain ASCII characters for the tree instead of Unicode.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-J&lt;/code&gt; - Output as JSON.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-l&lt;/code&gt; - Use a flat list instead of the tree layout.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-n&lt;/code&gt; - Omit the header row.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-o LIST&lt;/code&gt; - Choose which columns to display.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-o +LIST&lt;/code&gt; - Add columns to the default output.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-O&lt;/code&gt; - Display every available column.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-p&lt;/code&gt; - Print full device paths such as &lt;code&gt;/dev/sda1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-P&lt;/code&gt; - Output as &lt;code&gt;KEY=&amp;quot;value&amp;quot;&lt;/code&gt; pairs for shell scripts.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-r&lt;/code&gt; - Raw output with no padding.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-S&lt;/code&gt; - Show information about SCSI devices only.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-t&lt;/code&gt; - Print topology information (alignment, I/O sizes).&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;How is lsblk different from fdisk -l?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;lsblk&lt;/code&gt; reads from &lt;code&gt;sysfs&lt;/code&gt; and &lt;code&gt;udev&lt;/code&gt; and shows a tree of devices with mount points and filesystem details, all without root privileges for the common cases. &lt;a href="https://linuxize.com/post/fdisk-command-in-linux/"&gt;&lt;code&gt;fdisk -l&lt;/code&gt;&lt;/a&gt;
reads the partition table directly from the device and focuses on partition layout, sector offsets, and disk identifiers. Use &lt;code&gt;lsblk&lt;/code&gt; for a quick overview and &lt;code&gt;fdisk&lt;/code&gt; when you care about the raw partition table.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why is a device missing from lsblk output?&lt;/strong&gt;&lt;br&gt;
Run &lt;code&gt;lsblk -a&lt;/code&gt; to include empty devices and RAM disks that are normally hidden. If you just connected or changed a device, run &lt;code&gt;udevadm settle&lt;/code&gt; before trying &lt;code&gt;lsblk&lt;/code&gt; again so udev can finish processing the event. A drive that the kernel never detected cannot appear in the list; check the cable or port and inspect recent messages with &lt;code&gt;sudo dmesg | tail -n 30&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How can I get only the UUID of a partition?&lt;/strong&gt;&lt;br&gt;
Combine &lt;code&gt;-n&lt;/code&gt; to drop the header, &lt;code&gt;-o UUID&lt;/code&gt; to pick the column, and the device path:&lt;br&gt;
&lt;code&gt;lsblk -no UUID /dev/sda3&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Does lsblk show LVM and encrypted volumes?&lt;/strong&gt;&lt;br&gt;
Yes. LVM logical volumes appear as children of their physical volume with type &lt;code&gt;lvm&lt;/code&gt;, and LUKS-encrypted containers show as &lt;code&gt;crypt&lt;/code&gt;. The tree layout makes the stacking relationship easy to follow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Can lsblk show disk model or serial number?&lt;/strong&gt;&lt;br&gt;
Yes. Add them to the output list: &lt;code&gt;lsblk -o NAME,MODEL,SERIAL,SIZE&lt;/code&gt;. Some virtual devices do not expose these fields, so they may be empty.&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;lsblk&lt;/code&gt; is the fastest way to answer the &amp;ldquo;what disks and partitions does this machine have?&amp;rdquo; question, and its JSON and column-selection options make it equally useful inside scripts. Pair it with &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 usage stats and with &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;
when you are ready to attach a new device.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/lsblk-command-in-linux/featured_hu_e9209e660fdd0df0.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>How to Tail and Filter Logs in Real Time on Linux</title><link>https://linuxize.com/post/how-to-tail-and-filter-logs-in-real-time/</link><pubDate>Tue, 11 Aug 2026 09:30:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/how-to-tail-and-filter-logs-in-real-time/</guid><category>linux commands</category><description>Follow Linux logs in real time with tail -F, filter live output with grep, pause with less +F, and stream systemd service logs with journalctl -f.</description><content:encoded>&lt;p&gt;When you reproduce a bug or restart a service, the most useful view is often the log scrolling live as it happens. You want to see each new line when it is written, but you may also need to hide routine entries so the warning or error that matters does not disappear in the noise.&lt;/p&gt;
&lt;p&gt;Linux provides several ways to follow logs. Use &lt;code&gt;tail -F&lt;/code&gt; for a plain log file, &lt;code&gt;less +F&lt;/code&gt; when you need to pause and search, and &lt;code&gt;journalctl -f&lt;/code&gt; for output stored in the systemd journal. This guide shows how to use each tool and filter the live stream.&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 printable quick references, see the &lt;a href="https://linuxize.com/cheatsheet/tail/"&gt;tail&lt;/a&gt;
, &lt;a href="https://linuxize.com/cheatsheet/grep/"&gt;grep&lt;/a&gt;
, &lt;a href="https://linuxize.com/cheatsheet/less/"&gt;less&lt;/a&gt;
, and &lt;a href="https://linuxize.com/cheatsheet/journalctl/"&gt;journalctl&lt;/a&gt;
cheatsheets.&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;Follow a file&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tail -f /var/log/app.log&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Follow and survive rotation&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tail -F /var/log/app.log&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Follow only newly appended lines&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tail -n 0 -F /var/log/app.log&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Show last N lines, then follow&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tail -n 100 -f /var/log/app.log&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Follow several files at once&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tail -F /var/log/a.log /var/log/b.log&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Follow, scroll, search, and filter&lt;/td&gt;
&lt;td&gt;&lt;code&gt;less +F /var/log/app.log&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Follow a systemd unit&lt;/td&gt;
&lt;td&gt;&lt;code&gt;journalctl -f -u nginx&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Follow errors and more severe entries&lt;/td&gt;
&lt;td&gt;&lt;code&gt;journalctl -f -p err&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Filter file logs by text&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tail -F app.log | grep -i error&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Filter journal messages by text&lt;/td&gt;
&lt;td&gt;&lt;code&gt;journalctl -f -u nginx -g 'timeout|failed'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="follow-a-file-with-tail--f"&gt;Follow a File with tail -f &lt;a class="headline-link" href="#follow-a-file-with-tail--f" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;tail&lt;/code&gt; command prints the end of a file, and the &lt;code&gt;-f&lt;/code&gt; (&lt;code&gt;--follow&lt;/code&gt;) option keeps reading as new data is appended. This is the quickest way to watch a plain-text log:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tail -f /var/log/nginx/access.log&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The terminal shows the last 10 lines and then waits, adding each new request as it arrives. Press &lt;code&gt;Ctrl+C&lt;/code&gt; to stop. To start with more context, specify the number of existing lines to show:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tail -n &lt;span class="m"&gt;100&lt;/span&gt; -f /var/log/nginx/access.log&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 ignore existing entries and display only lines appended after the command starts, set the initial line count to zero:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tail -n &lt;span class="m"&gt;0&lt;/span&gt; -f /var/log/nginx/access.log&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;One catch is log rotation. Plain &lt;code&gt;-f&lt;/code&gt; follows the open file descriptor, so it can keep watching the old file after that file is renamed. On GNU &lt;code&gt;tail&lt;/code&gt;, &lt;code&gt;-F&lt;/code&gt; is equivalent to &lt;code&gt;--follow=name --retry&lt;/code&gt;: it watches the path and keeps trying to reopen the file when it is replaced or temporarily unavailable.&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tail -F /var/log/nginx/access.log&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You can also watch more than one file in the same session. Pass several paths, and &lt;code&gt;tail&lt;/code&gt; prints a &lt;code&gt;==&amp;gt; path &amp;lt;==&lt;/code&gt; header each time the output switches to a different 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;tail -F /var/log/nginx/access.log /var/log/nginx/error.log&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Those headers tell you which file produced the lines that follow, which matters when two logs use a similar format.&lt;/p&gt;
&lt;p&gt;Use &lt;code&gt;-F&lt;/code&gt; for a long-running session on a rotating log. The &lt;a href="https://linuxize.com/post/linux-tail-command/"&gt;tail command&lt;/a&gt;
guide covers line counts, byte offsets, and other follow options.&lt;/p&gt;
&lt;h2 id="filter-a-live-stream-with-grep"&gt;Filter a Live Stream with grep &lt;a class="headline-link" href="#filter-a-live-stream-with-grep" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Pipe the followed output into &lt;code&gt;grep&lt;/code&gt; to keep only matching lines. The following command ignores case, so it matches &lt;code&gt;error&lt;/code&gt;, &lt;code&gt;ERROR&lt;/code&gt;, and other capitalization variants:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tail -F /var/log/app.log &lt;span class="p"&gt;|&lt;/span&gt; grep -i error&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;In this two-command pipeline, &lt;code&gt;grep&lt;/code&gt; writes directly to the terminal and normally displays each match immediately. If you send its output into another command, GNU &lt;code&gt;grep&lt;/code&gt; switches to full buffering. Add &lt;code&gt;--line-buffered&lt;/code&gt; before the next pipeline stage so matches continue moving one line at a 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;tail -F /var/log/app.log &lt;span class="p"&gt;|&lt;/span&gt; grep --line-buffered -i error &lt;span class="p"&gt;|&lt;/span&gt; awk &lt;span class="s1"&gt;&amp;#39;{ print $1, $2, $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;p&gt;Here, &lt;code&gt;grep&lt;/code&gt; flushes each match to &lt;code&gt;awk&lt;/code&gt;, which prints the first two and last whitespace-separated fields. A later command can still have its own buffering rules, so check each stage if output remains delayed.&lt;/p&gt;
&lt;p&gt;Use extended regular expressions to match several severity words in one 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;tail -F /var/log/app.log &lt;span class="p"&gt;|&lt;/span&gt; grep -Ei &lt;span class="s1"&gt;&amp;#39;error|warning|critical&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 hide a noisy health-check endpoint while keeping all other requests, invert the match with &lt;code&gt;-v&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;tail -F /var/log/nginx/access.log &lt;span class="p"&gt;|&lt;/span&gt; grep -v &lt;span class="s1"&gt;&amp;#39;/health&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;The &lt;a href="https://linuxize.com/post/how-to-use-grep-command-to-search-files-in-linux/"&gt;grep command&lt;/a&gt;
guide covers fixed strings, regular expressions, and inverted matches in more detail.&lt;/p&gt;
&lt;h2 id="scroll-and-search-with-less-f"&gt;Scroll and Search with less +F &lt;a class="headline-link" href="#scroll-and-search-with-less-f" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;tail -f&lt;/code&gt; shows new lines but does not let you inspect earlier entries without stopping the command. The &lt;code&gt;less&lt;/code&gt; pager has its own follow mode and lets you switch between live output and normal navigation:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;less +F /var/log/app.log&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;Ctrl+C&lt;/code&gt; to pause following. You can then scroll with the arrow keys or search forward by typing &lt;code&gt;/pattern&lt;/code&gt; and pressing &lt;code&gt;Enter&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To hide nonmatching lines, type &lt;code&gt;&amp;amp;pattern&lt;/code&gt; and press &lt;code&gt;Enter&lt;/code&gt;. Enter &lt;code&gt;&amp;amp;&lt;/code&gt; followed by &lt;code&gt;Enter&lt;/code&gt; to clear the filter. Press uppercase &lt;code&gt;F&lt;/code&gt; to return to follow mode; any active filter remains in effect while new lines arrive. The &lt;a href="https://linuxize.com/post/less-command-in-linux/"&gt;less command&lt;/a&gt;
guide covers navigation, searches, and display options.&lt;/p&gt;
&lt;h2 id="follow-a-service-with-journalctl--f"&gt;Follow a Service with journalctl -f &lt;a class="headline-link" href="#follow-a-service-with-journalctl--f" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;On a systemd system, service output captured by &lt;code&gt;systemd-journald&lt;/code&gt; is read with &lt;code&gt;journalctl&lt;/code&gt;. Use &lt;code&gt;-f&lt;/code&gt; to follow new journal entries and &lt;code&gt;-u&lt;/code&gt; to limit them to one 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;journalctl -f -u nginx&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This follows entries associated with the &lt;code&gt;nginx&lt;/code&gt; unit, including service lifecycle messages and output sent to the journal. Applications can also write dedicated files. If Nginx access requests are stored in &lt;code&gt;/var/log/nginx/access.log&lt;/code&gt;, follow that file with &lt;code&gt;tail -F&lt;/code&gt; instead.&lt;/p&gt;
&lt;p&gt;Because journal entries contain structured fields, you can filter by priority. A single priority includes that level and all more severe levels, so &lt;code&gt;err&lt;/code&gt; shows &lt;code&gt;err&lt;/code&gt;, &lt;code&gt;crit&lt;/code&gt;, &lt;code&gt;alert&lt;/code&gt;, and &lt;code&gt;emerg&lt;/code&gt; entries:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 -f -u nginx -p err&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;-g&lt;/code&gt; (&lt;code&gt;--grep&lt;/code&gt;) to match a regular expression against the &lt;code&gt;MESSAGE&lt;/code&gt; field while following:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 -f -u nginx -g &lt;span class="s1"&gt;&amp;#39;timeout|failed&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 follow warnings and more severe entries from the current boot across all accessible units, combine &lt;code&gt;-f&lt;/code&gt;, &lt;code&gt;-p&lt;/code&gt;, and &lt;code&gt;-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="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 -f -b -p warning&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/journalctl-command-in-linux/"&gt;journalctl command&lt;/a&gt;
guide covers time ranges, boot selection, unit filters, and journal fields.&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 log destination determines the first choice. Use &lt;code&gt;tail -F&lt;/code&gt; for a rotating plain-text file, then pipe it through &lt;code&gt;grep&lt;/code&gt; when you need text filtering. Use &lt;code&gt;less +F&lt;/code&gt; when you expect to pause, scroll backward, search, or interactively hide nonmatching lines. Use &lt;code&gt;journalctl -f&lt;/code&gt; when the messages are stored in the systemd journal and you want to filter by unit, priority, boot, or message text.&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;Permission denied when opening a log&lt;/strong&gt;&lt;br&gt;
Check the file permissions with &lt;code&gt;ls -l /path/to/log&lt;/code&gt;. Run &lt;code&gt;sudo tail -F /path/to/log&lt;/code&gt; only if your account is authorized to read that log. For the system journal, administrators can use &lt;code&gt;sudo journalctl&lt;/code&gt;, while many distributions also grant journal access through groups such as &lt;code&gt;adm&lt;/code&gt; or &lt;code&gt;systemd-journal&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No new lines appear&lt;/strong&gt;&lt;br&gt;
Confirm that the application is still appending to the path you opened. If the file was rotated or recreated, restart with &lt;code&gt;tail -F&lt;/code&gt;. Also remember that &lt;code&gt;tail&lt;/code&gt; follows appended data; it does not report text rewritten in place earlier in the file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;journalctl -p err misses a message containing ERROR&lt;/strong&gt;&lt;br&gt;
The &lt;code&gt;-p&lt;/code&gt; option checks the structured &lt;code&gt;PRIORITY&lt;/code&gt; field, not words inside the message. If an application logs the word &lt;code&gt;ERROR&lt;/code&gt; without assigning an error priority, use a text filter such as &lt;code&gt;journalctl -f -u SERVICE -g 'error'&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;Start with the unfiltered stream to confirm that you have the correct source, then add one filter at a time so you do not hide the event you are trying to diagnose. When a filter proves useful more than once, save it as a shell alias or a short script so the next incident starts from a view you already trust.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/how-to-tail-and-filter-logs-in-real-time/featured_hu_5b652935d1ba1fa6.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>Regular Expressions Explained: How Regex Patterns Work</title><link>https://linuxize.com/post/regular-expressions-basics/</link><pubDate>Mon, 03 Aug 2026 10:10:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/regular-expressions-basics/</guid><category>linux commands</category><description>Regular expressions explained from the ground up: how anchors, character classes, quantifiers, and groups build patterns that work in grep, sed, and awk.</description><content:encoded>&lt;p&gt;Sooner or later every command line session runs into a matching problem that plain text search cannot solve: find lines that start with a number, pull every email address out of a log, or replace dates in one format with another. Regular expressions (regex) are the pattern language that solves these problems, and the same core syntax works in &lt;code&gt;grep&lt;/code&gt;, &lt;code&gt;sed&lt;/code&gt;, &lt;code&gt;awk&lt;/code&gt;, text editors, and most programming languages.&lt;/p&gt;
&lt;p&gt;This guide explains the building blocks of regular expressions, anchors, character classes, quantifiers, grouping, and alternation, and shows how the same pattern carries from &lt;code&gt;grep&lt;/code&gt; to &lt;code&gt;sed&lt;/code&gt; and &lt;code&gt;awk&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="what-a-regular-expression-is"&gt;What a Regular Expression Is &lt;a class="headline-link" href="#what-a-regular-expression-is" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A regular expression is a pattern that describes a set of strings. Instead of matching one literal word, a pattern like &lt;code&gt;^error [0-9]+&lt;/code&gt; matches any line that starts with &amp;ldquo;error&amp;rdquo; followed by a space and one or more digits.&lt;/p&gt;
&lt;p&gt;The examples in this guide use GNU &lt;code&gt;grep -E&lt;/code&gt; (extended regular expressions), so every pattern works as written. You can test any of them by piping text into &lt;a href="https://linuxize.com/post/how-to-use-grep-command-to-search-files-in-linux/"&gt;grep&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;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;error 404&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep -E &lt;span class="s1"&gt;&amp;#39;^error [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;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 404&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The line prints because it matches the pattern. When there is no match, &lt;code&gt;grep&lt;/code&gt; prints nothing and returns a non-zero exit status. Always single-quote the pattern so the shell does not interpret characters like &lt;code&gt;$&lt;/code&gt; and &lt;code&gt;*&lt;/code&gt; before &lt;code&gt;grep&lt;/code&gt; sees them.&lt;/p&gt;
&lt;h2 id="literal-characters-and-metacharacters"&gt;Literal Characters and Metacharacters &lt;a class="headline-link" href="#literal-characters-and-metacharacters" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Most characters in a regex match themselves: the pattern &lt;code&gt;cat&lt;/code&gt; matches the string &amp;ldquo;cat&amp;rdquo; anywhere in a line, including inside &amp;ldquo;concatenate&amp;rdquo;. A handful of characters have special meanings instead of matching literally:&lt;/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;. ^ $ * + ? ( ) [ ] { } | \&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;These are the metacharacters, and the rest of this guide is about what they do. To match one of them literally, escape it with a backslash: &lt;code&gt;\.&lt;/code&gt; matches a real dot, &lt;code&gt;\$&lt;/code&gt; a real dollar sign.&lt;/p&gt;
&lt;h2 id="anchors-and-word-boundaries"&gt;Anchors and Word Boundaries &lt;a class="headline-link" href="#anchors-and-word-boundaries" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Anchors and word boundaries do not match characters; they match positions in the line.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;^&lt;/code&gt; - Matches the start of the line.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$&lt;/code&gt; - Matches the end of the line.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;\b&lt;/code&gt; - Matches a word boundary, the position between a word character and a non-word character.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;\b&lt;/code&gt; boundary is a GNU &lt;code&gt;grep&lt;/code&gt; extension rather than part of POSIX extended regular expression syntax.&lt;/p&gt;
&lt;p&gt;The difference is easiest to see on real input. The following input contains three similar lines, but the pattern prints only the line that consists of exactly the word &amp;ldquo;root&amp;rdquo;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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;root\nroot:x:0:0\nchroot\n&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep -E &lt;span class="s1"&gt;&amp;#39;^root$&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;root&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Without anchors, the pattern &lt;code&gt;root&lt;/code&gt; would also match &amp;ldquo;chroot&amp;rdquo; or a line where &amp;ldquo;root&amp;rdquo; appears in the middle. Word boundaries solve the substring problem without pinning the match to the whole 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="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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;the cat scattered&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep -E -o &lt;span class="s1"&gt;&amp;#39;\bcat\b&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;cat&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;-o&lt;/code&gt; flag prints only the matched text. Notice that &amp;ldquo;scattered&amp;rdquo; did not produce a match, because &amp;ldquo;cat&amp;rdquo; inside it is not surrounded by word boundaries.&lt;/p&gt;
&lt;h2 id="character-classes-matching-sets"&gt;Character Classes: Matching Sets &lt;a class="headline-link" href="#character-classes-matching-sets" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Square brackets match one character from a set:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;[abc]&lt;/code&gt; - One character: a, b, or c.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[a-z]&lt;/code&gt; - One lowercase letter; ranges also work for &lt;code&gt;[0-9]&lt;/code&gt; and &lt;code&gt;[A-Z]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[^abc]&lt;/code&gt; - Negation: one character that is NOT a, b, or c.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, to match &amp;ldquo;gray&amp;rdquo; and &amp;ldquo;grey&amp;rdquo; with one 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="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;gray\ngrey\ngroy\n&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep -E &lt;span class="s1"&gt;&amp;#39;gr[ae]y&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;gray
grey&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The third line does not match because &amp;ldquo;o&amp;rdquo; is not in the set. Inside brackets, most metacharacters lose their special meaning; &lt;code&gt;[.]&lt;/code&gt; matches a literal dot.&lt;/p&gt;
&lt;p&gt;POSIX character classes are named shortcuts that work inside brackets: &lt;code&gt;[[:digit:]]&lt;/code&gt; is equivalent to &lt;code&gt;[0-9]&lt;/code&gt;, &lt;code&gt;[[:alpha:]]&lt;/code&gt; matches letters, &lt;code&gt;[[:space:]]&lt;/code&gt; matches whitespace. Many tools also support the Perl-style shorthands &lt;code&gt;\d&lt;/code&gt; wherever PCRE is available (&lt;code&gt;grep -P&lt;/code&gt;), but the bracket forms are the portable choice for shell work.&lt;/p&gt;
&lt;h2 id="the-dot-and-quantifiers-matching-repetition"&gt;The Dot and Quantifiers: Matching Repetition &lt;a class="headline-link" href="#the-dot-and-quantifiers-matching-repetition" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The dot &lt;code&gt;.&lt;/code&gt; matches any single character except a newline. Quantifiers apply to the preceding item and control how many times it may repeat:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;*&lt;/code&gt; - Zero or more times.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;+&lt;/code&gt; - One or more times.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;?&lt;/code&gt; - Zero or one time (optional).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{n}&lt;/code&gt; - Exactly n times.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{n,m}&lt;/code&gt; - Between n and m times.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Combining the dot with a quantifier gives &lt;code&gt;.*&lt;/code&gt;, which matches anything, including nothing. A more precise example matches an IPv4-looking address by requiring one to three digits in each 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;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;server at 192.168.1.10 is up&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep -E -o &lt;span class="s1"&gt;&amp;#39;[0-9]{1,3}(\.[0-9]{1,3}){3}&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;192.168.1.10&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Reading it piece by piece: &lt;code&gt;[0-9]{1,3}&lt;/code&gt; matches the first number group, &lt;code&gt;\.&lt;/code&gt; matches a literal dot, and the parentheses with &lt;code&gt;{3}&lt;/code&gt; repeat the dot-plus-number sequence three times.&lt;/p&gt;
&lt;p&gt;A common beginner mistake is reaching for &lt;code&gt;*&lt;/code&gt; when &lt;code&gt;+&lt;/code&gt; is meant. The pattern &lt;code&gt;[0-9]*&lt;/code&gt; happily matches an empty string, so it succeeds on every line; &lt;code&gt;[0-9]+&lt;/code&gt; actually requires a digit.&lt;/p&gt;
&lt;h2 id="grouping-and-alternation"&gt;Grouping and Alternation &lt;a class="headline-link" href="#grouping-and-alternation" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Parentheses group parts of a pattern, and the pipe &lt;code&gt;|&lt;/code&gt; provides alternation (OR):&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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;error: disk full\nwarning: low memory\ninfo: started\n&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep -E &lt;span class="s1"&gt;&amp;#39;^(error|warning):&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;error: disk full
warning: low memory&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The group limits the alternation to the two words before the colon; without parentheses, &lt;code&gt;^error|warning:&lt;/code&gt; would mean &amp;ldquo;starts with error, OR contains warning: anywhere&amp;rdquo;, which is rarely what you want.&lt;/p&gt;
&lt;p&gt;Groups also capture what they match, and the captured text can be reused. In &lt;a href="https://linuxize.com/post/how-to-use-sed-to-find-and-replace-string-in-files/"&gt;sed&lt;/a&gt;
, &lt;code&gt;\1&lt;/code&gt; refers to the first group, which makes reordering text possible:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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;2026-01-15&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; sed -E &lt;span class="s1"&gt;&amp;#39;s/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3.\2.\1/&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;15.01.2026&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The three groups capture the year, month, and day, and the replacement writes them back in reverse order.&lt;/p&gt;
&lt;h2 id="basic-vs-extended-regular-expressions"&gt;Basic vs Extended Regular Expressions &lt;a class="headline-link" href="#basic-vs-extended-regular-expressions" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;POSIX defines two regex dialects, and the difference trips up almost everyone at some point. In basic regular expressions (BRE), which plain &lt;code&gt;grep&lt;/code&gt; and &lt;code&gt;sed&lt;/code&gt; use, the characters &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;?&lt;/code&gt;, &lt;code&gt;|&lt;/code&gt;, &lt;code&gt;{}&lt;/code&gt;, and &lt;code&gt;()&lt;/code&gt; match literally, and you must escape them (&lt;code&gt;\+&lt;/code&gt;, &lt;code&gt;\(&lt;/code&gt; &amp;hellip; &lt;code&gt;\)&lt;/code&gt;) to get the special behavior. In extended regular expressions (ERE), they are special by default.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Construct&lt;/th&gt;
&lt;th&gt;ERE (&lt;code&gt;grep -E&lt;/code&gt;, &lt;code&gt;sed -E&lt;/code&gt;, &lt;code&gt;awk&lt;/code&gt;)&lt;/th&gt;
&lt;th&gt;BRE (plain &lt;code&gt;grep&lt;/code&gt;, &lt;code&gt;sed&lt;/code&gt;)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;One or more&lt;/td&gt;
&lt;td&gt;&lt;code&gt;+&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;\+&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optional&lt;/td&gt;
&lt;td&gt;&lt;code&gt;?&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;\?&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Repetition&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{n,m}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;\{n,m\}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grouping&lt;/td&gt;
&lt;td&gt;&lt;code&gt;(...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;\(...\)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alternation&lt;/td&gt;
&lt;td&gt;&lt;code&gt;a|b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;a\|b&lt;/code&gt;, GNU extension only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;That last row is the one to watch. Alternation is not part of POSIX BRE at all, so &lt;code&gt;\|&lt;/code&gt; works in GNU &lt;code&gt;grep&lt;/code&gt; and GNU &lt;code&gt;sed&lt;/code&gt; but fails silently on the BSD &lt;code&gt;sed&lt;/code&gt; that ships with macOS.&lt;/p&gt;
&lt;p&gt;In practice, the simplest rule is: pass &lt;code&gt;-E&lt;/code&gt; to &lt;code&gt;grep&lt;/code&gt; and &lt;code&gt;sed&lt;/code&gt; and write in the extended dialect, as every example in this guide does. &lt;code&gt;awk&lt;/code&gt; uses extended syntax natively:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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;alice 92\nbob 47\ncarol 78\n&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; awk &lt;span class="s1"&gt;&amp;#39;/^[ab]/ {print $1}&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;alice
bob&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;a href="https://linuxize.com/post/awk-command/"&gt;awk&lt;/a&gt;
pattern selects lines starting with &amp;ldquo;a&amp;rdquo; or &amp;ldquo;b&amp;rdquo; and prints the first field. &lt;code&gt;grep -P&lt;/code&gt; enables a third dialect, Perl-compatible regular expressions (PCRE), which adds features like &lt;code&gt;\d&lt;/code&gt; and lookarounds; reach for it when the POSIX dialects run out.&lt;/p&gt;
&lt;h2 id="one-pattern-three-tools"&gt;One Pattern, Three Tools &lt;a class="headline-link" href="#one-pattern-three-tools" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The syntax is shared, but each tool wraps it differently, which is the part that usually causes confusion when moving a working pattern from one command to another. Create a small log file to follow along:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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;error: disk full\nwarning: low memory\ninfo: started\n&amp;#39;&lt;/span&gt; &amp;gt; app.log&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;grep&lt;/code&gt; is a filter, the pattern is the whole job and needs nothing around 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;grep -E &lt;span class="s1"&gt;&amp;#39;^(error|warning):&amp;#39;&lt;/span&gt; app.log&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;sed&lt;/code&gt; prints every input line unless you suppress that with &lt;code&gt;-n&lt;/code&gt;, so selecting lines takes an address followed by an explicit &lt;code&gt;p&lt;/code&gt; 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;sed -E -n &lt;span class="s1"&gt;&amp;#39;/^(error|warning):/p&amp;#39;&lt;/span&gt; app.log&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;awk&lt;/code&gt; reads a bare pattern with no action block as &amp;ldquo;print the matching line&amp;rdquo;:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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;/^(error|warning):/&amp;#39;&lt;/span&gt; app.log&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;error: disk full
warning: low memory&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;All three print the same two lines and skip the &amp;ldquo;info&amp;rdquo; line. Where the tools part company is what happens after the match. &lt;code&gt;grep&lt;/code&gt; reports lines and stops there, &lt;code&gt;sed&lt;/code&gt; can rewrite the matched text, and &lt;code&gt;awk&lt;/code&gt; splits each matching line into fields you can work 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;awk -F&lt;span class="s1"&gt;&amp;#39;: &amp;#39;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/^(error|warning):/ {print $2}&amp;#39;&lt;/span&gt; app.log&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;disk full
low memory&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Setting the field separator to &lt;code&gt;': '&lt;/code&gt; makes &lt;code&gt;$2&lt;/code&gt; the message text, so the pattern selects the lines and the action block pulls out the part you actually wanted. This is why it pays to learn regex once as its own subject: the pattern you write today for &lt;code&gt;grep&lt;/code&gt; is the same pattern you will paste into &lt;code&gt;sed&lt;/code&gt;, &lt;code&gt;awk&lt;/code&gt;, a text editor, or a Python script tomorrow.&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/regex/"&gt;Regex cheatsheet&lt;/a&gt;
.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Matches&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;Any single character&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;^&lt;/code&gt; / &lt;code&gt;$&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start / end of line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Word boundary in GNU grep&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[abc]&lt;/code&gt; / &lt;code&gt;[^abc]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One of the set / one not in the set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;[0-9]&lt;/code&gt;, &lt;code&gt;[[:digit:]]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One digit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;*&lt;/code&gt; / &lt;code&gt;+&lt;/code&gt; / &lt;code&gt;?&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Zero or more / one or more / optional&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;{n,m}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Between n and m repetitions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;(foo|bar)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;foo or bar&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Text captured by the first group&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Literal dot (escaped metacharacter)&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;Anchors, character classes, quantifiers, and groups combine into patterns that handle most day-to-day matching on Linux, and the same vocabulary carries over to editors and programming languages. To put the syntax to work in specific tools, see our guides to &lt;a href="https://linuxize.com/post/regular-expressions-in-grep/"&gt;regular expressions in grep&lt;/a&gt;
and &lt;a href="https://linuxize.com/post/how-to-use-sed-to-find-and-replace-string-in-files/"&gt;find and replace with sed&lt;/a&gt;
.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/regular-expressions-basics/featured_hu_e5f430d4863b69dd.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>git worktree: Work on Multiple Branches at Once</title><link>https://linuxize.com/post/git-worktree/</link><pubDate>Sun, 02 Aug 2026 09:20:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/git-worktree/</guid><category>git</category><category>linux commands</category><description>Use git worktree to check out multiple branches in separate directories, handle urgent fixes without stashing, and clean up linked worktrees safely.</description><content:encoded>&lt;p&gt;A common frustration when working with Git is needing to switch branches while your current working tree has uncommitted changes. You can &lt;a href="https://linuxize.com/post/git-stash/"&gt;stash the changes&lt;/a&gt;
, switch branches, make the fix, switch back, and restore the stash, but that flow interrupts your work and can create conflicts. &lt;code&gt;git worktree&lt;/code&gt; lets you check out another branch in a separate directory, so both workspaces remain available at the same time.&lt;/p&gt;
&lt;p&gt;This guide explains how to create, list, lock, prune, and remove linked worktrees, with a hotfix workflow you can copy.&lt;/p&gt;
&lt;h2 id="how-git-worktree-works"&gt;How git worktree Works &lt;a class="headline-link" href="#how-git-worktree-works" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A Git repository normally has one working tree: the directory where you edit files. With &lt;code&gt;git worktree&lt;/code&gt;, you can attach additional working trees to the same repository. Each linked worktree has its own working directory, index, and &lt;code&gt;HEAD&lt;/code&gt;, while all worktrees share the same object store and repository history. You do not create another clone; you create another workspace for the same repository.&lt;/p&gt;
&lt;h2 id="git-worktree-syntax"&gt;git worktree Syntax &lt;a class="headline-link" href="#git-worktree-syntax" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The most-used forms of the command 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="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;git worktree add [OPTIONS] &amp;lt;path&amp;gt; [&amp;lt;commit-ish&amp;gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git worktree list [OPTIONS]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git worktree remove [--force] &amp;lt;worktree&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git worktree prune [OPTIONS]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git worktree lock [--reason &amp;lt;string&amp;gt;] &amp;lt;worktree&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git worktree unlock &amp;lt;worktree&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="add-a-worktree-for-an-existing-branch"&gt;Add a Worktree for an Existing Branch &lt;a class="headline-link" href="#add-a-worktree-for-an-existing-branch" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Suppose you are working on a feature branch and want a separate checkout of &lt;code&gt;main&lt;/code&gt;. Add it in a sibling directory without leaving your current branch:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 worktree add ../project-main main&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;Preparing worktree (checking out &amp;#39;main&amp;#39;)
HEAD is now at a1b2c3d Fix typo in README&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The new directory &lt;code&gt;../project-main&lt;/code&gt; contains &lt;code&gt;main&lt;/code&gt;, while your original working tree stays on the feature branch. Git refuses this command if &lt;code&gt;main&lt;/code&gt; is already checked out in another worktree.&lt;/p&gt;
&lt;h2 id="add-a-worktree-with-a-new-branch"&gt;Add a Worktree with a New Branch &lt;a class="headline-link" href="#add-a-worktree-with-a-new-branch" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For a hotfix, create a new branch from &lt;code&gt;main&lt;/code&gt; and check it out in one step:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 worktree add -b hotfix/payment-null ../project-hotfix main&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;Preparing worktree (new branch &amp;#39;hotfix/payment-null&amp;#39;)
HEAD is now at a1b2c3d Fix typo in README&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;-b&lt;/code&gt; flag creates &lt;code&gt;hotfix/payment-null&lt;/code&gt; from &lt;code&gt;main&lt;/code&gt; and checks it out in the new directory. After making the fix and running your tests, commit and push from that worktree:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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; ../project-hotfix
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Edit the files and run your tests&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git add .
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git commit -m &lt;span class="s2"&gt;&amp;#34;Fix payment handler&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git push -u origin hotfix/payment-null&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Your original directory remains on the feature branch throughout this workflow.&lt;/p&gt;
&lt;h2 id="list-all-worktrees"&gt;List All Worktrees &lt;a class="headline-link" href="#list-all-worktrees" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To display the main worktree and every linked worktree, 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;git worktree list&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/user/project abc1234 [feature/new-dashboard]
/home/user/project-hotfix def5678 [hotfix/payment-null]
/home/user/project-main a1b2c3d [main]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The first entry is always the main worktree. Each linked worktree shows its path, current commit, and checked-out branch.&lt;/p&gt;
&lt;h2 id="run-tests-across-branches-in-parallel"&gt;Run Tests Across Branches in Parallel &lt;a class="headline-link" href="#run-tests-across-branches-in-parallel" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Worktrees are useful for running a test suite against two commits at the same time. For a temporary test directory that does not need its own branch, create a detached worktree:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 worktree add --detach ../project-test main
&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="nb"&gt;cd&lt;/span&gt; ../project-test &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm ci &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;wait&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The first background job installs dependencies and runs the tests against &lt;code&gt;main&lt;/code&gt; in &lt;code&gt;../project-test&lt;/code&gt;, while the second runs in your current worktree. Both worktrees share the Git object store but keep their working files and installed dependencies separate.&lt;/p&gt;
&lt;h2 id="remove-a-worktree"&gt;Remove a Worktree &lt;a class="headline-link" href="#remove-a-worktree" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When you are done with a linked worktree, 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;git worktree remove ../project-hotfix&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This deletes the linked directory and unregisters it from the repository. It does not delete the &lt;code&gt;hotfix/payment-null&lt;/code&gt; branch.&lt;/p&gt;
&lt;p&gt;Git refuses to remove a worktree with modified or untracked files. Check its status before deciding whether to force removal:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 -C ../project-hotfix status&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 &lt;code&gt;--force&lt;/code&gt; option deletes the linked directory even when it contains modified or untracked files. Commit or copy anything you need before running it.&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;If you are certain that the remaining files are disposable, force the removal 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 worktree remove --force ../project-hotfix&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="prune-stale-worktree-references"&gt;Prune Stale Worktree References &lt;a class="headline-link" href="#prune-stale-worktree-references" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you delete a worktree directory manually, Git retains its administrative entry. Preview the stale entries that qualify for removal 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 worktree prune --dry-run --verbose&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;After reviewing the output, remove those entries 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 worktree prune --verbose&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This command removes stale metadata, not active worktree directories. &lt;code&gt;git gc&lt;/code&gt; also prunes missing entries older than the configured &lt;code&gt;gc.worktreePruneExpire&lt;/code&gt; period, which defaults to three months.&lt;/p&gt;
&lt;h2 id="lock-a-worktree"&gt;Lock a Worktree &lt;a class="headline-link" href="#lock-a-worktree" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If a worktree lives on a removable drive or a network path that may be temporarily unavailable, lock it so &lt;code&gt;prune&lt;/code&gt; does not 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;git worktree lock --reason &lt;span class="s2"&gt;&amp;#34;on external drive&amp;#34;&lt;/span&gt; ../project-hotfix&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Unlock it when the path is available 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;git worktree unlock ../project-hotfix&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;A lock also protects the worktree from &lt;code&gt;git worktree remove&lt;/code&gt;, and a single &lt;code&gt;--force&lt;/code&gt; is not enough to override 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="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;fatal: cannot remove a locked working tree, lock reason: on external drive
use &amp;#39;remove -f -f&amp;#39; to override or unlock first&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Unlock the worktree first, or pass the force option twice:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 worktree remove --force --force ../project-hotfix&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/git/"&gt;Git 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;git worktree add ../path branch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check out an existing branch in a linked worktree&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git worktree add -b new-branch ../path main&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a new branch from &lt;code&gt;main&lt;/code&gt; in a linked worktree&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git worktree add --detach ../path main&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a detached worktree for testing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git worktree list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List the main and linked worktrees&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git worktree lock --reason &amp;quot;text&amp;quot; ../path&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Protect a temporarily unavailable worktree from pruning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git worktree remove ../path&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete and unregister a clean linked worktree&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git worktree prune --dry-run --verbose&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Preview stale administrative entries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git worktree repair /new/path&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Repair the link to a manually moved worktree&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;Git reports that a branch is already checked out&lt;/strong&gt;&lt;br&gt;
A branch can normally be checked out in only one worktree. Create a new branch from it 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;git worktree add -b new-branch ../project-new main&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 the files for testing and do not plan to commit, use a detached worktree:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 worktree add --detach ../project-test main&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Git cannot find a manually moved worktree&lt;/strong&gt;&lt;br&gt;
If you moved a linked directory without &lt;code&gt;git worktree move&lt;/code&gt;, repair its administrative link from another worktree:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 worktree repair /new/path&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Use the new location as the argument. Git reconnects the linked directory without checking it out 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;&lt;code&gt;git worktree&lt;/code&gt; keeps parallel tasks isolated without creating another clone or disturbing your current files. Create a dedicated branch for changes, use detached worktrees for temporary tests, and see the &lt;a href="https://linuxize.com/post/git-branch-command/"&gt;git branch guide&lt;/a&gt;
when you need to rename, inspect, or delete the branches afterward.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/git-worktree/featured_hu_8ad545d280dbf955.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>killall Command in Linux: Kill Processes by Name</title><link>https://linuxize.com/post/killall-command-in-linux/</link><pubDate>Sat, 01 Aug 2026 08:40:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/killall-command-in-linux/</guid><category>linux commands</category><description>Use the Linux killall command to stop processes by name, select signals, filter by user or age, confirm matches, and compare killall with kill and pkill.</description><content:encoded>&lt;p&gt;When a program misbehaves, it may leave several processes running. Modern browsers start separate worker processes, and a stuck script may have been launched several times in different terminals. Killing each one with the &lt;a href="https://linuxize.com/post/kill-command-in-linux/"&gt;&lt;code&gt;kill&lt;/code&gt; command&lt;/a&gt;
means looking up every PID first. The &lt;code&gt;killall&lt;/code&gt; command skips that step: you give it a process name, and it signals every process running under that name.&lt;/p&gt;
&lt;p&gt;This guide explains how to use &lt;code&gt;killall&lt;/code&gt; to terminate processes by name, choose the signal to send, and narrow matches by user, age, or pattern.&lt;/p&gt;
&lt;h2 id="installing-killall"&gt;Installing killall &lt;a class="headline-link" href="#installing-killall" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;killall&lt;/code&gt; is part of the &lt;code&gt;psmisc&lt;/code&gt; package, which is preinstalled on most desktop and server distributions. If the command is missing, install it from the standard repositories.&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 psmisc&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 psmisc&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;On Linux, the &lt;code&gt;psmisc&lt;/code&gt; version of &lt;code&gt;killall&lt;/code&gt; affects only processes matching the given name. On some other Unix systems, such as Solaris and AIX, &lt;code&gt;killall&lt;/code&gt; signals nearly every process on the machine, which is how those systems tear things down at shutdown. Confirm which implementation is installed before running &lt;code&gt;killall&lt;/code&gt; on a remote system, especially as root.&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="killall-syntax"&gt;killall Syntax &lt;a class="headline-link" href="#killall-syntax" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The general syntax of the &lt;code&gt;killall&lt;/code&gt; command is as follows:&lt;/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;killall [OPTIONS] NAME...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;NAME&lt;/code&gt; is the process name to match. You can pass more than one name, and &lt;code&gt;killall&lt;/code&gt; signals every process that matches any of them. Unlike &lt;code&gt;pkill&lt;/code&gt;, which matches partial names, &lt;code&gt;killall&lt;/code&gt; requires the name to match exactly.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;killall&lt;/code&gt; exits with status zero when it successfully signals at least one process for every name you supplied. If a name does not match, it prints a message and returns a non-zero 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;killall firefox&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;firefox: no process found&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="killing-a-process-by-name"&gt;Killing a Process by Name &lt;a class="headline-link" href="#killing-a-process-by-name" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To terminate a process, pass its 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;killall firefox&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;By default, &lt;code&gt;killall&lt;/code&gt; sends the &lt;code&gt;SIGTERM&lt;/code&gt; signal, which asks each matching process to shut down cleanly. The command produces no output when it succeeds. Add &lt;code&gt;-v&lt;/code&gt; to confirm what was signalled:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;killall -v vlc&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;Killed vlc(8143) with signal 15&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The output shows the process name, its PID, and the signal number. Signal 15 is &lt;code&gt;SIGTERM&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Before sending a signal, use &lt;a href="https://linuxize.com/post/pgrep-command-in-linux/"&gt;&lt;code&gt;pgrep&lt;/code&gt;&lt;/a&gt;
to preview exact matches and their full command 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="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pgrep -a -x firefox&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If this command prints nothing, check &lt;code&gt;ps -e -o pid,comm&lt;/code&gt; to find the process name that Linux reports.&lt;/p&gt;
&lt;h2 id="sending-a-specific-signal"&gt;Sending a Specific Signal &lt;a class="headline-link" href="#sending-a-specific-signal" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When a process ignores &lt;code&gt;SIGTERM&lt;/code&gt;, you can send a stronger signal. The &lt;code&gt;-s&lt;/code&gt; option accepts a signal name or 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;killall -s KILL myscript&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;SIGKILL&lt;/code&gt; cannot be caught or ignored, so the process is terminated immediately without a chance to clean up. The same signal can be written in two shorter forms:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;killall -9 myscript
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;killall -SIGKILL myscript&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;SIGKILL&lt;/code&gt; only after a plain &lt;code&gt;killall&lt;/code&gt; has failed. A process killed this way cannot flush buffers or remove its temporary files, which is why trying &lt;code&gt;SIGTERM&lt;/code&gt; first is the safer habit. For a longer discussion of signals and when to use each, see our guide on &lt;a href="https://linuxize.com/post/how-to-kill-a-process-in-linux/"&gt;how to kill a process in Linux&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;Signals are also useful for more than termination. For example, BIND reloads its configuration when the &lt;code&gt;named&lt;/code&gt; process receives &lt;code&gt;SIGHUP&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 killall -s HUP named&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To list all signal names that &lt;code&gt;killall&lt;/code&gt; understands, 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;killall -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;HUP INT QUIT ILL TRAP ABRT BUS FPE KILL USR1 SEGV USR2 PIPE ALRM TERM STKFLT
CHLD CONT STOP TSTP TTIN TTOU URG XCPU XFSZ VTALRM PROF WINCH POLL PWR SYS&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="matching-names-case-insensitively"&gt;Matching Names Case-Insensitively &lt;a class="headline-link" href="#matching-names-case-insensitively" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Process name matching is case-sensitive by default. If you are not sure about the capitalization, add &lt;code&gt;-I&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;killall -I &lt;span class="s2"&gt;&amp;#34;teamviewer&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;TeamViewer&lt;/code&gt;, &lt;code&gt;teamviewer&lt;/code&gt;, and any other case variant of the name.&lt;/p&gt;
&lt;h2 id="matching-with-regular-expressions"&gt;Matching with Regular Expressions &lt;a class="headline-link" href="#matching-with-regular-expressions" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;-r&lt;/code&gt; option interprets the name as an extended regular expression, so you can signal several related processes at once:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;killall -r &lt;span class="s1"&gt;&amp;#39;^chrom.*&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;The &lt;code&gt;^&lt;/code&gt; anchor limits the match to names that begin with &lt;code&gt;chrom&lt;/code&gt;, such as &lt;code&gt;chrome&lt;/code&gt; and &lt;code&gt;chromium&lt;/code&gt;. Quote the pattern so the shell does not interpret special characters before &lt;code&gt;killall&lt;/code&gt; sees them.&lt;/p&gt;
&lt;h2 id="killing-processes-owned-by-a-user"&gt;Killing Processes Owned by a User &lt;a class="headline-link" href="#killing-processes-owned-by-a-user" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;-u&lt;/code&gt; option restricts matches to processes owned by a specific 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;killall -u sarah node&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This terminates only the &lt;code&gt;node&lt;/code&gt; processes running under the &lt;code&gt;sarah&lt;/code&gt; account, leaving other users&amp;rsquo; &lt;code&gt;node&lt;/code&gt; processes alone.&lt;/p&gt;
&lt;p&gt;If you pass &lt;code&gt;-u&lt;/code&gt; without a process name, &lt;code&gt;killall&lt;/code&gt; signals every process the user owns:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 killall -u sarah&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Be careful with this form. It terminates the user&amp;rsquo;s entire session, including their shell and any editors with unsaved work. Combine it with &lt;code&gt;-i&lt;/code&gt; when you want to review each process first.&lt;/p&gt;
&lt;h2 id="confirming-each-kill-interactively"&gt;Confirming Each Kill Interactively &lt;a class="headline-link" href="#confirming-each-kill-interactively" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;-i&lt;/code&gt; option asks for confirmation before signalling each matching 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;killall -i node&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;Kill node(2211) ? (y/N)
Kill node(2384) ? (y/N)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Answer &lt;code&gt;y&lt;/code&gt; to signal a process or press Enter to skip it. Interactive mode is a good safety net when a name is common enough to match processes you did not have in mind.&lt;/p&gt;
&lt;h2 id="filtering-by-process-age"&gt;Filtering by Process Age &lt;a class="headline-link" href="#filtering-by-process-age" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;-o&lt;/code&gt; (older) and &lt;code&gt;-y&lt;/code&gt; (younger) options filter matches by how long a process has been running. The time is a whole number followed by a unit: &lt;code&gt;s&lt;/code&gt; for seconds, &lt;code&gt;m&lt;/code&gt; for minutes, &lt;code&gt;h&lt;/code&gt; for hours, &lt;code&gt;d&lt;/code&gt; for days, &lt;code&gt;w&lt;/code&gt; for weeks, &lt;code&gt;M&lt;/code&gt; for months, and &lt;code&gt;y&lt;/code&gt; for years.&lt;/p&gt;
&lt;p&gt;To kill &lt;code&gt;myscript&lt;/code&gt; processes that have been running for more than an hour:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;killall -o 1h myscript&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To kill only instances started within the last ten minutes:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;killall -y 10m myscript&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The age filters are handy for cleaning up stuck workers or runaway cron jobs while leaving fresh, healthy instances running.&lt;/p&gt;
&lt;h2 id="waiting-for-processes-to-die"&gt;Waiting for Processes to Die &lt;a class="headline-link" href="#waiting-for-processes-to-die" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;By default, &lt;code&gt;killall&lt;/code&gt; sends the signal and returns immediately. The &lt;code&gt;-w&lt;/code&gt; option makes it wait until all signalled processes have actually terminated:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;killall -w myscript &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;all stopped&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 useful in scripts that must not continue until a service is fully down. The wait can continue forever if the signal is ignored, has no effect, or leaves the process in a zombie state.&lt;/p&gt;
&lt;p&gt;To put a ten-second limit on the wait, run &lt;code&gt;killall&lt;/code&gt; through the &lt;a href="https://linuxize.com/post/timeout-command-in-linux/"&gt;&lt;code&gt;timeout&lt;/code&gt; command&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;timeout 10s killall -w myscript&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If the limit expires, &lt;code&gt;timeout&lt;/code&gt; stops the &lt;code&gt;killall&lt;/code&gt; command, but the target process may still be running. Inspect it with &lt;code&gt;pgrep&lt;/code&gt; before deciding whether to send &lt;code&gt;SIGKILL&lt;/code&gt;. A process that remains in a zombie state can also cause &lt;code&gt;killall -w&lt;/code&gt; to keep waiting.&lt;/p&gt;
&lt;h2 id="killall-vs-kill-vs-pkill"&gt;killall vs kill vs pkill &lt;a class="headline-link" href="#killall-vs-kill-vs-pkill" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;All three commands send signals; they differ in how you select the target processes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;kill&lt;/code&gt; targets a specific PID. It is the most precise option, but you have to look the PID up first.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;killall&lt;/code&gt; targets an exact process name and signals every instance of it.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://linuxize.com/post/pkill-command-in-linux/"&gt;&lt;code&gt;pkill&lt;/code&gt;&lt;/a&gt;
targets a name pattern, so &lt;code&gt;pkill fire&lt;/code&gt; matches &lt;code&gt;firefox&lt;/code&gt;. It can also match against the full command line with &lt;code&gt;-f&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The exact-name behavior of &lt;code&gt;killall&lt;/code&gt; makes it more predictable than &lt;code&gt;pkill&lt;/code&gt; for everyday use: &lt;code&gt;killall node&lt;/code&gt; cannot accidentally match &lt;code&gt;node_exporter&lt;/code&gt;. Linux limits the process name stored in &lt;code&gt;/proc/PID/stat&lt;/code&gt; to 15 characters. For longer names, &lt;code&gt;killall&lt;/code&gt; may have to fall back to those first 15 characters when the full name is unavailable. Add &lt;code&gt;-e&lt;/code&gt; to skip a long-name match that cannot be verified exactly.&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;ul&gt;
&lt;li&gt;&lt;code&gt;-s SIGNAL&lt;/code&gt; - Send the given signal instead of &lt;code&gt;SIGTERM&lt;/code&gt;. Also accepts the &lt;code&gt;-SIGNAL&lt;/code&gt; and numeric forms.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-l&lt;/code&gt; - List known signal names.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-v&lt;/code&gt; - Report each signal that was successfully sent.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-q&lt;/code&gt; - Do not complain when no process matched.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-I&lt;/code&gt; - Match process names case-insensitively.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-r&lt;/code&gt; - Interpret the name as an extended regular expression.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-e&lt;/code&gt; - Require an exact match for names longer than 15 characters.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-u USER&lt;/code&gt; - Match only processes owned by the given user.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-i&lt;/code&gt; - Ask for confirmation before signalling each process.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-o TIME&lt;/code&gt; - Match only processes older than the given age.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-y TIME&lt;/code&gt; - Match only processes younger than the given age.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-w&lt;/code&gt; - Wait until all signalled processes have died.&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/kill/"&gt;kill 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;Terminate all processes with a name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;killall firefox&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Force kill after a failed terminate&lt;/td&gt;
&lt;td&gt;&lt;code&gt;killall -9 firefox&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reload BIND&amp;rsquo;s configuration&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo killall -s HUP named&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ignore name capitalization&lt;/td&gt;
&lt;td&gt;&lt;code&gt;killall -I teamviewer&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Match names by regex&lt;/td&gt;
&lt;td&gt;&lt;code&gt;killall -r '^chrom.*'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kill a user&amp;rsquo;s instances of a program&lt;/td&gt;
&lt;td&gt;&lt;code&gt;killall -u sarah node&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kill all of a user&amp;rsquo;s processes&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo killall -u sarah&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Confirm each kill&lt;/td&gt;
&lt;td&gt;&lt;code&gt;killall -i node&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kill instances older than one hour&lt;/td&gt;
&lt;td&gt;&lt;code&gt;killall -o 1h myscript&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wait until processes exit&lt;/td&gt;
&lt;td&gt;&lt;code&gt;killall -w myscript&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List signal names&lt;/td&gt;
&lt;td&gt;&lt;code&gt;killall -l&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;killall: no process found&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;killall&lt;/code&gt; requires the process name to match exactly unless you use &lt;code&gt;-r&lt;/code&gt; or &lt;code&gt;-I&lt;/code&gt;. Run &lt;code&gt;pgrep -a pattern&lt;/code&gt; or &lt;code&gt;ps -e -o pid,comm&lt;/code&gt; to check the reported name, capitalization, and command line before trying again.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;Operation not permitted&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
You can signal your own processes, but another user&amp;rsquo;s process normally requires root privileges. Verify the exact target first, then rerun the command with &lt;code&gt;sudo&lt;/code&gt; only when needed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;killall -w does not return&lt;/strong&gt;&lt;br&gt;
The process may have ignored the signal, stayed in a zombie state, or been replaced by a new process with the same PID while &lt;code&gt;killall&lt;/code&gt; was checking it. Stop waiting with &lt;code&gt;Ctrl+C&lt;/code&gt;, inspect the target with &lt;code&gt;pgrep -a -x NAME&lt;/code&gt;, and use &lt;code&gt;SIGKILL&lt;/code&gt; only if the process is still running and cannot shut down cleanly.&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;killall&lt;/code&gt; terminates every instance of a program in one command, with filters for user, age, and case when the name alone is too broad. When you need pattern matching instead of exact names, reach for &lt;a href="https://linuxize.com/post/pkill-command-in-linux/"&gt;&lt;code&gt;pkill&lt;/code&gt;&lt;/a&gt;
, and when you need to inspect processes before killing them, start with &lt;a href="https://linuxize.com/post/pgrep-command-in-linux/"&gt;&lt;code&gt;pgrep&lt;/code&gt;&lt;/a&gt;
.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/killall-command-in-linux/featured_hu_c482ee53e524f8c8.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>AWS CLI Cheatsheet</title><link>https://linuxize.com/cheatsheet/aws-cli/</link><pubDate>Fri, 31 Jul 2026 13:35:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/cheatsheet/aws-cli/</guid><description>Quick reference for AWS CLI commands, profiles, and output filtering</description><content:encoded>&lt;div class="card"&gt;
&lt;h3 id="installation-and-setup"&gt;Installation and Setup &lt;a class="headline-link" href="#installation-and-setup" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Install AWS CLI version 2 and set the basic configuration. Full walkthrough in &lt;a href="https://linuxize.com/post/how-to-install-and-configure-aws-cli-on-linux/"&gt;installing and configuring the AWS CLI on Linux&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;curl https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip -o awscliv2.zip&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Download the x86_64 installer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;curl https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip -o awscliv2.zip&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Download the ARM64 installer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;unzip awscliv2.zip&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Extract the installer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo ./aws/install&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install AWS CLI version 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo ./aws/install --update&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Update from a freshly extracted installer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws --version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the installed version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws configure&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Interactive credential and region setup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws configure list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show active settings and where they come from&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws configure get region&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read a single config value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws configure set region eu-central-1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Write a single config value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;complete -C aws_completer aws&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enable Bash command completion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 help&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Open the reference for a service&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="profiles-and-authentication"&gt;Profiles and Authentication &lt;a class="headline-link" href="#profiles-and-authentication" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Static access keys configured with &lt;code&gt;aws configure&lt;/code&gt; live in &lt;code&gt;~/.aws/credentials&lt;/code&gt;, while other settings live in &lt;code&gt;~/.aws/config&lt;/code&gt;. Prefer temporary credentials from &lt;code&gt;aws login&lt;/code&gt; or IAM Identity Center.&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;aws login&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sign in with AWS console credentials&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws login --profile dev&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sign in to a named profile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws login --remote&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sign in from a host without a browser&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws logout --profile dev&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Clear cached login credentials for a profile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws configure --profile dev&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Configure a profile with an access key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws s3 ls --profile dev&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run one command as a profile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;export AWS_PROFILE=dev&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use a profile for the whole shell&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws configure list-profiles&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List configured profiles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws configure sso&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set up an IAM Identity Center profile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws sso login --profile dev&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start or refresh an SSO session&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws sso logout&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Clear cached SSO credentials&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws sts get-caller-identity&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the account and identity in use&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws sts assume-role --role-arn arn --role-session-name cli&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Get temporary role credentials&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;export AWS_REGION=eu-west-1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Override the region for the shell&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;Frequently used options. Availability depends on the service and operation.&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;--region eu-west-1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Target a specific region&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--profile dev&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use a named profile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--output json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set the response format&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--no-cli-pager&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print output instead of opening a pager&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--dry-run&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check permissions without acting on supported EC2 operations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--cli-auto-prompt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prompt for parameters interactively&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--no-paginate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Return only the first page from a paginated operation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--page-size 100&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set the API page size for a paginated operation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--max-items 20&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Limit output from a paginated operation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--cli-input-json file://params.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read parameters for a modeled API operation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--generate-cli-skeleton&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print a parameter template for a modeled API operation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--debug&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print the full request and response trace&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="output-formatting-and-queries"&gt;Output Formatting and Queries &lt;a class="headline-link" href="#output-formatting-and-queries" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;--query&lt;/code&gt; option uses JMESPath and runs client side. Service-specific options such as &lt;code&gt;--filters&lt;/code&gt; run server side, which can reduce response size and improve response time for large data sets. Pipe &lt;code&gt;--output json&lt;/code&gt; into &lt;code&gt;jq&lt;/code&gt; when a query gets hard to read.&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;aws s3api list-buckets --output table&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Human-readable table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws s3api list-buckets --output text&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tab-delimited text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws s3api list-buckets --output yaml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;YAML response&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws s3api list-buckets --query &amp;quot;Buckets[].Name&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Return one field&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 describe-instances --query &amp;quot;Reservations[].Instances[].[InstanceId,State.Name]&amp;quot; --output text&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Return several fields as columns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 describe-instances --query &amp;quot;Reservations[].Instances[?State.Name=='running'].InstanceId&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Filter results client side&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 describe-instances --filters Name=instance-state-name,Values=running&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Filter results server side&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 describe-volumes --query &amp;quot;Volumes[0:5]&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Slice the result list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 wait instance-running --instance-ids i-0abc123&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Block until a state is reached&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="s3-buckets-and-objects"&gt;S3 Buckets and Objects &lt;a class="headline-link" href="#s3-buckets-and-objects" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Everyday transfers with the high-level &lt;code&gt;aws s3&lt;/code&gt; commands. More examples in &lt;a href="https://linuxize.com/post/aws-s3-commands/"&gt;aws s3 commands&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;aws s3 ls&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List all buckets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws s3 ls s3://bucket --recursive --summarize&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List objects with a size total&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws s3 cp file.txt s3://bucket/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Upload a file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws s3 cp s3://bucket/file.txt .&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Download a file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws s3 cp ./dir s3://bucket/dir --recursive&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Upload a directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws s3 sync ./dir s3://bucket/dir&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Copy only new and changed files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws s3 sync ./dir s3://bucket/dir --delete --dryrun&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Preview a mirror that removes stale files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws s3 rm s3://bucket/dir/ --recursive --dryrun&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Preview a recursive delete&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws s3 mb s3://bucket&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a bucket&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws s3 rb s3://bucket&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove an empty bucket&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws s3 presign s3://bucket/file.txt --expires-in 3600&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generate a temporary download link&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="ec2-instances"&gt;EC2 Instances &lt;a class="headline-link" href="#ec2-instances" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Launch, inspect, and control instances.&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;aws ec2 describe-instances&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List instances and their details&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 describe-instances --instance-ids i-0abc123&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Details for one instance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 describe-instance-status&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Health and scheduled events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 run-instances --image-id ami-0abc --instance-type t3.micro --key-name mykey&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Launch an instance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 start-instances --instance-ids i-0abc123&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start a stopped instance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 stop-instances --instance-ids i-0abc123&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stop an instance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 reboot-instances --instance-ids i-0abc123&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reboot an instance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 terminate-instances --instance-ids i-0abc123&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Permanently terminate an instance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 create-tags --resources i-0abc123 --tags Key=Name,Value=web&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tag a resource&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 describe-images --owners 099720109477 --filters &amp;quot;Name=name,Values=ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Find official Ubuntu 24.04 AMIs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 get-console-output --instance-id i-0abc123&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read the instance console log&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="security-groups-and-key-pairs"&gt;Security Groups and Key Pairs &lt;a class="headline-link" href="#security-groups-and-key-pairs" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Network access and SSH keys for EC2. Private key files are unencrypted, so keep them readable only by your user.&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;aws ec2 describe-security-groups&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List security groups&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 create-security-group --group-name web --description &amp;quot;Web tier&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a security group&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 authorize-security-group-ingress --group-id sg-0abc --protocol tcp --port 22 --cidr 203.0.113.10/32&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allow inbound traffic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 revoke-security-group-ingress --group-id sg-0abc --protocol tcp --port 22 --cidr 203.0.113.10/32&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove an inbound rule&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 delete-security-group --group-id sg-0abc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete a security group&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 create-key-pair --key-name mykey --query KeyMaterial --output text &amp;gt; mykey.pem&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a key pair and save the private key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;chmod 400 mykey.pem&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Restrict private key permissions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 describe-key-pairs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List key pairs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 delete-key-pair --key-name mykey&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete a key pair&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 describe-vpcs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List VPCs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 describe-subnets --filters Name=vpc-id,Values=vpc-0abc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List subnets in a VPC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 allocate-address&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reserve an elastic IP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 associate-address --instance-id i-0abc123 --allocation-id eipalloc-0abc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Attach an elastic IP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="ebs-volumes-and-snapshots"&gt;EBS Volumes and Snapshots &lt;a class="headline-link" href="#ebs-volumes-and-snapshots" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Block storage and backups.&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;aws ec2 describe-volumes&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;aws ec2 create-volume --size 20 --availability-zone eu-central-1a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 attach-volume --volume-id vol-0abc --instance-id i-0abc123 --device /dev/sdf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Attach a volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 detach-volume --volume-id vol-0abc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Detach a volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 delete-volume --volume-id vol-0abc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Permanently delete a volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 create-snapshot --volume-id vol-0abc --description &amp;quot;nightly&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Snapshot a volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 describe-snapshots --owner-ids self&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List your snapshots&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 copy-snapshot --region eu-west-1 --source-region eu-central-1 --source-snapshot-id snap-0abc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Copy a snapshot from eu-central-1 to eu-west-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ec2 delete-snapshot --snapshot-id snap-0abc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Permanently delete a snapshot&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="iam-users-and-roles"&gt;IAM Users and Roles &lt;a class="headline-link" href="#iam-users-and-roles" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Identities, policies, and access keys. Creating an access key prints its secret once, so store it securely and never commit it to version control.&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;aws iam list-users&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List IAM users&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws iam create-user --user-name dev&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws iam delete-user --user-name dev&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Permanently delete a user after removing dependencies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws iam list-roles&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List roles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws iam get-role --role-name deploy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show a role and its trust policy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws iam attach-user-policy --user-name dev --policy-arn arn:aws:iam::aws:policy/ReadOnlyAccess&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Attach a managed policy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws iam list-attached-user-policies --user-name dev&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List policies attached to a user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws iam create-access-key --user-name dev&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create an access key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws iam list-access-keys --user-name dev&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List access keys and creation dates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws iam update-access-key --user-name dev --access-key-id AKIA123 --status Inactive&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Disable a key before deleting it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws iam delete-access-key --user-name dev --access-key-id AKIA123&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Permanently delete an access key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws iam get-account-summary&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Account-wide IAM counts and limits&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="systems-manager-and-secrets"&gt;Systems Manager and Secrets &lt;a class="headline-link" href="#systems-manager-and-secrets" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Shell access without SSH, plus configuration and secret storage. The &lt;code&gt;start-session&lt;/code&gt; command needs the Session Manager plugin installed locally. Pass secret values from protected files so they do not enter shell history, and keep decrypted output out of shared 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;aws ssm start-session --target i-0abc123&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Open a shell on an instance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ssm describe-instance-information&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List instances managed by SSM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ssm send-command --instance-ids i-0abc123 --document-name AWS-RunShellScript --parameters commands=&amp;quot;uptime&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run a command on an instance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ssm get-command-invocation --command-id 1a2b --instance-id i-0abc123&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read the output of a sent command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ssm put-parameter --name /app/db_url --value file://db-url.txt --type SecureString&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Store an encrypted parameter from a file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ssm get-parameter --name /app/db_url --with-decryption&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read and decrypt a parameter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ssm get-parameters-by-path --path /app --recursive&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List parameters under a path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws secretsmanager list-secrets&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List secrets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws secretsmanager get-secret-value --secret-id prod/db --query SecretString --output text&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print a decrypted secret value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws secretsmanager create-secret --name prod/db --secret-string file://secret.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a secret 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="cloudwatch-logs-and-alarms"&gt;CloudWatch Logs and Alarms &lt;a class="headline-link" href="#cloudwatch-logs-and-alarms" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Read application and service logs from the terminal.&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;aws logs describe-log-groups&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List log groups&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws logs describe-log-streams --log-group-name /aws/lambda/fn&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List streams in a group&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws logs tail /aws/lambda/fn --follow&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stream new log events live&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws logs tail /aws/lambda/fn --since 1h --format short&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the last hour of logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws logs filter-log-events --log-group-name /aws/lambda/fn --filter-pattern ERROR&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search log events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws logs create-log-group --log-group-name /app/web&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a log group&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws logs put-retention-policy --log-group-name /app/web --retention-in-days 30&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set log retention&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws logs delete-log-group --log-group-name /app/web&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Permanently delete a log group and its logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws cloudwatch describe-alarms&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List alarms and their state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws cloudwatch describe-alarms --state-value ALARM&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show only firing alarms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="lambda-functions"&gt;Lambda Functions &lt;a class="headline-link" href="#lambda-functions" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Deploy and invoke functions. AWS CLI version 2 expects base64 input for blob parameters by default, so literal JSON passed to &lt;code&gt;--payload&lt;/code&gt; needs &lt;code&gt;--cli-binary-format raw-in-base64-out&lt;/code&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;aws lambda list-functions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List functions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws lambda get-function --function-name fn&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show configuration and code location&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws lambda invoke --function-name fn out.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Invoke a function&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws lambda invoke --function-name fn --cli-binary-format raw-in-base64-out --payload '{&amp;quot;key&amp;quot;:&amp;quot;value&amp;quot;}' out.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Invoke with a JSON payload&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws lambda update-function-code --function-name fn --zip-file fileb://fn.zip&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deploy new code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws lambda update-function-configuration --function-name fn --timeout 30&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change a setting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws lambda publish-version --function-name fn&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Publish an immutable version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws lambda list-versions-by-function --function-name fn&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;aws lambda delete-function --function-name fn&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Permanently delete a function&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="ecr-container-images"&gt;ECR Container Images &lt;a class="headline-link" href="#ecr-container-images" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Authenticate Docker against a registry with &lt;code&gt;aws ecr get-login-password --region eu-central-1&lt;/code&gt; piped into &lt;code&gt;docker login --username AWS --password-stdin 123456789012.dkr.ecr.eu-central-1.amazonaws.com&lt;/code&gt;. Container commands are in 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;aws ecr get-login-password --region eu-central-1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print a registry password for Docker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ecr describe-repositories&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List repositories&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ecr create-repository --repository-name app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a repository&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ecr list-images --repository-name app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List image tags and digests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ecr describe-images --repository-name app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show image size and push date&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ecr batch-delete-image --repository-name app --image-ids imageTag=old&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete an image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aws ecr delete-repository --repository-name app --force&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Permanently delete a repository and its images&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;</content:encoded></item><item><title>aws s3 Commands: cp, sync, ls, and rm Examples</title><link>https://linuxize.com/post/aws-s3-commands/</link><pubDate>Fri, 31 Jul 2026 10:10:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/aws-s3-commands/</guid><category>aws</category><description>Practical aws s3 command examples for listing buckets, copying files, syncing directories, filtering transfers, and deleting objects safely from Linux.</description><content:encoded>&lt;p&gt;Uploading a file to Amazon S3 through the web console works fine once. The tenth time, or the first time you need to move a whole directory tree, you want it in your shell history instead. The &lt;code&gt;aws s3&lt;/code&gt; command group gives you familiar Unix-style operations for S3: &lt;code&gt;ls&lt;/code&gt; to list, &lt;code&gt;cp&lt;/code&gt; to copy, &lt;code&gt;sync&lt;/code&gt; to mirror directories, and &lt;code&gt;rm&lt;/code&gt; to delete.&lt;/p&gt;
&lt;p&gt;This guide shows practical examples of the most used &lt;code&gt;aws s3&lt;/code&gt; commands, including recursive copies, include and exclude filters, and safe deletion with &lt;code&gt;--dryrun&lt;/code&gt;.&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;The examples require AWS CLI version 2 and an identity with permission to access the S3 resources you use. If the CLI is not ready yet, follow our guide on &lt;a href="https://linuxize.com/post/how-to-install-and-configure-aws-cli-on-linux/"&gt;installing and configuring the AWS CLI on Linux&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;Confirm that the CLI can authenticate to your account 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;aws sts get-caller-identity&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 your account ID and IAM identity, authentication is working. Individual S3 commands can still return &lt;code&gt;AccessDenied&lt;/code&gt; when that identity lacks the required bucket or object permissions.&lt;/p&gt;
&lt;p&gt;The AWS CLI has two command groups for S3. The &lt;code&gt;aws s3&lt;/code&gt; group used in this guide provides high-level file operations. The &lt;code&gt;aws s3api&lt;/code&gt; group exposes the raw API, one call per operation, and is only needed for tasks such as managing bucket policies or object versions.&lt;/p&gt;
&lt;p&gt;S3 paths use the form &lt;code&gt;s3://bucket-name/key&lt;/code&gt;. The general command structure 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;aws s3 &amp;lt;subcommand&amp;gt; [ARGUMENTS] [OPTIONS]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Commands such as &lt;code&gt;cp&lt;/code&gt;, &lt;code&gt;mv&lt;/code&gt;, and &lt;code&gt;sync&lt;/code&gt; take source and destination arguments. The remaining commands operate on one S3 path, except &lt;code&gt;aws s3 ls&lt;/code&gt;, which can omit the path to list every bucket.&lt;/p&gt;
&lt;h2 id="listing-buckets-and-objects"&gt;Listing Buckets and Objects &lt;a class="headline-link" href="#listing-buckets-and-objects" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Run &lt;code&gt;aws s3 ls&lt;/code&gt; with no arguments to list all buckets in the account:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aws s3 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;2025-11-04 09:12:33 my-bucket
2026-01-15 17:40:21 my-site-backups&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To list the contents of a bucket, pass the bucket 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;aws s3 ls s3://my-bucket&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; PRE logs/
2026-02-10 08:30:12 1048576 backup.tar.gz
2026-02-11 09:02:44 4523 notes.txt&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Entries marked &lt;code&gt;PRE&lt;/code&gt; are prefixes, the S3 equivalent of directories. To look inside one, append it to the path with a trailing slash, for example &lt;code&gt;aws s3 ls s3://my-bucket/logs/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Add &lt;code&gt;--recursive&lt;/code&gt; to walk the whole bucket, and combine it with &lt;code&gt;--human-readable&lt;/code&gt; and &lt;code&gt;--summarize&lt;/code&gt; to get readable sizes and totals:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aws s3 ls s3://my-bucket --recursive --human-readable --summarize&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;2026-02-10 08:30:12 1.0 MiB backup.tar.gz
2026-02-11 09:02:44 4.4 KiB notes.txt
2026-02-12 06:15:09 12.7 MiB logs/app.log
Total Objects: 3
Total Size: 13.7 MiB&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The summary at the bottom shows the count and combined size of the current objects listed under the bucket or prefix. It does not include noncurrent versions in a versioned bucket.&lt;/p&gt;
&lt;h2 id="uploading-and-downloading-files-with-cp"&gt;Uploading and Downloading Files with cp &lt;a class="headline-link" href="#uploading-and-downloading-files-with-cp" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;cp&lt;/code&gt; subcommand copies files between your machine and S3, in either direction. To upload a file to a bucket:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aws s3 cp backup.tar.gz s3://my-bucket/&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;upload: ./backup.tar.gz to s3://my-bucket/backup.tar.gz&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The object keeps its original name. To store it under a different key, spell out the full target 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;aws s3 cp backup.tar.gz s3://my-bucket/backups/backup-2026-02-10.tar.gz&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Downloading works the same way with the arguments reversed. The &lt;code&gt;.&lt;/code&gt; target saves the file into 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;aws s3 cp s3://my-bucket/backup.tar.gz .&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You can also copy directly between two buckets. The transfer happens inside AWS, so the data does not pass through your 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;aws s3 cp s3://my-bucket/backup.tar.gz s3://my-site-backups/backup.tar.gz&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id="copying-directories"&gt;Copying Directories &lt;a class="headline-link" href="#copying-directories" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To copy a directory and everything under it, add &lt;code&gt;--recursive&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;aws s3 cp ./logs s3://my-bucket/logs --recursive&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;When you only want some of the files, combine &lt;code&gt;--exclude&lt;/code&gt; and &lt;code&gt;--include&lt;/code&gt;. The filters are evaluated in the order given, and the later filter wins, so the usual pattern is to exclude everything and then include 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;aws s3 cp ./logs s3://my-bucket/logs --recursive --exclude &lt;span class="s2"&gt;&amp;#34;*&amp;#34;&lt;/span&gt; --include &lt;span class="s2"&gt;&amp;#34;*.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;This uploads only the &lt;code&gt;.log&lt;/code&gt; files and skips everything else in the directory.&lt;/p&gt;
&lt;p&gt;The most useful &lt;code&gt;cp&lt;/code&gt; options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--recursive&lt;/code&gt; - Copy all files under a directory or prefix.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--exclude&lt;/code&gt; / &lt;code&gt;--include&lt;/code&gt; - Filter files by pattern; later filters override earlier ones.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--storage-class&lt;/code&gt; - Choose the destination storage class, for example &lt;code&gt;STANDARD_IA&lt;/code&gt; for backups accessed infrequently.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--dryrun&lt;/code&gt; - Print what would be copied without transferring anything.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="streaming-to-and-from-s3"&gt;Streaming to and from S3 &lt;a class="headline-link" href="#streaming-to-and-from-s3" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;cp&lt;/code&gt; command accepts &lt;code&gt;-&lt;/code&gt; as a stand-in for standard input or output, which lets you pipe data straight to a bucket without a temporary 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;pg_dump mydb &lt;span class="p"&gt;|&lt;/span&gt; gzip &lt;span class="p"&gt;|&lt;/span&gt; aws s3 cp - s3://my-bucket/backups/mydb.sql.gz&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Reading works the same way in reverse. This prints an object to standard output where you can pipe it further:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aws s3 cp s3://my-bucket/backups/mydb.sql.gz - &lt;span class="p"&gt;|&lt;/span&gt; gunzip &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;h2 id="moving-and-renaming-objects-with-mv"&gt;Moving and Renaming Objects with mv &lt;a class="headline-link" href="#moving-and-renaming-objects-with-mv" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Amazon S3 does not rename an object in place. The &lt;code&gt;mv&lt;/code&gt; command copies the source to the new key and then deletes the source, so preview the operation before running 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;aws s3 mv s3://my-bucket/report-draft.pdf s3://my-bucket/report-final.pdf --dryrun&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Check both paths carefully, then run the command again without &lt;code&gt;--dryrun&lt;/code&gt;. The same command can move files between your machine and S3 or between two buckets.&lt;/p&gt;
&lt;h2 id="synchronizing-directories-with-sync"&gt;Synchronizing Directories with sync &lt;a class="headline-link" href="#synchronizing-directories-with-sync" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Where &lt;code&gt;cp --recursive&lt;/code&gt; copies everything every time, &lt;code&gt;sync&lt;/code&gt; transfers files that are missing, differ in size, or have a newer source modification time. To mirror a local directory to a bucket:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aws s3 sync ./website s3://my-bucket/website&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Run it again immediately and nothing is transferred, because both sides already match. This makes &lt;code&gt;sync&lt;/code&gt; the right tool for repeated jobs such as publishing a static site or shipping nightly backups.&lt;/p&gt;
&lt;p&gt;The reverse direction downloads new and changed objects from the bucket:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aws s3 sync s3://my-bucket/website ./website&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Syncing between two buckets also works: &lt;code&gt;aws s3 sync s3://my-bucket s3://my-site-backups&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;By default, &lt;code&gt;sync&lt;/code&gt; never deletes anything. Files removed from the source remain on the target. To make the target an exact mirror, add &lt;code&gt;--delete&lt;/code&gt;, but preview the result first because deleted objects are gone for good:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aws s3 sync ./website s3://my-bucket/website --delete --dryrun&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;(dryrun) upload: website/index.html to s3://my-bucket/website/index.html
(dryrun) delete: s3://my-bucket/website/old-page.html&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Every line is prefixed with &lt;code&gt;(dryrun)&lt;/code&gt;, so nothing has happened yet. Review the &lt;code&gt;delete:&lt;/code&gt; lines, and when the plan looks right, run the same command without &lt;code&gt;--dryrun&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The same &lt;code&gt;--exclude&lt;/code&gt; and &lt;code&gt;--include&lt;/code&gt; filters from &lt;code&gt;cp&lt;/code&gt; apply here. A common one keeps repository metadata out of the bucket:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aws s3 sync ./website s3://my-bucket/website --exclude &lt;span class="s2"&gt;&amp;#34;.git/*&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;Because &lt;code&gt;sync&lt;/code&gt; is idempotent, it pairs well with a scheduled job. See our guide on &lt;a href="https://linuxize.com/post/scheduling-cron-jobs-with-crontab/"&gt;scheduling cron jobs with crontab&lt;/a&gt;
if you want to run a sync every night.&lt;/p&gt;
&lt;h2 id="deleting-objects-with-rm"&gt;Deleting Objects with rm &lt;a class="headline-link" href="#deleting-objects-with-rm" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To delete a single object, pass its full 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;aws s3 rm s3://my-bucket/old-backup.tar.gz&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;delete: s3://my-bucket/old-backup.tar.gz&lt;/code&gt;&lt;/pre&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;There is no trash bin in S3. Unless the bucket has versioning enabled, a deleted object cannot be recovered. Always test recursive deletions with &lt;code&gt;--dryrun&lt;/code&gt; first.&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;To delete everything under a prefix, add &lt;code&gt;--recursive&lt;/code&gt; and preview the damage before committing:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aws s3 rm s3://my-bucket/logs/ --recursive --dryrun&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If the listed objects are the ones you expect, run the command again without &lt;code&gt;--dryrun&lt;/code&gt; to perform the deletion.&lt;/p&gt;
&lt;p&gt;On buckets with versioning enabled, &lt;code&gt;rm&lt;/code&gt; only inserts a delete marker; previous versions remain and continue to incur storage costs. Removing versions permanently requires the &lt;code&gt;aws s3api delete-object&lt;/code&gt; call with a version ID.&lt;/p&gt;
&lt;h2 id="creating-and-removing-buckets"&gt;Creating and Removing Buckets &lt;a class="headline-link" href="#creating-and-removing-buckets" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;mb&lt;/code&gt; (make bucket) subcommand creates a new bucket. General purpose bucket names must be unique across AWS accounts and Regions within an AWS partition, so pick something difficult to collide 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;aws s3 mb s3://my-unique-bucket-name --region eu-central-1&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;rb&lt;/code&gt; (remove bucket) subcommand deletes a bucket, but only when it is empty:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aws s3 rb s3://my-unique-bucket-name&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Adding &lt;code&gt;--force&lt;/code&gt; deletes the current objects in an unversioned bucket and then removes the bucket. It does not permanently delete object versions or delete markers, so removal fails when a versioned bucket still contains them. Treat &lt;code&gt;--force&lt;/code&gt; with the same caution as &lt;code&gt;rm --recursive&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="generating-temporary-download-links"&gt;Generating Temporary Download Links &lt;a class="headline-link" href="#generating-temporary-download-links" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Sometimes you need to hand a file to someone without making the bucket public. The &lt;code&gt;presign&lt;/code&gt; subcommand generates a URL that grants temporary access to a single object:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aws s3 presign s3://my-bucket/report.pdf --expires-in &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;The command prints a long URL that anyone can use to download the object for the next hour (3600 seconds). The default lifetime is one hour, and the maximum is seven days. A URL signed with temporary credentials expires when those credentials expire, even when &lt;code&gt;--expires-in&lt;/code&gt; requests a later time.&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/aws-cli/"&gt;AWS CLI 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;List all buckets&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws s3 ls&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List bucket contents&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws s3 ls s3://bucket&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List recursively with totals&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws s3 ls s3://bucket --recursive --summarize&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Upload a file&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws s3 cp file.txt s3://bucket/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Download a file&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws s3 cp s3://bucket/file.txt .&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Upload a directory&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws s3 cp ./dir s3://bucket/dir --recursive&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Preview renaming an object&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws s3 mv s3://bucket/old.txt s3://bucket/new.txt --dryrun&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mirror a directory&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws s3 sync ./dir s3://bucket/dir&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Preview removing stale files during a mirror&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws s3 sync ./dir s3://bucket/dir --delete --dryrun&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete an object&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws s3 rm s3://bucket/file.txt&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Preview deleting a prefix&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws s3 rm s3://bucket/dir/ --recursive --dryrun&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Create a bucket&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws s3 mb s3://bucket&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remove an empty bucket&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws s3 rb s3://bucket&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Temporary download link&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aws s3 presign s3://bucket/file.txt&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 cp &amp;ndash;recursive and sync?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;cp --recursive&lt;/code&gt; copies every file on every run. &lt;code&gt;sync&lt;/code&gt; compares both sides and transfers only new or changed files, which is faster and cheaper for repeated jobs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Can I use shell wildcards in S3 paths?&lt;/strong&gt;&lt;br&gt;
No. The AWS CLI does not interpret Unix-style wildcards in an S3 path such as &lt;code&gt;s3://bucket/*.log&lt;/code&gt;. Use &lt;code&gt;--recursive&lt;/code&gt; together with &lt;code&gt;--exclude&lt;/code&gt; and &lt;code&gt;--include&lt;/code&gt; filters instead.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How do I delete all files in a bucket?&lt;/strong&gt;&lt;br&gt;
For an unversioned bucket, preview &lt;code&gt;aws s3 rm s3://bucket --recursive --dryrun&lt;/code&gt;, then remove &lt;code&gt;--dryrun&lt;/code&gt; to empty it. The &lt;code&gt;aws s3 rb s3://bucket --force&lt;/code&gt; command can empty and remove an unversioned bucket in one step, but it fails when object versions or delete markers remain.&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;Make &lt;code&gt;--dryrun&lt;/code&gt; a habit before &lt;code&gt;mv&lt;/code&gt;, recursive &lt;code&gt;rm&lt;/code&gt;, or &lt;code&gt;sync --delete&lt;/code&gt;, then reuse the reviewed commands in your scripts and scheduled jobs.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/aws-s3-commands/featured_hu_541732d4103e130d.webp" medium="image" type="image/webp" width="1200" height="630"/></item><item><title>How to Install and Configure the AWS CLI on Linux</title><link>https://linuxize.com/post/how-to-install-and-configure-aws-cli-on-linux/</link><pubDate>Thu, 30 Jul 2026 09:20:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/how-to-install-and-configure-aws-cli-on-linux/</guid><category>aws</category><description>Install AWS CLI version 2 on Linux, authenticate with aws login or IAM Identity Center, manage profiles, and verify access to your AWS account.</description><content:encoded>&lt;p&gt;Clicking through the AWS console works for a one-off task, but the moment you manage instances, buckets, or DNS records regularly, you want those actions in your shell history and your scripts. The AWS CLI puts every AWS API behind a single &lt;code&gt;aws&lt;/code&gt; command, so listing instances, syncing a bucket, or rotating a key becomes a one-liner you can repeat and automate.&lt;/p&gt;
&lt;p&gt;This guide explains how to install AWS CLI version 2 on Linux, authenticate with short-term credentials, manage named profiles, and verify that your setup can access your AWS account.&lt;/p&gt;
&lt;h2 id="installing-aws-cli-version-2"&gt;Installing AWS CLI Version 2 &lt;a class="headline-link" href="#installing-aws-cli-version-2" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;AWS maintains an &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html" target="_blank" rel="noopener noreferrer"&gt;install script&lt;/a&gt;
that downloads, verifies, and installs the current AWS CLI release. It detects whether your Linux system uses an x86_64 or ARM processor, so the same command works on both architectures:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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://awscli.amazonaws.com/v2/install.sh &lt;span class="p"&gt;|&lt;/span&gt; bash&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The script installs the program for your user under &lt;code&gt;~/.local/share/aws-cli&lt;/code&gt; and creates &lt;code&gt;~/.local/bin/aws&lt;/code&gt;. This avoids requiring root access. Open a new terminal after installation, then verify 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;aws --version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The first part of the output should begin with &lt;code&gt;aws-cli/2.&lt;/code&gt;. The remaining version and system details depend on the current release and your Linux distribution.&lt;/p&gt;
&lt;p&gt;To install the CLI for every user instead, run the installer in system mode:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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://awscli.amazonaws.com/v2/install.sh &lt;span class="p"&gt;|&lt;/span&gt; sudo bash -s -- --system&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;System mode installs the files under &lt;code&gt;/usr/local/aws-cli&lt;/code&gt; and creates the command links in &lt;code&gt;/usr/local/bin&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;When a newer release is available, update a per-user 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;aws update&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;sudo aws update&lt;/code&gt; if you installed the CLI in system mode.&lt;/p&gt;
&lt;h2 id="configuring-aws-cli-authentication"&gt;Configuring AWS CLI Authentication &lt;a class="headline-link" href="#configuring-aws-cli-authentication" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;AWS recommends short-term credentials for people and workloads. The best configuration method depends on how your AWS account manages identities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;aws login&lt;/code&gt; when you already sign in to the AWS Management Console with an IAM or federated identity.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;aws configure sso&lt;/code&gt; when your organization uses AWS IAM Identity Center.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;aws configure&lt;/code&gt; with an IAM access key only when neither short-term method is available.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Do not configure the CLI with root-user access keys. Use a non-root identity with only the permissions required for your work.&lt;/p&gt;
&lt;h3 id="signing-in-with-aws-console-credentials"&gt;Signing In with AWS Console Credentials &lt;a class="headline-link" href="#signing-in-with-aws-console-credentials" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;AWS CLI version 2.32.0 and later supports &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-sign-in.html" target="_blank" rel="noopener noreferrer"&gt;&lt;code&gt;aws login&lt;/code&gt;&lt;/a&gt;
, which exchanges an existing console session for temporary credentials. Your AWS administrator must allow local developer sign-in for the identity. Start the browser-based flow 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;aws login&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The CLI asks for a default Region if one is not already configured, then opens your browser. Select the console session and identity you want to use. AWS stores the temporary session under &lt;code&gt;~/.aws/login/cache&lt;/code&gt; and refreshes its credentials while the session remains valid.&lt;/p&gt;
&lt;p&gt;To create or sign in to a named profile, add &lt;code&gt;--profile&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;aws login --profile work&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On a remote Linux server without a browser, use the cross-device flow:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aws login --remote&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;When the session reaches its maximum duration, run &lt;code&gt;aws login&lt;/code&gt; again. You can end it earlier with &lt;code&gt;aws logout&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="configuring-iam-identity-center"&gt;Configuring IAM Identity Center &lt;a class="headline-link" href="#configuring-iam-identity-center" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If your organization provides an AWS access portal, follow the &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-sso.html" target="_blank" rel="noopener noreferrer"&gt;IAM Identity Center configuration&lt;/a&gt;
instead of using &lt;code&gt;aws login&lt;/code&gt;. Run the setup wizard with a descriptive profile 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;aws configure sso --profile work&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The wizard asks for the SSO session name, start or issuer URL, SSO Region, account, permission set, default Region, and output format. It opens a browser so you can authorize the session.&lt;/p&gt;
&lt;p&gt;Sign in again whenever the SSO session expires:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aws sso login --profile work&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;IAM Identity Center stores the profile settings in &lt;code&gt;~/.aws/config&lt;/code&gt; and caches its tokens under &lt;code&gt;~/.aws/sso/cache&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="configuring-an-iam-access-key"&gt;Configuring an IAM Access Key &lt;a class="headline-link" href="#configuring-an-iam-access-key" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Some legacy tools and workloads still require a long-term access key. If you cannot use temporary credentials, create the key for a dedicated IAM user with least-privilege permissions, then configure a named 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;aws configure --profile legacy&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;AWS Access Key ID [None]: &amp;lt;your-access-key-id&amp;gt;
AWS Secret Access Key [None]: &amp;lt;your-secret-access-key&amp;gt;
Default region name [None]: eu-central-1
Default output format [None]: json&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Choose the Region where most of your resources run. JSON is a practical default output format for scripts, while &lt;code&gt;table&lt;/code&gt; is easier to scan interactively.&lt;/p&gt;
&lt;p&gt;The CLI writes access keys to &lt;code&gt;~/.aws/credentials&lt;/code&gt; and other settings to &lt;code&gt;~/.aws/config&lt;/code&gt;.&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;Never commit access keys, paste them into scripts, or bake them into images. If a key enters Git history or another public location, deactivate it immediately and create a replacement. Prefer temporary credentials whenever the application supports them.&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="verifying-the-configuration"&gt;Verifying the Configuration &lt;a class="headline-link" href="#verifying-the-configuration" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Ask AWS Security Token Service which identity the CLI resolved:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aws sts get-caller-identity&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;#34;UserId&amp;#34;: &amp;#34;AROAXAMPLE:dejan&amp;#34;,
&amp;#34;Account&amp;#34;: &amp;#34;123456789012&amp;#34;,
&amp;#34;Arn&amp;#34;: &amp;#34;arn:aws:sts::123456789012:assumed-role/Developer/dejan&amp;#34;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The response identifies the account, IAM user, or assumed role behind the current credentials. To test a named profile, add it to 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;aws sts get-caller-identity --profile work&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If the call succeeds, authentication works. Individual service commands can still fail when the identity lacks permission for that action.&lt;/p&gt;
&lt;h2 id="working-with-named-profiles"&gt;Working with Named Profiles &lt;a class="headline-link" href="#working-with-named-profiles" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Profiles keep settings and credentials for separate accounts or roles from interfering with each other. List the profiles currently 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;aws configure list-profiles&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Select a profile for one command with &lt;code&gt;--profile&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;aws s3 ls --profile work&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To use the same profile for the rest of the shell session, set &lt;code&gt;AWS_PROFILE&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;export&lt;/span&gt; &lt;span class="nv"&gt;AWS_PROFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;work&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;default&lt;/code&gt; profile is used when neither &lt;code&gt;--profile&lt;/code&gt; nor &lt;code&gt;AWS_PROFILE&lt;/code&gt; is set. A command-line &lt;code&gt;--profile&lt;/code&gt; option overrides the environment variable.&lt;/p&gt;
&lt;p&gt;On EC2 instances, ECS tasks, and other AWS compute services, attach an IAM role to the workload instead of storing access keys. For CI systems, use the platform&amp;rsquo;s OpenID Connect integration to assume an IAM role when available.&lt;/p&gt;
&lt;h2 id="running-your-first-commands"&gt;Running Your First Commands &lt;a class="headline-link" href="#running-your-first-commands" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The CLI follows a &lt;code&gt;aws &amp;lt;service&amp;gt; &amp;lt;operation&amp;gt;&lt;/code&gt; pattern. List your S3 buckets:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;aws s3 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;2025-11-02 14:11:20 backups-prod
2026-01-01 09:48:03 static-assets&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;List EC2 instances, trimmed down to the fields you care about with &lt;code&gt;--query&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;aws ec2 describe-instances &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; --query &lt;span class="s1"&gt;&amp;#39;Reservations[].Instances[].{ID:InstanceId,Type:InstanceType,State:State.Name}&amp;#39;&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; --output table&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;-------------------------------------------------
| DescribeInstances |
+----------------------+-----------+------------+
| ID | Type | State |
+----------------------+-----------+------------+
| i-0abcd1234ef567890 | t3.small | running |
| i-0fe9876543ba21001 | t3.micro | stopped |
+----------------------+-----------+------------+&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;--query&lt;/code&gt; option takes a JMESPath expression and works on every command, which saves you from piping JSON into other tools for simple filtering. The &lt;code&gt;--output&lt;/code&gt; option switches between &lt;code&gt;json&lt;/code&gt;, &lt;code&gt;yaml&lt;/code&gt;, &lt;code&gt;text&lt;/code&gt;, and &lt;code&gt;table&lt;/code&gt; per invocation.&lt;/p&gt;
&lt;p&gt;Every service has built-in help, so &lt;code&gt;aws s3 help&lt;/code&gt; and &lt;code&gt;aws ec2 describe-instances help&lt;/code&gt; open the relevant manual pages without leaving the terminal.&lt;/p&gt;
&lt;h2 id="enabling-command-completion"&gt;Enabling Command Completion &lt;a class="headline-link" href="#enabling-command-completion" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The installer includes &lt;code&gt;aws_completer&lt;/code&gt;, which suggests services, operations, and options when you press Tab. Confirm that your shell can find 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;command&lt;/span&gt; -v aws_completer&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The path is usually &lt;code&gt;~/.local/bin/aws_completer&lt;/code&gt; for a per-user installation or &lt;code&gt;/usr/local/bin/aws_completer&lt;/code&gt; for a system installation.&lt;/p&gt;
&lt;p&gt;For Bash, add this line to &lt;code&gt;~/.bashrc&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;~/.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;complete&lt;/span&gt; -C &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; -v aws_completer&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; aws&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Reload 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;&lt;span class="nb"&gt;source&lt;/span&gt; ~/.bashrc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For Zsh, add these lines to &lt;code&gt;~/.zshrc&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;~/.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;autoload bashcompinit &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; bashcompinit
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;complete&lt;/span&gt; -C &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; -v aws_completer&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; aws&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Then reload the Zsh 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;&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="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;aws: command not found&lt;/strong&gt;&lt;br&gt;
For a per-user installation, &lt;code&gt;~/.local/bin&lt;/code&gt; may be missing from &lt;code&gt;PATH&lt;/code&gt;. Add &lt;code&gt;export PATH=&amp;quot;$HOME/.local/bin:$PATH&amp;quot;&lt;/code&gt; to &lt;code&gt;~/.bashrc&lt;/code&gt; or &lt;code&gt;~/.zshrc&lt;/code&gt;, reload the file, and run &lt;code&gt;aws --version&lt;/code&gt; again. For a system installation, check that &lt;code&gt;/usr/local/bin&lt;/code&gt; is in &lt;code&gt;PATH&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Unable to locate credentials&lt;/strong&gt;&lt;br&gt;
No authentication method is configured, or &lt;code&gt;AWS_PROFILE&lt;/code&gt; points to a profile that does not exist. Run &lt;code&gt;aws configure list&lt;/code&gt; to see where the CLI is looking and what it found.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ExpiredToken after aws login&lt;/strong&gt;&lt;br&gt;
An older access key in &lt;code&gt;~/.aws/credentials&lt;/code&gt; can take precedence over login credentials for the same profile. Run &lt;code&gt;aws configure list&lt;/code&gt;, remove the stale key entries for that profile, and sign in again.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;An error occurred (InvalidClientTokenId)&lt;/strong&gt;&lt;br&gt;
For an access-key profile, the key is inactive, deleted, or mistyped. Replace it in IAM and run &lt;code&gt;aws configure --profile &amp;lt;profile-name&amp;gt;&lt;/code&gt; again.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;An error occurred (UnauthorizedOperation) or AccessDenied&lt;/strong&gt;&lt;br&gt;
The credentials are valid, but the current identity lacks permission for that action. Ask your AWS administrator to grant the required permission through the relevant role, permission set, or IAM policy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Could not connect to the endpoint URL&lt;/strong&gt;&lt;br&gt;
Check the configured Region with &lt;code&gt;aws configure get region&lt;/code&gt;. A value such as &lt;code&gt;eu-central1&lt;/code&gt; is invalid; the correct format is &lt;code&gt;eu-central-1&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;With AWS CLI version 2 installed and &lt;code&gt;aws sts get-caller-identity&lt;/code&gt; returning the expected account, you can start using service commands without storing long-lived credentials. For day-to-day bucket work, see our guide on &lt;a href="https://linuxize.com/post/aws-s3-commands/"&gt;the aws s3 commands&lt;/a&gt;
, and if you want those syncs on a schedule, our guide on &lt;a href="https://linuxize.com/post/scheduling-cron-jobs-with-crontab/"&gt;using cron to schedule jobs&lt;/a&gt;
.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/how-to-install-and-configure-aws-cli-on-linux/featured_hu_4c28e7449944e2e3.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>How to Install Kubernetes with kubeadm on Ubuntu 26.04</title><link>https://linuxize.com/post/how-to-install-kubernetes-with-kubeadm-on-ubuntu-26-04/</link><pubDate>Wed, 29 Jul 2026 09:40:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/how-to-install-kubernetes-with-kubeadm-on-ubuntu-26-04/</guid><category>kubernetes</category><category>devops</category><category>ubuntu</category><description>Install Kubernetes 1.36 on Ubuntu 26.04 with kubeadm and containerd, configure Flannel networking, join worker nodes, and verify the cluster.</description><content:encoded>&lt;p&gt;When you outgrow a single Docker host and need to run containers across several machines with rolling updates, self-healing, and service discovery built in, Kubernetes is the standard answer. The &lt;code&gt;kubeadm&lt;/code&gt; tool provides a minimal upstream method for bootstrapping a self-managed cluster on plain Linux servers.&lt;/p&gt;
&lt;p&gt;This guide explains how to set up Kubernetes 1.36 on Ubuntu 26.04 using &lt;code&gt;kubeadm&lt;/code&gt;, &lt;code&gt;kubelet&lt;/code&gt;, and &lt;code&gt;kubectl&lt;/code&gt; from the Kubernetes apt repository, with &lt;code&gt;containerd&lt;/code&gt; as the container runtime. By the end you will have a control-plane node, one or more worker nodes, and a test workload running across the cluster.&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;Disable swap&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo swapoff -a&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Install containerd&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt install -y containerd&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Add Kubernetes apt key&lt;/td&gt;
&lt;td&gt;&lt;code&gt;curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.36/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Install kube tools&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt install -y kubelet kubeadm kubectl&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hold kube versions&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo apt-mark hold kubelet kubeadm kubectl&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Init control plane&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo kubeadm init --pod-network-cidr=10.244.0.0/16&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Configure kubectl&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mkdir -p ~/.kube &amp;amp;&amp;amp; sudo cp /etc/kubernetes/admin.conf ~/.kube/config &amp;amp;&amp;amp; sudo chown $(id -u):$(id -g) ~/.kube/config&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Get nodes&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kubectl get nodes&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Get pods (all namespaces)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;kubectl get pods -A&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generate join command&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo kubeadm token create --print-join-command&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;Two or more servers running Ubuntu 26.04 with at least 2 GB of RAM each. The control-plane node needs at least 2 CPUs.&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://linuxize.com/post/how-to-create-a-sudo-user-on-ubuntu/"&gt;user with sudo privileges&lt;/a&gt;
on each server.&lt;/li&gt;
&lt;li&gt;Full network connectivity between the nodes and Internet access for downloading packages and container images.&lt;/li&gt;
&lt;li&gt;A stable IP address plus a unique hostname, MAC address, and product UUID on every node.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The same instructions apply to control-plane and worker nodes up to the cluster bootstrap step. Run every command on every node unless the section says otherwise.&lt;/p&gt;
&lt;p&gt;If a host or cloud firewall filters traffic between the nodes, allow the required traffic between these sources and destination ports:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Destination&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Ports&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Control plane API&lt;/td&gt;
&lt;td&gt;Cluster nodes and administrators&lt;/td&gt;
&lt;td&gt;TCP &lt;code&gt;6443&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;etcd on the control plane&lt;/td&gt;
&lt;td&gt;Control-plane nodes only&lt;/td&gt;
&lt;td&gt;TCP &lt;code&gt;2379-2380&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Control-plane kubelet&lt;/td&gt;
&lt;td&gt;Control-plane components&lt;/td&gt;
&lt;td&gt;TCP &lt;code&gt;10250&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scheduler and controller manager&lt;/td&gt;
&lt;td&gt;Control-plane node itself&lt;/td&gt;
&lt;td&gt;TCP &lt;code&gt;10257&lt;/code&gt; and &lt;code&gt;10259&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Worker kubelet&lt;/td&gt;
&lt;td&gt;Control-plane nodes&lt;/td&gt;
&lt;td&gt;TCP &lt;code&gt;10250&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;kube-proxy health endpoint&lt;/td&gt;
&lt;td&gt;Node itself and load balancers that use it&lt;/td&gt;
&lt;td&gt;TCP &lt;code&gt;10256&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NodePort services&lt;/td&gt;
&lt;td&gt;Clients that need access&lt;/td&gt;
&lt;td&gt;TCP and UDP &lt;code&gt;30000-32767&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flannel VXLAN&lt;/td&gt;
&lt;td&gt;Other cluster nodes&lt;/td&gt;
&lt;td&gt;UDP &lt;code&gt;8472&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Restrict these rules to trusted cluster and administration networks. Do not expose the etcd or kubelet ports directly to the Internet.&lt;/p&gt;
&lt;h2 id="step-1-prepare-the-system"&gt;Step 1: Prepare the System &lt;a class="headline-link" href="#step-1-prepare-the-system" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The kubelet refuses to start when swap is active unless you configure it to tolerate swap. This guide uses the default behavior, so turn swap off for the current session and comment out swap entries in &lt;code&gt;/etc/fstab&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 swapoff -a
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo sed -i &lt;span class="s1"&gt;&amp;#39;/[[:space:]]swap[[:space:]]/ s/^[^#]/#&amp;amp;/&amp;#39;&lt;/span&gt; /etc/fstab&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Check that no active swap devices remain:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;swapon --show&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The command should return no output.&lt;/p&gt;
&lt;p&gt;Load the kernel modules required by the container runtime and Kubernetes networking:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 tee /etc/modules-load.d/k8s.conf &lt;span class="s"&gt;&amp;lt;&amp;lt;EOF
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;overlay
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;br_netfilter
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;EOF&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;sudo modprobe overlay
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo modprobe br_netfilter&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Confirm that both modules are loaded:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lsmod &lt;span class="p"&gt;|&lt;/span&gt; grep -E &lt;span class="s1"&gt;&amp;#39;br_netfilter|overlay&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;Enable the sysctl settings that allow iptables to see bridged traffic and let IP forwarding 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;sudo tee /etc/sysctl.d/k8s.conf &lt;span class="s"&gt;&amp;lt;&amp;lt;EOF
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;net.bridge.bridge-nf-call-iptables = 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;net.bridge.bridge-nf-call-ip6tables = 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;net.ipv4.ip_forward = 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;EOF&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;sudo sysctl --system&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Verify the two main networking settings:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sysctl net.ipv4.ip_forward net.bridge.bridge-nf-call-iptables&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Both values should be &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="step-2-install-containerd"&gt;Step 2: Install containerd &lt;a class="headline-link" href="#step-2-install-containerd" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Kubernetes uses the Container Runtime Interface (CRI) to communicate with a container runtime. Ubuntu 26.04 provides containerd 2.x in its 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 apt update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install -y containerd&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If Docker is already installed from the official Docker repository, the &lt;code&gt;containerd.io&lt;/code&gt; package provides the same runtime and conflicts with the &lt;code&gt;containerd&lt;/code&gt; package. Skip the install above and continue with the configuration below.&lt;/p&gt;
&lt;p&gt;Generate a complete containerd configuration, then enable the &lt;code&gt;systemd&lt;/code&gt; cgroup driver so it matches the kubelet configuration created by &lt;code&gt;kubeadm&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 mkdir -p /etc/containerd
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;containerd config default &lt;span class="p"&gt;|&lt;/span&gt; sudo tee /etc/containerd/config.toml &amp;gt; /dev/null
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo sed -i &lt;span class="s1"&gt;&amp;#39;s/SystemdCgroup = false/SystemdCgroup = true/&amp;#39;&lt;/span&gt; /etc/containerd/config.toml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl restart containerd
&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; containerd&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Confirm that the service is running and the setting is present:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 containerd
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo grep &lt;span class="s1"&gt;&amp;#39;SystemdCgroup = true&amp;#39;&lt;/span&gt; /etc/containerd/config.toml&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 print &lt;code&gt;active&lt;/code&gt;, and the second should show the enabled cgroup setting.&lt;/p&gt;
&lt;h2 id="step-3-add-the-kubernetes-apt-repository"&gt;Step 3: Add the Kubernetes apt Repository &lt;a class="headline-link" href="#step-3-add-the-kubernetes-apt-repository" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Kubernetes publishes a separate package repository for every minor release. This guide follows the supported &lt;code&gt;v1.36&lt;/code&gt; series, and apt installs the newest patch release available in that repository.&lt;/p&gt;
&lt;p&gt;Install the prerequisites and download the 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 install -y apt-transport-https ca-certificates curl gpg
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo mkdir -p -m &lt;span class="m"&gt;755&lt;/span&gt; /etc/apt/keyrings
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.36/deb/Release.key &lt;span class="p"&gt;|&lt;/span&gt; sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Add the 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="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.36/deb/ /&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; sudo tee /etc/apt/sources.list.d/kubernetes.list&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Refresh 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;h2 id="step-4-install-kubelet-kubeadm-and-kubectl"&gt;Step 4: Install kubelet, kubeadm, and kubectl &lt;a class="headline-link" href="#step-4-install-kubelet-kubeadm-and-kubectl" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Install the three Kubernetes tools and pin their versions so an &lt;code&gt;apt upgrade&lt;/code&gt; does not change them by accident:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 kubelet kubeadm kubectl
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt-mark hold kubelet kubeadm kubectl&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Confirm 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;kubeadm version
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubelet --version
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl version --client&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;apt-mark hold&lt;/code&gt; flag is important because Kubernetes upgrades follow a specific procedure (&lt;code&gt;kubeadm upgrade&lt;/code&gt;), and a casual &lt;code&gt;apt upgrade&lt;/code&gt; could break the cluster.&lt;/p&gt;
&lt;p&gt;The kubelet may restart repeatedly at this point because it does not have a cluster configuration yet. This is expected and stops after &lt;code&gt;kubeadm init&lt;/code&gt; or &lt;code&gt;kubeadm join&lt;/code&gt; configures the node.&lt;/p&gt;
&lt;h2 id="step-5-initialize-the-control-plane"&gt;Step 5: Initialize the Control Plane &lt;a class="headline-link" href="#step-5-initialize-the-control-plane" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Run the next two steps only on the control-plane node.&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;This procedure creates one control-plane node, so the cluster is not highly available. For production workloads that must survive a control-plane failure, design a multi-control-plane cluster with a load balancer and a shared &lt;code&gt;--control-plane-endpoint&lt;/code&gt; before initialization.&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Pick a CIDR for the pod network that does not overlap with your host network. The example uses &lt;code&gt;10.244.0.0/16&lt;/code&gt;, which is the default for Flannel. Bootstrap the cluster:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 kubeadm init --pod-network-cidr&lt;span class="o"&gt;=&lt;/span&gt;10.244.0.0/16&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The command pulls the control-plane container images, generates certificates, writes the kubeconfig files under &lt;code&gt;/etc/kubernetes/&lt;/code&gt;, and starts the static pods for the API server, controller manager, scheduler, and etcd. When it finishes you will see a block 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;Your Kubernetes control-plane has initialized successfully!
To start using your cluster, you need to run the following as a regular user:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join 192.168.1.10:6443 --token abcdef.0123456789abcdef \
--discovery-token-ca-cert-hash sha256:1234...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Copy the join command somewhere safe. The bootstrap token is a credential that allows a node to authenticate while joining the cluster, so do not publish or share it with untrusted users.&lt;/p&gt;
&lt;p&gt;Set up the kubeconfig for your sudo user so &lt;code&gt;kubectl&lt;/code&gt; works without 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;mkdir -p &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.kube
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo cp -i /etc/kubernetes/admin.conf &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.kube/config
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo chown &lt;span class="k"&gt;$(&lt;/span&gt;id -u&lt;span class="k"&gt;)&lt;/span&gt;:&lt;span class="k"&gt;$(&lt;/span&gt;id -g&lt;span class="k"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.kube/config&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The copied &lt;code&gt;admin.conf&lt;/code&gt; grants cluster administrator access. Keep &lt;code&gt;~/.kube/config&lt;/code&gt; private and do not distribute it as a general user credential.&lt;/p&gt;
&lt;p&gt;Verify that the API server is reachable:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get nodes&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 control-plane node with a status of &lt;code&gt;NotReady&lt;/code&gt;. The status changes to &lt;code&gt;Ready&lt;/code&gt; after you install a pod network in the next step.&lt;/p&gt;
&lt;h2 id="step-6-install-a-pod-network"&gt;Step 6: Install a Pod Network &lt;a class="headline-link" href="#step-6-install-a-pod-network" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Pods cannot communicate until the cluster has a network plugin. Flannel is one of the simplest options that works with the default &lt;code&gt;10.244.0.0/16&lt;/code&gt; CIDR:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Watch the system pods come up:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get pods -n kube-flannel
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get pods -n kube-system
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get nodes&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;After a minute or so, the Flannel and core system pods should report &lt;code&gt;Running&lt;/code&gt;, and the control-plane node should show &lt;code&gt;Ready&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you prefer Calico, Cilium, or another CNI plugin, install it now instead of Flannel and pass the matching &lt;code&gt;--pod-network-cidr&lt;/code&gt; value to &lt;code&gt;kubeadm init&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="step-7-join-worker-nodes"&gt;Step 7: Join Worker Nodes &lt;a class="headline-link" href="#step-7-join-worker-nodes" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Run this step on each worker node.&lt;/p&gt;
&lt;p&gt;Use the &lt;code&gt;kubeadm join&lt;/code&gt; command that was printed at the end of &lt;code&gt;kubeadm init&lt;/code&gt;. If you lost it or the token has expired (tokens are valid for 24 hours by default), generate a new one on the control-plane node:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 kubeadm token create --print-join-command&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Then run the printed command on the worker:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 kubeadm join 192.168.1.10:6443 --token &amp;lt;token&amp;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; --discovery-token-ca-cert-hash sha256:&amp;lt;hash&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Back on the control-plane node, verify that the new node has joined:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get nodes&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The output now lists the worker. It moves from &lt;code&gt;NotReady&lt;/code&gt; to &lt;code&gt;Ready&lt;/code&gt; once the kubelet finishes its registration and the network plugin reports healthy.&lt;/p&gt;
&lt;h2 id="step-8-test-the-cluster"&gt;Step 8: Test the Cluster &lt;a class="headline-link" href="#step-8-test-the-cluster" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Deploy a small workload to confirm the cluster schedules pods and that networking is 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;kubectl create deployment nginx-test --image&lt;span class="o"&gt;=&lt;/span&gt;nginx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl expose deployment nginx-test --port&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;80&lt;/span&gt; --type&lt;span class="o"&gt;=&lt;/span&gt;NodePort
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl rollout status deployment/nginx-test
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get pods,svc&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 &lt;code&gt;nginx-test&lt;/code&gt; pod in the &lt;code&gt;Running&lt;/code&gt; state and a NodePort service with a port in the &lt;code&gt;30000-32767&lt;/code&gt; range:&lt;/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 READY STATUS RESTARTS AGE
pod/nginx-test-7b7bf6d9b8-8pxnf 1/1 Running 0 25s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/nginx-test NodePort 10.105.10.123 &amp;lt;none&amp;gt; 80:31234/TCP 12s&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Open &lt;code&gt;http://&amp;lt;node_ip&amp;gt;:&amp;lt;nodeport&amp;gt;&lt;/code&gt; in a browser and you should see the Nginx welcome page. Tear the demo down once you confirm it works:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl delete service nginx-test
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl delete deployment nginx-test&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;kubeadm init&lt;/code&gt; fails with &amp;ldquo;container runtime is not running&amp;rdquo;&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;containerd&lt;/code&gt; is not running, or its CRI plugin is disabled. Inspect the service with &lt;code&gt;sudo systemctl status containerd --no-pager&lt;/code&gt; and check &lt;code&gt;/etc/containerd/config.toml&lt;/code&gt; for a &lt;code&gt;disabled_plugins&lt;/code&gt; entry containing &lt;code&gt;cri&lt;/code&gt;. Remove &lt;code&gt;cri&lt;/code&gt; from that list, confirm that &lt;code&gt;SystemdCgroup = true&lt;/code&gt;, and restart the service with &lt;code&gt;sudo systemctl restart containerd&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Nodes stay &lt;code&gt;NotReady&lt;/code&gt; after init&lt;/strong&gt;&lt;br&gt;
The pod network is not installed or the CNI pods are crashing. Run &lt;code&gt;kubectl get pods -n kube-flannel&lt;/code&gt; and &lt;code&gt;kubectl logs -n kube-flannel daemonset/kube-flannel-ds --tail=50&lt;/code&gt; to inspect Flannel. Confirm that the &lt;code&gt;--pod-network-cidr&lt;/code&gt; passed to &lt;code&gt;kubeadm init&lt;/code&gt; matches the CIDR expected by the CNI plugin.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cgroup driver mismatch warning in kubelet logs&lt;/strong&gt;&lt;br&gt;
The kubelet uses &lt;code&gt;systemd&lt;/code&gt; and the runtime uses &lt;code&gt;cgroupfs&lt;/code&gt;, or vice versa. Edit &lt;code&gt;/etc/containerd/config.toml&lt;/code&gt;, set &lt;code&gt;SystemdCgroup = true&lt;/code&gt;, and restart &lt;code&gt;containerd&lt;/code&gt;. Restart the kubelet with &lt;code&gt;sudo systemctl restart kubelet&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;The connection to the server localhost:8080 was refused&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;kubectl&lt;/code&gt; is reading the wrong kubeconfig. Run &lt;code&gt;kubectl config view&lt;/code&gt; to confirm the current context, and make sure &lt;code&gt;~/.kube/config&lt;/code&gt; exists for the user running the command. On the control-plane node, copy &lt;code&gt;/etc/kubernetes/admin.conf&lt;/code&gt; to &lt;code&gt;~/.kube/config&lt;/code&gt; and fix ownership.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Worker join times out&lt;/strong&gt;&lt;br&gt;
The control-plane host is not reachable from the worker on TCP port &lt;code&gt;6443&lt;/code&gt;, or the token has expired. Confirm the route and firewall rules, then run &lt;code&gt;sudo kubeadm token create --print-join-command&lt;/code&gt; on the control plane to generate a fresh join command.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Nginx NodePort does not respond&lt;/strong&gt;&lt;br&gt;
The workload can be healthy while a host or cloud firewall blocks the assigned NodePort. Read the port from &lt;code&gt;kubectl get service nginx-test&lt;/code&gt;, then allow that TCP port only from the client network that needs access.&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;Do I still need Docker?&lt;/strong&gt;&lt;br&gt;
No. Kubernetes communicates with container runtimes through the CRI, and &lt;code&gt;containerd&lt;/code&gt; is enough for this cluster. Kubernetes 1.24 removed the built-in Docker integration known as dockershim. If you also want to build images on the same host, install Docker as a separate tool. See &lt;a href="https://linuxize.com/post/how-to-install-docker-on-ubuntu-26-04/"&gt;How to Install Docker on Ubuntu 26.04&lt;/a&gt;
for the build environment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why is swap disabled?&lt;/strong&gt;&lt;br&gt;
The kubelet refuses to start by default when it detects active swap. Kubernetes can use swap with an explicit kubelet configuration, but disabling it keeps this installation aligned with the default behavior.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Can I run a single-node cluster on the control plane?&lt;/strong&gt;&lt;br&gt;
Yes. Remove the control-plane taint with &lt;code&gt;kubectl taint nodes --all node-role.kubernetes.io/control-plane-&lt;/code&gt;. After that, the scheduler places workload pods on the control-plane node.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How do I upgrade Kubernetes later?&lt;/strong&gt;&lt;br&gt;
Follow the kubeadm node upgrade procedure and move through one minor version at a time. The process includes upgrading &lt;code&gt;kubeadm&lt;/code&gt;, running &lt;code&gt;kubeadm upgrade plan&lt;/code&gt; and &lt;code&gt;kubeadm upgrade apply&lt;/code&gt;, then upgrading the kubelet and kubectl. The package holds prevent these components from changing during a routine &lt;code&gt;apt upgrade&lt;/code&gt;.&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 Kubernetes cluster on Ubuntu 26.04. From here you can deploy applications with manifests or Helm charts, set up an ingress controller, and add storage classes that match your environment. For a single-node lab on the same host, an alternative is to start with &lt;code&gt;k3s&lt;/code&gt; or &lt;code&gt;minikube&lt;/code&gt; and migrate to a &lt;code&gt;kubeadm&lt;/code&gt; cluster as your needs grow.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/how-to-install-kubernetes-with-kubeadm-on-ubuntu-26-04/featured_hu_768004ad5f191962.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>update-alternatives Command on Ubuntu and Debian</title><link>https://linuxize.com/post/update-alternatives-command/</link><pubDate>Mon, 27 Jul 2026 09:10:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/update-alternatives-command/</guid><category>linux commands</category><category>ubuntu</category><category>debian</category><description>Use update-alternatives on Ubuntu and Debian to list, register, switch, and remove program alternatives, with automatic and manual mode examples.</description><content:encoded>&lt;p&gt;When several installed programs provide the same command, you need a reliable way to choose which one runs by default. Hard-coding paths in scripts or rebuilding symbolic links by hand makes that choice difficult to maintain.&lt;/p&gt;
&lt;p&gt;On Ubuntu, Debian, and their derivatives, the &lt;code&gt;update-alternatives&lt;/code&gt; command manages these selections through symbolic links in &lt;code&gt;/etc/alternatives&lt;/code&gt;. This guide explains how to inspect alternative groups, switch programs interactively or from a script, register new choices, and return a group to automatic mode.&lt;/p&gt;
&lt;h2 id="how-it-works"&gt;How It Works &lt;a class="headline-link" href="#how-it-works" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;update-alternatives&lt;/code&gt; maintains link groups. Each group has a generic command name and one or more registered program paths. For example, the &lt;code&gt;java&lt;/code&gt; group controls which installed Java runtime &lt;code&gt;/usr/bin/java&lt;/code&gt; starts.&lt;/p&gt;
&lt;p&gt;The generic command is not linked directly to the selected program. Instead, &lt;code&gt;/usr/bin/java&lt;/code&gt; points to &lt;code&gt;/etc/alternatives/java&lt;/code&gt;, which points to the active Java binary. You can read our &lt;a href="https://linuxize.com/post/how-to-create-symbolic-links-in-linux-using-the-ln-command/"&gt;symbolic links guide&lt;/a&gt;
for more about this two-step link structure.&lt;/p&gt;
&lt;p&gt;Each group operates in one of two modes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Automatic mode&lt;/strong&gt; selects the registered alternative with the highest priority.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Manual mode&lt;/strong&gt; keeps the administrator&amp;rsquo;s selection until it is changed explicitly or becomes invalid.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The command stores its administrative state under &lt;code&gt;/var/lib/dpkg/alternatives&lt;/code&gt;.&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 command 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;update-alternatives [OPTIONS] COMMAND&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Commands that change an alternative group require &lt;code&gt;sudo&lt;/code&gt;. Listing and displaying alternatives does not.&lt;/p&gt;
&lt;h2 id="list-alternative-groups"&gt;List Alternative Groups &lt;a class="headline-link" href="#list-alternative-groups" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To see every master alternative group registered on the 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;update-alternatives --get-selections&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;awk auto /usr/bin/mawk
editor auto /bin/nano
pager auto /bin/more
vi auto /usr/bin/vim.basic&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The second column shows whether the group is in &lt;code&gt;auto&lt;/code&gt; or &lt;code&gt;manual&lt;/code&gt; mode. The final column contains the selected program path. Your groups and paths will differ according to the packages installed on your system.&lt;/p&gt;
&lt;p&gt;To list only the registered paths for one group, pass its name to &lt;code&gt;--list&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;update-alternatives --list java&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;/usr/lib/jvm/java-17-openjdk-amd64/bin/java
/usr/lib/jvm/java-21-openjdk-amd64/bin/java&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This example has Java 17 and Java 21 registered. If the named group does not exist, the command reports that no alternatives are registered for it.&lt;/p&gt;
&lt;h2 id="display-an-alternative"&gt;Display an Alternative &lt;a class="headline-link" href="#display-an-alternative" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To see all registered options for a specific alternative and which one is currently active:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;update-alternatives --display java&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;java - auto mode
link best version is /usr/lib/jvm/java-21-openjdk-amd64/bin/java
link currently points to /usr/lib/jvm/java-21-openjdk-amd64/bin/java
link java is /usr/bin/java
slave java.1.gz is /usr/share/man/man1/java.1.gz
/usr/lib/jvm/java-17-openjdk-amd64/bin/java - priority 1711
slave java.1.gz: /usr/lib/jvm/java-17-openjdk-amd64/man/man1/java.1.gz
/usr/lib/jvm/java-21-openjdk-amd64/bin/java - priority 2111
slave java.1.gz: /usr/lib/jvm/java-21-openjdk-amd64/man/man1/java.1.gz&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The output lists each registered path with its priority. In &lt;code&gt;auto&lt;/code&gt; mode, the highest priority wins. The &lt;code&gt;slave&lt;/code&gt; lines show related files that follow the same selection, in this case the &lt;code&gt;java&lt;/code&gt; manual page.&lt;/p&gt;
&lt;h2 id="switch-between-alternatives"&gt;Switch Between Alternatives &lt;a class="headline-link" href="#switch-between-alternatives" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To choose interactively from the registered options, 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 update-alternatives --config java&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;There are 2 choices for the alternative java (providing /usr/bin/java).
Selection Path Priority Status
------------------------------------------------------------
* 0 /usr/lib/jvm/java-21-openjdk-amd64/bin/java 2111 auto mode
1 /usr/lib/jvm/java-17-openjdk-amd64/bin/java 1711 manual mode
2 /usr/lib/jvm/java-21-openjdk-amd64/bin/java 2111 manual mode
Press &amp;lt;enter&amp;gt; to keep the current choice[*], or type selection number: 1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Type the number of a manual choice and press Enter to pin that version. Selecting &lt;code&gt;0&lt;/code&gt; keeps or restores automatic mode, while pressing Enter without a number keeps the current choice.&lt;/p&gt;
&lt;p&gt;When a group is in manual mode, a package installation can register another choice but will not replace your valid selection.&lt;/p&gt;
&lt;h2 id="set-an-alternative-noninteractively"&gt;Set an Alternative Noninteractively &lt;a class="headline-link" href="#set-an-alternative-noninteractively" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;--set&lt;/code&gt; command changes an alternative without displaying a menu, which makes it useful in provisioning scripts. Use &lt;code&gt;--list&lt;/code&gt; as shown above to copy the exact path, then pass it to &lt;code&gt;--set&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 update-alternatives --set java /usr/lib/jvm/java-17-openjdk-amd64/bin/java&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The selected group enters manual mode. The path must already be registered in that group.&lt;/p&gt;
&lt;p&gt;Alternative groups are independent unless their provider registered related files as slave links. For example, switching the &lt;code&gt;java&lt;/code&gt; group does not necessarily switch &lt;code&gt;javac&lt;/code&gt;. Use &lt;code&gt;--display&lt;/code&gt; to check which links belong to a group.&lt;/p&gt;
&lt;h2 id="register-a-new-alternative"&gt;Register a New Alternative &lt;a class="headline-link" href="#register-a-new-alternative" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When you install a program outside the package manager, you can register it with &lt;code&gt;--install&lt;/code&gt;. Assume two releases of a program are installed at &lt;code&gt;/opt/acme-tool-1.0/bin/acme-tool&lt;/code&gt; and &lt;code&gt;/opt/acme-tool-2.0/bin/acme-tool&lt;/code&gt;. Confirm that both executable paths exist before registering 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;ls -l /opt/acme-tool-1.0/bin/acme-tool /opt/acme-tool-2.0/bin/acme-tool&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Register both releases under the same &lt;code&gt;acme-tool&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;sudo update-alternatives --install /usr/local/bin/acme-tool acme-tool /opt/acme-tool-1.0/bin/acme-tool &lt;span class="m"&gt;100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo update-alternatives --install /usr/local/bin/acme-tool acme-tool /opt/acme-tool-2.0/bin/acme-tool &lt;span class="m"&gt;200&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 arguments 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="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;--install LINK NAME PATH PRIORITY&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;LINK&lt;/code&gt; is the path where the generic command link will be created (&lt;code&gt;/usr/local/bin/acme-tool&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NAME&lt;/code&gt; is the alternative group name (&lt;code&gt;acme-tool&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PATH&lt;/code&gt; is the actual binary (&lt;code&gt;/opt/acme-tool-1.0/bin/acme-tool&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PRIORITY&lt;/code&gt; is the numeric weight used in &lt;code&gt;auto&lt;/code&gt; mode (higher wins).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this example, automatic mode selects version 2.0 because it has the higher priority.&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 use &lt;code&gt;update-alternatives&lt;/code&gt; to replace &lt;code&gt;/usr/bin/python3&lt;/code&gt; on Ubuntu or Debian. System tools depend on the distribution&amp;rsquo;s default Python interpreter. Use versioned commands, virtual environments, &lt;code&gt;pyenv&lt;/code&gt;, or a separate command under &lt;code&gt;/usr/local/bin&lt;/code&gt; instead.&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="remove-an-alternative"&gt;Remove an Alternative &lt;a class="headline-link" href="#remove-an-alternative" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before removing a registered path, display the group and confirm the exact 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;update-alternatives --display acme-tool&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Then deregister the 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;sudo update-alternatives --remove acme-tool /opt/acme-tool-1.0/bin/acme-tool&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The command removes the path from the alternatives database but does not delete the program itself. If you remove the active path, the group returns to automatic mode and selects the best remaining alternative.&lt;/p&gt;
&lt;h2 id="reset-to-auto-mode"&gt;Reset to Auto Mode &lt;a class="headline-link" href="#reset-to-auto-mode" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you manually pinned an alternative and want to go back to automatic selection by priority:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 update-alternatives --auto java&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The system returns to the highest-priority option.&lt;/p&gt;
&lt;h2 id="switch-the-editor-alternative"&gt;Switch the Editor Alternative &lt;a class="headline-link" href="#switch-the-editor-alternative" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;editor&lt;/code&gt; alternative provides the system-wide &lt;code&gt;/usr/bin/editor&lt;/code&gt; command. Programs that invoke this path follow the selected alternative, while tools such as &lt;code&gt;sensible-editor&lt;/code&gt; may honor user-specific settings and environment variables first. To switch the system editor choice:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 update-alternatives --config editor&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Select an installed editor such as &lt;code&gt;vim.basic&lt;/code&gt; from the list. Applications with their own editor setting do not necessarily follow this choice. Git, for example, checks &lt;code&gt;GIT_EDITOR&lt;/code&gt;, &lt;code&gt;core.editor&lt;/code&gt;, &lt;code&gt;VISUAL&lt;/code&gt;, and &lt;code&gt;EDITOR&lt;/code&gt; before using its compiled 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;List all alternative groups&lt;/td&gt;
&lt;td&gt;&lt;code&gt;update-alternatives --get-selections&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List paths in one group&lt;/td&gt;
&lt;td&gt;&lt;code&gt;update-alternatives --list NAME&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Display group details&lt;/td&gt;
&lt;td&gt;&lt;code&gt;update-alternatives --display NAME&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Choose interactively&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo update-alternatives --config NAME&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Select a path noninteractively&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo update-alternatives --set NAME PATH&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Return to automatic mode&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo update-alternatives --auto NAME&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Register a path&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo update-alternatives --install LINK NAME PATH PRIORITY&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deregister a path&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo update-alternatives --remove NAME PATH&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;no alternatives for NAME&lt;/code&gt;&lt;/strong&gt;
The group is not registered. Check the available names with &lt;code&gt;update-alternatives --get-selections&lt;/code&gt;. Install a package that provides the group or register an existing executable with &lt;code&gt;--install&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;alternative path ... does not exist&lt;/code&gt;&lt;/strong&gt;
The path passed to &lt;code&gt;--install&lt;/code&gt; must already exist. Check it with &lt;code&gt;ls -l PATH&lt;/code&gt;, then correct the path or install the program before trying again.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A regular file blocks the alternatives link&lt;/strong&gt;
Do not immediately add &lt;code&gt;--force&lt;/code&gt;. Check which package owns the path with &lt;code&gt;dpkg -S /path/to/command&lt;/code&gt;, and back up locally installed files before replacing them with an alternatives-managed link.&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;--config&lt;/code&gt; for interactive changes, &lt;code&gt;--set&lt;/code&gt; for scripts, and &lt;code&gt;--auto&lt;/code&gt; when you want priority-based selection again. For a practical example with multiple JDKs, see &lt;a href="https://linuxize.com/post/how-to-check-java-version/"&gt;How to Check Java Version&lt;/a&gt;
.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/update-alternatives-command/featured_hu_63fd47dfcb6ed134.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>How to Convert MP4 to MP3 with ffmpeg</title><link>https://linuxize.com/post/convert-mp4-to-mp3-with-ffmpeg/</link><pubDate>Sun, 26 Jul 2026 08:20:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/convert-mp4-to-mp3-with-ffmpeg/</guid><category>ffmpeg</category><description>Convert MP4 video to MP3 audio from the Linux command line with ffmpeg, including quality settings, lossless extraction, trimming, and batch conversion.</description><content:encoded>&lt;p&gt;A recorded lecture, a podcast published as video, a concert clip you only ever listen to: sometimes all you want from an MP4 is its audio track. &lt;code&gt;ffmpeg&lt;/code&gt; does this in one command, with full control over the output quality.&lt;/p&gt;
&lt;p&gt;This guide shows how to convert MP4 to MP3 with &lt;code&gt;ffmpeg&lt;/code&gt;, pick sensible quality settings, extract audio without any quality loss, and convert files in bulk.&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 need &lt;code&gt;ffmpeg&lt;/code&gt; installed. 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 ffmpeg&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;These commands install both &lt;code&gt;ffmpeg&lt;/code&gt; and &lt;code&gt;ffprobe&lt;/code&gt;. For version-specific instructions, see our guides on installing ffmpeg on &lt;a href="https://linuxize.com/post/how-to-install-ffmpeg-on-ubuntu-20-04/"&gt;Ubuntu&lt;/a&gt;
and &lt;a href="https://linuxize.com/post/how-to-install-ffmpeg-on-debian-10/"&gt;Debian&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="converting-mp4-to-mp3"&gt;Converting MP4 to MP3 &lt;a class="headline-link" href="#converting-mp4-to-mp3" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The simplest form gives &lt;code&gt;ffmpeg&lt;/code&gt; an input, selects the LAME MP3 encoder, and names the output 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;ffmpeg -i video.mp4 -c:a libmp3lame audio.mp3&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This decodes the audio track from &lt;code&gt;video.mp4&lt;/code&gt;, encodes it as MP3 with default settings, and writes &lt;code&gt;audio.mp3&lt;/code&gt;. The video stream is dropped automatically, since an MP3 file cannot contain one.&lt;/p&gt;
&lt;p&gt;For better control, the recommended form adds two 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;ffmpeg -i video.mp4 -vn -c:a libmp3lame -q:a &lt;span class="m"&gt;2&lt;/span&gt; audio.mp3&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;-vn&lt;/code&gt; flag explicitly discards the video stream, &lt;code&gt;-c:a libmp3lame&lt;/code&gt; selects the LAME MP3 encoder, and &lt;code&gt;-q:a 2&lt;/code&gt; selects variable bitrate (VBR) quality level 2. This level typically averages around 190 kb/s, although the actual bitrate depends on the audio.&lt;/p&gt;
&lt;h2 id="choosing-the-quality"&gt;Choosing the Quality &lt;a class="headline-link" href="#choosing-the-quality" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The MP3 encoder accepts a VBR quality scale from 0 (best) to 9 (smallest). These approximate bitrates vary with the source:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-q:a 0&lt;/code&gt; - Around 245 kb/s; highest quality VBR, for music you care about.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-q:a 2&lt;/code&gt; - Around 190 kb/s; the common sweet spot.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-q:a 5&lt;/code&gt; - Around 130 kb/s; fine for speech and podcasts.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-q:a 7&lt;/code&gt; - Around 100 kb/s; small files, audible compression on music.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you need a constant bitrate instead, for example for a device that struggles with VBR, use &lt;code&gt;-b:a&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;ffmpeg -i video.mp4 -vn -c:a libmp3lame -b:a 192k audio.mp3&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Keep in mind that encoding cannot add quality back: if the source audio is a 96 kb/s stream, encoding it at 320 kb/s only produces a larger file, not a better-sounding one.&lt;/p&gt;
&lt;h2 id="extracting-audio-without-quality-loss"&gt;Extracting Audio Without Quality Loss &lt;a class="headline-link" href="#extracting-audio-without-quality-loss" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Converting to MP3 always re-encodes, which costs a small amount of quality. If you only need the original audio track, check its codec before deciding whether to convert 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;ffprobe -v error -select_streams a:0 -show_entries &lt;span class="nv"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;codec_name -of &lt;span class="nv"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;noprint_wrappers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1:nokey&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; video.mp4&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;aac&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;AAC audio is common in MP4 files and can be copied into an &lt;code&gt;.m4a&lt;/code&gt; container without re-encoding:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ffmpeg -i video.mp4 -map 0:a:0 -c:a copy audio.m4a&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;-map 0:a:0&lt;/code&gt; option selects the first audio stream, and &lt;code&gt;-c:a copy&lt;/code&gt; moves the compressed audio without decoding or encoding it. The audio data remains unchanged, and the operation usually finishes much faster than an MP3 conversion. If &lt;code&gt;ffprobe&lt;/code&gt; reports another codec, use a compatible container or convert the audio instead.&lt;/p&gt;
&lt;h2 id="converting-part-of-a-file"&gt;Converting Part of a File &lt;a class="headline-link" href="#converting-part-of-a-file" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To convert only a section, combine the conversion with a start time and a duration:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ffmpeg -ss 00:05:30 -i video.mp4 -t 00:06:30 -vn -c:a libmp3lame -q:a &lt;span class="m"&gt;2&lt;/span&gt; clip.mp3&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;-ss&lt;/code&gt; option starts at 5:30, and &lt;code&gt;-t 00:06:30&lt;/code&gt; converts the next six minutes and thirty seconds, ending at 12:00. This is handy for pulling one song out of a concert recording or one answer out of a long interview.&lt;/p&gt;
&lt;h2 id="batch-converting-multiple-files"&gt;Batch Converting Multiple Files &lt;a class="headline-link" href="#batch-converting-multiple-files" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;ffmpeg&lt;/code&gt; takes one input at a time, so converting a directory of MP4 files uses a small &lt;a href="https://linuxize.com/post/bash-for-loop/"&gt;bash for loop&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;&lt;span class="nb"&gt;shopt&lt;/span&gt; -s nullglob
&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; f in *.mp4&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; ffmpeg -n -i &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; -vn -c:a libmp3lame -q:a &lt;span class="m"&gt;2&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;f&lt;/span&gt;&lt;span class="p"&gt;%.mp4&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.mp3&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;nullglob&lt;/code&gt; setting skips the loop when the directory contains no MP4 files. The &lt;code&gt;${f%.mp4}&lt;/code&gt; expansion strips the &lt;code&gt;.mp4&lt;/code&gt; extension so each MP3 keeps the original filename, while &lt;code&gt;-n&lt;/code&gt; prevents existing files from being overwritten. The quotes around the variables keep filenames with spaces intact.&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;Unknown encoder &amp;rsquo;libmp3lame&amp;rsquo;&lt;/strong&gt;&lt;br&gt;
Your &lt;code&gt;ffmpeg&lt;/code&gt; build lacks the LAME MP3 encoder, which happens with some minimal or codec-restricted packages. Check for it with &lt;code&gt;ffmpeg -hide_banner -encoders | grep -w libmp3lame&lt;/code&gt;, then install an FFmpeg package that includes the encoder.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Output file has no sound&lt;/strong&gt;&lt;br&gt;
The source may carry multiple audio tracks and the wrong one was picked. Inspect the file with &lt;code&gt;ffprobe -hide_banner video.mp4&lt;/code&gt;, then select the track explicitly with &lt;code&gt;-map&lt;/code&gt;, for example &lt;code&gt;-map 0:a:1&lt;/code&gt; for the second audio stream.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Could not write the M4A output header&lt;/strong&gt;&lt;br&gt;
The source audio codec is not compatible with the M4A container. Use a container that supports the original codec, or replace &lt;code&gt;-c:a copy&lt;/code&gt; with &lt;code&gt;-c:a aac -b:a 192k&lt;/code&gt; to create a compatible M4A file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The output file already exists&lt;/strong&gt;&lt;br&gt;
FFmpeg asks before replacing an existing file. Use a different output name, add &lt;code&gt;-n&lt;/code&gt; to keep the existing file, or add &lt;code&gt;-y&lt;/code&gt; only when you intend to overwrite 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;One command covers the everyday case: &lt;code&gt;ffmpeg -i video.mp4 -vn -c:a libmp3lame -q:a 2 audio.mp3&lt;/code&gt;, with &lt;code&gt;-q:a&lt;/code&gt; trading size against quality. For more conversion, compression, and inspection examples, see our &lt;a href="https://linuxize.com/post/ffmpeg-command-in-linux/"&gt;&lt;code&gt;ffmpeg&lt;/code&gt; command guide&lt;/a&gt;
.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/convert-mp4-to-mp3-with-ffmpeg/featured_hu_6c2ecd86779650c2.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>How to Find Which Process Is Using a Port in Linux</title><link>https://linuxize.com/post/how-to-find-which-process-is-using-a-port/</link><pubDate>Sat, 25 Jul 2026 07:30:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/how-to-find-which-process-is-using-a-port/</guid><category>networking</category><category>linux commands</category><description>Find the Linux process listening on a TCP or UDP port with ss, lsof, fuser, or netstat, then inspect and stop it safely.</description><content:encoded>&lt;p&gt;You start a service, and it refuses to launch with an error like &amp;ldquo;bind: address already in use&amp;rdquo;. Something is already holding the port, but the message does not tell you what. Before you can fix it, you need to map the port back to a process, find its PID, and decide whether to stop it. Linux gives you several tools for this, and each one answers the question in a slightly different way.&lt;/p&gt;
&lt;p&gt;This guide shows how to find the process using a port with &lt;code&gt;ss&lt;/code&gt;, &lt;code&gt;lsof&lt;/code&gt;, &lt;code&gt;fuser&lt;/code&gt;, and &lt;code&gt;netstat&lt;/code&gt;, and how to stop it once you have the PID.&lt;/p&gt;
&lt;p&gt;If you need an inventory of every service accepting connections instead of one specific port, see the guide on &lt;a href="https://linuxize.com/post/check-listening-ports-linux/"&gt;checking listening ports in Linux&lt;/a&gt;
. Note that &lt;code&gt;127.0.0.1:8080&lt;/code&gt; and &lt;code&gt;0.0.0.0:8080&lt;/code&gt; collide, because binding to all interfaces includes the &lt;a href="https://linuxize.com/post/what-is-localhost/"&gt;loopback address&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 printable quick references, see the &lt;a href="https://linuxize.com/cheatsheet/ss/"&gt;ss cheatsheet&lt;/a&gt;
, &lt;a href="https://linuxize.com/cheatsheet/lsof/"&gt;lsof cheatsheet&lt;/a&gt;
, and &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;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;Find a TCP listener with ss&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo ss -ltnp 'sport = :80'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Find a UDP socket with ss&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo ss -lunp 'sport = :53'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Find a TCP listener with lsof&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo lsof -nP -iTCP:80 -sTCP:LISTEN&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Find processes with fuser&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo fuser -v 80/tcp&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Find a TCP listener with netstat&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo netstat -ltnp | grep ':80 '&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inspect a process&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ps -fp PID&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stop an unmanaged process&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo kill PID&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Send SIGTERM with fuser&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo fuser -k -TERM 80/tcp&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="finding-the-process-with-ss"&gt;Finding the Process with ss &lt;a class="headline-link" href="#finding-the-process-with-ss" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://linuxize.com/post/ss-command-in-linux/"&gt;&lt;code&gt;ss&lt;/code&gt;&lt;/a&gt;
command is the standard choice on current Linux distributions. It comes from the &lt;code&gt;iproute2&lt;/code&gt; package and supports filters that match an exact port without piping the output through another 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;sudo ss -ltnp &lt;span class="s1"&gt;&amp;#39;sport = :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;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 Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:((&amp;#34;nginx&amp;#34;,pid=1433,fd=5),(&amp;#34;nginx&amp;#34;,pid=1432,fd=5))
LISTEN 0 511 [::]:80 [::]:* users:((&amp;#34;nginx&amp;#34;,pid=1433,fd=6),(&amp;#34;nginx&amp;#34;,pid=1432,fd=6))&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Run the command with &lt;code&gt;sudo&lt;/code&gt; so &lt;code&gt;ss&lt;/code&gt; can show process details for sockets owned by other users. The options provide the following information:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-l&lt;/code&gt; - Show only listening sockets.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-t&lt;/code&gt; - Show TCP sockets.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-n&lt;/code&gt; - Print numeric port numbers instead of resolving service names.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-p&lt;/code&gt; - Show the process that owns each socket.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;sport = :80&lt;/code&gt; expression matches local port 80 exactly, so it does not also return ports such as 8080. In the output above, &lt;code&gt;nginx&lt;/code&gt; holds the port. The &lt;code&gt;Process&lt;/code&gt; column lists two process IDs because the master process, &lt;code&gt;1432&lt;/code&gt;, opened the socket and passed it to its worker, &lt;code&gt;1433&lt;/code&gt;. Either PID leads you back to the same service. The two rows show that &lt;code&gt;nginx&lt;/code&gt; listens on both IPv4 and IPv6.&lt;/p&gt;
&lt;p&gt;UDP sockets do not use the TCP &lt;code&gt;LISTEN&lt;/code&gt; state. To check a UDP port, replace &lt;code&gt;-t&lt;/code&gt; with &lt;code&gt;-u&lt;/code&gt;. For example, the following command identifies the process bound to UDP port 53:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 ss -lunp &lt;span class="s1"&gt;&amp;#39;sport = :53&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;State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
UNCONN 0 0 0.0.0.0:53 0.0.0.0:* users:((&amp;#34;dnsmasq&amp;#34;,pid=902,fd=4))&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;UNCONN&lt;/code&gt; state is normal for a UDP server because UDP does not establish connections before exchanging data.&lt;/p&gt;
&lt;h2 id="finding-the-process-with-lsof"&gt;Finding the Process with lsof &lt;a class="headline-link" href="#finding-the-process-with-lsof" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://linuxize.com/post/lsof-command-in-linux/"&gt;&lt;code&gt;lsof&lt;/code&gt;&lt;/a&gt;
command lists open files, including network sockets. Use numeric output and restrict the result to TCP listeners on the port:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 lsof -nP -iTCP:80 -sTCP:LISTEN&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;COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx 1432 root 6u IPv4 28319 0t0 TCP *:80 (LISTEN)
nginx 1432 root 7u IPv6 28320 0t0 TCP *:80 (LISTEN)
nginx 1433 www-data 6u IPv4 28319 0t0 TCP *:80 (LISTEN)
nginx 1433 www-data 7u IPv6 28320 0t0 TCP *:80 (LISTEN)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;-nP&lt;/code&gt; options prevent hostname and service-name lookups, while &lt;code&gt;-iTCP:80&lt;/code&gt; selects TCP port 80 and &lt;code&gt;-sTCP:LISTEN&lt;/code&gt; excludes established connections. The output reports the command, PID, user, and file descriptor. Here &lt;code&gt;lsof&lt;/code&gt; gives you the same two &lt;code&gt;nginx&lt;/code&gt; processes that &lt;code&gt;ss&lt;/code&gt; reported, each listed once for IPv4 and once for IPv6, and it also shows the user each one runs as, which is useful when a master process drops privileges for its workers.&lt;/p&gt;
&lt;p&gt;For UDP port 53, use 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="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 lsof -nP -iUDP:53&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The pattern is the same, except that &lt;code&gt;-iUDP:53&lt;/code&gt; selects the UDP port and there is no state filter, because UDP sockets never enter the &lt;code&gt;LISTEN&lt;/code&gt; state.&lt;/p&gt;
&lt;h2 id="finding-the-process-with-fuser"&gt;Finding the Process with fuser &lt;a class="headline-link" href="#finding-the-process-with-fuser" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When you want a PID without the full socket table, &lt;code&gt;fuser&lt;/code&gt; provides a short command. Pass the port number followed by the protocol:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 fuser 80/tcp&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;80/tcp: 1432 1433&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You get the PID list and nothing else, which makes the plain form easy to pass to another command. To include the user, access type, and command name, add the &lt;code&gt;-v&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 fuser -v 80/tcp&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; USER PID ACCESS COMMAND
80/tcp: root 1432 F.... nginx
www-data 1433 F.... nginx&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The port appears once on the first row, and every process below it belongs to that same port. The &lt;code&gt;ACCESS&lt;/code&gt; column describes how each process uses the socket, where &lt;code&gt;F&lt;/code&gt; marks it as open for writing.&lt;/p&gt;
&lt;p&gt;You must specify &lt;code&gt;tcp&lt;/code&gt; or &lt;code&gt;udp&lt;/code&gt; because the same numeric port can be used by both protocols.&lt;/p&gt;
&lt;h2 id="finding-the-process-with-netstat"&gt;Finding the Process with netstat &lt;a class="headline-link" href="#finding-the-process-with-netstat" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;On older systems you may still find &lt;a href="https://linuxize.com/post/netstat-command-in-linux/"&gt;&lt;code&gt;netstat&lt;/code&gt;&lt;/a&gt;
, part of the &lt;code&gt;net-tools&lt;/code&gt; package. Use it as a fallback when &lt;code&gt;ss&lt;/code&gt; 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 netstat -ltnp &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;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;tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1432/nginx: master
tcp6 0 0 :::80 :::* LISTEN 1432/nginx: master&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The last column ties PID &lt;code&gt;1432&lt;/code&gt; to the &lt;code&gt;nginx&lt;/code&gt; master process, and &lt;code&gt;netstat&lt;/code&gt; truncates that name if it grows too long. Unlike &lt;code&gt;ss&lt;/code&gt; and &lt;code&gt;lsof&lt;/code&gt;, it reports a single process per socket rather than the whole group. The space after &lt;code&gt;:80&lt;/code&gt; in the filter prevents it from matching a longer port number such as 8080. For a UDP port, use &lt;code&gt;-lunp&lt;/code&gt; instead of &lt;code&gt;-ltnp&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="inspecting-and-stopping-the-process"&gt;Inspecting and Stopping the Process &lt;a class="headline-link" href="#inspecting-and-stopping-the-process" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once you have the PID, confirm what started the process before stopping it. The following &lt;a href="https://linuxize.com/post/ps-command-in-linux/"&gt;&lt;code&gt;ps&lt;/code&gt;&lt;/a&gt;
command shows the full command and its parent PID:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 -fp &lt;span class="m"&gt;1432&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;UID PID PPID C STIME TTY TIME CMD
root 1432 1 0 07:10 ? 00:00:00 nginx: master process /usr/sbin/nginx&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A service manager or container runtime may own the process. In that case, stop it through its manager so it does not immediately start again. For example, stop an Nginx systemd service 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 stop nginx&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;Make sure you understand what the process does before stopping it. Terminating a database or another production service to free a port can interrupt active work or cause data loss.&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For an unmanaged process, send &lt;code&gt;SIGTERM&lt;/code&gt; with &lt;code&gt;kill&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 &lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="m"&gt;1432&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;SIGTERM&lt;/code&gt; asks the process to shut down cleanly. If the process ignores it, you can use &lt;code&gt;sudo kill -KILL 1432&lt;/code&gt; as a last resort, but &lt;code&gt;SIGKILL&lt;/code&gt; gives it no opportunity to save state or remove temporary files. For more detail about signals, see the guide on &lt;a href="https://linuxize.com/post/how-to-kill-a-process-in-linux/"&gt;how to kill a process in Linux&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;After inspecting the verbose &lt;code&gt;fuser&lt;/code&gt; output, you can send &lt;code&gt;SIGTERM&lt;/code&gt; to every process using the TCP port:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 fuser -k -TERM 80/tcp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The explicit &lt;code&gt;-TERM&lt;/code&gt; matters because &lt;code&gt;fuser -k&lt;/code&gt; sends &lt;code&gt;SIGKILL&lt;/code&gt; by default.&lt;/p&gt;
&lt;h2 id="confirming-the-port-is-free"&gt;Confirming the Port Is Free &lt;a class="headline-link" href="#confirming-the-port-is-free" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Run the same &lt;code&gt;ss&lt;/code&gt; query after stopping 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 ss -ltnp &lt;span class="s1"&gt;&amp;#39;sport = :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 no socket row appears below the header, nothing is listening on TCP port 80 in the current network namespace. If you changed a service configuration instead of stopping it, start the service again and check the new port with the same command.&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;The port appears but the process name is missing&lt;/strong&gt;&lt;br&gt;
Run the command with &lt;code&gt;sudo&lt;/code&gt;. Without root privileges, these tools may not be able to read details for sockets owned by another user.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ss returns nothing but the application still reports a conflict&lt;/strong&gt;&lt;br&gt;
Check that you are using the correct protocol. Use &lt;code&gt;-lunp&lt;/code&gt; for UDP instead of &lt;code&gt;-ltnp&lt;/code&gt; for TCP. The listener may also be inside another network namespace or container, so run the check in the same environment as the application.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The process starts again after you stop it&lt;/strong&gt;&lt;br&gt;
A service manager or container runtime is restarting it. Stop or reconfigure the systemd service, Docker container, or other supervisor instead of repeatedly killing its PID.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Several PIDs appear for the same port&lt;/strong&gt;&lt;br&gt;
Some servers use a master process with several workers that share one socket. Other applications can use the &lt;code&gt;SO_REUSEPORT&lt;/code&gt; option. Identify the parent service and manage the group instead of stopping one worker at a time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;netstat: command not found&lt;/strong&gt;&lt;br&gt;
The &lt;code&gt;net-tools&lt;/code&gt; package is not installed on many current distributions. Use &lt;code&gt;ss&lt;/code&gt; instead.&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;ss&lt;/code&gt; first when you need the process behind a specific port, then turn to &lt;code&gt;lsof&lt;/code&gt; or &lt;code&gt;fuser&lt;/code&gt; when you need a different view. Inspect the PID and its manager before stopping anything, especially on a production system.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/how-to-find-which-process-is-using-a-port/featured_hu_bc6efb012febea0f.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>CIDR Notation and Subnetting Explained</title><link>https://linuxize.com/post/cidr-notation-and-subnetting-explained/</link><pubDate>Fri, 24 Jul 2026 10:50:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/cidr-notation-and-subnetting-explained/</guid><category>networking</category><description>CIDR notation defines IP network size with a prefix length. This guide explains subnet masks, address ranges, host counts, and practical IPv4 subnetting.</description><content:encoded>&lt;p&gt;When you assign a static address, configure a firewall rule, or divide a private network, you will encounter values such as &lt;code&gt;192.168.10.0/24&lt;/code&gt;. The address identifies the network, while the number after the slash tells you how much of the address is fixed and how much remains available for hosts.&lt;/p&gt;
&lt;p&gt;This guide explains how CIDR prefixes map to subnet masks, how to find network and broadcast addresses, and how to divide an IPv4 block into smaller subnets.&lt;/p&gt;
&lt;p&gt;For instant calculations and a visual binary split, use the &lt;a href="https://linuxize.com/tools/subnet-calculator/"&gt;subnet calculator&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="what-cidr-notation-means"&gt;What CIDR Notation Means &lt;a class="headline-link" href="#what-cidr-notation-means" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;CIDR stands for Classless Inter-Domain Routing. It replaced the older class-based system with variable-length prefixes, allowing networks to be sized and routed more precisely.&lt;/p&gt;
&lt;p&gt;An IPv4 CIDR block contains an address followed by a slash and a prefix length:&lt;/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;192.168.10.0/24&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;An IPv4 address contains 32 bits. The &lt;code&gt;/24&lt;/code&gt; prefix means the first 24 bits identify the network, leaving 8 bits for addresses inside that network. A longer prefix fixes more network bits and creates a smaller block. A shorter prefix leaves more host bits and creates a larger block.&lt;/p&gt;
&lt;p&gt;The prefix can range from &lt;code&gt;/0&lt;/code&gt;, which covers the entire IPv4 address space, to &lt;code&gt;/32&lt;/code&gt;, which identifies one address. CIDR notation is used for both network allocation and routing, so the same format can describe a LAN subnet, a firewall source range, or a single host route.&lt;/p&gt;
&lt;h2 id="network-bits-and-host-bits"&gt;Network Bits and Host Bits &lt;a class="headline-link" href="#network-bits-and-host-bits" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To see the &lt;code&gt;/24&lt;/code&gt; boundary, write the address and mask in binary:&lt;/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;Address: 11000000.10101000.00001010.00000000
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Mask: 11111111.11111111.11111111.00000000
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;lt;------ network ------&amp;gt;&amp;lt;-- host --&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Every &lt;code&gt;1&lt;/code&gt; in the mask belongs to the network prefix. Every &lt;code&gt;0&lt;/code&gt; leaves a bit that can vary within the block. Because &lt;code&gt;/24&lt;/code&gt; leaves 8 host bits, the block contains &lt;code&gt;2^8&lt;/code&gt;, or 256, addresses.&lt;/p&gt;
&lt;p&gt;The network portion stays constant for every address in the block. Only the last 8 bits change, producing the range &lt;code&gt;192.168.10.0&lt;/code&gt; through &lt;code&gt;192.168.10.255&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="convert-a-cidr-prefix-to-a-subnet-mask"&gt;Convert a CIDR Prefix to a Subnet Mask &lt;a class="headline-link" href="#convert-a-cidr-prefix-to-a-subnet-mask" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A subnet mask expresses the same boundary as a CIDR prefix, but in dotted-decimal form. A &lt;code&gt;/24&lt;/code&gt; has 24 one bits followed by 8 zero bits:&lt;/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;11111111.11111111.11111111.00000000
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;255.255.255.0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Prefixes that end on an octet boundary are easy to recognize: &lt;code&gt;/8&lt;/code&gt; is &lt;code&gt;255.0.0.0&lt;/code&gt;, &lt;code&gt;/16&lt;/code&gt; is &lt;code&gt;255.255.0.0&lt;/code&gt;, and &lt;code&gt;/24&lt;/code&gt; is &lt;code&gt;255.255.255.0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For a prefix such as &lt;code&gt;/26&lt;/code&gt;, the first two bits of the final octet also belong to the 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="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;11111111.11111111.11111111.11000000
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;255.255.255.192&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The binary value &lt;code&gt;11000000&lt;/code&gt; equals 192, so &lt;code&gt;/26&lt;/code&gt; maps to &lt;code&gt;255.255.255.192&lt;/code&gt;. The remaining six host bits provide &lt;code&gt;2^6&lt;/code&gt;, or 64, total addresses per subnet.&lt;/p&gt;
&lt;h2 id="calculate-the-number-of-addresses-and-hosts"&gt;Calculate the Number of Addresses and Hosts &lt;a class="headline-link" href="#calculate-the-number-of-addresses-and-hosts" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For a normal IPv4 subnet, calculate the total number of addresses with this formula:&lt;/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;Total addresses = 2^(32 - prefix length)&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;/27&lt;/code&gt; leaves five host bits, so it contains &lt;code&gt;2^5 = 32&lt;/code&gt; addresses. The first address is normally reserved as the network address, and the last is the broadcast address, leaving 30 addresses for host interfaces.&lt;/p&gt;
&lt;p&gt;Two prefixes need separate treatment:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;/31&lt;/code&gt; contains two addresses. On a point-to-point link, &lt;a href="https://www.rfc-editor.org/rfc/rfc3021.html" target="_blank" rel="noopener noreferrer"&gt;RFC 3021&lt;/a&gt;
treats both as host addresses because that link has no need for a separate network or broadcast address.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;/32&lt;/code&gt; contains one address and represents a host route, not a conventional multi-host subnet.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The familiar &lt;code&gt;total minus 2&lt;/code&gt; rule therefore applies to conventional IPv4 subnets from &lt;code&gt;/0&lt;/code&gt; through &lt;code&gt;/30&lt;/code&gt;, not blindly to every prefix.&lt;/p&gt;
&lt;h2 id="find-the-network-and-broadcast-addresses"&gt;Find the Network and Broadcast Addresses &lt;a class="headline-link" href="#find-the-network-and-broadcast-addresses" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Consider the address &lt;code&gt;192.168.10.34/27&lt;/code&gt;. A &lt;code&gt;/27&lt;/code&gt; mask is &lt;code&gt;255.255.255.224&lt;/code&gt;, which creates blocks of 32 addresses in the final octet:&lt;/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;0-31, 32-63, 64-95, 96-127, 128-159, 160-191, 192-223, 224-255&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The address &lt;code&gt;.34&lt;/code&gt; falls in the &lt;code&gt;32-63&lt;/code&gt; block. That gives us:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Network address: &lt;code&gt;192.168.10.32&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;First usable address: &lt;code&gt;192.168.10.33&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Last usable address: &lt;code&gt;192.168.10.62&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Broadcast address: &lt;code&gt;192.168.10.63&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can find the block size by subtracting the changing mask octet from 256. For &lt;code&gt;/27&lt;/code&gt;, &lt;code&gt;256 - 224 = 32&lt;/code&gt;. Starting at zero, count in increments of 32 until you find the range that contains the address.&lt;/p&gt;
&lt;p&gt;On Linux, the &lt;a href="https://linuxize.com/post/linux-ip-command/"&gt;&lt;code&gt;ip&lt;/code&gt; command&lt;/a&gt;
displays each interface address together with its CIDR 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;ip -brief address&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;lo UNKNOWN 127.0.0.1/8 ::1/128
eth0 UP 192.168.10.34/27&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The output shows that &lt;code&gt;eth0&lt;/code&gt; uses the &lt;code&gt;/27&lt;/code&gt; network calculated above.&lt;/p&gt;
&lt;h2 id="subnet-a-24-network-into-26-blocks"&gt;Subnet a /24 Network into /26 Blocks &lt;a class="headline-link" href="#subnet-a-24-network-into-26-blocks" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Suppose you have &lt;code&gt;192.168.10.0/24&lt;/code&gt; and need four equal networks. Moving from &lt;code&gt;/24&lt;/code&gt; to &lt;code&gt;/26&lt;/code&gt; borrows two host bits for subnetting. Two borrowed bits produce &lt;code&gt;2^2 = 4&lt;/code&gt; subnets.&lt;/p&gt;
&lt;p&gt;Each &lt;code&gt;/26&lt;/code&gt; contains 64 total addresses and 62 conventional host addresses:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Subnet&lt;/th&gt;
&lt;th&gt;Usable host range&lt;/th&gt;
&lt;th&gt;Broadcast&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;192.168.10.0/26&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;192.168.10.1&lt;/code&gt; to &lt;code&gt;192.168.10.62&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;192.168.10.63&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;192.168.10.64/26&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;192.168.10.65&lt;/code&gt; to &lt;code&gt;192.168.10.126&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;192.168.10.127&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;192.168.10.128/26&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;192.168.10.129&lt;/code&gt; to &lt;code&gt;192.168.10.190&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;192.168.10.191&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;192.168.10.192/26&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;192.168.10.193&lt;/code&gt; to &lt;code&gt;192.168.10.254&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;192.168.10.255&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The subnet boundaries increase by 64 because &lt;code&gt;/26&lt;/code&gt; leaves six host bits and &lt;code&gt;2^6 = 64&lt;/code&gt;. A network address must start on one of these boundaries. For example, &lt;code&gt;192.168.10.64/26&lt;/code&gt; is valid, but &lt;code&gt;192.168.10.70/26&lt;/code&gt; is an address inside that network rather than the network identifier.&lt;/p&gt;
&lt;h2 id="combine-networks-with-route-aggregation"&gt;Combine Networks with Route Aggregation &lt;a class="headline-link" href="#combine-networks-with-route-aggregation" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;CIDR also lets routers summarize adjacent networks into one larger route. For example, these four &lt;code&gt;/24&lt;/code&gt; networks are contiguous:&lt;/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;192.168.0.0/24
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;192.168.1.0/24
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;192.168.2.0/24
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;192.168.3.0/24&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;They can be advertised as the single route &lt;code&gt;192.168.0.0/22&lt;/code&gt;. A &lt;code&gt;/22&lt;/code&gt; contains 1024 addresses, exactly the same total as four &lt;code&gt;/24&lt;/code&gt; blocks.&lt;/p&gt;
&lt;p&gt;Aggregation works only when the networks are contiguous and aligned on the larger prefix boundary. The four networks from &lt;code&gt;192.168.1.0/24&lt;/code&gt; through &lt;code&gt;192.168.4.0/24&lt;/code&gt; cannot form one &lt;code&gt;/22&lt;/code&gt;, even though their total size is the same, because the range does not start on a &lt;code&gt;/22&lt;/code&gt; boundary.&lt;/p&gt;
&lt;h2 id="cidr-notation-in-ipv6"&gt;CIDR Notation in IPv6 &lt;a class="headline-link" href="#cidr-notation-in-ipv6" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;IPv6 uses CIDR prefixes over a 128-bit address. 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="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;2001:db8:1234:5600::/64&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;/64&lt;/code&gt; prefix fixes the first 64 bits and leaves the remaining 64 bits for the interface identifier. A &lt;code&gt;/64&lt;/code&gt; is the common size for an IPv6 LAN, especially when Stateless Address Autoconfiguration (SLAAC) is used, although routing prefixes can have other lengths.&lt;/p&gt;
&lt;p&gt;IPv6 has no broadcast address. Multicast replaces broadcast behavior, and all-zero or all-one values are legal within address fields unless a specific rule says otherwise. See the &lt;a href="https://linuxize.com/post/ip4-vs-ip6/"&gt;IPv4 vs IPv6 comparison&lt;/a&gt;
for the broader differences between the protocols.&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;Prefix&lt;/th&gt;
&lt;th&gt;Subnet mask&lt;/th&gt;
&lt;th style="text-align: right"&gt;Total addresses&lt;/th&gt;
&lt;th style="text-align: right"&gt;Conventional usable hosts&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/8&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;255.0.0.0&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;16,777,216&lt;/td&gt;
&lt;td style="text-align: right"&gt;16,777,214&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/16&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;255.255.0.0&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;65,536&lt;/td&gt;
&lt;td style="text-align: right"&gt;65,534&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/20&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;255.255.240.0&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;4,096&lt;/td&gt;
&lt;td style="text-align: right"&gt;4,094&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/22&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;255.255.252.0&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;1,024&lt;/td&gt;
&lt;td style="text-align: right"&gt;1,022&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/24&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;255.255.255.0&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;256&lt;/td&gt;
&lt;td style="text-align: right"&gt;254&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/25&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;255.255.255.128&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;128&lt;/td&gt;
&lt;td style="text-align: right"&gt;126&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/26&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;255.255.255.192&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;64&lt;/td&gt;
&lt;td style="text-align: right"&gt;62&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/27&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;255.255.255.224&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;32&lt;/td&gt;
&lt;td style="text-align: right"&gt;30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/28&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;255.255.255.240&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;16&lt;/td&gt;
&lt;td style="text-align: right"&gt;14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/29&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;255.255.255.248&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;8&lt;/td&gt;
&lt;td style="text-align: right"&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/30&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;255.255.255.252&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;4&lt;/td&gt;
&lt;td style="text-align: right"&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/31&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;255.255.255.254&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;2&lt;/td&gt;
&lt;td style="text-align: right"&gt;2 on point-to-point links&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/32&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;255.255.255.255&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;1&lt;/td&gt;
&lt;td style="text-align: right"&gt;1 host route&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;The CIDR prefix tells you exactly where the network bits end and the host bits begin. Once you can convert that boundary to a mask and block size, you can calculate ranges, divide networks, and check firewall rules without relying on the old address classes.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/cidr-notation-and-subnetting-explained/featured_hu_46840b6d13fe43eb.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>mount Cheatsheet</title><link>https://linuxize.com/cheatsheet/mount/</link><pubDate>Fri, 24 Jul 2026 07:45:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/cheatsheet/mount/</guid><description>Quick reference for the mount command: mount and unmount filesystems, mount options, UUID and label mounts, network shares, and /etc/fstab entries</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;mount&lt;/code&gt; and &lt;code&gt;umount&lt;/code&gt; command forms.&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;mount DEVICE DIR&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Attach a device to a mount point&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mount -t TYPE DEVICE DIR&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mount with an explicit filesystem type&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mount -o OPTIONS DEVICE DIR&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mount with a comma-separated option list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mount DIR&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mount an entry already defined in &lt;code&gt;/etc/fstab&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mount -a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mount &lt;code&gt;/etc/fstab&lt;/code&gt; filesystems except &lt;code&gt;noauto&lt;/code&gt; entries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;umount DIR&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Detach the filesystem mounted at &lt;code&gt;DIR&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Most mount operations require root privileges, so prefix them with &lt;code&gt;sudo&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="list-mounted-filesystems"&gt;List Mounted Filesystems &lt;a class="headline-link" href="#list-mounted-filesystems" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Inspect what is currently mounted and where.&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;mount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List all mounts using the legacy display; prefer &lt;code&gt;findmnt&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;findmnt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show mounts as a readable tree&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;findmnt /dev/sdb1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show where a specific device is mounted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;findmnt -t ext4,xfs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List only the given filesystem types&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;findmnt --fstab --verify&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check &lt;code&gt;/etc/fstab&lt;/code&gt; for errors before rebooting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cat /proc/mounts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read active mounts in the current mount namespace&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;List block devices with type, label, and UUID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;df -hT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show mounted filesystems with type and free space&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="common-mount-options"&gt;Common Mount Options &lt;a class="headline-link" href="#common-mount-options" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Values passed to &lt;code&gt;-o&lt;/code&gt; or listed in the fourth &lt;code&gt;/etc/fstab&lt;/code&gt; field.&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;defaults&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Common defaults are &lt;code&gt;rw,suid,dev,exec,auto,nouser,async&lt;/code&gt;; exact defaults vary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ro&lt;/code&gt; / &lt;code&gt;rw&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mount read-only or read-write&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;noexec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Block execution of binaries on the filesystem&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nosuid&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ignore setuid and setgid bits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nodev&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ignore device files on the filesystem&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;noatime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Skip access-time updates, which reduces disk writes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sync&lt;/code&gt; / &lt;code&gt;async&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Write synchronously or let the kernel buffer writes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;noauto&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Skip this entry during &lt;code&gt;mount -a&lt;/code&gt; and at boot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;user&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allow a non-root user to mount; implies &lt;code&gt;noexec,nosuid,nodev&lt;/code&gt; unless overridden&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nofail&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Continue booting when the device is missing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uid=1000,gid=1000&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set owner and group on filesystems without Unix permissions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;remount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Change options on an already mounted filesystem&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; &lt;code&gt;sudo mount -o remount,rw /&lt;/code&gt; remounts the root filesystem read-write.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="mount-by-uuid-or-label"&gt;Mount by UUID or Label &lt;a class="headline-link" href="#mount-by-uuid-or-label" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Device names such as &lt;code&gt;/dev/sdb1&lt;/code&gt; can change between boots, so identify the filesystem instead.&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 -f&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the UUID and label of every filesystem&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo blkid&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print device type, label, and UUID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo blkid /dev/sdb1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print the attributes of one device&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo mount UUID=1a2b3c4d-... /mnt/data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mount by filesystem UUID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo mount LABEL=backup /mnt/backup&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mount by filesystem label&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo mount -U 1a2b3c4d-... /mnt/data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Short form of the UUID lookup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo mount -L backup /mnt/backup&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Short form of the label lookup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo e2label /dev/sdb1 backup&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set a label on an ext2, ext3, or ext4 filesystem&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Prefer &lt;code&gt;UUID=&lt;/code&gt; or &lt;code&gt;LABEL=&lt;/code&gt; for persistent local mounts in &lt;code&gt;/etc/fstab&lt;/code&gt; because device names can change.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="filesystem-types"&gt;Filesystem Types &lt;a class="headline-link" href="#filesystem-types" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Values for &lt;code&gt;-t&lt;/code&gt; and for the third &lt;code&gt;/etc/fstab&lt;/code&gt; field.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&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;ext4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Widely used general-purpose Linux filesystem&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;xfs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;High-performance journaling filesystem, default on RHEL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;btrfs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Copy-on-write filesystem with snapshots and subvolumes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;vfat&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;FAT32, used by USB drives and EFI system partitions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;exfat&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Large-file FAT variant for USB drives and SD cards&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ntfs3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;In-kernel NTFS driver available on supported kernels&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;iso9660&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Optical disc and ISO image filesystem&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tmpfs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;In-memory filesystem backed by RAM and swap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;nfs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Network File System share&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cifs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SMB or Windows network share&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;auto&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Let &lt;code&gt;mount&lt;/code&gt; detect the type&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="network-and-special-mounts"&gt;Network and Special Mounts &lt;a class="headline-link" href="#network-and-special-mounts" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Mount forms beyond a plain local 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 mount -t nfs server:/srv/data /mnt/data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mount an NFS export&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo mount -t cifs //server/share /mnt/share -o username=user&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mount a Windows or Samba share&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo mount -t cifs //server/share /mnt/share -o credentials=/etc/smb-credentials&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mount CIFS with a credentials file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sshfs user@host:/remote/dir /mnt/remote&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mount a remote directory over SSH&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo mount -o loop image.iso /mnt/iso&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mount an ISO image through a loop device&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo mount --bind /src /dst&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Expose an existing directory at a second path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo mount --rbind /src /dst&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bind a directory together with its submounts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo mount -t tmpfs -o size=512M tmpfs /mnt/ram&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a 512 MB RAM-backed filesystem&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Keep CIFS credentials outside project directories and version control. Protect the file with &lt;code&gt;sudo chmod 600 /etc/smb-credentials&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="unmounting"&gt;Unmounting &lt;a class="headline-link" href="#unmounting" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Detach a filesystem and deal with a busy mount point.&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 umount /mnt/data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unmount by mount point&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo umount /dev/sdb1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unmount by device (obsolete; use the mount point)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo umount -R /mnt/data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unmount a mount point and everything below it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo umount -l /mnt/data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lazy unmount: detach now, release when no longer in use&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo umount -f /mnt/share&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Force an unmount, mainly for unreachable network shares&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo umount -a -t nfs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unmount every NFS filesystem&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lsof +f -- /mnt/data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List open files under the mount point&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fuser -mv /mnt/data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show processes and users holding the mount point&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo fuser -km /mnt/data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Kill every process using the mount point; destructive&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Review &lt;code&gt;fuser -mv&lt;/code&gt; output before using &lt;code&gt;fuser -km&lt;/code&gt;. Use lazy or forced unmounts only when a normal unmount cannot work: &lt;code&gt;-f&lt;/code&gt; can lose unwritten data, while &lt;code&gt;-l&lt;/code&gt; hides the path before all references are released.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="persistent-mounts-in-etcfstab"&gt;Persistent Mounts in /etc/fstab &lt;a class="headline-link" href="#persistent-mounts-in-etcfstab" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Each line has six space-separated fields.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&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;1. Device&lt;/td&gt;
&lt;td&gt;&lt;code&gt;UUID=&lt;/code&gt;, &lt;code&gt;LABEL=&lt;/code&gt;, a device path, or a remote share&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2. Mount point&lt;/td&gt;
&lt;td&gt;Existing directory, or &lt;code&gt;none&lt;/code&gt; for swap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3. Type&lt;/td&gt;
&lt;td&gt;Filesystem type, such as &lt;code&gt;ext4&lt;/code&gt;, &lt;code&gt;nfs&lt;/code&gt;, or &lt;code&gt;cifs&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4. Options&lt;/td&gt;
&lt;td&gt;Comma-separated mount options, or &lt;code&gt;defaults&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5. Dump&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt; in nearly all cases; &lt;code&gt;1&lt;/code&gt; marks the filesystem for &lt;code&gt;dump&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6. Pass&lt;/td&gt;
&lt;td&gt;Order of &lt;code&gt;fsck&lt;/code&gt; at boot: &lt;code&gt;1&lt;/code&gt; for root, &lt;code&gt;2&lt;/code&gt; for others, &lt;code&gt;0&lt;/code&gt; to skip&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; &lt;code&gt;UUID=1a2b3c4d-... /mnt/data ext4 defaults,nofail 0 2&lt;/code&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 findmnt --fstab --verify&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Validate the file before rebooting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo mount -a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Apply mountable entries after validating &lt;code&gt;/etc/fstab&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl daemon-reload&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Regenerate the systemd mount units after an edit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo mount /mnt/data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mount one entry using only its mount point&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Use &lt;code&gt;nofail&lt;/code&gt; only for optional mounts. Add &lt;code&gt;_netdev&lt;/code&gt; when a network-backed filesystem is not recognized as network-dependent from its type.&lt;/p&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 mount errors and what to check.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Error&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;target is busy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Find the holder with &lt;code&gt;fuser -mv DIR&lt;/code&gt; or &lt;code&gt;lsof +f -- DIR&lt;/code&gt;, then close it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;unknown filesystem type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install the required helper or driver, such as &lt;code&gt;ntfs-3g&lt;/code&gt;, &lt;code&gt;nfs-common&lt;/code&gt; or &lt;code&gt;nfs-utils&lt;/code&gt;, or &lt;code&gt;cifs-utils&lt;/code&gt;; confirm kernel support for exFAT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wrong fs type, bad option, bad superblock&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Confirm the type with &lt;code&gt;lsblk -f&lt;/code&gt; and check &lt;code&gt;dmesg&lt;/code&gt;. If repair is needed, unmount it and use the correct filesystem checker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mount point does not exist&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create the directory first with &lt;code&gt;sudo mkdir -p DIR&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;permission denied&lt;/code&gt; on CIFS&lt;/td&gt;
&lt;td&gt;Verify credentials and server share permissions; &lt;code&gt;uid=&lt;/code&gt; and &lt;code&gt;gid=&lt;/code&gt; only control local ownership&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;special device does not exist&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Recheck the UUID with &lt;code&gt;blkid&lt;/code&gt;; the device name may have changed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Files are hidden after mounting&lt;/td&gt;
&lt;td&gt;The mounted filesystem covers existing directory contents; unmount it and inspect the underlying path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Boot drops to emergency mode&lt;/td&gt;
&lt;td&gt;Remount root with &lt;code&gt;mount -o remount,rw /&lt;/code&gt;, fix the bad &lt;code&gt;/etc/fstab&lt;/code&gt; line, and validate it; add &lt;code&gt;nofail&lt;/code&gt; only if the mount is optional&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 and cheatsheets for complete storage 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-mount-and-unmount-file-systems-in-linux/"&gt;How to Mount and Unmount File Systems in Linux&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Full guide to &lt;code&gt;mount&lt;/code&gt; and &lt;code&gt;umount&lt;/code&gt; with practical examples&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/etc-fstab-file/"&gt;Understanding the /etc/fstab File in Linux&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Field-by-field breakdown of persistent mount entries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-mount-an-nfs-share-in-linux/"&gt;How to Mount an NFS Share in Linux&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Install the client, mount a share, and troubleshoot errors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-mount-cifs-windows-share-on-linux/"&gt;How to Mount a Windows Share on Linux Using CIFS&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Credentials files, ownership, and permissions for SMB shares&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-mount-iso-file-on-linux/"&gt;How to Mount ISO File on Linux&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Loop devices and the graphical mounting method&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/post/how-to-use-sshfs-to-mount-remote-directories-over-ssh/"&gt;How to Use SSHFS to Mount Remote Directories over SSH&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Mount a remote directory and browse it like a local one&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/cheatsheet/df/"&gt;df Cheatsheet&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Check free space on mounted filesystems&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/cheatsheet/fdisk/"&gt;fdisk Cheatsheet&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Partition a disk before creating a filesystem&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;</content:encoded></item><item><title>SSH Connection Refused: Causes and Fixes</title><link>https://linuxize.com/post/fix-ssh-connection-refused/</link><pubDate>Thu, 23 Jul 2026 14:20:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/fix-ssh-connection-refused/</guid><category>ssh</category><description>Fix the SSH "Connection refused" error by checking the destination, listening port, OpenSSH service or socket, firewall rules, and port forwarding.</description><content:encoded>&lt;p&gt;You try to log in and SSH stops immediately with &lt;code&gt;connect to host example.com port 22: Connection refused&lt;/code&gt;. The failure happens before SSH checks your key or password, so changing authentication settings will not fix it.&lt;/p&gt;
&lt;p&gt;An immediate refusal means the target address has no process listening on that port, or a firewall or network device actively rejected the request. The usual causes are a stopped SSH server, the wrong host or port, a listener bound to another address, a firewall reject rule, or port forwarding to the wrong machine. A connection that hangs and then times out is different because no response came back.&lt;/p&gt;
&lt;p&gt;This guide starts with checks you can run from the client, then moves to the SSH service, listening port, firewall, and network path on the server.&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/ssh/"&gt;SSH cheatsheet&lt;/a&gt;
.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Check&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;Confirm the destination address&lt;/td&gt;
&lt;td&gt;&lt;code&gt;getent ahosts example.com&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Show SSH connection details&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ssh -vvv user@example.com&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test the default SSH port&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nc -vz example.com 22&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check for a listener on port 22&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo ss -ltnp 'sport = :22'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Show the effective SSH port and addresses&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo sshd -T | grep -E '^(port|listenaddress) '&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check SSH on Ubuntu or Debian&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl status ssh.service ssh.socket&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check SSH on Fedora or RHEL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo systemctl status sshd&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check UFW rules&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo ufw status numbered&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check firewalld services&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo firewall-cmd --list-services&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="confirm-the-host-and-port-from-the-client"&gt;Confirm the Host and Port from the Client &lt;a class="headline-link" href="#confirm-the-host-and-port-from-the-client" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before changing the server, confirm that the hostname resolves to the address 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;getent ahosts example.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If the result points to an old server or the wrong public address, correct the DNS record or connect to the intended IP address directly. Reaching the wrong host can produce a refusal even when SSH is working on the real server.&lt;/p&gt;
&lt;p&gt;Next, run SSH with verbose 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;ssh -vvv user@example.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The relevant lines look 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;debug1: Connecting to example.com [203.0.113.10] port 22.
ssh: connect to host example.com port 22: Connection refused&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The client resolved &lt;code&gt;example.com&lt;/code&gt; to &lt;code&gt;203.0.113.10&lt;/code&gt; and received an immediate refusal from port 22. You can test the TCP port without starting an SSH login:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nc -vz example.com &lt;span class="m"&gt;22&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 server uses a custom port, test and connect to that port 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;nc -vz example.com &lt;span class="m"&gt;2222&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh -p &lt;span class="m"&gt;2222&lt;/span&gt; user@example.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;A hostname can also have separate IPv4 and IPv6 addresses. Test each address family when one DNS record may point to the wrong 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;ssh -4 user@example.com
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh -6 user@example.com&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The remaining checks require access to the server through a cloud console, virtual machine console, physical terminal, or another management channel.&lt;/p&gt;
&lt;h2 id="check-whether-ssh-is-listening"&gt;Check Whether SSH Is Listening &lt;a class="headline-link" href="#check-whether-ssh-is-listening" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The listening socket is the quickest way to tell whether the server can accept SSH connections on port 22. Run this command on 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 ss -ltnp &lt;span class="s1"&gt;&amp;#39;sport = :22&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;A working listener appears as either &lt;code&gt;sshd&lt;/code&gt; or &lt;code&gt;systemd&lt;/code&gt;, depending on whether the distribution runs the daemon continuously or uses socket activation. If the command returns nothing, no process is accepting connections on port 22.&lt;/p&gt;
&lt;p&gt;To see the port and addresses from the effective OpenSSH configuration, 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 sshd -T &lt;span class="p"&gt;|&lt;/span&gt; grep -E &lt;span class="s1"&gt;&amp;#39;^(port|listenaddress) &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;Typical output 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;port 22
listenaddress [::]:22
listenaddress 0.0.0.0:22&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The output shows that SSH is configured for port 22 on all IPv6 and IPv4 addresses. If it reports another port, connect with the &lt;code&gt;-p&lt;/code&gt; option and verify that the firewall allows that port. See our guide on &lt;a href="https://linuxize.com/post/how-to-change-ssh-port-in-linux/"&gt;changing the SSH port&lt;/a&gt;
if you need to correct the configuration.&lt;/p&gt;
&lt;h2 id="check-the-ssh-service-or-socket"&gt;Check the SSH Service or Socket &lt;a class="headline-link" href="#check-the-ssh-service-or-socket" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Linux distributions use different systemd unit names for OpenSSH. Ubuntu and Debian use &lt;code&gt;ssh.service&lt;/code&gt;, while Fedora, RHEL, and their derivatives use &lt;code&gt;sshd.service&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;On Ubuntu and Debian, check 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 systemctl status ssh.service&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Ubuntu 22.10 and later use &lt;code&gt;ssh.socket&lt;/code&gt; by default, so check that unit as well:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 ssh.socket&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;ssh.socket&lt;/code&gt; is active and owns port 22, &lt;code&gt;ssh.service&lt;/code&gt; may remain inactive until a client connects. In that setup, an inactive service alone does not indicate a problem. If the socket is disabled or stopped, enable it and 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 &lt;span class="nb"&gt;enable&lt;/span&gt; --now ssh.socket&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On systems without &lt;code&gt;ssh.socket&lt;/code&gt;, enable and start the service 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 systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; --now ssh&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, check and start &lt;code&gt;sshd&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 sshd
&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 sshd&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If the unit reports &lt;code&gt;failed&lt;/code&gt;, validate 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;sudo sshd -t&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;No output means the configuration passed the test. If OpenSSH prints an error, correct the reported line before restarting the unit. You can also inspect the current boot logs on Ubuntu and Debian:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 ssh.service -u ssh.socket -b&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For Fedora and RHEL, use the &lt;code&gt;sshd&lt;/code&gt; unit 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 journalctl -u sshd -b&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="install-the-openssh-server"&gt;Install the OpenSSH Server &lt;a class="headline-link" href="#install-the-openssh-server" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Some minimal installations include the SSH client but not the server. If &lt;code&gt;systemctl&lt;/code&gt; reports that the SSH unit does not exist, install the &lt;code&gt;openssh-server&lt;/code&gt; package.&lt;/p&gt;
&lt;h3 id="ubuntu-debian-and-derivatives"&gt;Ubuntu, Debian, and Derivatives &lt;a class="headline-link" href="#ubuntu-debian-and-derivatives" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Update the package index and install the 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 update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install openssh-server&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The package normally starts the appropriate SSH service or socket during installation. Check port 22 again with &lt;code&gt;ss&lt;/code&gt; instead of assuming that the listener started successfully.&lt;/p&gt;
&lt;h3 id="fedora-rhel-and-derivatives"&gt;Fedora, RHEL, and Derivatives &lt;a class="headline-link" href="#fedora-rhel-and-derivatives" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Install the server package, then enable and start &lt;code&gt;sshd&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 openssh-server
&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 sshd&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="check-the-listening-address"&gt;Check the Listening Address &lt;a class="headline-link" href="#check-the-listening-address" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;SSH can run normally but listen only on the loopback interface or one private address. In that case, a local connection works while connections to other server addresses are refused.&lt;/p&gt;
&lt;p&gt;Review the &lt;code&gt;listenaddress&lt;/code&gt; lines from &lt;code&gt;sshd -T&lt;/code&gt;. A value such as &lt;code&gt;127.0.0.1:22&lt;/code&gt; accepts only local IPv4 connections. OpenSSH reads its main configuration from &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt; and may also read files under &lt;code&gt;/etc/ssh/sshd_config.d/&lt;/code&gt;. A listener on a &lt;a href="https://linuxize.com/post/what-is-localhost/"&gt;loopback address&lt;/a&gt;
is reachable from the server itself and from nowhere else.&lt;/p&gt;
&lt;p&gt;Before changing &lt;code&gt;Port&lt;/code&gt; or &lt;code&gt;ListenAddress&lt;/code&gt;, keep console access open and validate the new configuration with &lt;code&gt;sudo sshd -t&lt;/code&gt;. On Ubuntu systems using socket activation, the socket must also reload the generated listening configuration. The port-change guide linked above covers the service and socket steps in order.&lt;/p&gt;
&lt;h2 id="check-the-firewall"&gt;Check the Firewall &lt;a class="headline-link" href="#check-the-firewall" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A firewall is less likely to cause an immediate refusal than a missing listener. Rules that silently drop traffic usually end with &lt;code&gt;Connection timed out&lt;/code&gt;, while an explicit &lt;code&gt;REJECT&lt;/code&gt; rule can return &lt;code&gt;Connection refused&lt;/code&gt;. Check the firewall after confirming the service and port.&lt;/p&gt;
&lt;p&gt;If the server uses UFW, list its rules with 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 ufw status numbered&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Allow the standard OpenSSH profile when SSH uses port 22:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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;For a custom port, allow the exact TCP port 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 ufw allow 2222/tcp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On a server using firewalld, first check the services allowed in the active zone:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 firewall-cmd --list-services&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Add SSH to both the runtime and permanent configurations if it 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="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 firewall-cmd --add-service&lt;span class="o"&gt;=&lt;/span&gt;ssh
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo firewall-cmd --permanent --add-service&lt;span class="o"&gt;=&lt;/span&gt;ssh&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For a custom port, use &lt;code&gt;--add-port=2222/tcp&lt;/code&gt; in both commands. Our &lt;a href="https://linuxize.com/post/how-to-setup-a-firewall-with-ufw-on-ubuntu-24-04/"&gt;UFW firewall guide&lt;/a&gt;
explains how to review and manage rules in more detail.&lt;/p&gt;
&lt;h2 id="check-port-forwarding-and-external-firewalls"&gt;Check Port Forwarding and External Firewalls &lt;a class="headline-link" href="#check-port-forwarding-and-external-firewalls" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If SSH works from the same machine or local network but fails from the internet, the OpenSSH listener may already be correct. Check that the router or virtualization platform forwards the external port to the server&amp;rsquo;s current private address and SSH port.&lt;/p&gt;
&lt;p&gt;The same issue can occur with a virtual machine, container, load balancer, or cloud firewall. Confirm that public DNS points to the expected system and that each forwarding rule uses the same port you pass to &lt;code&gt;ssh&lt;/code&gt;. Cloud firewall rules often drop traffic and cause a timeout, but an edge device that rejects the request or forwards it to the wrong host can produce a refusal.&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;Connection refused started after an SSH configuration change&lt;/strong&gt;&lt;br&gt;
Run &lt;code&gt;sudo sshd -t&lt;/code&gt; and correct every reported error. On Ubuntu with socket activation, reload systemd and restart &lt;code&gt;ssh.socket&lt;/code&gt; after a valid &lt;code&gt;Port&lt;/code&gt; or &lt;code&gt;ListenAddress&lt;/code&gt; change, then verify the listener with &lt;code&gt;ss&lt;/code&gt; before closing console access.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SSH works with localhost but not the server address&lt;/strong&gt;&lt;br&gt;
Check the effective &lt;code&gt;ListenAddress&lt;/code&gt; values and the firewall rules for the external interface. A listener restricted to &lt;code&gt;127.0.0.1&lt;/code&gt; or &lt;code&gt;::1&lt;/code&gt; cannot accept remote connections.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;IPv4 works but IPv6 is refused&lt;/strong&gt;&lt;br&gt;
The server may have an AAAA record without an IPv6 listener. Compare &lt;code&gt;ssh -4&lt;/code&gt; and &lt;code&gt;ssh -6&lt;/code&gt;, then correct the DNS record or configure SSH to listen on the intended IPv6 address.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Only one client address is refused&lt;/strong&gt;&lt;br&gt;
A source-specific firewall rule or a tool such as Fail2ban may be rejecting that address. Review the firewall rules and, if Fail2ban is installed, check the SSH jail with &lt;code&gt;sudo fail2ban-client status sshd&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SSH reaches the server but reports Permission denied&lt;/strong&gt;&lt;br&gt;
The network path and listener are working, and the failure has moved to authentication. Follow the &lt;a href="https://linuxize.com/post/fix-ssh-permission-denied-publickey/"&gt;SSH Permission denied (publickey) guide&lt;/a&gt;
instead.&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;Once the port test succeeds, SSH should move on to host-key verification or authentication. Before exposing the restored service to the internet, review our &lt;a href="https://linuxize.com/post/ssh-hardening-best-practices/"&gt;SSH hardening best practices&lt;/a&gt;
and restrict firewall access where possible.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/fix-ssh-connection-refused/featured_hu_6253d7bc6f917ca6.webp" medium="image" type="image/webp" width="1200" height="630"/></item><item><title>Dockerfile COPY vs ADD: Which to Use</title><link>https://linuxize.com/post/dockerfile-copy-vs-add/</link><pubDate>Wed, 22 Jul 2026 10:25:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/dockerfile-copy-vs-add/</guid><category>docker</category><description>Compare Dockerfile COPY and ADD for local files, build stages, remote URLs, Git repositories, and tar extraction, with clear guidance on when to use each.</description><content:encoded>&lt;p&gt;When a Docker image needs application code, configuration, or a release archive, both &lt;code&gt;COPY&lt;/code&gt; and &lt;code&gt;ADD&lt;/code&gt; can place files in the image. For an ordinary local file, &lt;code&gt;COPY app.py /app/&lt;/code&gt; and &lt;code&gt;ADD app.py /app/&lt;/code&gt; produce the same result, but the instructions differ once archives, remote sources, or build stages are involved.&lt;/p&gt;
&lt;p&gt;Use &lt;code&gt;COPY&lt;/code&gt; for local files and artifacts from other build stages. Use &lt;code&gt;ADD&lt;/code&gt; when you want automatic tar extraction or need BuildKit to fetch a remote HTTP or Git source. This guide compares both instructions and explains which one fits each case.&lt;/p&gt;
&lt;h2 id="what-copy-does"&gt;What COPY Does &lt;a class="headline-link" href="#what-copy-does" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;COPY&lt;/code&gt; takes files or directories from the build context and places them at a destination inside 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="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="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;COPY&lt;/span&gt; requirements.txt /app/&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; src/ /app/src/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;An archive copied this way stays an archive, and a source path cannot point outside the build context. Two options cover common permission needs by setting ownership and mode as the files are copied:&lt;/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="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;COPY&lt;/span&gt; --chown&lt;span class="o"&gt;=&lt;/span&gt;appuser:appgroup --chmod&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;644&lt;/span&gt; config.yml /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;COPY&lt;/code&gt; can also read from a named context, another image, or an earlier stage in a multi-stage build. The &lt;code&gt;--from&lt;/code&gt; option is commonly used to move compiled artifacts into a smaller production 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="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="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;COPY&lt;/span&gt; --from&lt;span class="o"&gt;=&lt;/span&gt;builder /app/target/server /usr/local/bin/server&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="what-add-does"&gt;What ADD Does &lt;a class="headline-link" href="#what-add-does" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For local files and directories, &lt;code&gt;ADD&lt;/code&gt; behaves much like &lt;code&gt;COPY&lt;/code&gt;. It also understands local tar archives, remote URLs, and Git repository sources.&lt;/p&gt;
&lt;p&gt;When the source is a local tar archive, either uncompressed or compressed with gzip, bzip2, or xz, &lt;code&gt;ADD&lt;/code&gt; extracts it into the destination instead of copying the archive 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="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="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;ADD&lt;/span&gt; rootfs.tar.gz /&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Docker detects an archive from its contents, not its filename. A file named &lt;code&gt;rootfs.tar.gz&lt;/code&gt; that does not contain a recognized tar archive is copied without an extraction error. When you need the archive itself inside the image, use &lt;code&gt;COPY&lt;/code&gt;, or use &lt;code&gt;ADD --unpack=false&lt;/code&gt; with Dockerfile syntax 1.17 or later.&lt;/p&gt;
&lt;p&gt;For remote sources, &lt;code&gt;ADD&lt;/code&gt; can download an HTTP or HTTPS URL. BuildKit supports &lt;code&gt;--checksum&lt;/code&gt; so the build fails if the downloaded content does not match the expected SHA-256 digest. Here is a checksum-verified download:&lt;/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="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="c"&gt;# syntax=docker/dockerfile:1&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;ADD&lt;/span&gt; --checksum&lt;span class="o"&gt;=&lt;/span&gt;sha256:24454f830cdb571e2c4ad15481119c43b3cafd48dd869a9b2945d1036d1dc68d &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; https://mirrors.edge.kernel.org/pub/linux/kernel/Historic/linux-0.01.tar.gz &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; /tmp/linux-0.01.tar.gz&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Remote tar archives are downloaded without extraction by default. Dockerfile syntax 1.17 added &lt;code&gt;--unpack=true&lt;/code&gt; for builds that should download and extract a remote tar archive in one instruction.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ADD&lt;/code&gt; can also clone a Git repository from an HTTPS or SSH address. A branch, tag, commit, or subdirectory can be selected in the URL, and &lt;code&gt;--checksum&lt;/code&gt; can pin a Git source to a commit. These behaviors are covered in the official &lt;a href="https://docs.docker.com/reference/dockerfile/#add" target="_blank" rel="noopener noreferrer"&gt;Dockerfile ADD reference&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="key-differences"&gt;Key Differences &lt;a class="headline-link" href="#key-differences" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Source or behavior&lt;/th&gt;
&lt;th&gt;COPY&lt;/th&gt;
&lt;th&gt;ADD&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Local files and directories&lt;/td&gt;
&lt;td&gt;Copies them&lt;/td&gt;
&lt;td&gt;Copies them&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local tar archive&lt;/td&gt;
&lt;td&gt;Keeps the archive&lt;/td&gt;
&lt;td&gt;Extracts it by default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remote HTTP or HTTPS URL&lt;/td&gt;
&lt;td&gt;Not supported&lt;/td&gt;
&lt;td&gt;Downloads it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remote Git repository&lt;/td&gt;
&lt;td&gt;Not supported&lt;/td&gt;
&lt;td&gt;Clones it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Checksum for a remote source&lt;/td&gt;
&lt;td&gt;Not applicable&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--checksum&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remote tar extraction&lt;/td&gt;
&lt;td&gt;Not supported&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--unpack=true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build stage, named context, or image&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--from&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Not supported&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="choosing-between-copy-and-add"&gt;Choosing Between COPY and ADD &lt;a class="headline-link" href="#choosing-between-copy-and-add" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Use &lt;code&gt;COPY&lt;/code&gt; for application source code, configuration, static assets, and other ordinary files from the build context. It states the intent directly and ensures that an archive remains intact. &lt;code&gt;COPY --from&lt;/code&gt; is also the correct choice for moving artifacts between build stages or importing files from another image.&lt;/p&gt;
&lt;p&gt;Use &lt;code&gt;ADD&lt;/code&gt; when its source-aware behavior is part of the task. Local tar extraction is useful for assembling a root filesystem or installing a vendored release archive without storing the compressed archive in its own image layer.&lt;/p&gt;
&lt;p&gt;For example, this instruction extracts a local application archive directly into &lt;code&gt;/opt/app/&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="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="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;ADD&lt;/span&gt; app-1.4.2-dist.tar.gz /opt/app/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For remote artifacts, current &lt;a href="https://docs.docker.com/build/building/best-practices/#add-or-copy" target="_blank" rel="noopener noreferrer"&gt;Docker build best practices&lt;/a&gt;
favor &lt;code&gt;ADD&lt;/code&gt; with &lt;code&gt;--checksum&lt;/code&gt; because BuildKit can cache the remote source precisely and verify its contents. A &lt;code&gt;RUN&lt;/code&gt; instruction with &lt;code&gt;curl&lt;/code&gt; or &lt;code&gt;wget&lt;/code&gt; is still appropriate when you need custom request handling or additional processing. Keep verification, extraction, and cleanup in the same &lt;code&gt;RUN&lt;/code&gt; instruction so temporary files do not remain in a separate layer.&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;Choose &lt;code&gt;COPY&lt;/code&gt; for local content and multi-stage artifacts. Choose &lt;code&gt;ADD&lt;/code&gt; when you need automatic tar extraction or a checksum-verified remote source. For the complete image-building workflow, see our guide on &lt;a href="https://linuxize.com/post/how-to-build-docker-images-with-dockerfile/"&gt;building Docker images with a Dockerfile&lt;/a&gt;
, and keep the &lt;a href="https://linuxize.com/cheatsheet/docker/"&gt;Docker cheatsheet&lt;/a&gt;
nearby for the rest of the instruction set.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/dockerfile-copy-vs-add/featured_hu_9fe7b15945bda5c3.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>head Cheatsheet</title><link>https://linuxize.com/cheatsheet/head/</link><pubDate>Tue, 21 Jul 2026 13:20:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/cheatsheet/head/</guid><description>Quick reference for showing the first lines or bytes of files and limiting command output with head 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 &lt;code&gt;head&lt;/code&gt; command forms.&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;head FILE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the first 10 lines of a file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;head -n 20 FILE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the first 20 lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;head -c 100 FILE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the first 100 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;command | head -n 10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Limit piped output to 10 lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;head -- -notes.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read a file 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="select-lines"&gt;Select Lines &lt;a class="headline-link" href="#select-lines" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Control how many lines are printed.&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;head -n 5 /etc/passwd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the first 5 lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;head -n 1 data.csv&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print the header row of a CSV file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;head -n 100 app.log&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Preview the first 100 log entries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;head -n -5 FILE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show all lines except the last 5 (GNU)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;head -n 2 FILE1 FILE2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the first 2 lines of each file&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="select-bytes"&gt;Select Bytes &lt;a class="headline-link" href="#select-bytes" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Read a fixed number of bytes instead of lines.&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;head -c 100 FILE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the first 100 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;head -c 2K FILE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the first 2048 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;head -c 1MB FILE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the first 1,000,000 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;head -c -512 FILE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show all bytes except the last 512 (GNU)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;head -c 512 disk.img &amp;gt; header.bin&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Save the first 512 bytes to 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="multiple-files-and-headers"&gt;Multiple Files and Headers &lt;a class="headline-link" href="#multiple-files-and-headers" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Control the file-name headers shown with multiple inputs.&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;head FILE1 FILE2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show 10 lines from each file with headers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;head -n 5 *.log&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the first 5 lines of every matched log&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;head -q FILE1 FILE2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Suppress file-name headers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;head -v FILE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Always print a file-name header&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;head -n 5 FILE1 - FILE2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read files and standard input in sequence&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="pipelines"&gt;Pipelines &lt;a class="headline-link" href="#pipelines" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Combine &lt;code&gt;head&lt;/code&gt; with other text-processing 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;ls -t | head -n 5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the 5 most recently modified entries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sort -nr scores.txt | head -n 10&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the 10 highest numeric values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;grep 'ERROR' app.log | head -n 20&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the first 20 matching errors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tail -n +20 FILE | head -n 11&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Extract lines 20 through 30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;head -n 20 FILE | wc -w&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Count words in the first 20 lines&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="useful-options"&gt;Useful Options &lt;a class="headline-link" href="#useful-options" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Common GNU &lt;code&gt;head&lt;/code&gt; flags and long forms.&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;-n NUM&lt;/code&gt;, &lt;code&gt;--lines=NUM&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print the first &lt;code&gt;NUM&lt;/code&gt; lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-c NUM&lt;/code&gt;, &lt;code&gt;--bytes=NUM&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print the first &lt;code&gt;NUM&lt;/code&gt; bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-q&lt;/code&gt;, &lt;code&gt;--quiet&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Never print file-name headers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-v&lt;/code&gt;, &lt;code&gt;--verbose&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Always print file-name headers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-z&lt;/code&gt;, &lt;code&gt;--zero-terminated&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read and write NUL-delimited items (GNU)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--help&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show command help&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 the installed version&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Use &lt;code&gt;head -n 5&lt;/code&gt; instead of the obsolete &lt;code&gt;head -5&lt;/code&gt; syntax in scripts.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="card"&gt;
&lt;h3 id="nul-delimited-input"&gt;NUL-Delimited Input &lt;a class="headline-link" href="#nul-delimited-input" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Handle file names containing spaces, newlines, or other special characters.&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;find . -type f -print0 | head -z -n 5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Select the first 5 NUL-delimited paths&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;find . -type f -print0 | head -z -n 5 | xargs -0 -r ls -l&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pass the first 5 paths safely to &lt;code&gt;ls&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;printf 'one\0two\0' | head -z -n 1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Print the first NUL-delimited item&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The &lt;code&gt;-z&lt;/code&gt; option is specific to GNU &lt;code&gt;head&lt;/code&gt;.&lt;/p&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;head&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;Only 10 lines are shown&lt;/td&gt;
&lt;td&gt;This is the default; set the count with &lt;code&gt;-n NUM&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File-name headers appear&lt;/td&gt;
&lt;td&gt;Multiple files trigger headers; add &lt;code&gt;-q&lt;/code&gt; to suppress them&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Binary output looks garbled&lt;/td&gt;
&lt;td&gt;Redirect byte output to a file or inspect it with &lt;code&gt;hexdump&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;head -n -5&lt;/code&gt; fails&lt;/td&gt;
&lt;td&gt;Negative counts are a GNU feature and are not portable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Upstream command reports a broken pipe&lt;/td&gt;
&lt;td&gt;&lt;code&gt;head&lt;/code&gt; exits after collecting enough input; some producers report the closed pipe&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 and cheatsheets for complete text-processing 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-head-command/"&gt;head Command in Linux&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Full guide to lines, bytes, files, and pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/cheatsheet/tail/"&gt;tail Cheatsheet&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Read the end of files and follow logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/cheatsheet/cat/"&gt;cat Cheatsheet&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Display and combine file contents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/cheatsheet/sort/"&gt;sort Cheatsheet&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Order lines before selecting top results&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://linuxize.com/cheatsheet/wc/"&gt;wc Cheatsheet&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Count lines, words, and bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;</content:encoded></item><item><title>lsusb, lspci, and lshw: List Hardware in Linux</title><link>https://linuxize.com/post/list-hardware-in-linux/</link><pubDate>Tue, 21 Jul 2026 09:40:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/list-hardware-in-linux/</guid><category>linux commands</category><description>Identify the hardware in a Linux machine: list USB devices with lsusb, find PCI cards and their drivers with lspci, and get a full inventory with lshw.</description><content:encoded>&lt;p&gt;Sooner or later you need to know exactly what is inside a machine: which Wi-Fi chip refuses to work, whether the system sees a plugged-in USB device at all, or what to put in a purchase order for more RAM. Three commands answer these questions from the terminal, each covering a different part of the hardware inventory: &lt;code&gt;lsusb&lt;/code&gt; for USB devices, &lt;code&gt;lspci&lt;/code&gt; for PCI devices such as network cards and GPUs, and &lt;code&gt;lshw&lt;/code&gt; for a complete inventory of everything.&lt;/p&gt;
&lt;p&gt;This guide explains how to use all three, and which one to reach for depending on the question you are asking.&lt;/p&gt;
&lt;h2 id="installing-the-tools"&gt;Installing the Tools &lt;a class="headline-link" href="#installing-the-tools" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The three commands live in three small packages, most of which are preinstalled on desktop distributions.&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 usbutils pciutils lshw&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 usbutils pciutils lshw&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="listing-usb-devices-with-lsusb"&gt;Listing USB Devices with lsusb &lt;a class="headline-link" href="#listing-usb-devices-with-lsusb" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Run &lt;code&gt;lsusb&lt;/code&gt; without arguments to list every device on the USB buses:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lsusb&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;Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 004: ID 046d:c52b Logitech, Inc. Unifying Receiver
Bus 001 Device 003: ID 13d3:56a6 IMC Networks Integrated Camera
Bus 001 Device 002: ID 8087:0aaa Intel Corp. Bluetooth 9460/9560
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Each line shows the bus and device number, then the ID in &lt;code&gt;vendor:product&lt;/code&gt; form, then a human-readable name looked up from the USB ID database. The &lt;code&gt;1d6b&lt;/code&gt; root hubs are the controllers themselves, not devices you plugged in.&lt;/p&gt;
&lt;p&gt;The classic use is answering &amp;ldquo;does the system see this thing at all&amp;rdquo;: run &lt;code&gt;lsusb&lt;/code&gt;, plug the device in, run it again, and compare. If a new line appears, the system has enumerated the device, so drivers, firmware, and application support are the next places to check. Enumeration does not prove that the device or cable is fully healthy, but it confirms that the USB host received a response. If nothing appears, try another port or cable before blaming the OS. Recent kernel messages from &lt;a href="https://linuxize.com/post/dmesg-command-in-linux/"&gt;&lt;code&gt;dmesg&lt;/code&gt;&lt;/a&gt;
show the same plug events with more detail.&lt;/p&gt;
&lt;p&gt;The tree view adds the piece the flat list lacks, which drivers claimed each device and at what speed it connected:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lsusb -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;/: Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/6p, 10000M
/: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/12p, 480M
|__ Port 4: Dev 2, If 0, Class=Wireless, Driver=btusb, 12M
|__ Port 5: Dev 3, If 0, Class=Video, Driver=uvcvideo, 480M
|__ Port 9: Dev 4, If 2, Class=Human Interface Device, Driver=usbhid, 12M&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A device present in &lt;code&gt;lsusb&lt;/code&gt; but showing no &lt;code&gt;Driver=&lt;/code&gt; entry here has been enumerated but has no kernel driver bound to that interface. That can indicate a missing kernel module or firmware package, although some devices are intentionally controlled by a userspace driver such as libusb. A USB 3 drive showing &lt;code&gt;480M&lt;/code&gt; instead of &lt;code&gt;5000M&lt;/code&gt; is connected through a USB 2 port or cable, a common reason external disks underperform.&lt;/p&gt;
&lt;p&gt;For full descriptors of a single device, filter by its ID and add &lt;code&gt;-v&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 lsusb -v -d 046d:c52b&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The verbose output is long; without root, parts of it are hidden.&lt;/p&gt;
&lt;h2 id="listing-pci-devices-with-lspci"&gt;Listing PCI Devices with lspci &lt;a class="headline-link" href="#listing-pci-devices-with-lspci" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;PCI is where the built-in hardware lives: network controllers, graphics cards, storage controllers, and sound chips. The bare command lists them all:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lspci&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;00:00.0 Host bridge: Intel Corporation Device a706
00:02.0 VGA compatible controller: Intel Corporation Raptor Lake-P [Iris Xe Graphics]
00:14.3 Network controller: Intel Corporation Raptor Lake PCH CNVi WiFi
00:1f.3 Audio device: Intel Corporation Raptor Lake-P/U/H cAVS
01:00.0 Non-Volatile memory controller: Samsung Electronics Co Ltd NVMe SSD Controller PM9A1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The leading &lt;code&gt;00:14.3&lt;/code&gt; style address identifies the device&amp;rsquo;s slot and function, and the text after the class name identifies the chip. This is the fastest way to find out exactly which Wi-Fi or Ethernet chip a machine has, which is the first thing any driver search needs.&lt;/p&gt;
&lt;p&gt;The single most useful option is &lt;code&gt;-k&lt;/code&gt;, which shows the kernel driver bound to each device:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lspci -k&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;00:14.3 Network controller: Intel Corporation Raptor Lake PCH CNVi WiFi
Subsystem: Intel Corporation Device 0094
Kernel driver in use: iwlwifi
Kernel modules: iwlwifi&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;Kernel modules&lt;/code&gt; lists what could drive the device, and &lt;code&gt;Kernel driver in use&lt;/code&gt; shows what actually is. For a device that normally needs a kernel driver, modules listed but no driver in use can indicate missing firmware, a blacklisted module, or a binding problem. Some PCI functions do not need their own driver, so confirm the expected behavior before treating the missing line as an error. On systems with two GPUs, &lt;code&gt;lspci -k&lt;/code&gt; is also the quick way to see which one is bound to which driver.&lt;/p&gt;
&lt;p&gt;Two more options come up regularly. &lt;code&gt;-nn&lt;/code&gt; appends the numeric &lt;code&gt;[vendor:device]&lt;/code&gt; IDs, which are what you paste into a search engine or a driver compatibility list when the text name is ambiguous:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lspci -nn &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;00:14.3 Network controller [0280]: Intel Corporation Raptor Lake PCH CNVi WiFi [8086:51f1]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And &lt;code&gt;-s&lt;/code&gt; narrows the output to one slot, combined with &lt;code&gt;-v&lt;/code&gt; or &lt;code&gt;-vv&lt;/code&gt; for details like memory ranges, capabilities, and the current link speed of the slot:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 lspci -vv -s 01:00.0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id="getting-a-full-inventory-with-lshw"&gt;Getting a Full Inventory with lshw &lt;a class="headline-link" href="#getting-a-full-inventory-with-lshw" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Where &lt;code&gt;lsusb&lt;/code&gt; and &lt;code&gt;lspci&lt;/code&gt; each cover one bus, &lt;code&gt;lshw&lt;/code&gt; walks everything the kernel knows: CPU, memory, firmware, disks, controllers, and the buses in between. Run it as root or the output will be incomplete. The full report is pages long, so start with the summary 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 lshw -short&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;H/W path Device Class Description
===========================================================
system ThinkPad T14 Gen 4
/0/0 memory 64KiB BIOS
/0/4 processor 13th Gen Intel Core i7-1355U
/0/13 memory 32GiB System Memory
/0/13/0 memory 16GiB SODIMM DDR5 5600 MHz
/0/13/1 memory 16GiB SODIMM DDR5 5600 MHz
/0/100/2 display Raptor Lake-P [Iris Xe Graphics]
/0/100/14.3 wlp0s20f3 network Raptor Lake PCH CNVi WiFi
/0/100/1d/0 /dev/nvme0 storage SAMSUNG MZVL21T0HCLR-00B00&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This one screen answers most inventory questions: what model the machine is, which memory modules &lt;code&gt;lshw&lt;/code&gt; detected, and what storage and network hardware is present. The two 16 GiB entries show how the detected memory is populated, but they do not confirm whether the machine has another empty slot. Check the vendor&amp;rsquo;s service manual before planning an upgrade.&lt;/p&gt;
&lt;p&gt;To dig into one category, filter by class:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 lshw -C memory
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo lshw -C network&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The class view for memory may include per-slot details such as speed and part numbers when the system firmware exposes them. For reports, &lt;code&gt;lshw&lt;/code&gt; exports machine-readable and browsable formats:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 lshw -json &amp;gt; hardware.json
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo lshw -sanitize -html &amp;gt; hardware.html&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The JSON output preserves detailed fields for local inventory tooling. Use the sanitized HTML file when sharing a report because &lt;code&gt;-sanitize&lt;/code&gt; removes potentially sensitive values such as serial numbers and IP addresses.&lt;/p&gt;
&lt;p&gt;For completeness: CPU details have their own dedicated tools, covered in our guide on &lt;a href="https://linuxize.com/post/get-cpu-information-on-linux/"&gt;getting CPU information on Linux&lt;/a&gt;
, and &lt;code&gt;sudo dmidecode&lt;/code&gt; reads the firmware&amp;rsquo;s DMI tables directly when you need BIOS versions or serial numbers that &lt;code&gt;lshw&lt;/code&gt; summarizes.&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;List USB devices&lt;/td&gt;
&lt;td&gt;&lt;code&gt;lsusb&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;USB tree with drivers and speeds&lt;/td&gt;
&lt;td&gt;&lt;code&gt;lsusb -t&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Full descriptors for one USB device&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo lsusb -v -d 046d:c52b&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List PCI devices&lt;/td&gt;
&lt;td&gt;&lt;code&gt;lspci&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PCI devices with kernel drivers&lt;/td&gt;
&lt;td&gt;&lt;code&gt;lspci -k&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PCI devices with numeric IDs&lt;/td&gt;
&lt;td&gt;&lt;code&gt;lspci -nn&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Details for one PCI slot&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo lspci -vv -s 01:00.0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hardware summary table&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo lshw -short&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One hardware class in detail&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo lshw -C network&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Machine-readable inventory&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo lshw -json&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sanitized HTML report&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sudo lshw -sanitize -html&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;lsusb&lt;/code&gt; confirms detection, &lt;code&gt;lspci -k&lt;/code&gt; connects chips to drivers, and &lt;code&gt;lshw&lt;/code&gt; turns a closed case into a readable inventory; between the three, &amp;ldquo;what hardware is this and is it working&amp;rdquo; stops requiring a screwdriver. When a device shows up in these listings but misbehaves, the kernel log usually explains why, so &lt;a href="https://linuxize.com/post/dmesg-command-in-linux/"&gt;&lt;code&gt;dmesg&lt;/code&gt;&lt;/a&gt;
is the natural next stop.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/list-hardware-in-linux/featured_hu_da0040808dad9163.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>How to Comment Out Multiple Lines in Vim</title><link>https://linuxize.com/post/vim-comment-multiple-lines/</link><pubDate>Mon, 20 Jul 2026 11:20:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/vim-comment-multiple-lines/</guid><category>vim</category><description>Comment out multiple lines in Vim with visual block mode, line ranges, or bundled gc mappings, then remove comments with the matching commands.</description><content:encoded>&lt;p&gt;Commenting out a block of lines is one of those edits you make constantly: disabling a section of a config file, switching off a function while debugging, or keeping an old approach around while testing a new one. In most editors that is select plus a single shortcut. Classic Vim has no comment shortcut out of the box, but it has three techniques that are just as fast once you know them, and recent versions ship a toggle plugin as well.&lt;/p&gt;
&lt;p&gt;This guide shows how to comment out multiple lines in Vim with visual block mode, the substitute command, and the built-in comment plugin, and how to remove the comments again with each method.&lt;/p&gt;
&lt;h2 id="commenting-with-visual-block-mode"&gt;Commenting with Visual Block Mode &lt;a class="headline-link" href="#commenting-with-visual-block-mode" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The classic answer uses visual block mode to insert a comment character at the start of every selected line:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Move the cursor to the first column of the first line you want to comment.&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;Ctrl+V&lt;/code&gt; to enter visual block mode.&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;j&lt;/code&gt; (or the down arrow) until every target line is selected.&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;Shift+I&lt;/code&gt; to insert at the left edge of the block.&lt;/li&gt;
&lt;li&gt;Type the comment character, &lt;code&gt;#&lt;/code&gt; for shell scripts and config files.&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;Esc&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;On Windows, &lt;code&gt;Ctrl+V&lt;/code&gt; may be mapped to paste. If it does not start visual block mode, use &lt;code&gt;Ctrl+Q&lt;/code&gt; instead.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;#&lt;/code&gt; appears on the first line while you type, which looks like the operation failed. It did not: the moment you press &lt;code&gt;Esc&lt;/code&gt;, Vim repeats the insert on every line of the block.&lt;/p&gt;
&lt;p&gt;For languages with two-character comments, type the whole prefix in step 5, for example &lt;code&gt;//&lt;/code&gt; for C, JavaScript, and Go, or &lt;code&gt;--&lt;/code&gt; for SQL and Lua. The entire string is inserted on each line.&lt;/p&gt;
&lt;p&gt;Because this selection starts in the first column, &lt;code&gt;I&lt;/code&gt; prefixes every selected line, including empty lines. If you start the block farther to the right, Vim skips lines that end before the block&amp;rsquo;s left edge. Use the substitute method below when you want to prefix a line range regardless of line length.&lt;/p&gt;
&lt;h2 id="uncommenting-with-visual-block-mode"&gt;Uncommenting with Visual Block Mode &lt;a class="headline-link" href="#uncommenting-with-visual-block-mode" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The reverse uses the same block selection to delete a column of characters:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Place the cursor on the first comment character of the first line.&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;Ctrl+V&lt;/code&gt;, then &lt;code&gt;j&lt;/code&gt; down to the last commented line.&lt;/li&gt;
&lt;li&gt;If the comment prefix is longer than one character, press &lt;code&gt;l&lt;/code&gt; once for each additional character to widen the block over the whole prefix.&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;x&lt;/code&gt; or &lt;code&gt;d&lt;/code&gt; to delete the selected block on every line.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The highlighted column disappears from all lines at once. If you also inserted a space after the comment character, include it in the block before deleting.&lt;/p&gt;
&lt;h2 id="commenting-with-the-substitute-command"&gt;Commenting with the Substitute Command &lt;a class="headline-link" href="#commenting-with-the-substitute-command" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The substitute command edits a range of lines in one pass, which makes it the better tool when you already know the line numbers or when the block is large. To comment lines 10 through 20:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="vi"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-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;vi&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-vi" data-lang="vi"&gt;:10,20s/^/#/&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The range &lt;code&gt;10,20&lt;/code&gt; limits the substitution to those lines, &lt;code&gt;^&lt;/code&gt; matches the empty start of each line, and the replacement inserts &lt;code&gt;#&lt;/code&gt; there. Unlike block insert, this prefixes every line in the range, including blank ones.&lt;/p&gt;
&lt;p&gt;You can also select the lines first with &lt;code&gt;V&lt;/code&gt; and a movement, then press &lt;code&gt;:&lt;/code&gt;. Vim inserts the &lt;code&gt;'&amp;lt;,'&amp;gt;&lt;/code&gt; range for you, and you complete 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="vi"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-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;vi&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-vi" data-lang="vi"&gt;:&amp;#39;&amp;lt;,&amp;#39;&amp;gt;s/^/#/&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Other ranges work the same way: &lt;code&gt;%&lt;/code&gt; for the whole file and &lt;code&gt;.,+4&lt;/code&gt; for the current line and the four below it. If you use these often, turning on line numbers makes ranges much quicker to read off the screen; see &lt;a href="https://linuxize.com/post/how-to-show-line-numbers-in-vim/"&gt;How to Show Line Numbers in Vim&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;To uncomment, delete the leading character instead of inserting 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="vi"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-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;vi&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-vi" data-lang="vi"&gt;:10,20s/^#//&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This removes the first &lt;code&gt;#&lt;/code&gt; of each line in the range and leaves lines without one untouched. For a two-character prefix such as &lt;code&gt;//&lt;/code&gt;, escaping the slashes gets noisy, so switch the delimiter to another punctuation character:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="vi"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-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;vi&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-vi" data-lang="vi"&gt;:&amp;#39;&amp;lt;,&amp;#39;&amp;gt;s#^//##&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The substitute command accepts almost any punctuation as its separator, and using &lt;code&gt;#&lt;/code&gt; here keeps the &lt;code&gt;//&lt;/code&gt; readable. The full syntax, including flags and confirmation mode, is covered in &lt;a href="https://linuxize.com/post/vim-find-replace/"&gt;Vim Find and Replace&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="keeping-indentation-with-normal"&gt;Keeping Indentation with :normal &lt;a class="headline-link" href="#keeping-indentation-with-normal" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Both methods above put the comment character in column one. To insert it in front of the first non-blank character instead, preserving the code&amp;rsquo;s indentation profile, run a normal mode command over the range:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="vi"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-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;vi&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-vi" data-lang="vi"&gt;:&amp;#39;&amp;lt;,&amp;#39;&amp;gt;norm I#&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;:norm&lt;/code&gt; command replays its argument as keystrokes on every line in the range, and &lt;code&gt;I&lt;/code&gt; in normal mode enters insert mode at the first non-blank character, so each line gets &lt;code&gt;#&lt;/code&gt; right before its content rather than at the margin. Undo treats the whole operation as a single change, so &lt;code&gt;u&lt;/code&gt; reverts every line at once.&lt;/p&gt;
&lt;h2 id="toggling-comments-with-gc"&gt;Toggling Comments with gc &lt;a class="headline-link" href="#toggling-comments-with-gc" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Modern Vim and Neovim can toggle comments with a mapping that knows each filetype&amp;rsquo;s comment syntax. Neovim 0.10 and later has it built in with no setup: select lines with &lt;code&gt;V&lt;/code&gt; and press &lt;code&gt;gc&lt;/code&gt;, or use &lt;code&gt;gcc&lt;/code&gt; for the current line and &lt;code&gt;gc&lt;/code&gt; followed by a motion, such as &lt;code&gt;gcip&lt;/code&gt; for a paragraph. Pressing &lt;code&gt;gc&lt;/code&gt; on commented lines uncomments them.&lt;/p&gt;
&lt;p&gt;Vim 9.1.0375 and later bundles a similar optional comment package. Enable it for the current session with &lt;code&gt;:packadd comment&lt;/code&gt;, or add this line to your vimrc to load it on startup:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="vim"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-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;~/.vimrc&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;vim&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-vim" data-lang="vim"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;packadd&lt;/span&gt; &lt;span class="nx"&gt;comment&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 loading the package, the &lt;code&gt;gc&lt;/code&gt; mappings work as described above and pick the right comment string automatically: &lt;code&gt;#&lt;/code&gt; in a shell script, &lt;code&gt;//&lt;/code&gt; in C, and &lt;code&gt;&amp;quot;&lt;/code&gt; in a vimrc. On older Vim versions, the widely used &lt;a href="https://github.com/tpope/vim-commentary" target="_blank" rel="noopener noreferrer"&gt;commentary.vim&lt;/a&gt;
plugin provides the same familiar mappings.&lt;/p&gt;
&lt;p&gt;If you comment code many times a day, this is the method worth adopting, since toggling with &lt;code&gt;gc&lt;/code&gt; replaces both the comment and uncomment workflows in one mapping.&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/vim/"&gt;Vim 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;Keys or command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Comment a block&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Ctrl+V&lt;/code&gt;, select lines, &lt;code&gt;Shift+I&lt;/code&gt;, type &lt;code&gt;#&lt;/code&gt;, &lt;code&gt;Esc&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Uncomment a block&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Ctrl+V&lt;/code&gt;, select the comment column, &lt;code&gt;x&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Comment lines 10-20&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:10,20s/^/#/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Comment a visual selection&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:'&amp;lt;,'&amp;gt;s/^/#/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Uncomment lines 10-20&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:10,20s/^#//&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Comment after indentation&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:'&amp;lt;,'&amp;gt;norm I#&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Toggle comments (Neovim 0.10+, Vim 9.1.0375+)&lt;/td&gt;
&lt;td&gt;select with &lt;code&gt;V&lt;/code&gt;, press &lt;code&gt;gc&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Toggle the current line&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gcc&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Undo the whole operation&lt;/td&gt;
&lt;td&gt;&lt;code&gt;u&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;For an occasional block, &lt;code&gt;Ctrl+V&lt;/code&gt;, &lt;code&gt;Shift+I&lt;/code&gt;, &lt;code&gt;Esc&lt;/code&gt; is the fastest habit to build, and &lt;code&gt;:s/^/#/&lt;/code&gt; handles precise ranges and blank lines. If commenting is part of your daily editing, enable the &lt;code&gt;gc&lt;/code&gt; mappings and get toggling in both directions. For selecting larger regions to operate on, see &lt;a href="https://linuxize.com/post/vim-select-all/"&gt;How to Select All in Vim&lt;/a&gt;
.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/vim-comment-multiple-lines/featured_hu_a720d88355e6c44c.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>What Is an Inode in Linux</title><link>https://linuxize.com/post/what-is-an-inode-in-linux/</link><pubDate>Sun, 19 Jul 2026 09:35:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/what-is-an-inode-in-linux/</guid><category>disk</category><description>An inode stores Linux file metadata, not its name or data. See how inode numbers and hard links work, then check inode usage with ls, stat, and df.</description><content:encoded>&lt;p&gt;Running out of disk space normally means that a filesystem has no free bytes left. Sometimes, however, a command fails with &amp;ldquo;No space left on device&amp;rdquo; while &lt;a href="https://linuxize.com/post/how-to-check-disk-space-in-linux-using-the-df-command/"&gt;&lt;code&gt;df -h&lt;/code&gt;&lt;/a&gt;
still shows available space. One possible cause is inode exhaustion: the filesystem can store more data, but it has no free metadata records for new files. Our guide on &lt;a href="https://linuxize.com/post/fix-no-space-left-on-device/"&gt;fixing &amp;ldquo;No space left on device&amp;rdquo; when df shows free space&lt;/a&gt;
walks through that check and the other causes of the same error.&lt;/p&gt;
&lt;p&gt;To understand why this happens, and why filenames and file contents are stored separately, you need to know what an inode is. This guide explains what an inode stores, how it relates to filenames and hard links, and how to check inode usage.&lt;/p&gt;
&lt;h2 id="what-an-inode-is"&gt;What an Inode Is &lt;a class="headline-link" href="#what-an-inode-is" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;An inode (short for &amp;ldquo;index node&amp;rdquo;) is a data structure that a Unix-style filesystem uses to store metadata about a file or directory. Each file has an inode number that identifies it within that filesystem. The same number can appear on another filesystem, so an inode number is not unique across the entire system.&lt;/p&gt;
&lt;p&gt;The inode holds almost everything the system needs to know about the file, except for its name and, in the usual case, the data itself. It records:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The file type, such as a regular file, directory, or symbolic link&lt;/li&gt;
&lt;li&gt;The permissions and owner and group IDs&lt;/li&gt;
&lt;li&gt;The file size and allocated blocks&lt;/li&gt;
&lt;li&gt;The access, data modification, and status change timestamps&lt;/li&gt;
&lt;li&gt;The hard-link count, which is the number of directory entries pointing to the file&lt;/li&gt;
&lt;li&gt;The block map or extents that locate the file&amp;rsquo;s data&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The exact on-disk format depends on the filesystem, but these fields are the ones you see through tools such as &lt;code&gt;ls&lt;/code&gt; and &lt;code&gt;stat&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="filenames-are-separate-from-inodes"&gt;Filenames Are Separate from Inodes &lt;a class="headline-link" href="#filenames-are-separate-from-inodes" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The detail that surprises many users is that the filename is not stored in the inode. A directory contains entries that map names to inode numbers. When you open &lt;code&gt;report.txt&lt;/code&gt;, the system looks up that name in the directory, finds the inode number, reads the inode, and locates the file&amp;rsquo;s data.&lt;/p&gt;
&lt;p&gt;This separation is what makes hard links possible. A hard link is another directory entry that points to the same inode. Both names are equal, and removing one name only decreases the inode&amp;rsquo;s link count. The filesystem releases the file&amp;rsquo;s storage after the last link is removed and no process still has the file open.&lt;/p&gt;
&lt;p&gt;For a practical comparison of the two link types, see &lt;a href="https://linuxize.com/post/hard-links-vs-symbolic-links/"&gt;hard links vs symbolic links&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="viewing-inode-numbers"&gt;Viewing Inode Numbers &lt;a class="headline-link" href="#viewing-inode-numbers" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To see the inode number of a file, use the &lt;code&gt;-i&lt;/code&gt; option with &lt;code&gt;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;ls -i report.txt&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;1310720 report.txt&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The first column, &lt;code&gt;1310720&lt;/code&gt;, is the inode number. To list inode numbers for everything in a directory, run &lt;code&gt;ls -li&lt;/code&gt;, which adds the inode number as the leftmost column of the long listing.&lt;/p&gt;
&lt;p&gt;To display the metadata associated with the inode, use the &lt;a href="https://linuxize.com/post/stat-command-in-linux/"&gt;&lt;code&gt;stat&lt;/code&gt;&lt;/a&gt;
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;stat report.txt&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; File: report.txt
Size: 482 Blocks: 8 IO Block: 4096 regular file
Device: 8,1 Inode: 1310720 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 1000/ linuxize) Gid: ( 1000/ linuxize)
Access: 2026-01-01 14:02:11.000000000 +0100
Modify: 2026-01-01 13:55:40.000000000 +0100
Change: 2026-01-01 13:55:40.000000000 +0100
Birth: 2026-01-01 13:55:40.000000000 +0100&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;Inode&lt;/code&gt; field shows the inode number, and &lt;code&gt;Links&lt;/code&gt; shows the hard-link count. &lt;code&gt;Change&lt;/code&gt; is the status change time, commonly called ctime. It changes when inode metadata such as permissions, ownership, or the link count changes. It is not the file creation time. The separate &lt;code&gt;Birth&lt;/code&gt; field shows creation time when the filesystem supports it.&lt;/p&gt;
&lt;p&gt;The filename appears at the top because you passed it to &lt;code&gt;stat&lt;/code&gt;; it is not part of the inode.&lt;/p&gt;
&lt;h2 id="how-filesystems-allocate-inodes"&gt;How Filesystems Allocate Inodes &lt;a class="headline-link" href="#how-filesystems-allocate-inodes" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Inode allocation depends on the filesystem. Ext2, ext3, and ext4 create inode tables when the filesystem is formatted. This gives the filesystem a fixed inode density, so a workload that creates millions of small files can use every inode before it uses every data block.&lt;/p&gt;
&lt;p&gt;You cannot change the inode density of an existing ext4 filesystem in place. Growing the filesystem can add block groups and more inodes, but if the filesystem cannot be expanded, the practical options are to remove unneeded files, move the workload, or recreate the filesystem with a higher inode count.&lt;/p&gt;
&lt;p&gt;XFS and Btrfs allocate inodes dynamically from available filesystem space instead of creating one fixed inode table at format time. They can still reject new files when the filesystem runs out of usable data or metadata space, but they do not have the same fixed inode-count limit as ext4.&lt;/p&gt;
&lt;h2 id="checking-inode-usage"&gt;Checking Inode Usage &lt;a class="headline-link" href="#checking-inode-usage" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To see how many inodes the filesystem containing &lt;code&gt;/var&lt;/code&gt; has and how many are free, pass the path to &lt;code&gt;df&lt;/code&gt; with the &lt;code&gt;-i&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;df -i /var&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;Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sdb1 655360 655360 0 100% /var&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;IUse%&lt;/code&gt; column is the one to watch. Here, inode usage on &lt;code&gt;/var&lt;/code&gt; is at 100 percent even though the byte-based &lt;code&gt;df -h /var&lt;/code&gt; might still report free space. The filesystem cannot create another file until an inode becomes available.&lt;/p&gt;
&lt;p&gt;A large cache, session directory, mail queue, or temporary directory is a common source of inode exhaustion. GNU &lt;code&gt;du&lt;/code&gt; can count inodes instead of bytes. The following command stays on the &lt;code&gt;/var&lt;/code&gt; filesystem, checks two directory levels, and displays the largest totals at the bottom:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="bash" data-prompt="$"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;polyline points="4 17 10 11 4 5"&gt;&lt;/polyline&gt;
&lt;line x1="12" y1="19" x2="20" y2="19"&gt;&lt;/line&gt;
&lt;/svg&gt;
&lt;span class="text-sm text-gray-600 dark:text-slate-400 font-medium"&gt;Terminal&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre 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 du --inodes -x -d &lt;span class="m"&gt;2&lt;/span&gt; /var 2&amp;gt;/dev/null &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; sort -n &lt;span class="p"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; tail -20&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Repeat the command on the directory with the highest count and increase the depth if needed. For other counting methods, see the guide on &lt;a href="https://linuxize.com/post/count-files-in-directory-on-linux/"&gt;counting files in a directory&lt;/a&gt;
.&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 remove files only because a directory has a high inode count. First identify which application owns the files and use its cleanup or retention policy when one is available.&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;After cleanup, run &lt;code&gt;df -i /var&lt;/code&gt; again to confirm that free inodes are available.&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;An inode connects a file&amp;rsquo;s metadata to its stored data while the directory keeps the filename separately. When a filesystem reports &amp;ldquo;No space left on device&amp;rdquo; despite having free bytes, check the affected path with &lt;code&gt;df -i&lt;/code&gt; before deciding what to remove.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/what-is-an-inode-in-linux/featured_hu_3fba85c440f62f6d.webp" medium="image" type="image/webp" width="1200" height="675"/></item><item><title>How to Select All in Vim: Copy, Delete, or Yank Every Line</title><link>https://linuxize.com/post/vim-select-all/</link><pubDate>Sat, 18 Jul 2026 09:15:00 +0200</pubDate><author>hello@linuxize.com (Linuxize)</author><guid>https://linuxize.com/post/vim-select-all/</guid><category>vim</category><description>Select all text in Vim with ggVG, yank every line with :%y, copy the whole file to the system clipboard, or delete all lines with :%d. No plugins required.</description><content:encoded>&lt;p&gt;Sooner or later you will need to grab the entire contents of a file open in Vim, whether to paste a config file into a support ticket, move a script into another editor, or wipe everything and start over. In most editors that is &lt;code&gt;Ctrl+A&lt;/code&gt; followed by &lt;code&gt;Ctrl+C&lt;/code&gt;. Vim does not work that way, and pressing &lt;code&gt;Ctrl+A&lt;/code&gt; increments the next number at or after the cursor instead of selecting text.&lt;/p&gt;
&lt;p&gt;The quickest way to select all text in Vim is the &lt;code&gt;ggVG&lt;/code&gt; command sequence in normal mode. This guide explains how it works and how to copy, delete, or run other commands on every line in the file, including copying the whole file to the system clipboard.&lt;/p&gt;
&lt;h2 id="selecting-all-text-with-ggvg"&gt;Selecting All Text with ggVG &lt;a class="headline-link" href="#selecting-all-text-with-ggvg" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To select all text in Vim, make sure you are in normal mode (press &lt;code&gt;Esc&lt;/code&gt; if you are not), then type &lt;code&gt;ggVG&lt;/code&gt;. The sequence is three separate commands executed one after another:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;gg&lt;/code&gt; - Move the cursor to the first line of the file&lt;/li&gt;
&lt;li&gt;&lt;code&gt;V&lt;/code&gt; - Start line-wise visual mode&lt;/li&gt;
&lt;li&gt;&lt;code&gt;G&lt;/code&gt; - Jump to the last line, extending the selection over the whole file&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Every line in the file is now highlighted. From here, Vim waits for an operator: press &lt;code&gt;y&lt;/code&gt; to yank (copy) the selection, &lt;code&gt;d&lt;/code&gt; to delete it, or any other visual mode command. To cancel the selection without doing anything, press &lt;code&gt;Esc&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="copying-yanking-all-lines"&gt;Copying (Yanking) All Lines &lt;a class="headline-link" href="#copying-yanking-all-lines" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;With the whole file selected, press &lt;code&gt;y&lt;/code&gt; to yank it into Vim&amp;rsquo;s default register. The full sequence is &lt;code&gt;ggVGy&lt;/code&gt;. You can then paste the text elsewhere in the same Vim session with &lt;code&gt;p&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Visual mode is not required, though. If you already know you want to copy everything, the ex command &lt;code&gt;:%y&lt;/code&gt; does the same thing without touching the selection:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="vi"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-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;vi&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-vi" data-lang="vi"&gt;:%y&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;%&lt;/code&gt; range means &amp;ldquo;all lines&amp;rdquo;, so &lt;code&gt;:%y&lt;/code&gt; yanks the entire file into the default register in one step. The normal mode equivalent is &lt;code&gt;ggyG&lt;/code&gt;, which yanks from the first line to the last without entering visual mode.&lt;/p&gt;
&lt;p&gt;To duplicate the whole file below itself, run &lt;code&gt;:%y&lt;/code&gt; followed by &lt;code&gt;G&lt;/code&gt; and &lt;code&gt;p&lt;/code&gt;: yank all lines, jump to the end, and paste.&lt;/p&gt;
&lt;h2 id="copying-all-lines-to-the-system-clipboard"&gt;Copying All Lines to the System Clipboard &lt;a class="headline-link" href="#copying-all-lines-to-the-system-clipboard" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Yanked text normally stays inside Vim. To paste the file into a browser, email, or another application, copy it to the system clipboard using the &lt;code&gt;&amp;quot;+&lt;/code&gt; register:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ggVG&amp;quot;+y&lt;/code&gt; - Select all, then yank the selection to the system clipboard&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gg&amp;quot;+yG&lt;/code&gt; - Yank all lines to the system clipboard without visual mode&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:%y+&lt;/code&gt; - Yank all lines to the system clipboard with an ex command&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After any of these, paste into the other application with the regular paste shortcut for your desktop environment.&lt;/p&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 &lt;code&gt;&amp;quot;+&lt;/code&gt; register requires Vim compiled with the &lt;code&gt;+clipboard&lt;/code&gt; feature. Check with &lt;code&gt;vim --version | grep clipboard&lt;/code&gt;. If the output shows &lt;code&gt;-clipboard&lt;/code&gt;, install a build with clipboard support, such as &lt;code&gt;vim-gtk3&lt;/code&gt; on Debian and Ubuntu systems.&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;If installing another Vim build is not an option, you can pipe the buffer to a clipboard utility instead. On X11 systems with &lt;code&gt;xclip&lt;/code&gt; 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="vi"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-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;vi&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-vi" data-lang="vi"&gt;:%w !xclip -selection clipboard&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This writes the whole buffer to the &lt;code&gt;xclip&lt;/code&gt; command, which places it on the clipboard. On Wayland, use &lt;code&gt;wl-copy&lt;/code&gt; from the &lt;code&gt;wl-clipboard&lt;/code&gt; package 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="vi"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-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;vi&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-vi" data-lang="vi"&gt;:%w !wl-copy&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For more on registers and clipboard integration, see &lt;a href="https://linuxize.com/post/how-to-copy-cut-paste-in-vim/"&gt;How to Copy, Cut and Paste in Vim&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="deleting-all-lines"&gt;Deleting All Lines &lt;a class="headline-link" href="#deleting-all-lines" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To select everything and delete it, use &lt;code&gt;ggVGd&lt;/code&gt;, or skip visual mode with &lt;code&gt;ggdG&lt;/code&gt; (move to the first line, then delete to the last line). The fastest option is again the ex command with the &lt;code&gt;%&lt;/code&gt; range:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="vi"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-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;vi&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-vi" data-lang="vi"&gt;:%d&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;All lines are removed and the file is left with a single empty line. The deleted text lands in the default register, so an accidental &lt;code&gt;:%d&lt;/code&gt; is easy to reverse: press &lt;code&gt;u&lt;/code&gt; to &lt;a href="https://linuxize.com/post/vim-undo-redo/"&gt;undo&lt;/a&gt;
, or paste the text back with &lt;code&gt;p&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To delete every line without replacing the current contents of the unnamed register, send the deleted text to the black hole register:&lt;/p&gt;
&lt;div class="code-block relative my-4 rounded-lg overflow-hidden border border-gray-200 dark:border-slate-700" data-lang="vi"&gt;
&lt;div class="code-header flex items-center justify-between px-4 py-2 bg-gray-50 dark:bg-slate-800/80 border-b border-gray-200 dark:border-slate-700"&gt;
&lt;div class="flex items-center gap-2"&gt;&lt;svg class="w-4 h-4 text-gray-500 dark:text-slate-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"&gt;
&lt;path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-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;vi&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code class="language-vi" data-lang="vi"&gt;:%d _&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This preserves whatever was already available for pasting with &lt;code&gt;p&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For ranges, patterns, and more deletion commands, see &lt;a href="https://linuxize.com/post/vim-delete-line/"&gt;How to Delete Lines in Vim&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id="running-other-commands-on-the-whole-file"&gt;Running Other Commands on the Whole File &lt;a class="headline-link" href="#running-other-commands-on-the-whole-file" aria-hidden="true"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Selecting all text is not limited to copy and delete. With &lt;code&gt;ggVG&lt;/code&gt; active, any visual mode operator applies to the entire file:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ggVG=&lt;/code&gt; - Re-indent every line (same as &lt;code&gt;gg=G&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ggVGu&lt;/code&gt; - Convert the whole file to lowercase&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ggVGU&lt;/code&gt; - Convert the whole file to uppercase&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ggVG&amp;gt;&lt;/code&gt; - Shift every line one indent level to the right&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For find and replace across the whole file, you do not need a selection at all: the substitute command takes the same &lt;code&gt;%&lt;/code&gt; range, as in &lt;code&gt;:%s/old/new/g&lt;/code&gt;. See &lt;a href="https://linuxize.com/post/vim-find-replace/"&gt;Vim Find and Replace&lt;/a&gt;
for the full syntax.&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/vim/"&gt;Vim 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;ggVG&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Select all lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ggVGy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Select all and yank (copy)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ggVGd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Select all and delete&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ggVG&amp;quot;+y&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Select all and copy to the system clipboard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ggyG&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yank all lines without visual mode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ggdG&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete all lines without visual mode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;:%y&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yank all lines (ex command)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;:%y+&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yank all lines to the system clipboard&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 all lines&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 all lines without replacing the unnamed register&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gg=G&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Re-indent the whole file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;:%s/old/new/g&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Replace across the whole file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;u&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Undo the last change&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;Use &lt;code&gt;ggVG&lt;/code&gt; when you want to see the whole-file selection before choosing an operator. When the action is already clear, the &lt;code&gt;%&lt;/code&gt; range is shorter, as in &lt;code&gt;:%y&lt;/code&gt;, &lt;code&gt;:%d&lt;/code&gt;, or &lt;code&gt;:%s/old/new/g&lt;/code&gt;. For selecting a smaller block to comment out, see &lt;a href="https://linuxize.com/post/vim-comment-multiple-lines/"&gt;How to Comment Out Multiple Lines in Vim&lt;/a&gt;
.&lt;/p&gt;</content:encoded><media:content url="https://linuxize.com/post/vim-select-all/featured_hu_5dde98562a666b1b.webp" medium="image" type="image/webp" width="1200" height="675"/></item></channel></rss>