<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: aL</title>
    <description>The latest articles on DEV Community by aL (@dev-charodeyka).</description>
    <link>https://dev.to/dev-charodeyka</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2357370%2Fd04ecece-cafa-4a19-9ae8-6d943bdb2555.png</url>
      <title>DEV Community: aL</title>
      <link>https://dev.to/dev-charodeyka</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dev-charodeyka"/>
    <language>en</language>
    <item>
      <title>OpenWrt: Your Router, Your Rules - Part 2: WAN Interface Configuration with LuCI</title>
      <dc:creator>aL</dc:creator>
      <pubDate>Tue, 28 Jul 2026 18:33:17 +0000</pubDate>
      <link>https://dev.to/dev-charodeyka/openwrt-your-router-your-rules-part-2-wan-interface-configuration-with-luci-37h0</link>
      <guid>https://dev.to/dev-charodeyka/openwrt-your-router-your-rules-part-2-wan-interface-configuration-with-luci-37h0</guid>
      <description>&lt;p&gt;&lt;em&gt;A quick recap:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/dev-charodeyka/openwrt-your-router-your-rules-part-0-your-router-your-right-j5j"&gt;Part 0&lt;/a&gt; of this series focused on the legal side of the story. According to European regulations on Internet access equipment, in most cases, &lt;em&gt;users have the right to use their own equipment (routers, gateways ecc) instead of the one supplied by their ISP&lt;/em&gt;. In that part I also clarified &lt;strong&gt;the roles&lt;/strong&gt; of the &lt;strong&gt;ONT&lt;/strong&gt;, modem, and &lt;strong&gt;router&lt;/strong&gt;, as these terms are often used interchangeably despite referring to different devices.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/dev-charodeyka/openwrt-your-router-your-rules-part-05-nat-and-cgnat-203m"&gt;Part 0.5&lt;/a&gt; I introduced the networking concepts needed for this guide, including IP address leasing, DHCP, Network Address Translation (NAT), and Carrier-Grade NAT (CGNAT).&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/dev-charodeyka/openwrt-your-router-your-rules-part1-keep-or-bypass-your-isps-router-9h9"&gt;Part 1&lt;/a&gt; I introduced 2 approaches to integrating an OpenWrt router into a home network: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;placing it behind the ISP's router&lt;/li&gt;
&lt;li&gt;replacing the ISP's router entirely &amp;lt;-- &lt;strong&gt;setup in this article&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Also, I explained in details MAC addresses - another way of identifying network interfaces besides IP addresses, which will be important during the configuration process covered in &lt;em&gt;this part&lt;/em&gt;. Moreover, in that part the difference between WAN and LAN ports was explained.&lt;/p&gt;




&lt;p&gt;Starting point of this article: my OpenWrt router, a &lt;a href="https://www.gl-inet.com/en-de/products/gl-ax1800" rel="noopener noreferrer"&gt;GL.iNet Flint (GL-AX1800)&lt;/a&gt;, is connected exactly as it should be, but it has no Internet connectivity.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;NB! From this point on, I'll simply refer to my OpenWrt router as &lt;strong&gt;Flint&lt;/strong&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;More details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The ISP's router has been disconnected and replaced by my OpenWrt  Flint router.&lt;/li&gt;
&lt;li&gt;The ISP's ONT is connected to the router's &lt;strong&gt;WAN&lt;/strong&gt; port using an Ethernet cable.&lt;/li&gt;
&lt;li&gt;The ISP's ONT converts the optical fibre signal into Ethernet (and vice versa). It does not perform routing, NAT, or DHCP ecc.&lt;/li&gt;
&lt;li&gt;From the router's perspective, the first network device it communicates with is the ISP's equipment beyond the ONT. This is the upstream peer for the &lt;code&gt;wan&lt;/code&gt; interface.&lt;/li&gt;
&lt;li&gt;Flint provides a local LAN and Wi-Fi network.&lt;/li&gt;
&lt;li&gt;Out-of-the-box, the router has &lt;strong&gt;no Internet connectivity&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Scope of this article:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configure the Flint's &lt;code&gt;wan&lt;/code&gt; interface via LuCI according to the ISP's requirements.&lt;/li&gt;
&lt;li&gt;Fix Internet connectivity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Glossary:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://github.com/openwrt/luci/wiki/Installation" rel="noopener noreferrer"&gt;LuCI&lt;/a&gt; is the web user interface of OpenWrt&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;LAN (Local Area Network) – the private network created by your OpenWrt router. Devices on that LAN use private IP address ranges (such as &lt;code&gt;192.168.x.x&lt;/code&gt;, &lt;code&gt;10.x.x.x&lt;/code&gt;, or &lt;code&gt;172.16.x.x/12&lt;/code&gt;)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;WAN (Wide Area Network) – the ISP - managed network that your OpenWrt router connects to in order to gain access to the Internet&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;DHCP (Dynamic Host Configuration Protocol) server - a network server that automatically provides and assigns IP addresses, default gateways and other network parameters to client devices&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Connecting to the router
&lt;/h3&gt;

&lt;p&gt;To do anything with your OpenWrt router, you first need to access the router itself. OpenWrt routers ship with a default LAN configuration that allows you to join the local network created by them. Looking at the user manual for my Flint, I can identify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the default IP address of the router,&lt;/li&gt;
&lt;li&gt;the default Wi-Fi network name (SSID),&lt;/li&gt;
&lt;li&gt;and the default Wi-Fi password.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are usually included in the shortened version of user manual or are elencati on the sticker on the bottom or back of your router.&lt;/p&gt;

&lt;p&gt;I want to configure my Flint's WAN interface through the LuCI web interface. To access my router's LuCI, I first need to join the network created by Flint.&lt;/p&gt;

&lt;h4&gt;
  
  
  1.1 Joining the router's network
&lt;/h4&gt;

&lt;p&gt;There are two ways to do this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using an Ethernet cable. The classic way. I connect my laptop to one of the router's LAN ports with an Ethernet cable.&lt;/li&gt;
&lt;li&gt;Using Wi-Fi. I connect to the Wi-Fi network broadcast by the router using the default Wi-Fi password printed on the router's label.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My Flint's administration panel (web UI) is available at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://192.168.8.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But simply knowing this address is not enough. To reach it, my device must be part of the same IP network. &lt;/p&gt;

&lt;h4&gt;
  
  
  1.2 Getting an IP address in router's LAN
&lt;/h4&gt;

&lt;p&gt;My Flint router runs a DHCP server out-of-the-box, so when I connect my laptop (whether by Ethernet or Wi-Fi) Flint automatically assigns an IP address to the device* I'm using (my laptop).&lt;/p&gt;

&lt;h4&gt;
  
  
  Theory: How one PC/laptop joins multiple networks
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;Note on "&lt;em&gt;": The IP address is not assigned to the laptop! It is assigned to the laptop's **network device&lt;/em&gt;* that I use to connect to my router's network. My laptop has two &lt;em&gt;network-capable devices&lt;/em&gt;: an Ethernet adapter and a Wi-Fi adapter. To join my Flint's network over an Ethernet cable, I use my laptop's &lt;em&gt;Ethernet network interface&lt;/em&gt;, which receives an IP address from Flint's DHCP server. At the same time, the wireless network interface remains completely independent and can stay connected to a different network (for example, a mobile hotspot) with Internet access while I'm configuring Flint.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once connected to Flint's &lt;strong&gt;LAN 1&lt;/strong&gt; port while also using my phone's mobile hotspot for Internet access, running &lt;code&gt;ip a&lt;/code&gt; on my laptop shows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ip a
&lt;span class="go"&gt;1: lo: Loopback interface (virtual interface that always points back to the computer itself and is used for communication between programs)
    inet 127.0.0.1/8 scope host lo
&lt;/span&gt;&lt;span class="c"&gt;    ...
&lt;/span&gt;&lt;span class="gp"&gt;2: enp0s31f6: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;: ethernet network interface
&lt;span class="go"&gt;    link/ether 02:11:22:33:44:55
    inet 192.168.8.196/24
&lt;/span&gt;&lt;span class="gp"&gt;3: wlp0s20f3: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;: wireless network interface
&lt;span class="go"&gt;    link/ether 02:aa:bb:cc:dd:ee
    inet 10.121.99.57/24 ...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

The hardware shown is only approximate. The laptop is connected to two independent networks at the same time: &lt;code&gt;enp0s31f6&lt;/code&gt; is the Ethernet interface connected to Flint's LAN, while &lt;code&gt;wlp0s20f3&lt;/code&gt; is the Wi-Fi interface connected to a mobile hotspot.




&lt;p&gt;The laptop contains two network devices (Ethernet and Wi-Fi). Linux exposes them as network interfaces that can be configured independently. Each network interface has its own MAC address, its own IP address, and its own network configuration.&lt;/p&gt;

&lt;p&gt;Although this is a single laptop, it is simultaneously connected to &lt;strong&gt;two completely separate LANs&lt;/strong&gt;. They do not overlap or interact with each other directly.&lt;/p&gt;

&lt;p&gt;In my example, the Ethernet interface belongs to the &lt;code&gt;192.168.8.0/24&lt;/code&gt; network, while the Wi-Fi interface belongs to the &lt;code&gt;10.121.99.0/24&lt;/code&gt; network. Since these networks use completely different private IP address ranges, there is no ambiguity about which network an address belongs to.&lt;/p&gt;

&lt;p&gt;What if both networks use &lt;code&gt;192.168.x.x&lt;/code&gt; addresses?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;192.168.8.57&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;192.168.17.1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Are they on the same network? I can find it out by reading the subnet mask.&lt;/p&gt;

&lt;h4&gt;
  
  
  Theory: Why /24 matters
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;/24&lt;/code&gt; prefix means the first 24 bits identify the network, leaving the last 8 bits for host addresses. In practice, this means devices on my Flint's LAN use addresses in the &lt;code&gt;192.168.8.x&lt;/code&gt; range.&lt;/p&gt;

&lt;p&gt;If my laptop had instead received &lt;code&gt;192.168.8.212/16&lt;/code&gt;, that would have mean that Flint's local network is much larger. It would include every address from &lt;code&gt;192.168.0.0&lt;/code&gt; to &lt;code&gt;192.168.255.255&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In other words, addresses like &lt;code&gt;192.168.1.1&lt;/code&gt;, &lt;code&gt;192.168.8.1&lt;/code&gt;, &lt;code&gt;192.168.50.20&lt;/code&gt;, &lt;code&gt;192.168.200.100&lt;/code&gt; - &lt;code&gt;192.168.x.x&lt;/code&gt; -  would all be considered part of the same LAN.&lt;/p&gt;

&lt;p&gt;This can cause problems. A &lt;code&gt;/16&lt;/code&gt; subnet makes every &lt;code&gt;192.168.x.x&lt;/code&gt; address appear to be local. If your ISP router (for example, if you place your OpenWrt router behind it instead of bypassing it), another router, or a VPN network also uses &lt;code&gt;192.168.x.x&lt;/code&gt; addresses, any device connected to your home network will incorrectly try to reach those addresses directly instead of forwarding the packets to your router. This creates address conflicts and is one of the reasons home routers almost always use a &lt;code&gt;/24&lt;/code&gt; subnet by default.&lt;/p&gt;

&lt;p&gt;Now that this little theoretical detour is over, I get back to the configuration. At this point, my laptop is connected to Flint's LAN and has received an IP address from its DHCP server, so both devices are members of the same network. That means Flint is now reachable, and I can access its admin web interface to continue the configuration.&lt;/p&gt;

&lt;h4&gt;
  
  
  1.3 Accessing LuCI from a web browser
&lt;/h4&gt;

&lt;p&gt;In a web browser I navigate to the path found it in the user manual or on a sticker attached to the device. The first page you'll see is most likely &lt;strong&gt;not LuCI&lt;/strong&gt;, but the vendor's own admin console - web interface. Many OpenWrt-based routers, including GL.iNet devices, ship with a custom interface that makes the initial setup easier while still exposing most of the common settings.&lt;/p&gt;

&lt;p&gt;After setting an administrator password, install &lt;strong&gt;LuCI&lt;/strong&gt;, the standard OpenWrt web interface. On GL.iNet devices, this can be done from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;System → Advanced Settings → Install LuCI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;LuCI is simply another package that can be installed on OpenWrt router. If your router doesn't come with it preinstalled, you can also install it from the router's shell using the &lt;code&gt;opkg&lt;/code&gt; package manager.&lt;/p&gt;

&lt;p&gt;My Flint router exposes LuCI at port &lt;code&gt;:8080&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3nvrghblz4j0s1d17ewp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3nvrghblz4j0s1d17ewp.png" alt="LuCI Web UI Login Page" width="799" height="269"&gt;&lt;/a&gt;&lt;/p&gt;
LuCI Web UI Login Page



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fevhemnowpodbev41t8y4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fevhemnowpodbev41t8y4.png" alt="Gl.iNet Web UI Login Page" width="800" height="516"&gt;&lt;/a&gt;&lt;/p&gt;
Gl.iNet Web UI Login Page






&lt;p&gt;Before moving on, it's worth taking a closer look at something I've already mentioned briefly: network devices and network interfaces. Understanding the difference between them is essential, because from this point onward almost everything you'll configure in OpenWrt revolves around them.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Theory: Physical network devices vs. network interfaces
&lt;/h3&gt;

&lt;p&gt;Earlier I showed that my laptop has 2 networking adapters: an Ethernet adapter and a Wi-Fi adapter. Debian exposes both as networking interfaces that can be configured independently.&lt;/p&gt;

&lt;p&gt;An OpenWrt router follows the same principle, but instead of just one Ethernet adapter and one Wi-Fi radio, it typically contains multiple Ethernet ports, one or more Wi-Fi radios and an internal Ethernet switch. All traffic entering or leaving the router is ultimately processed by the Linux kernel running on its CPU.&lt;/p&gt;

&lt;p&gt;Unlike a typical Linux distribution, OpenWrt separates networking into &lt;strong&gt;Devices&lt;/strong&gt; and &lt;strong&gt;Interfaces&lt;/strong&gt;. Understanding the difference between them is essential because the next configuration steps use both.&lt;/p&gt;

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

Router teardown view of the GL.iNet Flint 2 (right side) taken from &lt;a href="https://www.cnx-software.com/2023/12/26/gl-inet-gl-mt6000-flint-2-wifi-6-router-review-unboxing-teardown-first-try/" rel="noopener noreferrer"&gt;GL.iNet GL-MT6000 “Flint 2” WiFi 6 router review by Jean-Luc Aufranc&lt;/a&gt;. The labels are simplified and only approximate the internal architecture for explanatory purposes. Don't throw pantofole at me for the oversimplification, thx




&lt;p&gt;OpenWrt separates network configuration into Network Devices and Network Interfaces. Despite their names, both are software abstractions managed by Linux. Configuring a network device is not limited to changing properties of physical hardware such as an Ethernet controller. An OpenWrt network device can represent &lt;em&gt;physical&lt;/em&gt; hardware (&lt;code&gt;eth0&lt;/code&gt;), a bridge (&lt;code&gt;br-lan&lt;/code&gt;), a VPN tunnel (&lt;code&gt;wg0&lt;/code&gt;), or even a completely virtual device created entirely in software, such as the VLAN device that I'll create later in this article.&lt;/p&gt;

&lt;p&gt;In other words, &lt;code&gt;network devices&lt;/code&gt; represent the &lt;em&gt;networking endpoints&lt;/em&gt; themselves (&lt;code&gt;eth0&lt;/code&gt;, &lt;code&gt;br-lan&lt;/code&gt;, &lt;code&gt;eth1&lt;/code&gt; ecc). They expose low-level networking properties configuration. &lt;code&gt;Network interfaces&lt;/code&gt; (&lt;code&gt;wan&lt;/code&gt;, &lt;code&gt;lan&lt;/code&gt;, &lt;code&gt;guest&lt;/code&gt; ecc) sit one level above them. They describe how one of those network &lt;code&gt;devices&lt;/code&gt; should &lt;strong&gt;connect and participate&lt;/strong&gt; to a network, for example by using DHCP or a static IP configuration.&lt;/p&gt;

&lt;p&gt;The confusion between physical network interfaces and OpenWrt interfaces is common enough that the OpenWrt documentation has an article dedicated to clarifying it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;An “Interface” in the OpenWrt configuration must not be mixed up with a physical interface. It is tempting. [...]&lt;br&gt;
To clarify things (hopefully) let's avoid the term interface and replace it with something more neutral. What we actually configure in OpenWrt could be named a “Connector”. The configuration of a “Connector” combines all properties that are required to attach the device running OpenWrt to a network. [I'll call a physical device a “controller”]. This include a physical device and setup information that configures the controller in such a way that it allows the device to join the network.&lt;a href="https://openwrt.org/docs/guide-user/base-system/clarifying_interface_usage" rel="noopener noreferrer"&gt;Clarifying the term "Interface"&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h4&gt;
  
  
  Theory: A bit on OSI model - the layer where connectivity fails
&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;OSI (Open Systems Interconnection)&lt;/strong&gt; model is a conceptual framework that divides network communication into &lt;strong&gt;7 layers&lt;/strong&gt;, each responsible for a specific part of sending and receiving data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;7 Application → HTTP, DNS, DHCP
6 Presentation → Encryption, compression
5 Session → Managing communication sessions
4 Transport → TCP, UDP
3 Network → IPv4, IPv6, routing
2 Data Link → Ethernet, MAC addresses, VLANs
1 Physical → Cables, fibre, electrical/optical signal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my setup, Layer 1 is already working correctly. The Ethernet link between my Flint router and the ONT is up, the ONT converts Ethernet frames into GPON frames so they can travel over the optical fibre, and at the ISP's side they are converted back into Ethernet frames.&lt;/p&gt;

&lt;p&gt;The next step is &lt;strong&gt;Layer 2&lt;/strong&gt;, the &lt;strong&gt;Data Link layer&lt;/strong&gt;, where Ethernet operates. &lt;/p&gt;

&lt;p&gt;Right now, this is exactly where my connection fails. My router is sending Ethernet frames, but the ISP discards them before they can be processed further. As a result, my router never receives an IP address or any information about the ISP's network it is trying to join.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Layer 2 problem: one cable, multiple networks
&lt;/h4&gt;

&lt;p&gt;My router constantly sends and receives Ethernet frames. Every Ethernet frame contains a destination address, a source address, an EtherType field, a payload, and a CRC.&lt;/p&gt;

&lt;p&gt;My router receives the packet encapsulated in an Ethernet frame on one of its LAN interfaces. It then removes the incoming Ethernet header, performs a routing lookup to determine where the packet should go, and creates a new Ethernet frame for transmission through its WAN interface.&lt;/p&gt;

&lt;p&gt;With my setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Device connected to Flint's LAN via Wi-Fi
    ↓
OpenWrt Flint router
    ↓
Ethernet cable
    ↓
ONT
    ↓
GPON over optical fibre (My ISP's notation)
    ↓
ISP network
    ↓
Internet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ONT converts Ethernet frames into GPON frames so they can travel over the optical fibre. At the ISP's side, they are converted back into Ethernet frames and continue through the ISP's network.&lt;/p&gt;

&lt;p&gt;So, why does no packet destined for the Internet ever reach it? Why can't my Flint router EVEN join the ISP's network?&lt;/p&gt;

&lt;p&gt;As mentioned in the first part of this series, deploying network infrastructure is extremely expensive. ISPs therefore try to make the best possible use of their physical infrastructure. Instead of dedicating one physical cable to one logical network or service, the same cable can carry traffic belonging to multiple independent networks.&lt;/p&gt;

&lt;p&gt;In that scenario, the source and destination MAC addresses alone are no longer enough. They identify who is communicating, but not which logical network or service the frame belongs to. An additional identifier is needed.&lt;/p&gt;

&lt;h4&gt;
  
  
  Theory: IEEE 802.1Q and VLAN tags
&lt;/h4&gt;

&lt;p&gt;This problem is solved by the IEEE (Electrical and Electronics Engineers) 802.1Q standard, which introduced Virtual Local Area Networks (VLANs) for Ethernet networks. This 802.1Q standard inserts a small 4-byte tag into every Ethernet frame. Among other information, this tag contains a 12-bit VLAN Identifier (VID), which tells network equipment which logical network the frame belongs to.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My ISP's requirement: VLAN 835&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In fact my ISP Fastweb requires tagged modality:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;In case of BS-GPON fibre, the Ethernet WAN port of the router chosen by the customer as an alternative to the one supplied by Fastweb must be connected to the Optical Network Terminal (ONT), which is provided by Open Fiber during service activation, using a Category 5e or higher Ethernet cable. The connection must operate in tagged mode (VLAN ID 835; CoS 0 for data traffic and CoS 1 for voice traffic). &lt;a href="https://www.fastweb.it/adsl-fibra-ottica/dettagli/altri-modem/" rel="noopener noreferrer"&gt;Modem-router alternativi e Fastweb&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  4. Configuring the WAN port to add VLAN tags to outgoing Ethernet frames
&lt;/h3&gt;

&lt;p&gt;At this point, I know that Fastweb expects every Ethernet frame sent by my router to carry &lt;strong&gt;VLAN ID 835&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;However, an Ethernet WAN port is just that - an Ethernet port. By default, it sends ordinary Ethernet frames without any VLAN tag. Before my router can communicate with the ISP, I need to configure the WAN side so that every Ethernet frame leaving the WAN port is automatically tagged with VLAN ID 835.&lt;/p&gt;

&lt;p&gt;To see what an ordinary Ethernet frame looks like, here is captured traffic on my router's LAN:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;tcpdump &lt;span class="nt"&gt;-i&lt;/span&gt; br-lan &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nt"&gt;-vv&lt;/span&gt;
&lt;span class="go"&gt;tcpdump: listening on br-lan, link-type EN10MB (Ethernet), snapshot length 262144 bytes
&lt;/span&gt;&lt;span class="c"&gt;...
&lt;/span&gt;&lt;span class="gp"&gt;21:14:23.886143 3c:52:82:1a:7f:10 (oui Unknown) &amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;8e:b4:19:c3:55:d2 &lt;span class="o"&gt;(&lt;/span&gt;oui Unknown&lt;span class="o"&gt;)&lt;/span&gt;, ethertype IPv4 &lt;span class="o"&gt;(&lt;/span&gt;0x0800&lt;span class="o"&gt;)&lt;/span&gt;, length 66: &lt;span class="o"&gt;(&lt;/span&gt;tos 0xb8, ttl 64, &lt;span class="nb"&gt;id &lt;/span&gt;0, offset 0, flags &lt;span class="o"&gt;[&lt;/span&gt;DF], proto TCP &lt;span class="o"&gt;(&lt;/span&gt;6&lt;span class="o"&gt;)&lt;/span&gt;, length 52&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Timestamp&lt;/em&gt;: 21:14:23.886143&lt;br&gt;
&lt;em&gt;Source MAC address&lt;/em&gt;: 3c:52:82:1a:7f:10 &lt;br&gt;
&lt;em&gt;Dest MAC address&lt;/em&gt;: 8e:b4:19:c3:55:d2&lt;br&gt;
&lt;em&gt;Ethertype&lt;/em&gt;: IPv4&lt;/p&gt;

&lt;p&gt;Notice that there is no 802.1Q VLAN header. This is an ordinary Ethernet frame. &lt;strong&gt;After configuring tagging&lt;/strong&gt;, a capture on the physical WAN port looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;tcpdump &lt;span class="nt"&gt;-i&lt;/span&gt; eth0 &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nt"&gt;-vv&lt;/span&gt;
&lt;span class="go"&gt;tcpdump: listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
&lt;/span&gt;&lt;span class="gp"&gt;21:56:22.450934 ac:91:a1:2f:48:6c (oui Unknown) &amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;4c:16:fc:21:b8:4d &lt;span class="o"&gt;(&lt;/span&gt;oui Unknown&lt;span class="o"&gt;)&lt;/span&gt;, ethertype 802.1Q &lt;span class="o"&gt;(&lt;/span&gt;0x8100&lt;span class="o"&gt;)&lt;/span&gt;, length 287: vlan 835, p 0, ethertype IPv4 &lt;span class="o"&gt;(&lt;/span&gt;0x0800&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="o"&gt;(&lt;/span&gt;tos 0x2,ECT&lt;span class="o"&gt;(&lt;/span&gt;0&lt;span class="o"&gt;)&lt;/span&gt;, ttl 63, &lt;span class="nb"&gt;id &lt;/span&gt;0, offset 0, flags &lt;span class="o"&gt;[&lt;/span&gt;DF], proto TCP &lt;span class="o"&gt;(&lt;/span&gt;6&lt;span class="o"&gt;)&lt;/span&gt;, length 269&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Timestamp&lt;/em&gt;: 21:56:22.450934&lt;br&gt;
&lt;em&gt;Source MAC address&lt;/em&gt;: ac:91:a1:2f:48:6c (Flint router)&lt;br&gt;
&lt;em&gt;Dest MAC address&lt;/em&gt;: 5a🇩🇪44:91:07:b3 (ISP's device)&lt;br&gt;
&lt;strong&gt;Ethertype&lt;/strong&gt;: 802.1Q&lt;br&gt;
&lt;strong&gt;Vlan&lt;/strong&gt;: 835&lt;br&gt;
&lt;strong&gt;Inner EtherType&lt;/strong&gt;: IPv4&lt;/p&gt;





&lt;p&gt;So, how do I make every Ethernet frame leaving the WAN port carry VLAN ID 835? The physical WAN port on my router is represented by OpenWrt as network &lt;code&gt;device&lt;/code&gt; &lt;code&gt;eth0&lt;/code&gt;. At the moment, it transmits ordinary Ethernet frames. OpenWrt allows me to create another &lt;strong&gt;logical network device&lt;/strong&gt; based on &lt;code&gt;eth0&lt;/code&gt;. This new device will automatically insert VLAN ID 835 into every outgoing Ethernet frame while still using the same physical Ethernet port. Once that VLAN-aware network device exists, I'll simply tell the existing &lt;code&gt;wan&lt;/code&gt; interface to use it instead of the original &lt;code&gt;eth0&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  4. Creating a logical VLAN-aware network device based on the WAN port
&lt;/h4&gt;

&lt;p&gt;In LuCI, I navigate to &lt;strong&gt;Network → Interfaces → Devices&lt;/strong&gt; (the second tab).&lt;/p&gt;

&lt;p&gt;This is what I see:&lt;/p&gt;

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

&lt;p&gt;Since I need to deal with &lt;strong&gt;virtual networks&lt;/strong&gt;, I cannot modify the existing network device &lt;code&gt;eth0&lt;/code&gt; to do what I need. Instead, I have to create &lt;strong&gt;another logical (software) device&lt;/strong&gt; that will be based on &lt;code&gt;eth0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I click on "Add device configuration..." (in the left bottom corner below the list of existing devices).&lt;/p&gt;

&lt;p&gt;This new logical device will use &lt;code&gt;eth0&lt;/code&gt; as its physical transport, but it will automatically insert &lt;strong&gt;VLAN ID 835&lt;/strong&gt; into every outgoing Ethernet frame and accept incoming frames tagged with VLAN 835.&lt;/p&gt;

&lt;p&gt;You can think of it as adding another logical lane to the same physical cable. Physically, there is still only &lt;strong&gt;one Ethernet cable&lt;/strong&gt; between my router and the ONT, but logically multiple independent networks can coexist on it. The ISP uses exactly the same principle on its own infrastructure: one physical link can carry multiple logical services, distinguished by their VLAN tags.&lt;/p&gt;

&lt;p&gt;New device configuration:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Device type: VLAN (802.1q)&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;Base device: eth0 (WAN port)&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;VLAN ID: 835&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is the key configuration.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Device name: eth0.835 (following Dot1q notation)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;MAC address&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;...aaand here's something that solves the next potential problem!&lt;/p&gt;
&lt;h4&gt;
  
  
  4.1 ISP access control: MAC address filtering
&lt;/h4&gt;

&lt;p&gt;A quick recap on MAC addresses. Unlike IP addresses, MAC addresses are used only on the local link. Every &lt;em&gt;network device&lt;/em&gt; has its own MAC address, which identifies it on the local Ethernet network. Switches use MAC addresses to deliver Ethernet frames to the correct device. They are therefore fundamental to Layer 2 communication.&lt;/p&gt;

&lt;p&gt;Even though MAC addresses are unique and a little special because they can reveal information about the manufacturer of a network device, they are not necessarily glued or engraved into the hardware forever. Routers in particular can change or clone the MAC addresses used by their network devices.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;It is important to distinguish a computing device from a network device. My laptop is one computing device, but it contains an Ethernet port card and a Wi-Fi network card. Each network card has its own MAC address. The laptop itself does not have one cumulative MAC address representing the whole machine.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The same applies to a router. Remember I showed earlier the intercepted Ethernet frames from the LAN and WAN sides? The source of these frames was my Flint router, but the frames were sent through two different network devices (bridge LAN and eth0), each with its own MAC address.&lt;/p&gt;

&lt;p&gt;After fixing the VLAN tagging, my router finally received an IP address. However, Internet access was still restricted. Instead of reaching the Internet, every HTTP request I made with &lt;code&gt;curl&lt;/code&gt; was redirected to a Fastweb page asking me to register my router's MAC address.&lt;/p&gt;

&lt;p&gt;The ISP's logic is roughly this: 1 Internet contract === 1 Internet-facing device. The contract is associated with your customer account, and the router is identified by the MAC address of its WAN network device. Any other MAC address trying to use that connection is treated as an unknown device and is redirected to the registration page instead of being granted full Internet access.&lt;/p&gt;

&lt;p&gt;There are two ways to solve this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Register the MAC address of your new router (the one shown by default for &lt;code&gt;eth0&lt;/code&gt; in "Add device configuration..." ).&lt;/li&gt;
&lt;li&gt;Clone the MAC address of the ISP router.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;MAC cloning sounds much fancier than it really is. It is essentially just a copy paste operation. I simply copied the MAC address printed on the sticker on the back of my ISP's router and entered it into the MAC address field for &lt;code&gt;eth0.835&lt;/code&gt;. The sticker printed it as twelve hexadecimal characters without any separators, so I just added a colon every two characters. Remember that a MAC address is made up of hexadecimal digits (&lt;code&gt;0 - 9&lt;/code&gt; and &lt;code&gt;A - F&lt;/code&gt;).&lt;/p&gt;
&lt;h4&gt;
  
  
  4.2 Back to the configuration!
&lt;/h4&gt;

&lt;p&gt;New device configuration:&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;MAC address &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;copied from ISP's router.&lt;/p&gt;

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

&lt;p&gt;6,7. MTU and TX queue length &lt;/p&gt;

&lt;p&gt;Retype placeholder (MTU 1500 and TX length 1000) values.&lt;/p&gt;

&lt;p&gt;Voila!&lt;/p&gt;

&lt;p&gt;At this point, the VLAN-aware network device exists, but nothing is using it yet. OpenWrt still sends WAN traffic through the original &lt;code&gt;eth0&lt;/code&gt;. The last step is to tell the interface that handles WAN traffic (all traffic destined for the upstream ISP's network) to use the newly created software level &lt;code&gt;eth0.835&lt;/code&gt; device instead of the default network device &lt;code&gt;eth0&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  5. Configuring the &lt;code&gt;wan&lt;/code&gt; network interface
&lt;/h3&gt;

&lt;p&gt;This is the list of interfaces that my router has already configured that I see in &lt;strong&gt;Network → Interfaces&lt;/strong&gt; in LuCI:&lt;/p&gt;

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

&lt;p&gt;It is visible that &lt;code&gt;wan&lt;/code&gt; interface isn't functioning well yet. It has not obtained an IPv4 address, while the &lt;code&gt;lan&lt;/code&gt; and &lt;code&gt;guest&lt;/code&gt; interfaces have IPv4 addresses. I can also see that the IPv4 protocol configured for &lt;code&gt;wan&lt;/code&gt; is &lt;strong&gt;DHCP client&lt;/strong&gt;. This means the router requests one from the DHCP server on the upstream network - my ISP's network.&lt;/p&gt;

&lt;p&gt;Since I haven't yet told the &lt;code&gt;wan&lt;/code&gt; interface to use the VLAN-aware network device (&lt;code&gt;eth0.835&lt;/code&gt;), those DHCP requests are still leaving through the untagged &lt;code&gt;eth0&lt;/code&gt; device. Fastweb expects VLAN ID 835 on every frame, so it simply ignores them. As a result, no DHCP lease is offered, and the &lt;code&gt;wan&lt;/code&gt; interface remains without an IP address&lt;/p&gt;

&lt;p&gt;I click on "Edit" button of &lt;code&gt;wan&lt;/code&gt; interface to modify its configuration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwanplo1jck1ex9zd8rvm.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwanplo1jck1ex9zd8rvm.webp" alt=" raw `wan` endraw  interface configuration" width="800" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Protocol&lt;br&gt;
DHCP client&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Device&lt;br&gt;
I change &lt;code&gt;eth0&lt;/code&gt; to "Software VLAN &lt;code&gt;eth0.835&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Disabled &lt;br&gt;
unchecked&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Bring up on boot &lt;br&gt;
checked&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hostname to send when requesting DHCP&lt;br&gt;
Do not send a hostname&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;



&lt;p&gt;I'll also configure a few additional settings. If you look at the available tabs for the &lt;code&gt;wan&lt;/code&gt; interface, you can see that an OpenWrt &lt;code&gt;interface&lt;/code&gt; is responsible for much more than simply how to connect to some network. For example, there's a &lt;code&gt;Firewall Settings&lt;/code&gt; tab where the &lt;code&gt;interface&lt;/code&gt; can be assigned to one of the firewall zones. Firewall zones control how traffic entering or leaving through this interface is filtered. I'll leave this unchanged and keep the default configuration shipped with my router.&lt;/p&gt;

&lt;p&gt;You'll also see a &lt;code&gt;DHCP Server&lt;/code&gt; tab. Although every &lt;code&gt;interface&lt;/code&gt; can have one, enabling a DHCP server on the &lt;code&gt;wan&lt;/code&gt; interface usually makes no sense because &lt;code&gt;wan&lt;/code&gt; is a &lt;em&gt;client&lt;/em&gt; of the ISP's network, not the owner of it. On the &lt;code&gt;lan&lt;/code&gt; interface, however, it's a completely different story. The lan interface represents the router's local network, built on top of virtual bridge , which "merges" all LAN Ethernet ports and Wi-Fi radios. Here, running a DHCP server is exactly what I want.&lt;/p&gt;
&lt;h4&gt;
  
  
  5.1 Instruct &lt;code&gt;wan&lt;/code&gt; interface to use custom DNS servers
&lt;/h4&gt;

&lt;p&gt;If you open the &lt;code&gt;Advanced Settings&lt;/code&gt; tab, you'll find more configurations.  When using DHCP, the upstream network (in case of &lt;code&gt;wan&lt;/code&gt; interface - ISP's one) doesn't just provide an IP address to my Flint. A DHCP lease can also advertise the default gateway, DNS servers, subnet mask, lease time, and other network parameters.&lt;/p&gt;

&lt;p&gt;OpenWrt lets me decide whether to accept those parameters or override them. For example, I can and I want to ignore the DNS servers advertised by the ISP and instead configure DNS resolver of my choice.&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The Domain Name System (DNS) is the phonebook of the Internet. When users type domain names such as ‘google.com’ or ‘nytimes.com’ into web browsers, DNS is responsible for finding the correct IP address for those sites. Browsers then use those addresses to communicate with origin servers or CDN edge servers to access website information. This all happens thanks to DNS servers: machines dedicated to answering DNS queries.&lt;a href="https://www.cloudflare.com/learning/dns/what-is-a-dns-server/" rel="noopener noreferrer"&gt;What is DNS server&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  Why should you care which DNS server you use
&lt;/h4&gt;

&lt;p&gt;If you're comfortable configuring DNS servers directly on each of your devices, changing the router's DNS settings may not seem very important.&lt;/p&gt;

&lt;p&gt;For example, my Debian laptop is configured to use Quad9 (&lt;code&gt;9.9.9.9&lt;/code&gt;) directly. Whenever I type &lt;a href="https://dev.to/"&gt;https://dev.to/&lt;/a&gt; into my browser, my laptop sends the DNS query straight to Quad9 nameserver, regardless of which DNS servers my router uses.&lt;/p&gt;

&lt;p&gt;Most devices, however, aren't configured this way. A typical phone connected to home Wi-Fi simply uses the router as its DNS resolver. The phone doesn't know where to send DNS queries on the Internet; it just asks the default gateway - my Flint router.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Phone
   │
   │ "Where is dev.to?"
   ▼
Flint router
   │
   │ forwards the query
   ▼
Configured DNS server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If I accept for &lt;code&gt;wan&lt;/code&gt; interface the DNS servers advertised by my ISP via DHCP, every device on my network that relies on the router for DNS will end up using those servers. I may not know where they're located, who operates them, how fast they are, or what _privacy policy _they follow.&lt;/p&gt;

&lt;p&gt;A bit on privacy policy....&lt;/p&gt;

&lt;p&gt;A DNS server is just another server on the Internet, and your router (or your device) is its client. Every time it receives a DNS query, it &lt;strong&gt;necessarily sees who&lt;/strong&gt; is asking and &lt;strong&gt;which domain name&lt;/strong&gt; is being resolved. Conceptually, from the DNS server's point of view, every request looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client IP: 71.71.89.12
Query: something-maybe-i-would-like-to-be-private.net
Response: 203.0.113.42
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any DNS resolver itself deliberatly learns the domain names you ask it to resolve. That's one reason many people choose a DNS provider whose privacy policy they trust. In my case it is &lt;a href="https://quad9.net/" rel="noopener noreferrer"&gt;quad9&lt;/a&gt; 9.9.9.9&lt;/p&gt;

&lt;p&gt;How to:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fai68po7xq3ijtligngtk.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fai68po7xq3ijtligngtk.webp" alt="uncheck usage DNS of peer" width="800" height="625"&gt;&lt;/a&gt;&lt;/p&gt;

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




&lt;p&gt;It is done!!!&lt;/p&gt;

&lt;p&gt;All that's left is to click &lt;strong&gt;Save&lt;/strong&gt; followed by &lt;strong&gt;Save &amp;amp; Apply&lt;/strong&gt; (left bottom corner) Before applying the changes, LuCI even shows you exactly what is about to be modified. If you're curious, click "Changes" in the top-right corner. You'll see the underlying configuration changes that OpenWrt is about to apply.&lt;/p&gt;

&lt;p&gt;This is one of my favourite features of LuCI. Although it's a web interface, it's really just a graphical frontend for OpenWrt's configuration system. Looking at the pending changes is a great way to gradually learn how the same configuration could be performed from the command line over SSH, without using the web interface at all.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffnlj5k1gbl3ml4qvtyuq.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffnlj5k1gbl3ml4qvtyuq.webp" alt="Commands to do the same from CLI" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After applying the changes, the &lt;code&gt;wan&lt;/code&gt; interface successfully obtains an IPv4 address from my ISP (Fastweb) DHCP server. The router is now connected to the ISP's network through the VLAN-aware network device &lt;em&gt;eth0.835&lt;/em&gt; that present itself with the correct MAC address approved by my ISP, and Internet connectivity is finally up.&lt;/p&gt;

&lt;p&gt;Opening Status → Overview confirms that everything is working as expected.&lt;/p&gt;

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

&lt;p&gt;I can clearly see that I am CG-NATed, my IP address is from the pool of Private IP addresses. &lt;/p&gt;

</description>
      <category>vlan</category>
      <category>openwrt</category>
      <category>protocol</category>
      <category>wan</category>
    </item>
    <item>
      <title>OpenWrt: Your Router, Your Rules - Part1: Keep or Bypass Your ISP's Router?</title>
      <dc:creator>aL</dc:creator>
      <pubDate>Mon, 20 Jul 2026 15:44:21 +0000</pubDate>
      <link>https://dev.to/dev-charodeyka/openwrt-your-router-your-rules-part1-keep-or-bypass-your-isps-router-9h9</link>
      <guid>https://dev.to/dev-charodeyka/openwrt-your-router-your-rules-part1-keep-or-bypass-your-isps-router-9h9</guid>
      <description>&lt;p&gt;The previous two parts, &lt;a href="https://dev.to/dev-charodeyka/openwrt-your-router-your-rules-part-0-your-router-your-right-j5j"&gt;Part 0&lt;/a&gt; and &lt;a href="https://dev.to/dev-charodeyka/openwrt-your-router-your-rules-part-05-nat-and-cgnat-203m"&gt;Part 0.5&lt;/a&gt;, were intentionally numbered that way because they were mostly theoretical. They weren't directly about OpenWrt, but they provide the foundation needed to understand what comes next.&lt;/p&gt;

&lt;p&gt;By now, I hope it's clear what a router actually is, what it isn't, and that you won't confuse it with a modem or an ONT anymore. If your ISP supplied you with a single all-in-one gateway, you should also understand what is that device. This is important because, before start using your own OpenWrt router, you first need to understand where it fits into your home network.&lt;/p&gt;

&lt;p&gt;I also covered NAT and Carrier-Grade NAT (CGNAT). CGNAT is useful to know about because it may limit what you can do with your connection, for example if you want to host services at home. NAT, however, is fundamental Understanding how it works will make the rest of this series much easier to follow.&lt;/p&gt;

&lt;p&gt;Now it's finally time to start using OpenWrt. In this part I'll show how to introduce an OpenWrt router into an existing home network in the two ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Replacing the ISP's router.&lt;/li&gt;
&lt;li&gt;Connecting your OpenWrt router behind the ISP's router, keeping the ISP's devices in place while adding your own router as another node in the network.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the end of this part, your OpenWrt router will be physically connected and ready for configuration in the following chapter.&lt;/p&gt;




&lt;p&gt;I have a &lt;a href="https://www.gl-inet.com/products/gl-ax1800/#specs" rel="noopener noreferrer"&gt;GL.iNet router model Flint (GL-AX1800)&lt;/a&gt;. &lt;/p&gt;

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

&lt;p&gt;It's a bit old (currently the newest model is Flint 4), but gold router (still). It comes with GL.iNet's fancy user-friendly web interface and of course OpenWrt OS with its configuration interface LuCI.&lt;/p&gt;

&lt;p&gt;Little recap:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;OpenWrt is a Linux operating system targeting embedded devices. Instead of trying to create a single, static firmware, OpenWrt provides a fully writable filesystem with package management. This frees you from the application selection and configuration provided by the vendor and allows you to customize the device through the use of packages to suit any application (&lt;a href="https://github.com/openwrt/openwrt" rel="noopener noreferrer"&gt;OpenWrt GitHub Repo&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;GL.iNet's devices already come with OpenWrt installed, so there is no need to flash the firmware yourself. GL.iNet base their router's firmware on a specific OpenWrt release (or release branch), backport fixes as needed, and then add their own packages and interface. They also provide a clean and beginner-friendly web interface that exposes most of the features home users need.&lt;/p&gt;

&lt;p&gt;If you need more advanced functionality, you're not locked into that interface. You can always access the standard OpenWrt tools, either through LuCI, OpenWrt's own web interface, or by connecting to the router over SSH.&lt;/p&gt;

&lt;p&gt;I've been very happy with my GL.iNet devices, but they are by no means the only routers capable of running OpenWrt. &lt;/p&gt;

&lt;p&gt;For that reason, whenever possible I'll show both approaches: how to perform a task using the GL.iNet interface and how to do the same thing using standard OpenWrt (LuCI or the command line). That way, the guide remains useful even if you're using a different router.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Connecting your OpenWrt router
&lt;/h3&gt;

&lt;p&gt;Before connecting your router, it's important to understand what its different ports are used for and where each one should be connected in your home network.&lt;/p&gt;

&lt;h4&gt;
  
  
  1.1 LAN and WAN ports
&lt;/h4&gt;

&lt;p&gt;LAN stands for Local Area Network, while WAN stands for Wide Area Network.&lt;/p&gt;

&lt;p&gt;My GL.iNet Flint router has 4 LAN ports and 1 WAN port.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6n22ae9pmh2i3ounnk3d.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6n22ae9pmh2i3ounnk3d.webp" alt="Flint 1 ports view" width="631" height="443"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Having 4 LAN ports simply means I can connect up to four wired Ethernet devices directly to the router. Those devices don't have to be only PCs or laptops. They could just as well be  another router or NAS or any other Ethernet-capable device.&lt;/p&gt;

&lt;p&gt;So what makes the WAN port different?&lt;/p&gt;

&lt;p&gt;The WAN port is used to connect the router to an &lt;strong&gt;upstream network device&lt;/strong&gt;. In my case, an Ethernet cable runs from the WAN port to the ONT. The ONT converts the optical signal from the fiber into Ethernet frames, allowing my router to communicate with my ISP.&lt;/p&gt;

&lt;p&gt;On many home routers there is only one WAN port. Manufacturers often place it slightly apart from the LAN ports or give it a different color, commonly blue, to make it easier to identify. &lt;/p&gt;

&lt;p&gt;At first glance, the WAN and LAN ports appear to be completely different. But &lt;em&gt;a WAN port and a LAN port are, in most cases, physically identical Ethernet ports&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The real difference lies in how the router's operating system configures them. The port itself is just a physical connector. Behind it is exposed to the operating system as one or more network interfaces.&lt;/p&gt;

&lt;p&gt;On my router, all 4 LAN ports belong to one logical network interface (a bridge called &lt;code&gt;br-lan&lt;/code&gt; in OpenWrt), while the WAN port is assigned to a separate interface - &lt;code&gt;wan&lt;/code&gt;. Because they are separated, OpenWrt allows to configure and manage them differently.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuve1oabk2dajevn3c1lg.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuve1oabk2dajevn3c1lg.webp" alt="wan and br-lan interfaces" width="337" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
Screenshot from LuCI: Network --&amp;gt; Interfaces 



&lt;p&gt;The LAN interface is configured to allow connected devices to communicate with each other.&lt;/p&gt;

&lt;p&gt;The WAN interface, on the other hand, is configured to communicate with the upstream network and it belongs to a separate firewall zone considered untrusted.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The firewall, routing table, NAT and many other networking rules depend on this distinction.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is why you generally can't plug your ONT into a LAN port or connect your PC to the WAN port and expect everything to work automatically. The hardware itself is usually capable of doing either job, but the software has been configured with a very specific role for each interface.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;One of the strengths of OpenWrt is that these roles are not fixed. If your hardware supports it, you can reassign ports, create additional interfaces, use VLANs, or even turn a LAN port into a WAN port. The labels printed on the router's case are simply the manufacturer's default configuration, they are not permanent hardware limitations.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1.2 Setup 1: Connecting router behind the ISP's router
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffy4xmr0s5hps0i7wtxx3.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffy4xmr0s5hps0i7wtxx3.webp" alt="ONT-&gt; ISP's router -&gt; your router" width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The ISP router stays exactly where it is, while your OpenWrt router is connected to one of its LAN ports. By default, your OpenWrt router simply receives a private IP address from the ISP router via its DHCP and creates ANOTHER network behind it. In this case you can connect devices to both - to your ISP's router and you your OpenWrt router. &lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Usually plug-and-play. No ISP configuration is required.&lt;/li&gt;
&lt;li&gt;Little risk of accidentally breaking your Internet connection.&lt;/li&gt;
&lt;li&gt;If your ISP router provides IPTV or VoIP, those services continue working without any additional configuration.&lt;/li&gt;
&lt;li&gt;....mmm that's all&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Two routers performing routing (Double NAT)&lt;br&gt;
Additional re-routing of packages step from your devices and the Internet.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Limited control over the ISP's router&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The ISP router remains the gateway to the Internet. Most ISP routers run proprietary firmware with only a few settings exposed to the user. If something doesn't work as you would prefer, there is often very little you can inspect or change.&lt;/p&gt;

&lt;p&gt;You're essentially dealing with a black box: no access to detailed logs, no advanced firewall configuration, no any extra feature and usually all you have is 254 addresses for your home devices being the part of a unique LAN.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vendor-specific quirks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some routers implement unusual firewall rules that can impede a lot using your home network at maximum. These rules can &lt;a href="https://www.reddit.com/r/ItalyInformatica/comments/1nhwmfq/ma_il_modem_fastweb_%C3%A8_veramente_cos%C3%AC_limitato_o/?tl=en" rel="noopener noreferrer"&gt;impede sometimes even streaming of stuff from your phone&lt;/a&gt; to TV even though they are both connected to the same network. Or you can have troubles with playing online together with someone located elsewhere.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;My experience with my ISP - Fastweb:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The first surprise was that their router administration happens through a mobile app. Personally, I wasn't comfortable with that. I prefer managing my networking equipment locally.&lt;/p&gt;

&lt;p&gt;The Wi-Fi performance also wasn't great in my home. Signal strength dropped significantly in the rooms farthest from the router, and the throughput was noticeably lower than what I later achieved with my GL.iNet Flint.&lt;/p&gt;

&lt;p&gt;My phone would randomly disconnect and re-connect from Wi-Fi every few hours. I never discovered the exact cause. &lt;/p&gt;

&lt;p&gt;With this setup there is one concern:&lt;/p&gt;

&lt;p&gt;Private IP conflicts. If both the ISP router and your OpenWrt router use the same private subnet (for example, 192.168.1.0/24), routing won't work correctly. &lt;/p&gt;

&lt;p&gt;No conflict:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Internet
    │
   ONT
    │
ISP Router: 192.168.1.1/24
    │
OpenWrt WAN: 192.168.1.100
    │
OpenWrt Router: 192.168.8.1/24
    │
Devices: 192.168.8.x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Conflict:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ISP Router LAN:      192.168.1.0/24

OpenWrt WAN:         192.168.1.100

OpenWrt LAN:         192.168.1.1/24   ❌
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fortunately, OpenWrt usually detect this situation during the initial setup and automatically choose a different LAN subnet.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.3 Setup 2: Connecting router in place of the ISP's router
&lt;/h3&gt;

&lt;p&gt;Instead of connecting your OpenWrt router behind the ISP's router, you remove the ISP router entirely and connect your router directly to the ONT (or whatever device terminates your ISP's connection).&lt;/p&gt;

&lt;p&gt;If you live in the European Union, this is your right under the Router Freedom / Modem Libero regulations discussed in &lt;a href="https://dev.to/dev-charodeyka/openwrt-your-router-your-rules-part-0-your-router-your-right-j5j"&gt;Part 0&lt;/a&gt; of this series.&lt;/p&gt;

&lt;p&gt;The challenge is usually getting the connection parameters right. Depending on your ISP, you may need to configure DHCP, PPPoE, VLANs or settle the MAC address.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;MAC address&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A MAC (Media Access Control) address is a hardware identifier assigned to a network interface.&lt;/p&gt;

&lt;p&gt;Unlike an IP address, which depends on the network a device is connected to and may change over time, a MAC address is normally programmed into the network interface by the manufacturer and remains the same throughout the device's lifetime.&lt;/p&gt;

&lt;p&gt;A MAC address consists of 48 bits and is usually written as six hexadecimal numbers separated by colons.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;40:A5:EF:12:34:56
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although MAC addresses are designed to uniquely identify network interfaces, they are only used for communication within a single local network. Ethernet switches use MAC addresses to decide which physical port a frame should be forwarded to. Routers, on the other hand, use IP addresses to move packets between different networks.&lt;/p&gt;

&lt;p&gt;Suppose your laptop wants to send data to another computer connected to the same switch. It creates an Ethernet frame whose destination is the other computer's MAC address.&lt;/p&gt;

&lt;p&gt;But what if the destination is somewhere on the Internet?&lt;/p&gt;

&lt;p&gt;Your laptop sends the frame to your router's MAC address. The router removes the Ethernet frame, examines the IP packet inside it, decides where it should go next, and then creates a brand-new Ethernet frame with different source and destination MAC addresses for the next network segment. &lt;/p&gt;

&lt;p&gt;The MAC address ensures your device is recognized on the local network, while the IP address helps your device communicate effectively on the broader internet.&lt;/p&gt;

&lt;p&gt;MAC addresses never travel across the Internet. They are only used for communication between devices that share the same local network.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;NB! A MAC address identifies a network interface, not an entire device.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Then why does the ISP care about router's MAC address?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Some ISPs associate your connection with the MAC address of the device connected to their network. If a different MAC address suddenly appears, the ISP may refuse to provide Internet access until the new device is registered or authenticated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is why replacing the ISP router sometimes results in an apparently correct configuration that still has no Internet connectivity.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If this is the case how you ISP handles connectivity, then you sign the contract with ISP for Internet service -&amp;gt; your contract = 1 registered MAC address of the router they provided to you. When you replace the ISP router with your own, the ISP sees a completely different MAC address and refuse to provide Internet access until the new device's MAC address is registered.&lt;/p&gt;

&lt;p&gt;You have two options: communicate to the ISP your router's MAC address or make your router present itself with registered MAC address of router provided by ISP that is indicated on the sticker on the back part of ISP's router. &lt;/p&gt;

&lt;p&gt;OpenWrt makes this easy. You can configure the WAN interface to use any MAC address you choose, including the MAC address of the original ISP's router. This process is known as MAC cloning. Despite the name, nothing is actually copied to the hardware. OpenWrt simply instructs the network interface to present a different MAC address whenever it communicates with the ISP.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;In my case, replacing the Fastweb router was almost straightforward. DHCP worked, the router obtained an IP address, and all the settings appeared to be correct. However, there was still no Internet connectivity. After cloning the WAN MAC address of the original Fastweb router, the connection immediately started working.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Alllright! You can follow either of the two setups presented earlier.&lt;/p&gt;

&lt;p&gt;In the next part I will demonstrate Setup 2, where the ISP router is removed and replaced with my OpenWrt router. Firstly, I connect the WAN port of my GL.iNet Flint directly to the Ethernet port of my ONT....&lt;/p&gt;

</description>
      <category>openwrt</category>
      <category>networking</category>
      <category>vlan</category>
      <category>ont</category>
    </item>
    <item>
      <title>OpenWrt: Your Router, Your Rules - Part 0: Il diritto di scegliere il tuo router</title>
      <dc:creator>aL</dc:creator>
      <pubDate>Sat, 18 Jul 2026 11:10:47 +0000</pubDate>
      <link>https://dev.to/dev-charodeyka/openwrt-your-router-your-rules-part-0-il-diritto-di-scegliere-il-tuo-router-5e80</link>
      <guid>https://dev.to/dev-charodeyka/openwrt-your-router-your-rules-part-0-il-diritto-di-scegliere-il-tuo-router-5e80</guid>
      <description>&lt;p&gt;Inizialmente avevo pensato di scrivere una guida pratica su come configurare più SSID associati a VLAN differenti utilizzando OpenWrt (via LuCI). Poi, però, ho iniziato ad approfondire l'argomento e... è andata a finire come al solito. Ci sono semplicemente troppi concetti da chiarire prima. &lt;em&gt;Prometto che le prossime parti saranno finalmente dedicate alla configurazione pratica.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Questa parte interesserà probabilmente soprattutto chi vive nell'Unione Europea, e ancora di più chi vive in Italia. Farò infatti riferimento alla normativa europea per spiegare brevemente come gli Internet Service Provider (ISP) forniscono il servizio Internet e, in particolare, come gestiscono l'apparecchiatura che installano nelle nostre case. Vivendo in Italia, ho avuto modo di familiarizzare con alcune particolarità del mercato italiano.&lt;/p&gt;

&lt;p&gt;Oggi si parla molto della privacy di smartphone, computer, applicazioni e sistemi operativi. Ma quando si comincia davvero a interessarsi alla privacy, al networking e a capire che fine fanno i propri pacchetti una volta usciti dal computer, prima o poi l'attenzione finisce inevitabilmente su quel dispositivo che se ne sta silenzioso in un angolo della casa...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Floir1ui17f5yaef27qq3.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Floir1ui17f5yaef27qq3.webp" alt="Side-eye to Wi-Fi router" width="799" height="644"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Questa guida è pensata per chi vuole capire davvero come funziona la propria rete domestica e prenderne il controllo utilizzando un router compatibile con OpenWrt.&lt;/p&gt;

&lt;p&gt;OpenWrt non è propriamente uno strumento per chi parte completamente da zero, ma è molto meno complicato di quanto possa sembrare. Inoltre dispone di un'interfaccia web (LuCI) che rende la configurazione decisamente più accessibile.&lt;/p&gt;

&lt;p&gt;Se sei arrivato fin qui, probabilmente possiedi già un router compatibile con OpenWrt oppure stai pensando di acquistarne uno. Magari negli anni hai raccolto qualche nozione di networking qua e là e senti che è arrivato il momento di fare un passo in più, ma hai ancora qualche dubbio. Oppure hai paura di modificare qualcosa perché temi di rompere la connessione o di fare qualcosa che il tuo ISP non permette.&lt;/p&gt;

&lt;p&gt;Ho scritto questa guida perché mi sono ritrovato a raccogliere informazioni sparse tra decine di fonti diverse e ho pensato che valesse la pena riunirle in un unico posto.&lt;/p&gt;

&lt;p&gt;Credo anche che gli Internet Service Provider dovrebbero essere molto più trasparenti riguardo ai servizi e alle apparecchiature che offrono e non dovrebbero mai limitare la libertà dei clienti di scegliere il proprio router o di cambiare operatore. Quando questo non accade, per fortuna esiste una comunità di persone che condivide conoscenze ed esperienze. Questo articolo vuole essere un piccolo contributo a quella comunità.&lt;/p&gt;

&lt;p&gt;Per i lettori italiani, spero che questa guida possa essere utile per capire quali domande fare e quali aspetti valutare prima di scegliere o cambiare operatore Internet.&lt;/p&gt;

&lt;p&gt;In questa parte parlerò di:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Parte 0
 ├ Cosa il tuo ISP può (e non può) pretendere
 ├ OpenWrt
 ├ Come arriva Internet a casa
 └ ONT, modem e router: che differenza c'è?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  1. Internet Service Provider e normativa europea
&lt;/h3&gt;

&lt;p&gt;ISP è l'acronimo di Internet Service Provider, cioè l'operatore che fornisce l'accesso a Internet.&lt;/p&gt;

&lt;p&gt;Quando si vuole una connessione Internet a casa, invece di affidarsi alla rete mobile (che spesso offre meno traffico disponibile, una copertura meno stabile e velocità inferiori), il primo passo è capire quali operatori sono disponibili nella propria zona.&lt;/p&gt;

&lt;p&gt;Nella maggior parte dei casi la scelta non è enorme, ma avere a disposizione tre, quattro o cinque operatori è piuttosto comune. In generale, più grande è la città in cui vivi, maggiori saranno le alternative.&lt;/p&gt;

&lt;p&gt;Nell'Unione Europea il mercato delle telecomunicazioni è regolato da norme sulla concorrenza e sulla tutela dei consumatori. L'obiettivo è semplice: quando hai bisogno di un servizio, in questo caso dell'accesso a Internet, devi poter scegliere tra più operatori che competano tra loro su prezzo, velocità e servizi offerti.&lt;/p&gt;

&lt;p&gt;Non tutte le offerte "Internet casa a 27,99 € al mese" sono uguali.&lt;/p&gt;

&lt;h4&gt;
  
  
  1.1 L'accesso a Internet è un mercato un po' particolare
&lt;/h4&gt;

&lt;p&gt;Dal punto di vista economico, il servizio di accesso a Internet è un bene piuttosto particolare.&lt;/p&gt;

&lt;p&gt;L'infrastruttura fisica su cui si basa presenta molte caratteristiche tipiche di un monopolio naturale, proprio come la rete ferroviaria, quella elettrica o quella stradale. Costruire questa infrastruttura richiede investimenti enormi, motivo per cui duplicarla completamente per ogni operatore sarebbe economicamente irrealistico.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Con "servizio Internet" mi riferisco all'infrastruttura e ai servizi che consentono di accedere alla rete globale, non al World Wide Web, cioè ai siti web, alle applicazioni e ai contenuti disponibili online.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Internet non è composta da un unico gigantesco cavo che collega tutto il pianeta, ma lo stesso principio vale per gran parte della rete.&lt;/p&gt;

&lt;p&gt;Le dorsali nazionali e internazionali, le reti in fibra ottica e perfino molte infrastrutture wireless richiedono investimenti enormi. Per questo motivo è molto comune che operatori diversi condividano parte dell'infrastruttura oppure si affittino reciprocamente l'accesso alla rete, invece di costruirne una completamente nuova.&lt;/p&gt;

&lt;p&gt;In altre parole, la concorrenza esiste, ma spesso riguarda il servizio commerciale più che l'infrastruttura fisica sottostante.&lt;/p&gt;

&lt;p&gt;Questo rende il mercato decisamente più complesso e, in molti casi, mette il cliente in una posizione di svantaggio informativo.&lt;/p&gt;

&lt;h4&gt;
  
  
  &amp;nbsp;1.2 Cambiare operatore in Italia
&lt;/h4&gt;

&lt;p&gt;In Italia i principali operatori di rete fissa sono TIM, Vodafone, Fastweb e WindTre.&lt;/p&gt;

&lt;p&gt;Le norme europee sulla concorrenza non garantiscono soltanto la libertà di scegliere un operatore quando si sottoscrive un contratto, ma anche quella di cambiarlo successivamente senza dover affrontare penali irragionevoli, anche nel caso in cui si stiano utilizzando apparecchiature fornite dall'operatore stesso.&lt;/p&gt;

&lt;p&gt;Queste tutele sono recepite e applicate da AGCOM (Autorità per le Garanzie nelle Comunicazioni).&lt;/p&gt;

&lt;p&gt;Uno dei principi fondamentali è che, quando si decide di cambiare operatore, i costi di migrazione o di cessazione del contratto non possono superare il valore del canone mensile medio oppure i costi realmente sostenuti dall'operatore, scegliendo l'importo più basso tra i due.&lt;/p&gt;

&lt;p&gt;Inoltre, gli operatori non possono pretendere la restituzione indiscriminata di tutti gli sconti promozionali applicati durante il contratto: qualsiasi eventuale rimborso deve essere proporzionato ed equo.&lt;/p&gt;

&lt;p&gt;Non è sempre stato così.&lt;/p&gt;

&lt;p&gt;Fino a qualche anno fa alcuni operatori rendevano il cambio di gestore estremamente oneroso imponendo costi di recesso molto elevati, scoraggiando di fatto la concorrenza.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj5k8uzw6xjw9lr3zrv3t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj5k8uzw6xjw9lr3zrv3t.png" alt="AGCOM: you can always choose between a variety of packages and change both landline and mobile operators according to your needs" width="799" height="298"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;a href="https://www.agcom.it/switching-operator-and-terminating-contract" rel="noopener noreferrer"&gt;AGCOM on Switching operator and terminating contract&lt;/a&gt;



&lt;p&gt;Fin qui tutto molto bello. Ma c'è un dettaglio interessante.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk36pldbwl5omq7iyr35w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk36pldbwl5omq7iyr35w.png" alt="AGCOM: Charges for terminating contract" width="799" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Gli utenti possono continuare a pagare le rate residue relative ai beni o ai servizi acquistati anche dopo aver esercitato il recesso, purché il piano di rateizzazione non superi i 24 mesi. (&lt;a href="https://www.agcom.it/switching-operator-and-terminating-contract" rel="noopener noreferrer"&gt;Charges for terminating contract&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ed è proprio qui che nasce una domanda interessante. Di quale &lt;strong&gt;bene&lt;/strong&gt; stiamo parlando?&lt;/p&gt;

&lt;p&gt;Se il contratto per il servizio Internet è stato chiuso e il cliente è passato a un altro operatore, il servizio non esiste più. Che cosa rimane allora da pagare? La risposta è: l'apparecchiatura fornita dall'ISP.&lt;/p&gt;

&lt;h4&gt;
  
  
  &amp;nbsp;1.3 L'apparecchiatura fornita dal tuo operatore
&lt;/h4&gt;

&lt;p&gt;L'apparecchiatura fornita dall'operatore.&lt;/p&gt;

&lt;p&gt;Per un normale utente domestico significa quasi sempre un modem, un router (oppure un unico dispositivo che integra entrambe le funzioni) e qualche cavo. MA... Se non stiamo parlando di un'azienda con armadi rack pieni di apparati di rete, che tipo di dispositivo così costoso stiamo pagando a rate per ben 24 mesi? Ed ecco che entra in scena il protagonista di questo articolo:&lt;/p&gt;

&lt;p&gt;il router fornito dall'ISP.&lt;/p&gt;

&lt;h4&gt;
  
  
  &amp;nbsp;1.4 Il modem TIM da 240 euro
&lt;/h4&gt;

&lt;p&gt;Prendiamo come esempio una delle attuali offerte fibra di TIM (20 lug 2026).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnowr4k2tyvpacztnc7iy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnowr4k2tyvpacztnc7iy.png" alt="TIM Wi-Fi at Home offers" width="800" height="501"&gt;&lt;/a&gt;&lt;/p&gt;
Access date: 10 July 2026, 07:07 PM [Source](https://www.tim.it/fisso-e-mobile/fibra-e-adsl/fibra-internet-casa)



&lt;p&gt;Aprendo la sezione Dettagli costi, compare questa voce:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffbjsod5kqg450ic1z4ef.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffbjsod5kqg450ic1z4ef.png" alt="Cost details of TIM offer: modem that costs 240 euros included" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;MODEM TIM HUB+ (per tecnologia FTTC), MODEM TIM HUB Pro (per tecnologia FTTH)&lt;br&gt;
240€ rateizzati in 48 rate mensili da 5€.Nei primi 24 mesi dall’attivazione dell’offerta ti sarà applicato uno sconto di 5€/mese sull’acquisto del modem, pertanto, il costo complessivo del prodotto sarà di 120€ anziché 240€. (&lt;a href="https://www.tim.it/fisso-e-mobile/fibra-e-adsl/fibra-internet-casa" rel="noopener noreferrer"&gt;Source accessed 10/07/2026, free translation&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu2wl7c54kqs2ubczcwxx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu2wl7c54kqs2ubczcwxx.png" alt="Caso di recesso prima di 48 mesi" width="800" height="156"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In caso di recesso prima di 48 mesi saranno dovute le rate mancanti del modem TIM (unicamente quelle a titolo oneroso e non quelle in promo).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A prima vista 240 € non sono necessariamente un prezzo assurdo. Un buon gateway domestico (cioè un dispositivo che integra ONT e router) può tranquillamente costare quella cifra. Il punto, però, è un altro.&lt;/p&gt;

&lt;p&gt;Vale davvero 240 €? E, soprattutto: è davvero il dispositivo che vuoi acquistare?&lt;/p&gt;

&lt;p&gt;Guardiamo la scheda tecnica.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffv8zxg5d6rtt2qqijist.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffv8zxg5d6rtt2qqijist.png" alt="Technical specks of modem in question" width="799" height="547"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tre porte LAN. Una porta WAN. Wi-Fi dual band. E poi? Che processore monta? Quanta memoria RAM ha? Quanta storage? Quale chipset Wi-Fi utilizza? Supporta OpenWrt?&lt;/p&gt;

&lt;p&gt;Come dovrebbe un cliente capire se il dispositivo vale davvero quella cifra con una scheda tecnica così scarna? Probabilmente nessuno comprerebbe spontaneamente un router da 240 € in un negozio online se la descrizione si limitasse a queste informazioni.&lt;/p&gt;

&lt;p&gt;E non perché sia necessariamente un cattivo dispositivo. Potrebbe anche essere un ottimo gateway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Il problema è che il cliente non dispone delle informazioni necessarie per valutarlo consapevolmente.&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  1.5 Hai il diritto di usare il tuo router
&lt;/h4&gt;

&lt;p&gt;Ed è qui che entra in gioco la normativa europea. Il diritto dell'Unione Europea tutela la libertà degli utenti di scegliere l'apparecchiatura con cui accedere a Internet.&lt;/p&gt;

&lt;p&gt;In base al &lt;a href="https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:32015R2120" rel="noopener noreferrer"&gt;Regolamento (UE) 2015/2120&lt;/a&gt; sull'accesso a Internet aperta, gli operatori non possono obbligarti a utilizzare il loro router, purché il dispositivo scelto sia compatibile con la loro rete e rispetti i requisiti tecnici previsti.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkmlt4akkb1k713g8ve7z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkmlt4akkb1k713g8ve7z.png" alt="AGCOM on freedom to use terminal equipment of your own choice" width="799" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In pratica, questo significa che l'operatore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;non può rifiutarsi di collegare un dispositivo scelto dal cliente se conforme agli standard tecnici;&lt;/li&gt;
&lt;li&gt;non può applicare costi aggiuntivi né degradare la qualità del servizio perché utilizzi un router diverso dal suo;&lt;/li&gt;
&lt;li&gt;deve fornire tutte le informazioni tecniche necessarie per configurare un router alternativo;&lt;/li&gt;
&lt;li&gt;se il router fornito integra anche l'ONT per una connessione FTTH, deve consentire al cliente di richiedere un ONT esterno.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Naturalmente non posso parlare dell'esperienza di tutti gli operatori. Nel mio caso utilizzo Fastweb. Quando ho attivato la linea mi sono stati forniti due dispositivi distinti:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;un ONT;&lt;/li&gt;
&lt;li&gt;un router.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Del router Fastweb non faccio uso. Utilizzo invece il mio router personale, mentre continuo a usare l'ONT fornito dall'operatore, perché è il dispositivo che termina la connessione in fibra ottica. Ed è una configurazione comune, oltre che, nella maggior parte dei casi, la soluzione migliore.&lt;/p&gt;




&lt;p&gt;A questo punto dovrebbe essere chiaro un concetto fondamentale.&lt;/p&gt;

&lt;p&gt;Utilizzare la propria apparecchiatura di rete non è un favore concesso dall'operatore, ma un diritto riconosciuto dalla normativa europea. Se il router fornito dal tuo ISP non ti soddisfa, non sei obbligato a usarlo. Puoi scegliere liberamente il tuo router.&lt;/p&gt;

&lt;p&gt;Ed è proprio qui che entra in gioco OpenWrt.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Router con OpenWrt
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;OpenWrt è un sistema operativo Linux progettato per dispositivi embedded. Invece di offrire un firmware chiuso e statico, mette a disposizione un filesystem completamente scrivibile e un sistema di gestione dei pacchetti. In questo modo non sei più limitato alle applicazioni e alle configurazioni decise dal produttore, ma puoi personalizzare il dispositivo installando il software che preferisci. (&lt;a href="https://github.com/openwrt/openwrt" rel="noopener noreferrer"&gt;OpenWrt GitHub Repo&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In altre parole, OpenWrt è semplicemente un sistema operativo, proprio come Debian, Windows o macOS. I router moderni non sono altro che piccoli computer. Hanno un processore, memoria RAM e memoria di archiviazione. Per molti aspetti sono paragonabili a un Raspberry Pi.&lt;/p&gt;

&lt;p&gt;Personalmente preferisco Debian sul mio PC perché mi permette di avere il pieno controllo del sistema. Per lo stesso motivo utilizzo OpenWrt sul router: mi consente di configurare la mia rete domestica esattamente come desidero.&lt;/p&gt;

&lt;h4&gt;
  
  
  &amp;nbsp;2.1 Qualsiasi router può eseguire OpenWrt?
&lt;/h4&gt;

&lt;p&gt;No. Essendo un sistema operativo, OpenWrt ha bisogno di hardware compatibile. Come qualsiasi computer, anche un router deve avere:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;un processore (CPU) che esegue le istruzioni;&lt;/li&gt;
&lt;li&gt;memoria RAM per eseguire il sistema operativo;&lt;/li&gt;
&lt;li&gt;memoria flash dove conservare il firmware, la configurazione e gli eventuali pacchetti installati.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;E, proprio come accade con i PC, l'architettura del processore è fondamentale. Un sistema operativo deve essere compilato per la CPU su cui verrà eseguito.&lt;/p&gt;

&lt;p&gt;Ad esempio, non è possibile installare una versione di Debian compilata per processori x86 su un Mac con Apple Silicon, perché appartengono ad architetture completamente diverse. Lo stesso vale per i router. Non tutti i modelli sono compatibili con OpenWrt.&lt;/p&gt;

&lt;p&gt;Ed è proprio questo il motivo per cui, poco fa, criticavo la scheda tecnica del gateway TIM. Sapere soltanto che il dispositivo ha tre porte LAN e il Wi-Fi dual band dice ben poco.&lt;/p&gt;




&lt;p&gt;Prima di iniziare a configurare un router "intelligente", però, è necessario chiarire un'altra cosa.&lt;/p&gt;

&lt;p&gt;Che cosa stiamo realmente configurando? La rete? Il Wi-Fi? Internet? La LAN? La WAN? Che ruolo ha il router? A cosa si collega? E che cosa si collega al router?&lt;/p&gt;

&lt;p&gt;La prossima sezione servirà proprio a rispondere a queste domande.&lt;/p&gt;




&lt;h3&gt;
  
  
  &amp;nbsp;3. Come il tuo ISP ti collega a Internet
&lt;/h3&gt;

&lt;p&gt;WAN è l'acronimo di Wide Area Network. Per semplificare, puoi pensarla come "il resto del mondo", ovvero Internet.&lt;/p&gt;

&lt;p&gt;Quando a casa ti colleghi al Wi-Fi, il tuo obiettivo è quasi sempre uno solo: raggiungere Internet senza utilizzare la connessione dati del telefono e, possibilmente, con una copertura e prestazioni migliori.&lt;/p&gt;

&lt;p&gt;A fornirti questo accesso è il tuo Internet Service Provider (ISP). Ma in che modo arriva Internet fino a casa? Esistono due possibilità: via cavo oppure via radio.&lt;/p&gt;

&lt;h4&gt;
  
  
  3.1 Internet senza fili (FWA)
&lt;/h4&gt;

&lt;p&gt;"Wireless" non significa che il router comunica via Wi-Fi direttamente con la sede del tuo operatore. Nel caso della Fixed Wireless Access (FWA), la connessione avviene tramite onde radio trasmesse dalle antenne dell'ISP. A questo punto qualcuno potrebbe chiedersi:&lt;/p&gt;

&lt;p&gt;"Quindi è semplicemente Internet mobile?"&lt;/p&gt;

&lt;p&gt;In parte sì. Sia la rete mobile sia la FWA utilizzano onde radio, ma hanno scopi diversi. La rete mobile è progettata per dispositivi in movimento, come smartphone e tablet.&lt;/p&gt;

&lt;p&gt;La FWA, invece, è pensata per un punto fisso. Sul tetto o sul balcone dell'abitazione viene installata un'antenna orientata verso una specifica stazione radio dell'operatore. Questo permette di ottenere una connessione generalmente più stabile rispetto a quella di uno smartphone, anche se, nella maggior parte dei casi, non raggiunge le prestazioni della fibra ottica. La FWA è particolarmente diffusa nelle aree rurali o dove non è ancora arrivata una rete cablata.&lt;/p&gt;

&lt;h4&gt;
  
  
  3.2 Internet via cavo
&lt;/h4&gt;

&lt;p&gt;Quando invece la connessione arriva tramite un cavo, le tecnologie più diffuse sono due.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;FTTH (Fiber To The Home), cioè fibra ottica fino all'abitazione.&lt;/li&gt;
&lt;li&gt;DSL, che utilizza il tradizionale doppino telefonico in rame.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esiste anche una soluzione intermedia chiamata FTTC (Fiber To The Cabinet). In questo caso la fibra arriva fino all'armadio stradale più vicino, mentre l'ultimo tratto verso casa continua a utilizzare il vecchio doppino telefonico. È interessante notare che la disponibilità della fibra dipende molto dalla posizione geografica.&lt;/p&gt;

&lt;p&gt;Può capitare che un edificio sia già raggiunto dalla fibra ottica mentre quello di fronte debba ancora affidarsi alla DSL o alla FWA.&lt;/p&gt;

&lt;h4&gt;
  
  
  3.3 FTTH: la fibra arriva dentro casa
&lt;/h4&gt;

&lt;p&gt;Quando hai una connessione FTTH, il cavo in fibra ottica entra direttamente nella tua abitazione e termina in un dispositivo chiamato ONT (Optical Network Terminal).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ISP
  │
Fiber
  │
─────────────── Street
  │
Fiber
  │
🏠 Your home
  │
ONT
  │ Ethernet
Router
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se vivi in un condominio, la disponibilità della FTTH dipende dal fatto che l'edificio sia stato collegato oppure no alla rete in fibra. Nel mio caso, fortunatamente, lo era.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. ONT, modem e router
&lt;/h3&gt;

&lt;p&gt;Quando ho attivato la mia linea Fastweb, il tecnico ha portato la fibra ottica fino al mio appartamento. Una volta terminato il lavoro, mi sono ritrovato con due "scatole". La prima era collegata direttamente al cavo in fibra. La seconda era collegata alla prima tramite un normale cavo Ethernet.&lt;/p&gt;

&lt;p&gt;Nel mio caso erano due dispositivi distinti, anche se in molte installazioni possono essere integrati in un unico apparato.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvps25zm54of11a3x7ng9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvps25zm54of11a3x7ng9.png" alt="Box 1: ONT (the view from the back)" width="800" height="339"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnz3qs05dy5svj06snulv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnz3qs05dy5svj06snulv.png" alt="Box2: Router (the viw from the back" width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Prima di capire che cosa faccia ciascuno di essi, conviene dare un'occhiata allo schema generale.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3206pgs4xy7czec687vf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3206pgs4xy7czec687vf.png" alt="ISP network - ONT - Router - LAN" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;a href="https://www.blaszczak.pl/internet-swiatlowodowy-ftth-i-ont/" rel="noopener noreferrer"&gt;Robert Błaszczak: Internet światłowodowy FTTH i ONT&lt;/a&gt;
Little remark for _ACKCHYUALLY_ readers: comment n 4. is correct for ISP that use DHCP.



&lt;h4&gt;
  
  
  &amp;nbsp;4.1 ONT e modem: sono la stessa cosa?
&lt;/h4&gt;

&lt;p&gt;No.&lt;/p&gt;

&lt;p&gt;Il primo dispositivo, quello in cui arriva la fibra ottica, è un ONT (Optical Network Terminal). Non è corretto chiamarlo né router né modem, anche se spesso gli operatori usano questi termini in modo piuttosto disinvolto. Un modem prende il proprio nome da MOdulator-DEModulator.&lt;/p&gt;

&lt;p&gt;Il suo compito è convertire i dati digitali in un segnale adatto al mezzo trasmissivo utilizzato e compiere l'operazione inversa quando riceve dati. L'ONT svolge un ruolo simile, ma specifico per la fibra ottica.&lt;/p&gt;

&lt;p&gt;Converte infatti il segnale luminoso proveniente dalla fibra in normali frame Ethernet, che possono essere gestiti dal router. In altre parole, rappresenta il punto di collegamento tra la rete ottica del tuo operatore e la rete Ethernet di casa.&lt;/p&gt;

&lt;h4&gt;
  
  
  &amp;nbsp;4.2 Il router
&lt;/h4&gt;

&lt;p&gt;Il secondo dispositivo è il router. Per capire il suo ruolo, immagina di collegare direttamente il cavo Ethernet proveniente dall'ONT al tuo computer.&lt;/p&gt;

&lt;p&gt;Se il tuo operatore non richiede ulteriori forme di autenticazione, il computer riceverebbe direttamente un indirizzo IP pubblico e potrebbe navigare su Internet. Ma cosa succederebbe se volessi collegare anche lo smartphone? O la Smart TV? O un altro computer?&lt;/p&gt;

&lt;p&gt;Dovresti continuare a scollegare e ricollegare il cavo ogni volta. È esattamente questo il problema che il router risolve. Il router si collega a Internet tramite la porta WAN e crea la tua rete locale (LAN).&lt;/p&gt;

&lt;p&gt;Oltre a questo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;assegna indirizzi IP privati ai dispositivi della rete tramite DHCP;&lt;/li&gt;
&lt;li&gt;instrada il traffico tra LAN e Internet;&lt;/li&gt;
&lt;li&gt;esegue la Network Address Translation (NAT);&lt;/li&gt;
&lt;li&gt;protegge la rete locale tramite un firewall;&lt;/li&gt;
&lt;li&gt;e, nella maggior parte dei router domestici, fornisce anche la rete Wi-Fi grazie al punto di accesso integrato.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In poche parole, il router è il direttore del traffico della tua rete domestica.&lt;/p&gt;




&lt;p&gt;Se invece il tuo operatore ti ha fornito un solo dispositivo — come nel caso dell'offerta TIM vista in precedenza — significa che ONT e router sono integrati nello stesso apparato.&lt;/p&gt;

&lt;p&gt;Questo comporta una conseguenza importante. Non puoi semplicemente scollegarlo e sostituirlo con un router qualsiasi.&lt;/p&gt;

&lt;p&gt;Hai due possibilità:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;utilizzare un gateway che integri anch'esso un ONT compatibile con la tecnologia utilizzata dal tuo operatore;&lt;/li&gt;
&lt;li&gt;oppure, molto più comunemente, richiedere un ONT esterno e collegare a quello il tuo router personale.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ed è proprio qui che torna in gioco la normativa sul Modem Libero. Se l'ONT è integrato nel router fornito dall'operatore, il cliente ha il diritto di richiedere l'installazione di un ONT esterno, così da poter utilizzare liberamente il proprio router.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Considerate le condizioni di scenario tecnologico e di mercato attuale, per le offerte in tecnologia FTTH, è ammessa la restrizione in materia di scelta dell’ONT a condizione che la stessa non sia integrata con il router. Nel caso in cui l’offerta preveda una ONT integrata con il router, deve essere sempre possibile per l’utente richiedere la fornitura e l’installazione di una ONT esterna. In tal caso, la volontà dell’utente di avere una ONT esterna (funzionale alla libera scelta del terminale) dev’essere accertata esplicitamente già in sede di conclusione del contratto. Inoltre, qualora l’utente nel corso dell’esecuzione del contratto decida successivamente di volere usare un proprio apparato, i tempi di installazione dell’ONT esterna dovranno essere tempestivi (segnatamente, entro 5 giorni lavorativi).&lt;a href="https://www.agcom.it/agcom-per-te/i-miei-diritti/internet-aperta-e-modem-libero" rel="noopener noreferrer"&gt;AGCOM: In caso di servizio fornito con tecnologia FTTH (Fiber To The Home), l’operatore può imporre l’utilizzo di una specifica ONT (Optical Network Termination)? &lt;/a&gt;&lt;/em&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;L'operatore è tenuto a effettuare l'installazione senza ritardi ingiustificati e, secondo le indicazioni di AGCOM, entro cinque giorni lavorativi.&lt;/p&gt;




&lt;p&gt;Nello schema precedente ho mostrato come sono collegati tra loro ISP, ONT e router. Ho anche accennato al fatto che è l'ISP ad assegnare un indirizzo IP al router.&lt;/p&gt;

&lt;p&gt;Ma come funziona davvero questo meccanismo? La risposta è meno banale di quanto sembri.&lt;/p&gt;

&lt;p&gt;Nella prossima parte seguirò i primi istanti di vita del router sulla rete dell'operatore: vedremo come ottiene il suo indirizzo IP tramite DHCP, come funziona il NAT e perché molti utenti oggi si trovano dietro il Carrier-Grade NAT (CGNAT).&lt;/p&gt;

</description>
      <category>openwrt</category>
      <category>isp</category>
      <category>ont</category>
      <category>modemlibero</category>
    </item>
    <item>
      <title>OpenWrt: Your Router, Your Rules - Part 0.5: NAT and CGNAT</title>
      <dc:creator>aL</dc:creator>
      <pubDate>Fri, 17 Jul 2026 21:40:54 +0000</pubDate>
      <link>https://dev.to/dev-charodeyka/openwrt-your-router-your-rules-part-05-nat-and-cgnat-203m</link>
      <guid>https://dev.to/dev-charodeyka/openwrt-your-router-your-rules-part-05-nat-and-cgnat-203m</guid>
      <description>&lt;p&gt;In the previous article, I covered a different aspect of home Internet networking. I showed, with references to EU regulations, that users have the right to choose the equipment they use to access the Internet instead of being forced to use their ISP's device(s). I also explained which devices are actually needed in a home network and what each of them does. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;I still remember that this series is about OpenWrt, and that's exactly where it's heading.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Before getting there, though, there's one important question to answer:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How does a router actually get onto the Internet?&lt;/strong&gt; What happens the moment an Ethernet cable is plugged into the router's WAN port?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Part 0.5:
 ├ what your ISP can and cannot require
 ├ OpenWrt
 ├ home internet (WAN &amp;amp; Fiber)
 └ ONT vs modem vs router
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  1. IP address lease
&lt;/h3&gt;

&lt;p&gt;IP addresses can be imagined like mailing addresses, while network traffic, divided into packets, can be imagined like envelopes carrying letters. When you stumbled upon this article, your device actually started communicating with Dev.to's IP address (after Google redirected you here). Every request your browser makes is sent to one of Dev.to's servers, which has its own IP address. While you're reading this article, that communication doesn't stop. Your browser keeps exchanging packets with the server: loading images, checking for new data, fetching comments, and so on. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;You can think of this address as the "door" between your home network and the rest of the Internet. It is the address through which you interact with the Internet and through which the Internet can interact with you.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The important thing to understand is that this address has nothing to do with whether you use your ISP's router, a GL.iNet Flint, or any other router. Your public-facing address is assigned by your ISP, not by the router sitting in your home.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;NB! In the scope of this article I'll only talk about IPv4 addressing because it still dominates, and, for example, my ISP doesn't give me an IPv6 address at all.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When the ISP's technician gets all the network shaitan machinery working (the ONT, the router, or a single device that combines both), the router contacts the ISP's DHCP server -  (in most residential connections) (I'm skipping PPPoE) — and asks to join the network.&lt;/p&gt;

&lt;h4&gt;
  
  
  1.1 DHCP
&lt;/h4&gt;

&lt;p&gt;DHCP stands for Dynamic Host Configuration Protocol.&lt;/p&gt;

&lt;p&gt;Whenever a device connects to a network (client), it needs some information about this network to join it. Most importantly, it needs its own IP address on that network. It also needs to know the address of a router (the default gateway) that can forward traffic to other networks, and usually the address of one or more DNS servers that can translate domain names into IP addresses.&lt;/p&gt;

&lt;p&gt;Without DHCP, all of this information would have to be entered manually on every device. DHCP automates the process by assigning an IP address and providing the rest of the network configuration automatically.&lt;/p&gt;

&lt;p&gt;DORA process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Discover – The client: "Is there any DHCP server out there?"&lt;/li&gt;
&lt;li&gt;Offer – The DHCP server: "Yes! You can have 192.168.0.4."&lt;/li&gt;
&lt;li&gt;Request – the client politely asks: "I'd like to use 192.168.0.4, please."&lt;/li&gt;
&lt;li&gt;Acknowledge (ACK) – The server: "Approved. It's yours."&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once the router is connected to the WAN, it broadcasts a DHCP request (&lt;code&gt;DHCPDISCOVER&lt;/code&gt; message to locate available DHCP servers). The ISP's DHCP server receives it and assigns the router an IPv4 address.&lt;/p&gt;

&lt;p&gt;An IPv4 address is a number like &lt;code&gt;103.130.238.205&lt;/code&gt;, &lt;code&gt;8.8.8.8&lt;/code&gt;, &lt;code&gt;192.168.1.8&lt;/code&gt;, or &lt;code&gt;10.1.6.7&lt;/code&gt;. You can check the address with which you present yourself to the Internet on &lt;a href="https://whatismyipaddress.com/" rel="noopener noreferrer"&gt;What is my IP address?&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Network address translation
&lt;/h3&gt;

&lt;p&gt;After clicking the link above and seeing an identified address, you may think that it is your own unique personal address, almost like a tax number... BUT! no worries, in all likelihood, it is...:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftiapgflac9esbocri071.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftiapgflac9esbocri071.jpg" alt="Bugs Bunny communist meme" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shared IP address???&lt;/p&gt;

&lt;p&gt;But for the envelopes (data packets) to reach the correct destination, your public (visible on the Internet) IP address has to identify where they should arrive after being requested. You might therefore expect every Internet user to have their own unique public IP address... but nowadays it's very often not the case. ISPs can make thousands of customers share the same public IP address.&lt;/p&gt;

&lt;p&gt;WHY and HOW does this network communication not become a complete mess?&lt;/p&gt;

&lt;p&gt;WHY: It is because of the scarcity of IPv4 addresses. There are only about 4.3 billion possible IPv4 addresses (2³², since an IPv4 address is made of four 8 bit numbers). That may sound like a lot, but it really isn't. Before we even talk about enterprises, think about households. Nowadays it's completely normal for 2 people living together to own 4 - 6 Internet-connected devices each... Imagine if every single one of them needed its own unique public IP address just to access the Internet,  4.3 billions does not sound that big anymore.&lt;/p&gt;

&lt;p&gt;HOW: Network Address Translation (NAT) and Carrier Grade NAT.&lt;/p&gt;

&lt;p&gt;NAT literally does what its name says: it translates network addresses. But who performs this translation? Your router. And we are back to the main character of this article.&lt;/p&gt;

&lt;p&gt;I mentioned that the router creates Local Area Network. In case of house Wi-Fi this LAN becomes a network of all devices in this house connected to the same network created by router in wired or wireless mode. Inside this network, every device has its own UNIQUE PRIVATE IP address (192.168.x.x, 10.x.x.x, etc.), which cannot be reached directly from the Internet. In most of the cases, router handles the assignment of IP addresses to members of its network with his DHCP server. &lt;/p&gt;

&lt;p&gt;Why can't these private addresses be reached directly from the Internet? Because they were explicitly reserved for private networks. &lt;/p&gt;

&lt;p&gt;There are 3 private IPv4 address blocks: 10.0.0.0/8 (all 10.x.x.x); &lt;br&gt;
172.16.0.0/12 (all 172.16.0.0 – 172.31.255.255);&lt;br&gt;
192.168.0.0/16 (all 192.168.x.x). If your device has one of these addresses, it is not directly reachable from the public Internet. These addresses are free to reuse in millions of different homes, offices, schools, and companies all over the world &lt;strong&gt;because they are only meaningful inside their own local network&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Internet
     |
Public IP: 77.104.200.1
     |
Your Router --&amp;gt; does NAT
     |
-----------------------------
192.168.1.10  PC
192.168.1.20  Phone
192.168.1.30  Some robot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, if my PC got &lt;code&gt;192.168.4.17&lt;/code&gt; from the router, how does it reach the Internet?&lt;/p&gt;

&lt;p&gt;Whenever a device that is part of the LAN opens a website, the router remembers something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;laptop: 192.168.4.17:portX
        ↓ sent request to (Persian cats website)
92.113.23.178
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and when another device opens another website:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mobile phone:192.168.4.20:portY 
        ↓ sent request to (Parmesan choooeeese)
89.118.107.132
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The router also replaces the private source address (&lt;code&gt;192.168.x.x&lt;/code&gt;) with its public IP address before sending the packet out to the Internet.&lt;/p&gt;

&lt;p&gt;When a reply arrives from &lt;code&gt;89.118.107.132&lt;/code&gt;, the router looks it up in its NAT table and forwards it to the mobile phone. When a reply arrives from &lt;code&gt;92.113.23.178&lt;/code&gt;, it forwards it to the laptop, which happily displays a huge Persian cat picture.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Your router continuously keeps a lil table of these address and port "translations". This is called the **NAT table&lt;/em&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When replies come back, the router knows exactly which device requested them and forwards the packets accordingly. This translation between private and public addresses is called &lt;strong&gt;Network Address Translation (NAT)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The important part is that &lt;strong&gt;only the router's PUBLIC IP address is visible on the Internet&lt;/strong&gt;. All devices in your house LAN are identified in Internet by this address. And that public IP address is assigned by ISP.&lt;/p&gt;

&lt;p&gt;Now, about CGNAT. &lt;/p&gt;

&lt;h4&gt;
  
  
  2.1 CGNAT
&lt;/h4&gt;

&lt;p&gt;Carrier Grade NAT (CGNAT) is NAT performed by ISP. Instead of a handful of devices in your home LAN sharing one public IP address, thousands of customers with their dozen of devices can share the same public IP.&lt;/p&gt;

&lt;p&gt;The routers of those customers don't receive public IP addresses on their WAN interface. Instead, they receive carrier-private addresses from ranges reserved for that purpose.&lt;/p&gt;

&lt;p&gt;ISP keeps track of every connection and makes sure all little packettini travel to the Internet and reach back the correct customer. It acts like a giant traffic controller, making sure replies from the Internet always end up at the right destination.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
                   Public IP
               93.44.120.15
                     |
           ISP Carrier-Grade NAT
      ---------------------------------
      |               |               |
  Your router    Alice's router   Bob's router
 192.168.1.x      192.168.0.x      10.0.0.x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every customer still has their own private home network, but the ISP performs another NAT before packets reach the Internet. So if you are CGNAT-ted by your ISP then when you visit a website, the chain actually looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your device (192.168.4.10) 
↓ 
Your router → performs NAT 
↓ 
10.x.x.x (carrier-private address assigned by your ISP) 
↓ 
ISP CGNAT 
↓ 
93.44.120.15 (the public IP address visible on the Internet) 
↓ 
Internet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your ISP keeps a ginormous translation table that remembers which customer each connection belongs to. That's why everything still works, even though hundreds of people appear to have the same public IP.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why CGNAT is nasty?
&lt;/h4&gt;

&lt;p&gt;What to care about? For normal browsing, gaming, streaming, YouTube, Discord ... nothing to worry. The problems start when someone on the Internet needs to initiate a connection to you.&lt;/p&gt;

&lt;p&gt;For example, suppose you want to host a Minecraft server or SSH into your home PC from your friend house. If you had a stable public IPv4 &lt;/p&gt;

&lt;p&gt;With a normal public IPv4, you can just simply configure port forwarding on your router.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Internet
     |
93.44.120.15:1234
     |
Your Router (rule: redirect stuff to 192.168.1.50:4321)
     |
192.168.1.50:4321

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anyone connecting in SSH 93.44.120.15:51820 reaches your PC.&lt;/p&gt;

&lt;p&gt;With CGNAT, it is impossible. Because there are many other devices sharing same IP. The ISP has no idea where to send arrived requests, because nobody created that connection from inside first. Incoming connections are simply dropped, and port forwarding on your own router becomes useless. Your router never even gets the packets.&lt;/p&gt;

&lt;p&gt;But it is not all that hopeless. Most of ISP provide a service for a humble (not really) remuneration of static public IP. Fastweb (my ISP) provides for 3 euros extra per month.&lt;/p&gt;




&lt;p&gt;Whether you're using a router with OpenWrt or your ISP's router, NAT works exactly the same way. Understanding it now will make everything that follows - port forwarding, WireGuard, VLANs, and firewall rules - much easier.&lt;/p&gt;

</description>
      <category>nat</category>
      <category>networking</category>
      <category>ipv4</category>
      <category>dhcp</category>
    </item>
    <item>
      <title>OpenWrt: Your Router, Your Rules - Part 0: Your Router = Your Right.</title>
      <dc:creator>aL</dc:creator>
      <pubDate>Fri, 17 Jul 2026 19:25:07 +0000</pubDate>
      <link>https://dev.to/dev-charodeyka/openwrt-your-router-your-rules-part-0-your-router-your-right-j5j</link>
      <guid>https://dev.to/dev-charodeyka/openwrt-your-router-your-rules-part-0-your-router-your-right-j5j</guid>
      <description>&lt;p&gt;Initially, I planed to write a practical guide on how to configure multiple SSIDs mapped to different VLANs using OpenWrt and LuCI. But then I started diving deeper, and... well, it is what it is (as always). There are just so many concepts that need explaining first. I promise next parts will contain that technical guide.&lt;/p&gt;

&lt;p&gt;This part will probably be of interest for readers in the EU, even more so for those in Italy. I'll be referring to the European regulation to briefly discuss how ISPs provision Internet service (center of attention - equipment - router). I'm currently based in Italy, so I'm familiar with some of the Internet service peculiarities here.&lt;/p&gt;

&lt;p&gt;There is a lot to worry today about the privacy of our phones, computers, apps, and operating systems. Once one starts becoming curious about privacy, networking, and what actually happens to their Internet traffic, sooner or later attention turns to the device sitting quietly in the corner of the room...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Floir1ui17f5yaef27qq3.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Floir1ui17f5yaef27qq3.webp" alt="Side-eye to Wi-Fi router" width="799" height="644"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This guide is for people who want to understand and take control of their home network using a router that can run OpenWrt. OpenWrt itself is not really a tool for complete networking beginners. However, it is not as complicated as it may seem. It comes with a user-friendly web interface (LuCI).&lt;/p&gt;

&lt;p&gt;If you found this article, chances are you already have an OpenWrt compatible router or are thinking about getting one. Maybe you've picked up bits of networking knowledge over time and are ready to go one step further, but you're still a little unsure where to begin. Or perhaps you're hesitant to change anything because you're worried about breaking something or doing something your Internet Service Provider doesn't allow. &lt;/p&gt;

&lt;p&gt;I wrote this guide because I found myself collecting information from dozens of different places and I wanted to put it together in one place. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;I also believe Internet Service Providers should provide clear and transparent information about the services and equipment they offer and should never limit customers' freedom to choose their own equipment or switch providers. If they don't, fortunately there is a community that shares knowledge, and I want to contribute to this community.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For my Italian readers, I hope this guide helps you know what to ask and where to look before choosing or changing an Internet provider.&lt;/p&gt;

&lt;p&gt;What I will cover in this part:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Part 0:
 ├ what your ISP can and cannot require
 ├ OpenWrt
 ├ home internet (WAN &amp;amp; Fiber)
 └ ONT vs modem vs router
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  1. Internet Service Providers and EU regulations.
&lt;/h3&gt;

&lt;p&gt;ISP = Internet Service Provider&lt;/p&gt;

&lt;p&gt;When someone wants internet access at home instead of relying on mobile data (with its limited data allowance, weaker signal, and often lower or less consistent speeds) they first need to identify Internet Service providers available in their geographical area.&lt;/p&gt;

&lt;p&gt;Usually, the choice isn't huge, but having 3 - 5 providers to choose from is fairly common. In general, the larger the city you live in, the more options you're likely to have.&lt;/p&gt;

&lt;p&gt;In the European Union, competition is protected by strong antitrust and consumer protection regulations. The goal is to ensure that, when you need a service - in this case, internet access - you have more than one provider to choose from and that companies compete on price, speed, and the features they offer. Not every plan is the same "€27.99/month home internet" package.&lt;/p&gt;

&lt;h4&gt;
  
  
  1.1 Internet Service as a market's good
&lt;/h4&gt;

&lt;p&gt;From an economic perspective, internet service is a particular "good". Its physical barebone has many characteristics of a &lt;strong&gt;natural monopoly&lt;/strong&gt;, similar to railways, electricity grids, or road infrastructure. Building the underlying physical network is extremely expensive, so duplicating it over and over again is impossible.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;NB: Here, by internet service, I mean the infrastructure and service that provide access to the global Internet, the carrier that enables connectivity, not the World Wide Web itself, which consists of websites, applications, and other content ecc.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The internet itself doesn't consist of a single worldwide cable, but the same principle applies to many parts of the infrastructure. Long-distance backbone networks, local fiber deployments, and even wireless infrastructure require enormous investments. Because of that, different Internet Service Providers often share infrastructure or lease access from one another instead of building an entirely separate network. So while competition exists at the service level, the underlying physical infrastructure is often shared, making the market more nuanced and &lt;strong&gt;puts customers in a vulnerable and disadvanatgeous position&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  1.2 Italian regulations on switching ISP.
&lt;/h4&gt;

&lt;p&gt;In Italy there are 4 bigger "players" on Internet Service Provisioning market: Vodafone, TIM, Fastweb, Wind3.&lt;/p&gt;

&lt;p&gt;European competition laws are designed to ensure that you not only have a choice when signing up for internet service, but also that you're free to change providers later without facing unreasonable financial penalties, &lt;strong&gt;even if you're using equipment supplied by your ISP&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In Italy, these consumer protections are implemented and enforced by AGCOM - Autorità per le Garanzie nelle Comunicazioni. One of the key principles is that IF/when you want to &lt;strong&gt;change&lt;/strong&gt; electronic communications operator (ISP as example of this operator), termination and migration charges cannot exceed either the customer's average monthly fee or the actual costs incurred by the operator, whichever is lower. Operators also cannot simply demand repayment of every promotional discount they have ever applied; any repayment must be fair and proportionate. This wasn't always the case. Years ago, some ISPs effectively locked customers in by imposing very high exit fees, making switching providers much more difficult than it should have been.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj5k8uzw6xjw9lr3zrv3t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj5k8uzw6xjw9lr3zrv3t.png" alt="AGCOM: you can always choose between a variety of packages and change both landline and mobile operators according to your needs" width="799" height="298"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;a href="https://www.agcom.it/switching-operator-and-terminating-contract" rel="noopener noreferrer"&gt;AGCOM on Switching operator and terminating contract&lt;/a&gt;



&lt;p&gt;&lt;strong&gt;BUT!&lt;/strong&gt; Not everything is that bright.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk36pldbwl5omq7iyr35w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk36pldbwl5omq7iyr35w.png" alt="AGCOM: Charges for terminating contract" width="799" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;users will be able to carry on paying the remaining instalments due for goods and services even if they withdraw from the contract before expiry of the instalment plan, which may not in any case exceed 24 months (&lt;a href="https://www.agcom.it/switching-operator-and-terminating-contract" rel="noopener noreferrer"&gt;Charges for terminating contract&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;And here's the interesting part&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;What exactly is this "good" that you may keep an EX CUSTOMER paying for up to the next 24 months AFTER TERMINATION OF CONTRACT? Surely not the internet service - the customer just terminated that contract and switched to another ISP. So what remains?&lt;/p&gt;




&lt;h4&gt;
  
  
  1.3 Home Internet equipment provided by you ISP.
&lt;/h4&gt;

&lt;p&gt;The equipment supplied by the Operator (ISP)!  For a typical home customer, this usually means a modem, a router (or a single device that combines both), and a couple of cables.&lt;/p&gt;

&lt;p&gt;But wait...&lt;/p&gt;

&lt;p&gt;If we're not talking about an enterprise customer or a business with racks full of networking equipment, what &lt;strong&gt;kind of expensive equipment are we supposedly paying off for up to 24 months&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;And that brings us to the star of this article: the router provided by ISP!&lt;/p&gt;




&lt;h4&gt;
  
  
  1.4 TIM and their modem for 240 euros bundled with Internet Service offer.
&lt;/h4&gt;

&lt;p&gt;Here are TIM's current home internet offers (as of 10/07/2026):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnowr4k2tyvpacztnc7iy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnowr4k2tyvpacztnc7iy.png" alt="TIM Wi-Fi at Home offers" width="800" height="501"&gt;&lt;/a&gt;&lt;/p&gt;
Access date: 10 July 2026, 07:07 PM [Source](https://www.tim.it/fisso-e-mobile/fibra-e-adsl/fibra-internet-casa)



&lt;p&gt;Clicking on &lt;strong&gt;Dettagli costi&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffbjsod5kqg450ic1z4ef.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffbjsod5kqg450ic1z4ef.png" alt="Cost details of TIM offer: modem that costs 240 euros included" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;MODEM TIM HUB+ (per tecnologia FTTC), MODEM TIM HUB Pro (per tecnologia FTTH)&lt;br&gt;
€240, payable in 48 monthly installments of €5 each. During the first 24 months after activating the offer, you'll receive a €5/month discount on the modem purchase, reducing the total cost of the device from €240 to €120. (&lt;a href="https://www.tim.it/fisso-e-mobile/fibra-e-adsl/fibra-internet-casa" rel="noopener noreferrer"&gt;Source accessed 10/07/2026, free translation&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu2wl7c54kqs2ubczcwxx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu2wl7c54kqs2ubczcwxx.png" alt="Caso di recesso prima di 48 mesi" width="800" height="156"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you terminate the contract before the end of the 48-month period, you will be required to pay the remaining installments for the TIM modem, but only the paid installments, not those covered by the promotional discount. (&lt;a href="https://www.tim.it/fisso-e-mobile/fibra-e-adsl/fibra-internet-casa" rel="noopener noreferrer"&gt;Source accessed 10/07/2026, free translation&lt;/a&gt;).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;240 euros for "modem TIM HIB+/PRO". €240 isn't necessarily an unreasonable price for a home gateway (ONT + router actually, yeah to call it modem is wrong and misleading). BUT! The real question is whether the device you're paying for is actually worth €240 - and whether it's the device you want to own.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffv8zxg5d6rtt2qqijist.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffv8zxg5d6rtt2qqijist.png" alt="Technical specks of modem in question" width="799" height="547"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hmm... okay... 3 LAN ports, one WAN port, dual-band Wi-Fi...And? &lt;/p&gt;

&lt;p&gt;What about the CPU? How much RAM does it have? How much storage? Which Wi-Fi chipset is it using? Does it support OpenWrt? How am I supposed to evaluate whether it's worth €240 with a specification sheet like this?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Would anyone browsing an online store willingly spend €240 on a home gateway described only by the information above?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Probably not. And no one shouldn't.&lt;/p&gt;

&lt;p&gt;Not because it's 100% a bad device, it may well be a perfectly capable gateway, but because a customer doesn't have enough information to make an informed decision.&lt;/p&gt;




&lt;h4&gt;
  
  
  1.5 EU law on freedom to choose your own equipment to use to access the Internet.
&lt;/h4&gt;

&lt;p&gt;European law protects your freedom to choose your own equipment to use to access the Internet. Under &lt;a href="https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:32015R2120" rel="noopener noreferrer"&gt;Regulation (EU) 2015/2120 on open internet access&lt;/a&gt;, &lt;strong&gt;ISPs cannot force you to use their equipment as long as your own device complies with the applicable technical requirements&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkmlt4akkb1k713g8ve7z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkmlt4akkb1k713g8ve7z.png" alt="AGCOM on freedom to use terminal equipment of your own choice" width="799" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Key takeaways:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Operators may not refuse to connect terminal equipment chosen by the user, provided that it complies with applicable national and European regulations."&lt;br&gt;
"They may not impose additional charges, unjustified delays, or discriminate against the quality of the services provided."&lt;br&gt;
"Providers are required to supply adequate information on how to connect to their network and configure the customer's chosen replacement or additional terminal equipment."&lt;br&gt;
"If the operator supplies terminal equipment together with the internet service, it must ensure that the customer is free to choose their own equipment by providing all the necessary technical specifications."&lt;br&gt;
"If the service is provided over FTTH and the ONT is integrated with the router, the user must always be able to request the installation of an external ONT." (&lt;a href="https://www.agcom.it/agcom-per-te/i-miei-diritti/internet-aperta-e-modem-libero" rel="noopener noreferrer"&gt;source, original in Italian, free translation&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I am not a customer of TIM, and I can't comment on how they handle requests from customers who want to use their own equipment and what happens with that 240 euros gateway bundled with Internet service offer.&lt;/p&gt;

&lt;p&gt;My ISP is Fastweb. If I decide to leave Fastweb, my contract states that the migration/termination cost is around €30. Fastweb supplied me by default with a router and a separate ONT. I don't use their router, instead, I use my own router, but I do use the ONT they provided, since that's the device that terminates the fiber connection.&lt;/p&gt;

&lt;p&gt;That's a common setup for fiber connections, and in many cases it's exactly what you want.&lt;/p&gt;




&lt;p&gt;Now that I have covered the legal side, it is clear that using your own networking equipment is your right. If you are not satisfied with the equipment provided by your Internet Service Provider, you do not have to settle for it.&lt;/p&gt;

&lt;p&gt;You are free to use your own router. Routers capable of running OpenWrt are gaining more and more popularity because of the level of customization and control they offer.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Router with OpenWrt
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;OpenWrt is a Linux operating system targeting embedded devices. Instead of trying to create a single, static firmware, OpenWrt provides a fully writable filesystem with package management. This frees you from the application selection and configuration provided by the vendor and allows you to customize the device through the use of packages to suit any application (&lt;a href="https://github.com/openwrt/openwrt" rel="noopener noreferrer"&gt;OpenWrt GitHub Repo&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, OpenWrt is just like Debian, Windows, macOS ecc. Modern routers have a CPU, storage, and RAM. They're basically tiny computers, similar to a Raspberry Pi. OpenWrt is an operating system. Just like I prefer Debian to get the full experience out of my PC, OpenWrt lets me get the full experience out of my router and do advanced network configuration of my house LAN (local area network). &lt;/p&gt;

&lt;h4&gt;
  
  
  2.1 Any router can run OpenWrt?
&lt;/h4&gt;

&lt;p&gt;If OpenWrt is an operating system, then, just like any other operating system, it needs hardware to run on. That hardware needs a &lt;strong&gt;CPU&lt;/strong&gt; to execute instructions, &lt;strong&gt;RAM&lt;/strong&gt; to run the operating system and applications, and &lt;strong&gt;storage&lt;/strong&gt; to hold the firmware, configuration, and any additional packages you decide to install.&lt;/p&gt;

&lt;p&gt;And, just like with PCs, the CPU architecture matters. An operating system must support the processor it runs on. For example, you can't install the Debian on an Apple Silicon Mac because it's built for a completely different CPU architecture.&lt;/p&gt;

&lt;p&gt;The same principle applies to routers. Not every router is capable of running OpenWrt. That's why I criticized the specifications of the TIM gateway shown earlier. Listing only the number of ports, Wi-Fi bands tells almost nothing about the hardware inside. Without knowing the CPU, RAM, storage, or chipset, it's impossible to evaluate the device properly, or even determine whether theorically it can run OpenWrt.&lt;/p&gt;

&lt;p&gt;Before moving on to the configuration of a smart cool router, it is important to understand what exactly you are able to configure. Network? Wi-Fi? Internet? LAN? WAN? Router plugs where? What connects to router? The next section is about all of it.&lt;/p&gt;




&lt;h4&gt;
  
  
  3. How  ISP connects you to the Internet.
&lt;/h4&gt;

&lt;p&gt;WAN stands for Wide Area Network. For simplicity, think of it as the outside world or "the Internet".&lt;/p&gt;

&lt;p&gt;At home, the whole point of connecting to Wi-Fi is usually to reach the WAN without using your mobile data plan and to get better coverage around the house.&lt;/p&gt;

&lt;p&gt;An Internet Service Provider (ISP) is the one who gives you access to the WAN. How is that access provided? Either in a wired or wireless way. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Wireless&lt;/em&gt; doesn't mean your router connects to your ISP's office via Wi-Fi. Instead, it uses radio signals transmitted by ISP towers. This is called Fixed Wireless Access (FWA). But isn't that just mobile internet? Well, kind of. Both use radio signals, but mobile internet is designed for devices that move around, while FWA is designed for a fixed location. Your house will have an outdoor antenna pointed at a specific ISP tower, which often results in a more stable connection than a phone on a cellular network, but it's not the best. Is FWA common? Mostly in rural or isolated areas where physical cables have not been brought yet.&lt;/p&gt;

&lt;p&gt;Speaking of cables, there are two wired ways to deliver Internet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;FTTH - Fiber To The Home OR Fiber To The Node — Fiber To The Node: optical fiber.&lt;/li&gt;
&lt;li&gt;DSL - over telephone lines.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And here's the interesting part: you may end up with DSL or FWA even if your neighbors across the street have fiber. Availability is very location-dependent.&lt;/p&gt;

&lt;p&gt;If fiber reaches your house/appartment (FTTH), that's generally the best-case scenario. The fiber-optic cable runs all the way into your home (or apartment), where it terminates in an ONT (Optical Network Terminal).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ISP
  │
Fiber
  │
─────────────── Street
  │
Fiber
  │
🏠 Your home
  │
ONT
  │ Ethernet
Router
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is not always like this, it can be that the fiber connection does not reach your home. Instead, it ends at a neighborhood node (a cabinet or distribution point), and the last stretch to your home uses existing copper telephone lines.&lt;/p&gt;

&lt;p&gt;If you live in an apartment building, getting fiber Internet depends on whether your building is connected to a fiber network or not. Luckily, mine was.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Modem vs. ONT vs. Router
&lt;/h4&gt;

&lt;p&gt;After signing the contract with Fastweb, their technician extended the fiber connection from somewhere in the building to my apartment. Then he had connected it to a box (Box 1). And that box (Box 1) to another box (Box 2). That is my case, but it can be just one box.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvps25zm54of11a3x7ng9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvps25zm54of11a3x7ng9.png" alt="Box 1: ONT (the view from the back)" width="800" height="339"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnz3qs05dy5svj06snulv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnz3qs05dy5svj06snulv.png" alt="Box2: Router (the viw from the back" width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I will provide upfront this scheme that shows what these Boxes so and how they are connected. I took a pic from &lt;a href="https://www.blaszczak.pl/internet-swiatlowodowy-ftth-i-ont/" rel="noopener noreferrer"&gt;the article on ONT&lt;/a&gt; (written in Polish) and slightly modified it and added some comments.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3206pgs4xy7czec687vf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3206pgs4xy7czec687vf.png" alt="ISP network - ONT - Router - LAN" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;a href="https://www.blaszczak.pl/internet-swiatlowodowy-ftth-i-ont/" rel="noopener noreferrer"&gt;Robert Błaszczak: Internet światłowodowy FTTH i ONT&lt;/a&gt;
Little remark for _ACKCHYUALLY_ readers: comment n 4. is correct for ISP that use DHCP.



&lt;h4&gt;
  
  
  4.1 ONT vs. Modem
&lt;/h4&gt;

&lt;p&gt;That white Box 1 where the fiber cable enters is not the router and cannot router and cannot cretae any home network. It is an &lt;strong&gt;ONT (Optical Network Terminal)&lt;/strong&gt;, it is not technically correct to call it modem OR router!&lt;/p&gt;

&lt;p&gt;A modem converts digital data into a signal suitable for transmission over a specific physical medium, and vice versa. Modem comes from: MOdulator + DEModulator. &lt;/p&gt;

&lt;p&gt;An ONT is not called a modem, but it performs a role analogous to one. Its job is to convert the optical signals traveling through the fiber cable into an electrical Ethernet frames that the router can understand and manage. In other words, it acts as a bridge between the fiber-based network provided by your ISP and your home router.&lt;/p&gt;

&lt;h4&gt;
  
  
  4.2 Router
&lt;/h4&gt;

&lt;p&gt;Box 2 is exactly the router. To understand its main job, imagine plugging the Ethernet cable coming from the ONT directly into the laptop. Assuming the ISP doesn't require additional authentication, this laptop would receive a public IP address from the ISP, and it would have Internet access. But to connect phone, TV or another laptop, one would  have to keep unplugging and reconnecting the cable every time. That's exactly the problem the router solves.&lt;/p&gt;

&lt;p&gt;A router connects to the Internet through its WAN interface and creates Local Area Network (LAN). Besides that, it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;assigns private IP addresses to your devices (DHCP),&lt;/li&gt;
&lt;li&gt;forwards traffic between your LAN and the Internet,&lt;/li&gt;
&lt;li&gt;performs Network Address Translation (NAT),&lt;/li&gt;
&lt;li&gt;protects its local network with a firewall,&lt;/li&gt;
&lt;li&gt;and, in most consumer routers, provides Wi-Fi through an integrated wireless access point.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words, a router is &lt;strong&gt;the traffic manager&lt;/strong&gt; of home network.&lt;/p&gt;

&lt;p&gt;If, in your case, your ISP supplied only a single "box" for your fiber connection (forementioned TIM offer), it means that the ONT, the device that converts the optical signal into Ethernet frames is integrated into the router. &lt;br&gt;
&lt;strong&gt;In that case, you can't simply replace the device with your own router.&lt;/strong&gt; You would either need a fully featured gateway that also includes an integrated ONT (or supports the same fiber technology), or,much more commonly,you can ask your ISP to provide a separate external ONT and connect your own router to it.&lt;/p&gt;

&lt;p&gt;Again, the "Open Internet (Internet Aperta) and Modem Libero" regulations protect your right to do exactly that.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If the operator supplies an ONT integrated into the router, the user must always have the option to request the supply and installation of an external ONT.&lt;br&gt;
In such cases, the user's intention to use an external ONT (to enable the free choice of terminal equipment) must be explicitly confirmed when the contract is concluded.&lt;br&gt;
Furthermore, if the user later decides to use their own equipment during the course of the contract, the operator must install the external ONT without undue delay, specifically within five working days. &lt;a href="https://www.agcom.it/agcom-per-te/i-miei-diritti/internet-aperta-e-modem-libero" rel="noopener noreferrer"&gt;AGCOM: In caso di servizio fornito con tecnologia FTTH&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As mentioned in the diagram above on how the ONT, router and ISP are connected. I also mentioned that the ISP assigns (leases) an IP address to the router. &lt;/p&gt;

&lt;p&gt;The next piece of the puzzle is understanding how IP addressing works de - facto (it isn't as straightforward as it might seem). That will be the topic of the next part of this series, where I will explain IP addressing, NAT, and Carrier Grade NAT (CGNAT), and how they affect your home network.&lt;/p&gt;

</description>
      <category>ont</category>
      <category>router</category>
      <category>isp</category>
      <category>networking</category>
    </item>
    <item>
      <title>Building Debian packages from source in bootstrapped Debian</title>
      <dc:creator>aL</dc:creator>
      <pubDate>Thu, 17 Apr 2025 15:35:38 +0000</pubDate>
      <link>https://dev.to/dev-charodeyka/building-debian-packages-from-source-in-bootstrapped-debian-9el</link>
      <guid>https://dev.to/dev-charodeyka/building-debian-packages-from-source-in-bootstrapped-debian-9el</guid>
      <description>&lt;p&gt;If you’re a curious user - especially one who’s into customizing your Debian - you’ve probably run into this situation at least once: you discover a cool software, but it’s either not available in your Debian’s package repositories, or it is there, but the version is outdated. &lt;/p&gt;

&lt;p&gt;Newer versions often bring in completely new features that you actually want to use. When I think of Debian Stable, a few examples come to mind.&lt;/p&gt;

&lt;p&gt;Terminal emulator &lt;a href="https://alacritty.org/" rel="noopener noreferrer"&gt;Alacritty&lt;/a&gt;, for instance. The version in the Debian Stable repo is dated: Alacritty of this version uses a .yml config file, but newer versions have switched to .toml.&lt;/p&gt;

&lt;p&gt;Or &lt;a href="https://github.com/aristocratos/btop" rel="noopener noreferrer"&gt;btop&lt;/a&gt;, the version in Debian Stable doesn’t support NVIDIA GPU monitoring, while the current version does.&lt;/p&gt;

&lt;p&gt;And then there's Hyprland, a window manager that’s only available in Debian Unstable and Testing (Trixie). On Debian Stable, it’s not even in the repos at all.&lt;/p&gt;

&lt;p&gt;This article is dedicated to a terminal-based file manager &lt;a href="https://github.com/sxyazi/yazi" rel="noopener noreferrer"&gt;yazi&lt;/a&gt; that is super mega cool, but it’s not available in any of Debian’s repositories - not in Stable, not in Testing, not even in Unstable.&lt;/p&gt;




&lt;p&gt;NB! Building applications from source isn’t necessarily hard in terms of the actual process, but making them work, and more importantly, making sure they don’t break your system, is another story.&lt;/p&gt;

&lt;p&gt;Small utility apps that are meant to run in user space (i.e. they don’t touch anything kernel related, like modules, are generally less risky in terms of stability of your Debian afterwards). The main problem is that there’s no guarantee they’ll work properly due to dependencies. The more dependencies an app has, the higher the risk that it won’t work at all.&lt;/p&gt;

&lt;p&gt;Also, be very careful when a piece of software wants to downgrade some packages as part of its dependency list. I strongly recommend avoiding that. Downgrading system packages can mess up your system a lot.&lt;/p&gt;

&lt;p&gt;Before building anything from source, always evaluate potential security risks. Don’t rush into compiling some random app that has minimal activity on GitHub - especially without reviewing the source code carefully.&lt;/p&gt;

&lt;p&gt;NB2! I’m currently using Debian Testing (aka Trixie), which is to become Debian 13, I hope soon. If you’re trying to get Yazi working on Debian Stable, I can’t guarantee it’ll work due to dependency versions.&lt;/p&gt;




&lt;p&gt;What’s so particular about building software from source in a debootstrapped Debian, and what does that even mean?&lt;/p&gt;

&lt;p&gt;If you’ve ever developed something in Python or used Node.js then this will probably make sense to you. You might be familiar with Python virtual environments, or how Node.js projects usually live in their own directories with all the installed libraries.&lt;/p&gt;

&lt;p&gt;When you’re working inside a Python environment and need some libraries, you install them into that isolated environment. Move to another project? Create a new environment, install only what you need, and that’s it. No system-wide Python mess. Same story with Node.js, you usually don’t install JS libraries globally, they just live in the project folder. Clean, contained.&lt;/p&gt;

&lt;p&gt;Now, Debian bootstrapping offers a kind of similar idea but in the context of system-level stuff.&lt;/p&gt;

&lt;p&gt;Let’s say you want to install Yazi, which is written in Rust. You’ll need Rust installed on your system to build it. Or maybe you want to build &lt;a href="https://github.com/fairyglade/ly?tab=readme-ov-file" rel="noopener noreferrer"&gt;Ly display manager&lt;/a&gt;, written in Zig—you’ll need to install the Zig compiler. Other programs written in C? You’ll need a C compiler and a bunch of build-time libraries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But here’s the key point: software often has one set of requirements to build and a completely different set of requirements to run.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example, with Yazi, you only need Rust to build it. Once that’s done, Rust isn’t required to run the app. So, what do you do? Install Rust, build the thing in 10 minutes, and then… what? Leave Rust on your system? Delete it? What if the software needed tons of other build-time tools that you’ll never use again?&lt;/p&gt;

&lt;p&gt;That’s where &lt;code&gt;debootstrap&lt;/code&gt; comes in. It lets you create a sort of mini Debian environment:a clean, isolated system within your system. It’s not a full second Debian install, but it is like a mini mirror of your Debian system where you can install/build/test whatever you want without messing up your main OS. It’s like a virtual "project" space for your system-level experiments.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;code&gt;debootstrap&lt;/code&gt; is a tool which will install a Debian base system into a subdirectory of another, already installed system. It doesn't require an installation CD, just access to a Debian repository. It can also be installed and run from another operating system, so, for instance, you can use debootstrap to install Debian onto an unused partition from a running Gentoo system. It can also be used to create a rootfs for a machine of a different architecture, which is known as "cross-debootstrapping". (&lt;a href="https://wiki.debian.org/Debootstrap" rel="noopener noreferrer"&gt;Debian Wiki: Debootstrap&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;First, you need to install &lt;code&gt;debootstrap&lt;/code&gt; package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt install debootstrap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, I think it is convenient to elevate your user and execute commands as root user ($ changes for # in code snippets, meaning that these commands are run by root user)&lt;/p&gt;

&lt;p&gt;First, I create a directory where my bootstrapped Debian will reside:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# mkdir /trixie-chroot
## If you use Debian Stable:
# mkdir /stable-chroot

## And then, I "deboostrap" Debian into this directory
## NB! I use Trixie, and I Debootstrap Trixie!!!

# debootstrap trixie /trixie-chroot http://deb.debian.org/debian/
## If you use Debian Stable:
# debootstrap stable /stable-chroot http://deb.debian.org/debian/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As I mentioned, bootstrapped Debian is not a fully functional system and cannot function outside of the "main" Debian on which it resides. That is due to the fact that it is exactly your main OS sharing with bootstrapped Debian some its components in order to enable a simulation of a complete runtime environment inside the bootstrapped Debian, allowing it to behave like a real system.&lt;/p&gt;

&lt;p&gt;This is achieved by mounting pseudo filesystems to bootstrapped Debian, or more correctly, &lt;code&gt;chroot&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;chroot on Unix-like operating systems is an operation that changes the apparent root directory for the current running process and its children. (&lt;a href="https://wiki.debian.org/chroot" rel="noopener noreferrer"&gt;Debain Wiki: chroot&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It may sounds complicated and scary, but actually it is just a couple of commands &lt;code&gt;mount&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;First thing to do is to mount the proc filesystem inside the chroot (/trixie-chroot in my case). The proc filesystem provides access to kernel and process information. Many system tools (like ps, top, etc.) rely on /proc to function correctly. Without it tools inside the chroot won’t be able to access process info or kernel parameters.&lt;/p&gt;

&lt;p&gt;Second, is to mount the sysfs filesystem to /trixie-chroot. sysfs exposes kernel devices and attributes. It's required by many parts of the system like udev, systemd, etc. Without it hardware-related commands (or anything interacting with kernel devices) may not work inside the chroot.&lt;/p&gt;

&lt;p&gt;Third, you need to provide information to chroot Debian about DNS nameservers, especially if you have some custom configuration. Otherwise you will not be able reach internet from chroot and install packages.&lt;/p&gt;

&lt;p&gt;All these complex sounding things can be done with 3 commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# mount proc /trixie-chroot/proc -t proc
# mount sysfs /trixie-chroot/sys -t sysfs
# cp /etc/hosts /trixie-chroot/etc/hosts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you can "login" into bootstrapped Debian!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# chroot /trixie-chroot /bin/bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Now, we can start building Yazi from source! I’ll be following the official &lt;a href="https://yazi-rs.github.io/docs/installation/#debian" rel="noopener noreferrer"&gt;installation guide&lt;/a&gt;...&lt;br&gt;
...with one interesting modification you might find useful for other Rust-native apps.&lt;/p&gt;

&lt;p&gt;But let’s get started.&lt;/p&gt;

&lt;p&gt;First, it's important to note that Yazi has various dependencies: because as a terminal file manager, it can do things like preview images and different file types right inside the terminal! And of course, this kind of functionality requires some additional software to be installed.&lt;/p&gt;

&lt;p&gt;For me, the dependencies of Yazi looked pretty familiar. You can check them one by one if you’re unsure.&lt;br&gt;
So why am I installing them into a Debian chroot if I don't actually plan to use it?&lt;br&gt;
Well, I'm not that advanced when it comes to Rust apps. I’ve got more experience building from source using make, and if you’ve ever done that, you probably know about the &lt;code&gt;./configure&lt;/code&gt; step—it scans your system and sets the right parameters for the build.&lt;/p&gt;

&lt;p&gt;What often happens is, if something’s missing on your system, that is not required by any core feature of to be built software, these features are just get skipped during build processes, because requirements for them are not satisfied. I’m not sure if Rust works exactly the same way, but either way, it’s not a big deal: I’m planning to destroy this chroot after installation anyway.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;chroot # apt update
chroot # apt install ffmpeg 7zip jq poppler-utils fd-find ripgrep fzf zoxide imagemagick
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here comes NB for Debian Stable users: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Note that these dependencies are quite old on some Debian/Ubuntu versions and may cause Yazi to malfunction. In that case, you will need to manually build them from the latest source. (&lt;a href="https://yazi-rs.github.io/docs/installation/#debian" rel="noopener noreferrer"&gt;Yazi Installation guide&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As the next step, I need to install Rust, of course:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;chroot # apt install curl
chroot # curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
chroot # rustup update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And after this... I diverge from the official installation guide. Actually, this project is awesome, and installing it is super easy—to the point that it will literally leave only Rust installed on your system afterwards. Here's the command from the installation guide -&lt;br&gt;
&lt;code&gt;cargo install --locked yazi-fm yazi-cli&lt;/code&gt;. As you can see, this installs two packages directly on your system.&lt;/p&gt;

&lt;p&gt;However, in my chroot setup, this is kind of useless, because if I run that command, it’ll install those packages inside the chrooted Debian, which I obviously won’t be using.&lt;/p&gt;

&lt;p&gt;But here’s the cool part: Rust has this amazing crate called &lt;code&gt;cargo-deb&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Debian packages from Cargo projects&lt;br&gt;
This is a Cargo helper command which automatically creates binary Debian packages (.deb) from Cargo projects. (&lt;a href="https://docs.rs/crate/cargo-deb/latest" rel="noopener noreferrer"&gt;Rust Docs: cargo-deb&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now you can probably guess what my goal is : I want to get two &lt;code&gt;.deb&lt;/code&gt; files: one for &lt;code&gt;yazi-fm&lt;/code&gt; and one for &lt;code&gt;yazi-cli&lt;/code&gt;. Then I can just copy them over to my main system and install them with &lt;code&gt;dpkg&lt;/code&gt;. Simple and clean!&lt;/p&gt;

&lt;p&gt;To do that, first I need to install &lt;code&gt;cargo-deb&lt;/code&gt; from crates.io.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;chroot # rustup update  
chroot # apt install build-essentials # gcc compiler
chroot # cargo install cargo-deb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this, I  clone the Yazi project's GitHub repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;chroot # git clone https://github.com/sxyazi/yazi.git
chroot # cd yazi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then ... I just instruct Rust to build from source 2 .deb files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;chroot # cargo deb -p yazi-fm --locked
chroot # cargo deb -p yazi-cli --locked
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After these two process are completed 2 .deb files are placed into ./target/debian directory of yazi root directory.&lt;/p&gt;

&lt;p&gt;What is left is to copy these two files to the main system and install 2 packages from them using dpkg.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;chroot # exit
# exit 
$ cd # teleporting to home directory of my regular user
$ sudo cp /trixie-chroot/root/yazi/target/debian/yazi-cli_25.4.8-1_amd64.deb .
$ sudo cp /trixie-chroot/root/yazi/target/debian/yazi-fm_25.4.8-1_amd64.deb .

$ sudo dpkg -i yazi-fm_25.4.8-1_amd64.deb
$ sudo dpkg -i yazi-cli_25.4.8-1_amd64.deb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Voila!&lt;/p&gt;

&lt;p&gt;Command to launch Yazi File manager: &lt;code&gt;yazi&lt;/code&gt;&lt;br&gt;
Command to launch Yazi CLI: &lt;code&gt;ya&lt;/code&gt;&lt;/p&gt;

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

</description>
      <category>debootsrap</category>
      <category>rust</category>
      <category>cargo</category>
      <category>chroot</category>
    </item>
    <item>
      <title>Where to Start in Web Development: Ignoring Learning HTTP(S), URLs, DNS, IP, SSL Will Have Consequences...</title>
      <dc:creator>aL</dc:creator>
      <pubDate>Fri, 21 Mar 2025 18:49:50 +0000</pubDate>
      <link>https://dev.to/dev-charodeyka/where-to-start-in-web-development-ignoring-learning-https-urls-dns-ip-ssl-will-have-57le</link>
      <guid>https://dev.to/dev-charodeyka/where-to-start-in-web-development-ignoring-learning-https-urls-dns-ip-ssl-will-have-57le</guid>
      <description>&lt;p&gt;This is the second article in a series on Web Development. If you’re just starting out, I highly recommend reading &lt;a href="https://dev.to/dev-charodeyka/where-to-start-in-web-development-react-angular-svelte-or-somewhere-else-29a4"&gt;the first part&lt;/a&gt; first. In that part I illustrated and explained the DOM (Document Object Model); in this article, I’ll use the term DOM assuming you’re already familiar with it.&lt;/p&gt;




&lt;p&gt;This isn’t my first article on networking. From my profile stats, I’ve noticed that topics around networking get few readers, so I can guess that now you might be thinking, "I can skip this for now—I don’t really need it for starting with web dev". Well, here’s what I believe might happen on your web development learning path if you postpone learning the things listed in the title:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;CRUD Applications, the Database part will be pretty tough.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Improper database client configuration, struggle with setting up secure connections and handling data flows properly.&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;HTML Forms &amp;amp; HTTP methods mess-ups&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Sensitive data unintentionally spawning in URL because of GET method in form tag? Plus, in general, handling user inputs may be confusing.&lt;/em&gt;&lt;/p&gt; &lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Try/Catch Blocks in JS fetch functions&lt;/p&gt;
&lt;p&gt;&lt;em&gt;When you fetch data using JS, you’re dealing with network requests and responses. If you do not understand how those work, you will be confused by what you’re actually catching in an error.&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Debugging fetching functions&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Without understanding HTTP language or the request-response cycle, you will spend extra time trying to understand why your frontend does not display what it should. Let's not mention the situationships when some try to fetch something from remote servers pointing to the &lt;code&gt;localhost&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Hosting your web apps (portfolio, pet projects, etc.)&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Eventually, you’ll want to put your work online. Without networking fundamentals, you might not know even where to start.&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Routing in your web apps&lt;/p&gt;
&lt;p&gt;&lt;em&gt;If you don’t understand how URLs translate into requests and how servers respond, you might struggle with design of your app's routing schemes.&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;APIs&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Web development revolves around APIs. APIs are mostly about communication via network.&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Dockerizing your apps&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I noticed the &lt;strong&gt;urge&lt;/strong&gt; to containerize anything that is not yet containerized even on early stages of development, but containerization involves such things as port mappings, virtual networking etc. You might also struggle with linking containers together or exposing your app's parts properly.&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I hope I have convinced you somehow to invest your time in learning networking fundamentals.&lt;/p&gt;




&lt;p&gt;In the previous part’s conclusion, I showed this scheme visualizing on high level &lt;em&gt;how browsers work&lt;/em&gt;:&lt;/p&gt;

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

&lt;p&gt;As you can see in that diagram, the "query" from the browser’s address bar plays a key role: by using that "query", your &lt;strong&gt;browser’s networking layer&lt;/strong&gt; sends a request and receives a response from the &lt;em&gt;correct&lt;/em&gt; server. That response contains some &lt;em&gt;data&lt;/em&gt; (for example, &lt;code&gt;.html&lt;/code&gt;, &lt;code&gt;.css&lt;/code&gt;, and &lt;code&gt;.js&lt;/code&gt; files) that gets rendered—based on these files the Document Object Model is constructed by the browser—and then visualized.&lt;/p&gt;

&lt;p&gt;In this article, I’ll focus on what’s happening in the browser’s networking layer and also explain what &lt;strong&gt;servers&lt;/strong&gt; are and what role they play (Spoiler: "server" is not always about a backend!).&lt;/p&gt;

&lt;p&gt;Here is the roadmap:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Browser's Address Bar&lt;/li&gt;
&lt;li&gt;URIs and URLs&lt;/li&gt;
&lt;li&gt;HTTP Protocol

&lt;ol&gt;
&lt;li&gt;Internet Protocol and IP addresses&lt;/li&gt;
&lt;li&gt;Transmission Control Protocol (TCP)&lt;/li&gt;
&lt;li&gt;TCP/HTTP Network traffic&lt;/li&gt;
&lt;li&gt;Understanding Ports&lt;/li&gt;
&lt;li&gt;DNS&lt;/li&gt;
&lt;li&gt;TCP in action: 3-way handshake&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;HTTPS

&lt;ol&gt;
&lt;li&gt;Little extra info: multiple IP addresses and horizontal scaling of web apps&lt;/li&gt;
&lt;li&gt;About encryption&lt;/li&gt;
&lt;li&gt;TLS/SSL certificates&lt;/li&gt;
&lt;li&gt;Secure communication channel&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  1. Browser's Address Bar
&lt;/h3&gt;

&lt;p&gt;Modern browsers are quite powerful, and you might not even notice that when you type something in the address bar, you’re effectively using it like a search engine (default search engine of your browser). You can drop in any text, and the browser takes that text and forwards it to your default search engine—all without you really realizing. &lt;/p&gt;

&lt;p&gt;But if you’ve been using browsers and the internet for a long time, you might remember it wasn’t always like this. You couldn’t just throw any random text into the address bar and expect the browser to figure it out. Back then, you had to first go to the search engine’s website (e.g., Google or Bing) and then type your search query there.&lt;/p&gt;

&lt;p&gt;For demonstration purposes, I’ll use a silly "query". My browser is Brave, and its default search engine is Brave Search. I type this into the address bar: &lt;code&gt;difference between .com and .dev&lt;/code&gt;. This is what I see:&lt;/p&gt;

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

&lt;p&gt;What if I type the same "query" without spaces, and even worse, with dots between words - &lt;code&gt;difference.between.comand.dev&lt;/code&gt;? It can happen—maybe you’ve done it when typing quickly on your phone and missed the spaces. Such "query" results in:&lt;/p&gt;
 Error example: DNS address could not be resolved 



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



&lt;p&gt;So here comes the error: the browser definitely did not process both queries the same way. The first query was treated as a string that got passed to the search engine, but the second query (with dots) made the browser do something else instead of just processing it along as a search query.&lt;/p&gt;

&lt;p&gt;If I double-click on the address bar in both cases, here’s what I see:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;https://differencebetween.com.and.dev/&lt;/code&gt; — which leads to an error page&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://search.brave.com/search?q=difference+between+.com+and+.dev&amp;amp;source=desktop&amp;amp;summary=1&amp;amp;conversation=1dd960092fc9678fb88e64&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Those are the "queries" the browser processed. I keep putting "query" in quotes (" ") because the technically correct term is a &lt;strong&gt;URI&lt;/strong&gt;, and the actual "query" part is:&lt;br&gt;
&lt;code&gt;?q=difference+between+.com+and+.dev&amp;amp;source=desktop&amp;amp;summary=1&amp;amp;conversation=1dd960092fc9678fb88e64&lt;/code&gt;. That’s part of what I typed into the browser’s address bar. Let’s start with what a URI is.&lt;/p&gt;


&lt;h3&gt;
  
  
  2. URIs and URLs
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Uniform Resource Identifiers (URI) are used to identify "resources" on the web. URIs are commonly used as targets of HTTP requests, in which case the URI represents a location for a physical resource, such as a document, a photo, binary data. &lt;br&gt;
The most common type of URI is a Uniform Resource Locator (URL), which is known as the web address.&lt;br&gt;
A Uniform Resource Name (URN) is a URI that identifies a resource by name in a particular namespace. (&lt;a href="https://developer.mozilla.org/en-US/docs/Web/URI" rel="noopener noreferrer"&gt;MDN web docs: URIs&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A URL (Uniform Resource Locator) is renown term not only by developers but also by just users. The point is that a URL ∈ URI (is a subset of), meaning a URI is broader (every URL is a URI, but not every URI is a URL). A URL is a specific type of URI that not only identifies a resource but also provides the information needed to retrieve it (such as its network location and protocol).&lt;/p&gt;

&lt;p&gt;In the previous article, I gave examples of opening files from my PC using this URI:&lt;br&gt;&lt;br&gt;
&lt;code&gt;file:///home/lalala/Projects/DEVTO/webdev/randomPDF.pdf&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is a URI, but not exactly a URL—it’s a bit ambiguous because it could be called a URL if you consider it &lt;em&gt;a locator for a file resource&lt;/em&gt;. However, it is common to use “file URI” to emphasize that it accesses a local file rather than a resource over &lt;em&gt;HTTP/HTTPS&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;By contrast,&lt;br&gt;&lt;br&gt;
&lt;code&gt;https://search.brave.com/search?q=difference+between+.com+and+.dev&amp;amp;source=desktop&amp;amp;summary=1&amp;amp;conversation=1dd960092fc9678fb88e64&lt;/code&gt;&lt;br&gt;&lt;br&gt;
is definitely both a URI and a URL.&lt;/p&gt;

&lt;p&gt;First, I want to "decompose" one illustrative URL - &lt;code&gt;http://www.example.com:80/path/to/myfile.html?key1=value1&amp;amp;key2=value2#SomewhereInTheDocument&lt;/code&gt; to demonstrate all its functional parts, which can help in understanding how it works—this is a key moment:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;_1. &lt;code&gt;http://&lt;/code&gt; is the scheme of the URL, indicating which protocol the browser must use&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;www.example.com&lt;/code&gt; is the host name of the URI, indicating which Web server is being requested. Here, we use a domain name. It is also possible to directly use an IP address, but because it is less convenient, it is rare to do so, unless the server doesn't have a registered domain name&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:80&lt;/code&gt; is the port of the URL, indicating the technical "gate" used to access the resources on the web server. It is usually omitted if the web server uses the standard ports of the HTTP protocol (&lt;code&gt;80&lt;/code&gt; for HTTP and &lt;code&gt;443&lt;/code&gt; for HTTPS) to grant access to its resources. Otherwise, it is mandatory.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/path/to/myfile.html&lt;/code&gt; is the path of the URL, indicating the location of the resource on the web server. In the early days of the Web, this was an actual directory path to a physical location on the web server. Nowadays, web servers usually abstract this to an arbitrary location.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;?key1=value1&amp;amp;key2=value2&lt;/code&gt; is the query of the URL, which are extra parameters provided to the web server. The parameters are a list of key/value pairs prefixed by the ? symbol, and separated with the &amp;amp; symbol. These can be used to provide additional context about the resource being requested._&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's detangle it all one by one. I'm starting with the protocol.&lt;/p&gt;

&lt;p&gt;In a URL, anything before &lt;code&gt;://&lt;/code&gt; is a &lt;strong&gt;protocol&lt;/strong&gt;. When I was opening local files from my PC, I used the &lt;code&gt;file://&lt;/code&gt; protocol to access them. However, the most common and widespread protocol—and the one you'll 100% be dealing with—is HTTP(S). So it makes sense to explain it.&lt;/p&gt;


&lt;h3&gt;
  
  
  3. HTTP Protocol
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;HTTP: Hypertext Transfer Protocol (HTTP) is an application protocol that defines a language for clients and servers to speak to each other. This is like the language you use to order your goods. (&lt;a href="https://developer.mozilla.org/en-US/docs/Learn_web_development/Getting_started/Web_standards/How_the_web_works" rel="noopener noreferrer"&gt;MDN web docs: How the web works&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The browser acts a &lt;em&gt;client&lt;/em&gt;, and it communicates with &lt;em&gt;servers&lt;/em&gt; using the HTTP language (If it is indicated in the URL, i.e. when it starts with &lt;code&gt;http(s)://&lt;/code&gt;). This language per se is a &lt;em&gt;language&lt;/em&gt; of verbs—&lt;em&gt;actions&lt;/em&gt; with self-explanatory names like GET, PUT, DELETE, POST, TRACE, and others. The browser sends one of these &lt;em&gt;verbs&lt;/em&gt; to an address indicated in the URL (URL addresses I will cover later), and if that address is "valid", the server found on this address sends a response.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I want to clarify that, in this article, a server is simply a computational device—like the PC or notebook you’re using, but without a monitor or GUI. For simplicity, just think of it that way for now.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When (if) the server responds to the browser’s request, it does so using HTTP, as it was contacted via this protocol. If the request sent by browser got rejected by server for any reason, HTTP ensures that a client (browser) anyway receives a response. That’s very important (for your JS &lt;code&gt;try catch&lt;/code&gt; blocks :D). HTTP responses are more complex than requests (verbs) — the &lt;strong&gt;status codes&lt;/strong&gt; are a crucial part of any HTTP response.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;_HTTP response status codes indicate whether a specific HTTP request has been successfully completed. Responses are grouped in five classes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Informational responses (100 – 199)&lt;/li&gt;
&lt;li&gt;Successful responses (200 – 299)&lt;/li&gt;
&lt;li&gt;Redirection messages (300 – 399)&lt;/li&gt;
&lt;li&gt;Client error responses (400 – 499)&lt;/li&gt;
&lt;li&gt;Server error responses (500 – 599) (&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status" rel="noopener noreferrer"&gt;MDN web docs: URIs&lt;/a&gt;)_&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, let me show you some &lt;em&gt;nerd fun&lt;/em&gt; that will help you to understand in deep how HTTP protocol works. Of course you can learn all the status codes, methods of HTTP language, but I think it is not enough if HTTP protocol remains a block box for you, especially when it comes to the debugging of your web apps.&lt;/p&gt;

&lt;p&gt;The difficulty in understanding networking concepts is because it's kinda all happening under the hood – you don't see any of the communication statuses or responses in the raw format, until you get an error. This is true if you're a user; but if you're a dev, you will be intact even more with networking stuff. However, even if you're a dev and were avoiding networking by all means, I'll show you what's inside this black box.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let's start with the fact that the HTTP protocol sits on top of another protocol, TCP.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The Transmission Control Protocol (TCP) is one of the main protocols of the Internet protocol suite. It originated in the initial network implementation in which it complemented the Internet Protocol (IP). Therefore, the entire suite is commonly referred to as TCP/IP. TCP provides reliable, ordered, and error-checked delivery of a stream of octets (bytes) between applications running on hosts communicating via an IP network (&lt;a href="https://en.wikipedia.org/wiki/Transmission_Control_Protocol" rel="noopener noreferrer"&gt;Wikipedia:TCP&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here is the chain of protocols:&lt;/p&gt;

&lt;p&gt;HTTP Protocol &amp;lt;-- TCP Protocol &amp;lt;-- IP Protocol&lt;/p&gt;

&lt;p&gt;Let's start from the bottom up.&lt;/p&gt;
&lt;h4&gt;
  
  
  3.1 Internet Protocol and IP addresses
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;IP (Internet Protocol) has the task of delivering packets from the source host to the destination host &lt;strong&gt;solely based on the IP addresses&lt;/strong&gt; in the packet headers. For this purpose, IP defines packet structures that encapsulate the data to be delivered. It also defines addressing methods that are used to label the datagram with source and destination information (&lt;a href="https://en.wikipedia.org/wiki/Internet_Protocol" rel="noopener noreferrer"&gt;Wikipedia:IP&lt;/a&gt;).&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;IP may be quite familiar to you, especially when it comes to the term "IP address". The device from which you're reading this article has an IP address, and my PC, from which I'm writing this article, also has an IP address. DEV.TO, as a portal, has an IP address as well. Even though your device and my PC do not communicate directly, both our devices are communicating with DEV.TO.&lt;/p&gt;

&lt;p&gt;There are actually two versions of the IP protocol: IPv4 and IPv6. As stated in the Wikipedia quote above, the IP protocol is about communication between the &lt;em&gt;source&lt;/em&gt; and &lt;em&gt;destination&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;Try to think of packets of data as &lt;strong&gt;envelopes&lt;/strong&gt; with some info—&lt;em&gt;&lt;strong&gt;I&lt;/strong&gt;nternet &lt;strong&gt;P&lt;/strong&gt;rotocol is mostly about the structure of addresses&lt;/em&gt;. Each envelope with a &lt;em&gt;letter&lt;/em&gt; (data) inside has a &lt;em&gt;to&lt;/em&gt; (destination) and a &lt;em&gt;from&lt;/em&gt; (source).&lt;/p&gt;

&lt;p&gt;Now, what about IP versions? &lt;em&gt;Let's say that we lived in the old days when people resided in houses rather than apartments (one house=one household). The addressing system was quite different—just the street name, house number, city, and country. That system worked fine. But then, as time went on, globalization and urbanization began, and more people started living in cities. It can't be that only one family lives in a huge house, so houses started to be divided into apartments (one house=many households). Now, if a letter arrives with just the street, city, country, and house number, it will not find the correct Destinee. A new "addressing" mechanism had to be invented.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxspibutveh9lvwd5q8cy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxspibutveh9lvwd5q8cy.png" alt="envelope photo" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;a href="https://www.today.com/home/how-address-envelope-t156576" rel="noopener noreferrer"&gt;Image source&lt;/a&gt;



&lt;p&gt;That's exactly what happened with IPv4 (Internet Protocol version 4). It was created when the Internet was a far less "global"—few had access to it—and it worked fine. But look at us in 2025: each of us usually has at least two devices that need to be connected to the Internet, and many of us don't live alone but with family, so the numbers multiply. Here’s the thing: IPv4 addresses are mathematically limited—the number of unique addresses available is around 4 billion. If you calculate only for private use and individuals, it is misleading because think also about all big companies with many servers that use unique IP addresses if they're connected to the global Internet.&lt;/p&gt;

&lt;p&gt;So, a newer version of IP was born—IPv6 (Internet Protocol version 6). This version of the protocol completely changes the address structure and syntax, providing an enormous pool of unique addresses so that every server, every personal device can have its own unique address.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem is that IPv6 has not yet been widely adopted, and most of the Internet is still on IPv4&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So you may think there's a problem: network packets (the envelopes from the analogy above) are getting lost, because the addresses are "duplicated"... but that's not true. Various mechanisms somehow solve the issue of IPv4 addresses scarcity, and the core mechanism is NAT—Network Address Translation. I won't go into detail here, but if you genuinely want to advance your understandings in networking, you can read my other articles on networking: &lt;a href="https://dev.to/dev-charodeyka/debian-12-is-amazing-how-to-create-your-custom-codehouse-part-3b4-2ca5"&gt;this&lt;/a&gt;, and &lt;a href="https://dev.to/dev-charodeyka/virtualization-on-debian-with-libvirtqemukvm-networking-beyond-default-must-have-concepts-to-2ccn"&gt;this&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the scope of this article, I just want to explain the key point of how NAT affects your devices and any web app you will ever develop and host. &lt;strong&gt;Your devices don't have unique IPv4 addresses when connecting to the internet&lt;/strong&gt;. Firstly, you never connect directly to the internet from your devices unless you have a device directly attached to a fiber cable connected in a wired way to the global net. Yes, if you think the global internet is wireless, you're mistaken. The global internet is entirely wired, except for the internet provided by satellites. So, how do you access the internet from your devices? Mostly wirelessly, I imagine—you connect to a Wi-Fi router. And if you've ever handled internet provision at your home, you might have seen the operator arrive at your house with a thin cable, your to be Wi-Fi router and connecting it with a wire! Another alternative is using a SIM card in your router or mobile phone, which receives signals "wirelessly". &lt;/p&gt;

&lt;p&gt;While cellular towers broadcast wireless signals to phones, themselves they rely on a wired or high-capacity link to connect to the rest of the carrier’s network. In the case of Wi-Fi routers, fiber cable is physically brought to your internet provider's establishment, which is connected by cable—first to the city, then to the country, and then to the rest of the world.&lt;/p&gt;

&lt;p&gt;Anyway, that was just FYI. What's important for web development is this: at your home setup your Wi-Fi router has a unique IPv4 address*. (&lt;em&gt;* stands for the fact that first, most probably, this unique ipv4 address is not as long lasting as the classical physical address of a building - it is constantly changing - usually much more than once a day; second, there is such a thing as CGNAT and I leave it for you to check what is it&lt;/em&gt;). Your devices connected to Wi-Fi router don't have unique IPv4 addresses each. If you check your PC's IP address, you'll see something like 192.168.x.x.&lt;/p&gt;

&lt;p&gt;I see this this:&lt;br&gt;
(If you're on Mac or Windows, check the web on how to find your IP address information.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ip a
1: lo: &amp;lt;LOOPBACK,UP,LOWER_UP&amp;gt; mtu 65536 ....
    ...
    inet 127.0.0.1/8 scope host lo
    ...
2: eno1: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 ...
    ...
    inet 192.168.8.9/24 brd 192.168.8.255 
    ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two things you see in my output: &lt;code&gt;lo&lt;/code&gt; and &lt;code&gt;eno1: 192.168.8.9&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;eno1&lt;/code&gt; is the network interface—or the point of connection for my PC. It's the Ethernet cable connected to my Wi-Fi router, and that's its address. What is &lt;code&gt;lo&lt;/code&gt;? It's the &lt;em&gt;loopback&lt;/em&gt; interface. You probably know it well—it's the famous &lt;strong&gt;localhost&lt;/strong&gt; - &lt;code&gt;127.0.0.1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;My Wi-Fi router has a public IPv4 address. Also, it is creating a local network (my home network; LAN - local area network) that spans as long as the signals of my router reach. Any device that can connect to my Wi-Fi router becomes a part of this network. This network is a default &lt;strong&gt;private&lt;/strong&gt; network, and it uses IP addresses reserved for private usage. So, my PC has the IPv4 address 192.168.8.9, my phone - 192.168.8.10, my laptop - 192.168.8.5 and so on. This default local network can accommodate up to 254 devices simultaneously connected to my router. With the NAT mechanism, my Wi-Fi router &lt;em&gt;translates&lt;/em&gt; all the &lt;em&gt;requests&lt;/em&gt; and -&lt;em&gt;responses&lt;/em&gt; sent to global net from each device connected to it, granting them access to the internet (e.g request: I go to google from my PC to find a picture of kitty and then I download it).&lt;/p&gt;

&lt;p&gt;So, how is this all related to web dev?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You cannot directly host anything you develop on a &lt;strong&gt;private IP&lt;/strong&gt; address if you want to expose it to the global net.&lt;/li&gt;
&lt;li&gt;Anything!!! Absolutely anything you see on the web—any page you manage to open, any existing services, anything at all—resides/is stored on a "device" with an IP address.&lt;/li&gt;
&lt;li&gt;Whatever you visit on the web from a browser: A. exposes your external (e.g the router’s public IP) IP address (little remark: VPNs and proxies can mask your IP :-)) B. exposes its IP address to your browser.&lt;/li&gt;
&lt;li&gt;The alfa-numerical part following &lt;code&gt;http(s)://&lt;/code&gt; in a URL (roughly speaking, "the name" of a website) can be presented as an IP - e.g you can generally replace &lt;code&gt;http(s)://example.com&lt;/code&gt; with &lt;code&gt;http(s)://&amp;lt;ip_address&amp;gt;&lt;/code&gt; and access the site that way, however, some sites have domain-specific configurations, so entering an IP might not always load the intended site, but the point is that &lt;strong&gt;a domain name ultimately resolves to an IP&lt;/strong&gt; (on domains later on!!).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Okay, basta with IP. I introduced key information about IP addresses:&lt;/p&gt;

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

&lt;p&gt;Next up: TCP.&lt;/p&gt;

&lt;h4&gt;
  
  
  3.2 Transmission Control Protocol (TCP)
&lt;/h4&gt;

&lt;p&gt;In my analogy with envelopes and letters, the Transmission Control Protocol plays the role of a &lt;em&gt;delivery service&lt;/em&gt;. Thanks to IP, &lt;em&gt;envelopes&lt;/em&gt; (network packets with data) have a standardized addressing system, so &lt;strong&gt;if they're delivered responsibly&lt;/strong&gt;, the data will arrive from sender to recipient. This is where TCP comes into picture: its job is to deliver &lt;em&gt;envelopes&lt;/em&gt; (network packets with data) in a reliable way, ensuring they reach their destination.&lt;/p&gt;

&lt;p&gt;TCP isn’t the only protocol on top of IP. There’s also UDP (User Datagram Protocol), but I’ll leave it aside for now. This meme fits well to explain the difference:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcwjiki2qny0b1i23e3ig.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcwjiki2qny0b1i23e3ig.jpg" alt="meme about TCP and UDP diffs" width="640" height="853"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Anyway, since HTTP sits on top of TCP, let’s concentrate on TCP rather than on other protocols.&lt;/p&gt;

&lt;p&gt;To illustrate TCP in action, as well as HTTP and HTTPS, I’ll share the results of some network packets capturing. By the way, continuing my envelope analogy: &lt;em&gt;the HTTP protocol is like the language of the letter (data) inside the envelope (network packets). The sender writes the letter in a language, and the recipient responds in the same language.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;When a letter (data) is sent in the HTTP "language", anyone nosy who intercepts the envelope can open it and read the letter (data)  clearly, because it’s not &lt;strong&gt;encrypted&lt;/strong&gt;. But if the letter (data) is written in HTTPS "language", this nosy someone will only see some general information and then abracadabra instead of the actual content.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;HTTPS is a secure version of HTTP that stops bad people from reading your data while it is being transported. On the modern web, pretty much every server uses HTTPS, so if you don't include it explicitly, the browser assumes that is what you are using and adds it for you.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Anyway, I promised some nerd fun, and by that, I mean that I will capture network packets that travelling in and out of my PC to show you what is inside.&lt;/p&gt;

&lt;h4&gt;
  
  
  3.3 TCP/HTTP Network traffic
&lt;/h4&gt;

&lt;p&gt;For HTTP network traffic, I’ll use this site: &lt;code&gt;http://httpforever.com/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For HTTPS traffic, I’ll use the URL of my DEV.TO profile as an example: &lt;code&gt;https://dev.to/dev-charodeyka&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The tool I’ll use to capture network packets is &lt;a href="https://www.wireshark.org/" rel="noopener noreferrer"&gt;Wireshark&lt;/a&gt; (it has a GUI). I could do the same with &lt;code&gt;tcpdump&lt;/code&gt;, but for demonstration purposes, I want something visual.&lt;/p&gt;

&lt;p&gt;I’ll also be using a software called &lt;code&gt;curl&lt;/code&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;curl - transfer a URL&lt;/em&gt;&lt;br&gt;
&lt;em&gt;curl is a tool for transferring data from or to a server. It supports HTTP and HTTPS protocols.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this setup, &lt;code&gt;curl&lt;/code&gt; will act as the client in the typical client–server relationship. In web dev a browser is usually the client in such a setup. The command I’ll use is curl with a verbosity flag (&lt;code&gt;-v&lt;/code&gt;), so I can show you all the details.&lt;/p&gt;

&lt;p id="curlHTTP"&gt;Curl output&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -v http://httpforever.com/
* Host httpforever.com:80 was resolved.
* IPv6: 2604:a880:4:1d0::1f1:2000
* IPv4: 146.190.62.39
*   Trying [2604:a880:4:1d0::1f1:2000]:80...
* Immediate connect fail for 2604:a880:4:1d0::1f1:2000: Network is unreachable
*   Trying 146.190.62.39:80...
* Connected to httpforever.com (146.190.62.39) port 80
* using HTTP/1.x
&amp;gt; GET / HTTP/1.1
&amp;gt; Host: httpforever.com
&amp;gt; User-Agent: curl/8.12.1
&amp;gt; Accept: */*
&amp;gt;
* Request completely sent off
&amp;lt; HTTP/1.1 200 OK
&amp;lt; Server: nginx/1.18.0 (Ubuntu)
&amp;lt; Date: Mon, 17 Mar 2025 21:00:08 GMT
&amp;lt; Content-Type: text/html
&amp;lt; Content-Length: 5124
&amp;lt; Connection: keep-alive
&amp;lt; Referrer-Policy: strict-origin-when-cross-origin
&amp;lt; X-Content-Type-Options: nosniff
...
&amp;lt;
&amp;lt;!DOCTYPE HTML&amp;gt;
&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        ....
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Back to explanation&lt;/p&gt;

&lt;p&gt;Hear me out! What you’re seeing is exactly the same thing that happens under the hood when I visit this website from my browser. It might be confusing—like, how can the website (the "front end") also be about servers?&lt;/p&gt;

&lt;p&gt;Well, that confusion arises because there’s a common understanding that "frontend" refers to something to be run in a browser (client-facing), and "backend" is the stuff on servers. But actually... everything runs on servers. Hard-to-swallow pill: if you want to self-host your future web apps and avoid paying for web hosting or PaaS, welcome to servers and the Linux world.&lt;/p&gt;

&lt;p&gt;The backend and frontend parts of a web app can reside on the same physical machine (or the same cloud instance). However, on the software level, they are separated. Frontend is running on &lt;strong&gt;web servers&lt;/strong&gt; (software, "server" comes from the fact that this software is &lt;em&gt;serving&lt;/em&gt; something).&lt;/p&gt;

&lt;p&gt;This line from &lt;code&gt;curl&lt;/code&gt; output shows which web server the &lt;code&gt;http://httpforever.com/&lt;/code&gt;’s frontend is running on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt; Server: nginx/1.18.0 (Ubuntu)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The physical server has Ubuntu OS and is running the Nginx. Physical server's IP is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* IPv4: 146.190.62.39
default HTTP port is 80 so we see this:
*   Trying 146.190.62.39:80...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3.4 Understanding Ports
&lt;/h4&gt;

&lt;p&gt;Now, about ports (:80), even if it’s outside the main scope of this article, I’ll explain with an analogy so you understand what a port is.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Imagine you have a house. To enter, you have a door. If you remove the door, you can’t enter the house. If you don’t know where the door is, you also can’t get in. Let’s expand on that: maybe you have a door for you and your family, a little door for your cat, and a big door on the ground floor for your car. If you try to drive your car through the cat door, it won’t work, and if the cat goes through the garage door, it ends up somewhere it doesn’t need to be.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;It’s the same concept with ports. HTTPS traffic uses port 443, and HTTP uses port 80 by default. You may face the need to open other ports on your PC/server, as many services have their own default ports—like databases. All these ports need to be open if you want &lt;em&gt;external&lt;/em&gt; access for a given service. And it’s also about what requests or messages you’re pushing through a port. Returning to my analogy: &lt;em&gt;the cat enters the house through its little door looking for food. If it walked into the garage, it wouldn’t find its feeder&lt;/em&gt;. Same idea with software. For example, Mongo DataBase’s default port is 27017. If you try to send &lt;code&gt;curl&lt;/code&gt; requests there expecting an HTTP response, it won’t work, because MongoDB can’t respond in that way.&lt;/p&gt;

&lt;p&gt;Before I move on to the same command for an HTTPS site, I want to explain the first line of the &lt;code&gt;curl&lt;/code&gt; output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host httpforever.com:80 was resolved.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;/h4&gt;
&lt;h3 id="DNS"&gt;3.5 DNS&lt;/h3&gt;


&lt;p&gt;You can have a look again on the error that I encountered when I dropped in the browser's search bar an erroneous URL: &lt;code&gt;https://differencebetween.com.and.dev/&lt;/code&gt; DNS error&lt;/p&gt;

&lt;p&gt;Basically, the browser displayed an error because I was trying to reach an abracadabra web address, &lt;code&gt;differencebetween.com.and.dev&lt;/code&gt;, using the &lt;code&gt;https&lt;/code&gt; protocol. The browser couldn’t find a server at that "address" because it simply doesn’t exist. But how did the browser figure that out? Where did the browser "look up" that "address"? Does your browser have some kind of "address book" containing all the existing "addresses" on the web? Of course not. Every browser indeed checks an "address book" of the entire Internet, but it does so via the network – it doesn’t store it locally.&lt;/p&gt;

&lt;p&gt;Before I give a name to this "address book of the Internet", I want to explain what is in this book, gracefully connecting all the pieces of information I shared above. However, I guess you can already guess what a browser needs to find a server... an IP address, of course! Take another look at the curl output:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* Host httpforever.com:80 was resolved.
* IPv6: 2604:a880:4:1d0::1f1:2000
* IPv4: 146.190.62.39
*   Trying [2604:a880:4:1d0::1f1:2000]:80...
* Immediate connect fail for 2604:a880:4:1d0::1f1:2000: Network is unreachable
*   Trying 146.190.62.39:80...
* Connected to httpforever.com (146.190.62.39) port 80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;First thing that happened after &lt;code&gt;curl&lt;/code&gt; request was sent to &lt;code&gt;http://httpforever.com&lt;/code&gt; was that this web address was &lt;em&gt;resolved&lt;/em&gt;. Resolved de-facto means that the client (&lt;code&gt;curl&lt;/code&gt; in this case, though the same is completely true for browsers) received the information that &lt;code&gt;httpforever.com&lt;/code&gt; == &lt;code&gt;146.190.62.39&lt;/code&gt; (IPv4) and &lt;code&gt;httpforever.com&lt;/code&gt; == &lt;code&gt;2604:a880:4:1d0::1f1:2000&lt;/code&gt; (IPv6). The browser immediately tried to send the HTTP request to the first IP it found, which was the IPv6 address. But as I mentioned, the Internet and many servers are still only on IPv4, so the connection failed... then it quickly started to send the request to the IPv4 address, and the connection was successful!&lt;/p&gt;

&lt;p&gt;So, where do browsers get a "dictionary" that maps human-readable addresses to IP addresses? Browsers get it from DNS (Domain Name System).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The Domain Name System (DNS) is the phonebook of the Internet. Humans access information online through domain names, like nytimes.com or espn.com. Web browsers interact through Internet Protocol (IP) addresses. DNS translates domain names to IP addresses so browsers can load Internet resources. (&lt;a href="https://www.cloudflare.com/learning/dns/what-is-dns/" rel="noopener noreferrer"&gt;Cloudflare: What is DNS?&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I won’t go into too much detail on DNS, it is simple per se in what it is for, but it is not that simple to configure from the standpoint of networking configuration. Here is the schematic representation on how your browser reaches Domain Name System:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;URL example: https://www.cloudflare.com/learning/dns/what-is-dns/
[Browser] --&amp;gt; (Browser's internal DNS cache)
                    |
                    v
        Found IP of www.cloudflare.com?
        |                |
        No               Yes ---&amp;gt; resolved, sending request...
        |
        v
[Operating System of PC/server] --&amp;gt; (Local DNS cache &amp;amp; configuration)
                    |
                    v
        Found IP of www.cloudflare.com?
        |                |
        No               Yes ---&amp;gt; resolved, sending request...
        |
        v
[Router/Modem] --&amp;gt;Forwards DNS query
               Forwards where? Depends on configuration..
                │             OR               |
                v                              v
  [  External Public DNS           [Internet Service Provider's 
   (e.g. Google, 8.8.8.8)]                 (ISP) DNS] 
                |             OR               |
                v                              v
               Found IP of www.cloudflare.com?
                |                |
                No               Yes ---&amp;gt; resolved, sending request...
                |
                v
         www.cloudflare.com does not exists.   
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The key takeaway is that a "human" address in a URL is just for people. Underneath, there’s always an IP address. And as a first step, browsers try to look up the "translation" of the provided address into an IP. If it fails, that’s the end of the query and results in an unavoidable error. Another takeaway is that you can't, just out of the blue, decide that your server with IP 123.124.135.5 (for example) will have a domain name like &lt;code&gt;coolest-site.eu&lt;/code&gt; by specifying it in your server’s configurations. I hope that’s pretty obvious.&lt;/p&gt;
&lt;h4&gt;
  
  
  3.6 TCP in action: 3-way handshake
&lt;/h4&gt;

&lt;p&gt;Anyway, here is the nerd fun!!! &lt;em&gt;Haha, I called it nerd fun because once my colleague told me that I need to have a social life after I mentioned that I observed my home lab's network packets for hours to examine a peculiar network anomaly.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As I mentioned, I use Wireshark for monitoring network traffic on my PC and with this tool I can capture all the network packets that pass through it. If I start capturing everything, the output gets flooded in seconds, because any website I open in my browser ends up in the stats table. Currently, I have my PC connected to only one network, and I will capture packets traveling via this network. I will filter out only the packets for HTTP example site, &lt;code&gt;http://httpforever.com&lt;/code&gt;, as I already know the IP after the first launch of &lt;code&gt;curl&lt;/code&gt; I can use this network packets filter: &lt;code&gt;ip.addr==146.190.62.39 and tcp.port==80&lt;/code&gt;. So I start capturing with an active packet filter in Wireshark, and then I just open &lt;code&gt;http://httpforever.com&lt;/code&gt; in my browser.&lt;/p&gt;

&lt;p&gt;Here is what I see:&lt;/p&gt;

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

&lt;p&gt;That’s just what happened under the hood for one simple action: I tried to load the landing page of &lt;code&gt;http://httpforever.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let’s decompose it. First, for each network packet (each row in the table above = network packet) there’s the &lt;code&gt;source&lt;/code&gt; and &lt;code&gt;destination&lt;/code&gt;.  The source of the first packet is the private IP address of my PC, as the communication was started by my PC (specifically, by my browser). The destination of the first network packet is the IP of the site &lt;code&gt;http://httpforever.com&lt;/code&gt;. In the &lt;code&gt;Info&lt;/code&gt; column, as a first thing you see the ports &lt;code&gt;40500 → 80&lt;/code&gt;. Yes, &lt;code&gt;40500&lt;/code&gt; is the port used on my PC during this communication - &lt;em&gt;it’s an ephemeral port, which is basically a byproduct of NAT applied by my router&lt;/em&gt;. The first thing after ports in the first row, column Info (No. 7) you see is the abbreviature SYN — SYN (Synchronize) is the first step in making a handshake, &lt;em&gt;so it’s like extending your hand to someone else to initiate a handshake&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Next, the server where &lt;code&gt;http://httpforever.com&lt;/code&gt;'s frontend runs, sees the handshake request from my browser and &lt;strong&gt;acknowledges&lt;/strong&gt; it, which is the 2nd step of handshake - SYN-ACK (Synchronize-Acknowledge) (No. 8 line in the table above). &lt;/p&gt;

&lt;p&gt;Next, my browser sends an ACK to confirm the server’s SYN-ACK, and that &lt;strong&gt;completes the handshake&lt;/strong&gt; (No. 9 line in the table above). At this point, a reliable connection is established, and data transmission can begin.&lt;/p&gt;

&lt;p&gt;As you can see, the row No. 10 in the table informs that the network packet with HTTP GET method request was sent - data exchange between my browser and the server of &lt;code&gt;http://httpforever.com&lt;/code&gt; has started -after the handshake, my browser immediately requests what it needs. The server acknowledges the GET request and starts sending data (PSH) (rows No. 11 - 13 in the table above). &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Notice&lt;/strong&gt; there are &lt;strong&gt;more than one transfers of different lengths&lt;/strong&gt; before the line No.18 that contains HTTP response 200 - OK, meaning, that first GET request from my browser was completed. That is because, servers servers over HTTP never send everything requested in one big mega data transfer. For example, if you wanted to download a 10 GB video, the website's server from which you are downloading it  wouldn’t just dump the entire file at once—that would overwhelm your network connection. Instead, HTTP network packets are chunked, and the full content is split across those packets. How the data is spitted in network packets by size, is there some rule? There are limits on the server side and on your PC. In fact, without realizing it, you’ve already seen your PC’s limit per network packet if you connect your PC to the Internet via ethernet cable - by default it is 1500 bytes, and this value is called MTU - Maximum Transmission unit. Here is the recap of &lt;code&gt;$ ip a&lt;/code&gt; command, output of which contains &lt;code&gt;mtu&lt;/code&gt; value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ip a
...
2: eno1: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 ...
    ...
    inet 192.168.8.249/24 brd 192.168.8.255 
    ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some packets can for some reasons not arrive to client from the first attempt, however, TCP will do its best to ensure the delivery of everything, that's why it is considered as a reliable protocol. And that is why I called it in my analogy with envelopes and letters as a delivery service. 3 way handshakes, and reattempting of sending network packets if something goes wrong. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff5ucw3q9yi46bioha2y2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff5ucw3q9yi46bioha2y2.jpg" alt=" " width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;a href="https://www.europosters.it/woman-receiving-padded-envelope-from-delivery-service-courier-indoors-closeup-f243219110" rel="noopener noreferrer"&gt;Image source&lt;/a&gt;; this picture is used to stress out the fact that TCP acts as a "reliable delivery service" for network packets - not just dropping them "by the door", but requiring the "signature" as a confirmation of delivery



&lt;p&gt;&lt;em&gt;NB! the Wireshark output may be a bit not accurate for visualizing how network packets are split up due to the fact that Wireshark takes data segments and reassembles them into the complete application-level message. However, I wanted to demonstrate that network traffic is more complex that it seems and it is segmented.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;However, this is very important to know that data travels in chunks when requested—and never gets dumped all in one bulk. It is key for cases when you have to fetch/process huge amounts of data with your JavaScript/TypeScript code (for example, NDJSON). You can take advantage of streaming approaches instead of fetching an entire file at once without streaming and nuking your PC's RAM while trying to process it. Plus, all the "download status bars" are exactly about tracking the chunks of data that arrive and comparing how much is left to the total size of the data being downloaded.&lt;/p&gt;

&lt;p&gt;So, I showed you nerd fun with HTTP network traffic - to help you understand how HTTP works, and what happens when you try to open a website from your browser. Now, what's left to discuss is HTTPS.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. HTTPS
&lt;/h3&gt;

&lt;p&gt;If you look again at &lt;span&gt;the output of a request sent by &lt;code&gt;curl&lt;/code&gt; to website via HTTP&lt;/span&gt;, the most important part is the last section (which I horribly shortened), but here is the point: after all the info about HTTP communication, there’s actual data in HTML format - all the elements that will be displayed in your browser after it will reconstruct and &lt;span id="htmlHTTP"&gt;render DOM based&lt;/span&gt; on this html data.&lt;/p&gt;

&lt;p&gt;Also, if you look at line No.18 in the Wireshark table from above, you can see that the content type sent over as HTML to my browser was (text/html).&lt;/p&gt;

&lt;p&gt;Using Wireshark, I can actually follow each network packet. So, if I select the packet that was carrying the data, I can investigate it—and I see the content in plain text. Here it is:&lt;/p&gt;

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

&lt;p&gt;Getting ahead, here’s what I see if I capture network traffic for an HTTPS site: in Wireshark, I start tracking TCP port 443 and the IP of the DEV.TO website, start capturing packets, go to  &lt;code&gt;https://dev.to/dev-charodeyka&lt;/code&gt; in my browser, identify any packet with data in Wireshark table, and put my nose there, trying to see the data:&lt;/p&gt;

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

&lt;p&gt;Abracadabra! The real content, html data is hidden. In the process of transfer, data transferred via HTTPS—and not HTTP—is encrypted. So if someone tries to capture it and sniff the content, they won’t understand what it is.&lt;/p&gt;

&lt;p&gt;How is that possible? I’ll demonstrate by repeating the same procedure I did for the HTTP website—I’ll send a curl to the HTTPS site - &lt;code&gt;https://dev.to/dev-charodeyka&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -v https://dev.to/dev-charodeyka
0* Host dev.to:443 was resolved.
* IPv6: (none)
* IPv4: 151.101.66.217, 151.101.2.217, 151.101.194.217, 151.101.130.217
0*   Trying 151.101.66.217:443...
* GnuTLS ciphers: NORMAL:-ARCFOUR-128:-CTYPE-ALL:+CTYPE-X509:-VERS-SSL3.0
* ALPN: curl offers h2,http/1.1
* found 152 certificates in /etc/ssl/certs/ca-certificates.crt
* found 456 certificates in /etc/ssl/certs
* SSL connection using TLS1.2 / ECDHE_RSA_CHACHA20_POLY1305
*   server certificate verification OK
*   server certificate status verification SKIPPED
*   common name: dev.to (matched)
*   server certificate expiration date OK
*   server certificate activation date OK
*   certificate public key: RSA
*   certificate version: #3
*   subject: CN=dev.to
*   start date: Tue, 07 Jan 2025 22:00:10 GMT
*   expire date: Sun, 08 Feb 2026 22:00:09 GMT
*   issuer: C=BE,O=GlobalSign nv-sa,CN=GlobalSign Atlas R3 DV TLS CA 2024 Q4
* ALPN: server accepted h2
* Connected to dev.to (151.101.66.217) port 443
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://dev.to/dev-charodeyka
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: dev.to]
* [HTTP/2] [1] [:path: /dev-charodeyka]
* [HTTP/2] [1] [user-agent: curl/8.12.1]
* [HTTP/2] [1] [accept: */*]
&amp;gt; GET /dev-charodeyka HTTP/2
&amp;gt; Host: dev.to
&amp;gt; User-Agent: curl/8.12.1
&amp;gt; Accept: */*
&amp;gt;
* Request completely sent off
&amp;lt; HTTP/2 200
&amp;lt; server: Cowboy
....
&amp;lt; strict-transport-security: max-age=31557600
&amp;lt; content-length: 145678
&amp;lt;
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang="en"&amp;gt;
  &amp;lt;head&amp;gt;
  ....
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, same logic. Now, I hope you can read the output. &lt;br&gt;
First, the &lt;code&gt;dev.to&lt;/code&gt; address was resolved into an IP address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host dev.to:443 was resolved
* IPv4: 151.101.66.217, 151.101.2.217, 151.101.194.217, 151.101.130.217
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, not one, but many. Why?&lt;/p&gt;

&lt;h3&gt;
  
  
  4.1 Little extra info: multiple IP addresses horizontal scaling of web apps
&lt;/h3&gt;

&lt;p&gt;Well, I told you networking stuff is cool, and now, dear reader, if you've read this far, you'll also learn about load balancing and horizontal/vertical scaling. &lt;/p&gt;

&lt;p&gt;Small web applications that don’t have many visitors can run even on a Raspberry Pi (a tiny PC acting as a server). Indeed, they can even run on something like a phone or any device with limited computational power. However, when a website is pretty well-known - and it's not just a basic blog with zero interactivity, but more like an online shop or DEV.TO - one single server may not be enough to ensure that each user has nice smooth experience browsing it. And it's not only about server hardware specs like super-mega RAM or many many CPU cores; it's also about the network, haha. Network transmission speed does not multiply with number of cpu/ram.&lt;/p&gt;

&lt;p&gt;There are two ways of dealing with the hardware resources deficit of a server where web app is running: horizontal and vertical scaling. Let’s say you created a web app and hosted it on a Raspberry Pi. Then you added some cool backend algorithms that do something for your users, so even though you only have a few users, the algorithms demand computational power. You upgrade your Raspberry Pi to something more powerful with more RAM and a better CPU. One important detail is that your website was &lt;code&gt;coolest.site.eu&lt;/code&gt;, mapped to the IP &lt;code&gt;123.124.6.8&lt;/code&gt; - the IP address of that Raspberry Pi. You replace the Raspberry Pi, move your app to the new device, and also bind the  SAME IP to the new device. This is vertical scaling.&lt;/p&gt;

&lt;p&gt;But what if your web app doesn’t do anything too computationally heavy, yet you have thousands of users accessing it simultaneously? Sure, you could replace your Raspberry Pi with a super-mega server, but that might not be the best use of resources. Instead, you could buy four more Raspberry Pi, duplicate your web app on 3 of them, and thus end up with four different IP addresses—because they can’t all share one IP. Then you’d take 4th Raspberry Pi, install any load balancing software on it (like Nginx), and use it as a "sorter" of requests sent by browsers of your website users when they try to access it. For example, if a thousand users try to reach your website, they won’t all slam into one single Raspberry Pi. Instead, their browsers’ requests land on Load balancer's Raspberry Pi first. It sees how busy each Raspberry Pi is and directs traffic to whichever Pi is least busy at that moment. That’s horizontal scaling, and what load balancer does is balance the load among all available servers.&lt;/p&gt;

&lt;p&gt;Okay, next. As I mentioned before, HTTPS is the secure version of the HTTP protocol. If you look at the end of the &lt;code&gt;curl&lt;/code&gt; output I shared above, once again we see HTML data in plain text—but only after the browser successfully establishes communication with one of &lt;code&gt;dev.to&lt;/code&gt;’s servers that hosts the frontend code. That’s why in the Wireshark screenshot, when I tried to put my nose in a HTTPS network packet, I saw weird symbols instead of plain HTML. It’s because the data was encrypted.&lt;/p&gt;

&lt;h4&gt;
  
  
  4.2 About encryption
&lt;/h4&gt;

&lt;p&gt;If I send you the message 12334 145 672 849, like this, you won’t understand it—unless you know we have a decryption map such as 1=h, 2=e, 3=l, 4=o, 5=w, 6=a, 7=r, 8=y, 9=u, which transforms the message into "hello how are you". You can then respond using the same "encryption key". Of course, this is a very simplified example. In real encryption scenarios, there are usually two keys: public and private.&lt;/p&gt;

&lt;p&gt;In my simple example, public key will be containing this info: 1 -&amp;gt; ?, 2 -&amp;gt; ?, 3 -&amp;gt; ?, etc. From public key you only get partial information about encryption—you know how to encrypt the message - turn letters into numbers.&lt;br&gt;
Private key is the secret part that completes the map—only the holder of the private key can reliably invert the numbers into letters.&lt;br&gt;
The public key is indeed public, and it helps a client figure out how data will be encrypted by server. And where is the public key stored when we talk about servers that run websites and exposes them via https protocol? In a small document called a certificate.&lt;/p&gt;

&lt;p&gt;Returning to the &lt;code&gt;curl&lt;/code&gt; output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0*   Trying 151.101.66.217:443...
* GnuTLS ciphers: NORMAL:-ARCFOUR-128:-CTYPE-ALL:+CTYPE-X509:-VERS-SSL3.0
* ALPN: curl offers h2,http/1.1
* found 152 certificates in /etc/ssl/certs/ca-certificates.crt
* found 456 certificates in /etc/ssl/certs
* SSL connection using TLS1.2 / ECDHE_RSA_CHACHA20_POLY1305
*   server certificate verification OK
*   server certificate status verification SKIPPED
*   common name: dev.to (matched)
*   server certificate expiration date OK
*   server certificate activation date OK
*   certificate public key: RSA
*   certificate version: #3
*   subject: CN=dev.to
*   start date: Tue, 07 Jan 2025 22:00:10 GMT
*   expire date: Sun, 08 Feb 2026 22:00:09 GMT
*   issuer: C=BE,O=GlobalSign nv-sa,CN=GlobalSign Atlas R3 DV TLS CA 2024 Q4
* ALPN: server accepted h2
* Connected to dev.to (151.101.66.217) port 443
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, you see a certificate verification step because client (curl, in this case, but the same is for browser) found certificates. Where do they come from, and how does the browser (or curl) verify them?&lt;/p&gt;

&lt;h4&gt;
  
  
  4.3 TLS/SSL certificates
&lt;/h4&gt;

&lt;p&gt;Well, these certificates were created by &lt;code&gt;dev.to&lt;/code&gt;’s server admins. First, two keys were generated on the server: a public and a private key. The certificate includes the server’s public key and some extra data (domain name, organization info, etc.). The &lt;code&gt;dev.to&lt;/code&gt; admins sent this certificate to a Certificate Authority (CA) to have it signed. In &lt;code&gt;dev.to&lt;/code&gt;’s case, I can see the certificate was signed by CA called GlobalSign:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*   issuer: C=BE,O=GlobalSign nv-sa,CN=GlobalSign Atlas R3 DV TLS CA 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That CA verified that &lt;code&gt;dev.to&lt;/code&gt; actually owns the domain and then issued a signed certificate if everything was legitimate about this server and domain. The signed certificate is sent back to the &lt;code&gt;dev.to&lt;/code&gt; server. After this, &lt;code&gt;dev.to&lt;/code&gt;'s server is equipped with its private key (secret) and its public key that got embedded in the signed certificate (this is not secret). This signed certificate is TLS/SSL certificate&lt;/p&gt;

&lt;p&gt;When I try to access &lt;code&gt;https://dev.to/dev-charodeyka&lt;/code&gt; via a browser:&lt;/p&gt;

&lt;p&gt;I. My browser starts a connection via the Secure Socket Layer (SSL) protocol to the dev.to server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* SSL connection using TLS1.2 / ECDHE_RSA_CHACHA20_POLY1305
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;II. The &lt;code&gt;dev.to&lt;/code&gt; server sends its signed certificate (which contains the public key) to my browser.&lt;br&gt;
III. My browser verifies the certificate (checks expiration, domain match, CA trust, etc.). You can see that checking process in the output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*   server certificate verification OK
*   server certificate status verification SKIPPED
*   common name: dev.to (matched)
*   server certificate expiration date OK
*   server certificate activation date OK
*   certificate public key: RSA
*   certificate version: #3
*   subject: CN=dev.to
*   start date: Tue, 07 Jan 2025 22:00:10 GMT
*   expire date: Sun, 08 Feb 2026 22:00:09 GMT
*   issuer: C=BE,O=GlobalSign nv-sa,CN=GlobalSign Atlas R3 DV TLS CA 2024 Q4
* ALPN: server accepted h2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;IV. After the certificate is verified and approved by my browser, the &lt;code&gt;dev.to&lt;/code&gt; server and my browser establish a secure channel. Think of it like going into a private room so nobody else can hear what you're discussing with another person. In digital communication, that means all messages are encrypted in such a way that only the sender and recipient can understand them.&lt;/p&gt;

&lt;h4&gt;
  
  
  4.4 Secure communication channel
&lt;/h4&gt;

&lt;p&gt;To create this secure channel, both my browser and &lt;code&gt;dev.to&lt;/code&gt; server need to agree on encryption and decryption keys:&lt;/p&gt;

&lt;p&gt;1) They agree on the TLS (Transport Layer Security) version and cipher format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* SSL connection using TLS1.2 / ECDHE_RSA_CHACHA20_POLY1305
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2) The &lt;code&gt;dev.to&lt;/code&gt; server and my browser each create "ephemeral" key pairs for this specific session that my browser initialized.&lt;br&gt;
3) They exchange the public parts of these ephemeral keys (encrypted with or signed by the &lt;code&gt;dev.to&lt;/code&gt; server’s private key to ensure authenticity).&lt;br&gt;
4) A shared secret (the session key) is computed on both ends - my browser and &lt;code&gt;dev.to&lt;/code&gt; server.&lt;/p&gt;

&lt;p&gt;Now, my browser and the &lt;code&gt;dev.to&lt;/code&gt; server each have the same session key. All the HTML, images, and everything else is encrypted with this session key—and it’s unique to this session.&lt;/p&gt;

&lt;p&gt;Finally, once this secure channel is established, my browser sends encrypted HTTP requests and receives encrypted responses (like the HTML code for rendering my &lt;code&gt;dev.to&lt;/code&gt; profile).&lt;/p&gt;

&lt;p&gt;You can compare all these steps to the earlier HTTP Wireshark table—there, everything was visible in plain text. Under HTTPS, it’s all far not that clear. No one intercepting the packets from "outside" of the secured channel can read the data unless they managed to "steal" a secret key.&lt;/p&gt;

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




&lt;p&gt;Well... I guess that’s all for networking fundamentals in the context of web development. This article got quite long. &lt;/p&gt;

&lt;p&gt;The next (and concluding) part of this series will be mostly centered around the backend side of web development and JavaScript, but from a particular perspective - here is a teaser: can you use JavaScript the same you use Python?&lt;/p&gt;




&lt;p&gt;Summarizing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Don’t ignore the networking and servers. Even if you’re aiming to become a CSS/HTML/JS guru who can create amazing UIs, remember that understanding how servers and networking work is crucial. Otherwise, your frontend code could unintentionally breach security and expose sensitive data. Web dev isn’t just about pretty visuals; it’s also about safe and &lt;strong&gt;efficient&lt;/strong&gt; communication between clients and servers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;HTTPS is more complex than HTTP; it’s not simply a "version 2" of HTTP. Developing on HTTP and localhost won’t behave exactly the same as when you switch to HTTPS in production. Secure cookies, CORS, and encrypted data transfers all tax your code once communication passes from HTTP to HTTPS.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Get familiar with how ports work and how to monitor them. You don’t want to open a bazillion ports in your web app by repeatedly opening up database clients or other services each on a new port.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Understanding URLs is vital for setting up routing in your web apps, especially when you deal with protected endpoints that require authorization.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Understanding IP addresses is just &lt;strong&gt;basic&lt;/strong&gt; for any IT field. It will prevent you from sending your friend a URL to evaluate your cool web app that has as address your home network’s 192.168.x.x address.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A privacy-aware bonus that may encourage you to explore in-depth networking and DNS:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you’re a privacy-obsessed and always use a VPN to surf the web like an untraceable ninja, ensure that DNS queries are never forwarded to your ISP’s DNS server. If they are, you are a traceable ninja.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>http</category>
      <category>uri</category>
      <category>webdev</category>
      <category>dns</category>
    </item>
    <item>
      <title>Where to Start in Web Development: Your Browser as Your First IDE</title>
      <dc:creator>aL</dc:creator>
      <pubDate>Sun, 09 Mar 2025 22:50:15 +0000</pubDate>
      <link>https://dev.to/dev-charodeyka/where-to-start-in-web-development-react-angular-svelte-or-somewhere-else-29a4</link>
      <guid>https://dev.to/dev-charodeyka/where-to-start-in-web-development-react-angular-svelte-or-somewhere-else-29a4</guid>
      <description>&lt;p&gt;The reality of starting web development may be very confusing in the beginning. There are so many resources, courses, tutorials, and so on—but how do you choose the right one? Moreover, chances are that right from the start you’ll bump into React/Angular/Vue, because they are very popular &lt;em&gt;frameworks&lt;/em&gt;, and it might seem like all web development can be done only with one of these frameworks and it begins with picking a "right" framework.&lt;/p&gt;

&lt;p&gt;Plus, a lot of tutorials begin by setting up VS Code as the IDE (Integrated Development Environment). You start using VS Code, learn how to write &lt;code&gt;.html&lt;/code&gt; and &lt;code&gt;.css&lt;/code&gt; files, do some basic JavaScript scripting, and then—for simplicity—you install the VS Code Live Server. You click here, click there, maybe even run &lt;code&gt;npm run...&lt;/code&gt; and—boom—your first web app appears in the browser. Enjoy!&lt;/p&gt;

&lt;p&gt;But wait—what’s actually going on? &lt;code&gt;localhost&lt;/code&gt;? Some port (:3000)? The browser complaining about an unsecured HTTP connection? JavaScript seems weird, and it’s not clear how or where to use it, or what the use cases are. How is the browser displaying your scripts so nicely? What is Node.js, and how did installing it on your PC allow the browser to execute JS scripts? So many questions might come up...&lt;/p&gt;

&lt;p&gt;The worst thing you can do is leave these questions unanswered. As soon as you continue learning without truly understanding, these blind spots keep piling up. Eventually, you might realize you can’t develop anything based on your own ideas—at least not without following a step-by-step tutorial—because you don’t really know where to start.&lt;/p&gt;

&lt;p&gt;In general, if you’re just discovering web development field and want to begin somewhere, starting with any of frameworks (React, Svelte, Vue and Angular are frameworks)—will only confuse you, because &lt;strong&gt;they’re definitely not the tools to start with web development&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Even though, of course, HTML, CSS, and JavaScript (further - JS) are the fundamentals of web development, but don't worry—this article isn't about that you just need to first master them and you will become web dev. &lt;/p&gt;

&lt;p&gt;I am assuming that if you want to do web development, your final aim is to learn how to create web applications or websites—basically something that will be running in a &lt;strong&gt;browser&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And here is the first question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How browsers work? What happens "behind the hood" when you open your browser and homepage shows on your screen? How does your browser manage to display all the web sites you visit?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You may be aware of the fact that most of the web applications have a "backend" that is residing on some mysterious "servers". &lt;/p&gt;

&lt;p&gt;And here is the second question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What exactly are the "backend" and "server," and how does a browser communicate with a "server"? Which code is considered "frontend," and which is "backend"? What determines whether some code belongs to one or the other?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you cannot answer these question, that's "where" you must start in web development—by finding the answer to them.&lt;/p&gt;

&lt;p&gt;In this series of articles I will provide the answers to these questions illustrating them with hands on code examples. I will be writing code in a very direct way—no IDEs, no frameworks, no extra stuff. Just a browser, my PC's Linux OS, and basic JS and HTML. Because I also want to show you that you can do many things with just basic tools and that only your imagination is the limit. You don't need React, Nginx, Express, Angular, VSCode Live Server or something else to &lt;strong&gt;start trying&lt;/strong&gt; some web development. &lt;/p&gt;

&lt;p&gt;So, let's start!&lt;/p&gt;




&lt;p&gt;I’ve decided to write a set of articles on web development, rather than pack everything I want to share into one very long piece. This first article in the series—which you’re currently reading—will focus on using the browser as your powerful tool for web development. Here’s a roadmap:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Building confidence with a browser

&lt;ol&gt;
&lt;li&gt;Open random files in your browser to see what happens&lt;/li&gt;
&lt;li&gt;Browser's Developer Tools as an IDE (Integrated Development Environment)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;How does a browser work?

&lt;ol&gt;
&lt;li&gt;What is the &lt;code&gt;document&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Browser's rendering engine&lt;/li&gt;
&lt;li&gt;Document Object Model - DOM&lt;/li&gt;
&lt;li&gt;Browser's JavaScript Interpreter&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;a href="https://dev.to/dev-charodeyka/where-to-start-in-web-development-ignoring-learning-https-urls-dns-ip-ssl-will-have-57le"&gt;second part&lt;/a&gt; of this web development series focuses on the &lt;strong&gt;browser's networking layer&lt;/strong&gt;, specifically on &lt;strong&gt;URLs&lt;/strong&gt;, the &lt;strong&gt;HTTP protocol&lt;/strong&gt;, and a few &lt;strong&gt;must-know networking concepts&lt;/strong&gt; in general. Although the networking field in IT isn’t simple, it’s a tough pill you have to swallow  - if your goal is to get your web applications to a production-ready stage—rather than leaving them on your PC in some project folder, you must understand networks.&lt;/p&gt;

&lt;p&gt;You might question this statement, especially if you’ve already explored the two well-known branches of web development—backend and frontend—and decided that frontend is what you want to do. However, even if you’ve chosen to focus on one, that doesn’t mean you can completely ignore the other. You still need at least the basic concepts and an understanding of how both "branches" work—frontend and backend. The third part of this set of articles will be dedicated to "backend" with a focus on what makes a code "frontend" or "backend".&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;NB!&lt;/strong&gt; I use Linux, more precisely Debian, and I have an allergy to any other OS. So, if you use Windows or macOS, you might need to google how to  do the same things on your system. Anyway, whatever I do in this article is not related in any way to the type of Operational System.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Building confidence with a browser
&lt;/h3&gt;

&lt;p&gt;If before web development you were just a &lt;em&gt;user&lt;/em&gt; of your preferred browser, that golden time is over—now it's your working horse if you're aiming to be a front-end developer. As a &lt;em&gt;user&lt;/em&gt;, you might be very picky about browsers, choosing one that suits you. But the moment you decide to do front-end, hehe, soon you will install all the most common browsers for testing purposes—at least one per engine group they use (yes, even Microsoft Edge). I'll explain why later.&lt;/p&gt;

&lt;p&gt;So, the browser... Now, I am writing this article in the browser. In a tab I am working I see in the address bar something like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;https://dev.to/dev-charodeyka/where-to-start-in-web-development-bla-bla/edit&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And if I go to any other webpage, I'll see something like:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;https://some-site/home&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You may have noticed that you can use your browser not only just to view web sites, but also to open images, PDF files, and other types of files. For example, I don't have any PDF reader on my system, so I use the browser to view PDF files if needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.1 Open random files in your browser to see what happens
&lt;/h3&gt;

&lt;p&gt;Okay, so I open LibreOffice, drop some random text, and save it as a PDF on my PC and... I open it in the browser by typing in the address bar: &lt;code&gt;file:///home/lalala/Projects/DEVTO/webdev/randomPDF.pdf&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Little remark: pay attention to how a local file stored on my PC is opened in the browser vs how websites are opened— "file:///home/lalala/..." vs "&lt;a href="https://some-site/home." rel="noopener noreferrer"&gt;https://some-site/home.&lt;/a&gt;.". Do you notice the same syntax? The key part to notice is: &lt;code&gt;https://&lt;/code&gt; and &lt;code&gt;file://&lt;/code&gt; (third &lt;code&gt;/&lt;/code&gt; in &lt;code&gt;file:///home/lalala/...&lt;/code&gt; belongs to the absolute path of the file). It is important, I will return to this later in the article.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And here is what I see when I open a &lt;code&gt;randomPDF.pdf&lt;/code&gt; file in my Browser:&lt;/p&gt;

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

&lt;p&gt;What if I want to view a &lt;em&gt;text&lt;/em&gt; file with the same content instead of a PDF?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# first, I create it 
$ vim randomTXT.txt
Hellow!
I am a TXT file!
I am visualized in a browser!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I paste this into browser's address bar to open text file: &lt;code&gt;file:///home/lalala/Projects/DEVTO/webdev/randomTXT.txt&lt;/code&gt;&lt;/p&gt;

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

&lt;p&gt;As you can see, the text is just text—there’s no bigger font for a title line, because the TXT format does not provide any way to format/style the text... but a &lt;strong&gt;H&lt;/strong&gt;yper*&lt;em&gt;T&lt;/em&gt;&lt;em&gt;ext **M&lt;/em&gt;&lt;em&gt;arkup **L&lt;/em&gt;*anguage does—let’s create one!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# first, I create it 
$ vim randomHTML.html
&amp;lt;h1&amp;gt;Hellow!&amp;lt;/h1&amp;gt;
&amp;lt;h3&amp;gt;I am a TXT file!&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;I am visualized in a browser!&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I paste this into browser's address bar: &lt;code&gt;file:///home/lalala/Projects/DEVTO/webdev/randomHTML.html&lt;/code&gt;&lt;br&gt;
Here is the result:&lt;/p&gt;

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

&lt;p&gt;Let's add some &lt;em&gt;spice&lt;/em&gt; - styling of one word with &lt;strong&gt;C&lt;/strong&gt;ascading &lt;strong&gt;S&lt;/strong&gt;tyle &lt;strong&gt;S&lt;/strong&gt;heets - a &lt;em&gt;style sheet language used for specifying the presentation and styling of a document written in a markup language&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ vim randomHTML.html
&amp;lt;h1&amp;gt;Hellow!&amp;lt;/h1&amp;gt;
&amp;lt;h3&amp;gt;I am a random &amp;lt;span style="color: red;"&amp;gt;HTML&amp;lt;/span&amp;gt; file!&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;I am visualized in a browser!&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;/p&gt;

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

&lt;p&gt;I demonstrated HTML and CSS in action. If I want to display more text or change the color of some words, is modifying &lt;code&gt;randomHTML.html&lt;/code&gt; file and then refresh the browser's page to see the changes is the only way to do it? Heh, of course not. &lt;/p&gt;

&lt;h4&gt;
  
  
  1.2 Browser's Developer Tools as an IDE (Integrated Development Environment)
&lt;/h4&gt;

&lt;p&gt;First, I have to open the developer tools (hereafter, "dev tools") in my browser. If you're not sure how to open dev tools, you can easily find instructions online. A common method is to right-click anywhere on the page and select "Inspect" from the drop-down menu; however, this option may not always be available, as some websites block it. In that case, you can launch the dev tools from your browser's control panel, where you also can check a specific keyboard shortcut they are usually tied to.&lt;/p&gt;

&lt;p&gt;This is how my chromium based browser Brave dev tools look like (the quality of screenshot is not the best due to the automatic compression of DEVTO attachments):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw70hv6x2i7dvnweqlzlg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw70hv6x2i7dvnweqlzlg.png" alt=" " width="800" height="172"&gt;&lt;/a&gt;&lt;/p&gt;
Development tools of my Brave browsers are grouped into Elements, Console, Sources, Network, Performance, Memory, Application, Security. I add/delete/modify HTML elements from Elements Tab. 



&lt;p&gt;Using dev tools I can add/delete/modify HTML elements and CSS styles for them "on the fly". Of course dev tools are a pretty powerful tool and are not meant only for these silly modifications, but I just wanted to demonstrate how you can easily experiment with HTML and CSS directly from a browser.&lt;/p&gt;

&lt;p&gt;However, keep in mind that whatever I do with the browser’s dev tools doesn’t affect the original &lt;code&gt;.html&lt;/code&gt; file that I opened in the browser—all changes will be lost if I don’t save them into a &lt;strong&gt;separate file&lt;/strong&gt;. The same is true for any web page you inspect with dev tools and eventually modify something - of course it does not affect permanently the original page in any way.&lt;/p&gt;

&lt;p&gt;Okay, I played a bit with HTML and CSS from &lt;em&gt;Elements&lt;/em&gt; tab of dev tools. There’s another interesting tab in these tools called &lt;em&gt;Console&lt;/em&gt;, which is an interactive shell where I can write some JS code and execute it!&lt;/p&gt;

&lt;p&gt;Some silly "print" of the sum of two numbers and a couple of  evaluations of expressions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//JS code I ran in the Browser's console:
const a = 2
const b = 3
a&amp;gt;b
a===b
a&amp;lt;b
console.log(a+b)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you know what? Why not add a new HTML element to the displayed &lt;code&gt;.html&lt;/code&gt; file's content opened in a browser with JS? Because JS is definitely capable of it:&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//JS code I used to add a new text to my HTML document:

//first, I add &amp;lt;div&amp;gt;
const newRandomDiv = document.createElement('div');
//then, I add child of &amp;lt;div&amp;gt;, &amp;lt;p&amp;gt; with some text
newRandomDiv.innerHTML = '&amp;lt;p&amp;gt;Hellow, I was added by JavaScript!!!&amp;lt;/p&amp;gt;';
//my HTML document has the &amp;lt;body&amp;gt;, so I append new element as a child of it
document.body.appendChild(newRandomDiv);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, you might ask: what is the &lt;code&gt;document&lt;/code&gt;? I opened the &lt;em&gt;Console&lt;/em&gt; of dev tools and haven’t declared any &lt;code&gt;document&lt;/code&gt; variable or anything like that—where does it come from? I guess it’s obvious that the document in question is related to the HTML file that’s open in the tab:&lt;/p&gt;

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

&lt;h3&gt;
  
  
  2. How does a browser work?
&lt;/h3&gt;

&lt;p&gt;On the screenshot above, you can see that I used the browser’s JavaScript interactive shell to "print" the &lt;code&gt;document&lt;/code&gt;. From the output it is clear that it contains HTML elements from the original &lt;code&gt;.html&lt;/code&gt; file I opened in the browser, plus some other elements that I added manually by directly editing the HTML structure in the dev tools and also using JavaScript from the browser. However, what was not originally in the &lt;code&gt;randomHTML.html&lt;/code&gt; file that I opened are the tags like &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt;,&lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; tag represents the root of an HTML document. The  tag defines the document's body.&lt;br&gt;
The &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; element contains all the contents of an HTML document, such as headings, paragraphs, images, hyperlinks, tables, lists, etc.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  2.1 What is the &lt;code&gt;document&lt;/code&gt;?
&lt;/h4&gt;

&lt;p&gt;Everything, that is enclosed between &lt;code&gt;&amp;lt;html&amp;gt;&amp;lt;/html&amp;gt;&lt;/code&gt; tags is the &lt;code&gt;document&lt;/code&gt;. And, roughly speaking, that is what web development (front-end part) is about: working on this &lt;code&gt;document&lt;/code&gt; - bending it, mangling it, shaping it to make it look how you want. When I opened &lt;code&gt;randomHTML.html&lt;/code&gt; file with the browser I &lt;em&gt;loaded this file into the browser&lt;/em&gt;, and its content became a part of &lt;em&gt;document object&lt;/em&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;When an HTML document is loaded into a web browser, it becomes a document object.&lt;br&gt;
The document object is the root node of the HTML document.&lt;br&gt;
The document object is a property of the window object.&lt;br&gt;
The document object is accessed with: &lt;code&gt;window.document&lt;/code&gt; or just &lt;code&gt;document&lt;/code&gt; (&lt;a href="https://www.w3schools.com/jsref/dom_obj_document.asp" rel="noopener noreferrer"&gt;W3Schools: HTML DOM Documents&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What about the TXT and PDF files that I opened before? Well, &lt;strong&gt;their content&lt;/strong&gt; also become a part of document objects, when I open them in the browser.&lt;/p&gt;

&lt;p&gt;This is the &lt;code&gt;.txt&lt;/code&gt; file opened in my browser when I inspect the tab's content with dev tools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta name="color-scheme" content="light dark"&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;pre style="word-wrap: break-word; white-space: pre-wrap;"&amp;gt;Hellow!
     I am a random TXT file!
     I am visualized in a browser!
    &amp;lt;/pre&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may notice how interestingly the &lt;em&gt;raw text file content was "wrapped" into an HTML document object&lt;/em&gt; by my browser. It is not uncommon for modern browsers to create a minimal HTML "wrapper" for a content that a browser was requested to display and that it managed to recognize.&lt;/p&gt;

&lt;p&gt;In the plain text file, there was no any styling, like the first line in bold and enlarged font. But content of the &lt;code&gt;.txt&lt;/code&gt; file has the line breaks. They were preserved by the browser by attaching CSS rules for word-breaking, white space, and word wrapping!&lt;/p&gt;

&lt;p&gt;Let's have a look on the document object structure of PDF file opened in the browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body style="height: 100%; width: 100%; overflow: hidden; margin:0px; background-color: rgb(82, 86, 89);"&amp;gt;
    &amp;lt;embed name="DDDBE1725B1DA9C2C0B9CFA699CCB3B9" style="position:absolute; left: 0; top: 0;" width="100%" height="100%" src="about:blank" type="application/pdf" internalid="DDDBE1725B1DA9C2C0B9CFA699CCB3B9"&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What is similar to a &lt;code&gt;.txt&lt;/code&gt; file is that the browser’s PDF viewer is embedded in a minimal HTML document. While with a text file I was able to see the actual file's content directly and even modify it right from the browser, with a PDF file it's not the same. This kind of embedding is actually a byproduct of how my browser handles and renders PDF files.&lt;/p&gt;

&lt;p&gt;Can I mess it up with how PDF file is displayed using dev tools even if it is originally PDF file? Sure:&lt;/p&gt;

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

&lt;p&gt;Here is JS code I used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const redThingy = document.createElement('div');
redThingy.style.backgroundColor = 'red';

redThingy.style.width = '400px';
redThingy.style.height = '400px';

redThingy.style.position = 'absolute';
redThingy.style.top = '50%';
redThingy.style.left = '50%';
redThingy.style.transform = 'translate(-50%, -50%)';

redThingy.innerHTML = '&amp;lt;p&amp;gt;Hello, I was added by JavaScript to &amp;lt;span style="color: yellow;"&amp;gt;mess up&amp;lt;/span&amp;gt; PDF!!!&amp;lt;/p&amp;gt;';

document.body.appendChild(redThingy);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, obviously, this red box with text didn’t affect the PDF file itself in any way. What my modifications actually affected was just how my browser rendered the opened PDF file.&lt;/p&gt;

&lt;p&gt;I guess it’s time to summarize the point of all of these experiments.&lt;/p&gt;

&lt;p&gt;As you can see, a browser is a powerful tool. It's not just an "app" to open websites with added features like a photo viewer or PDF viewer.&lt;/p&gt;

&lt;h4&gt;
  
  
  2.2 Browser's rendering engine
&lt;/h4&gt;

&lt;p&gt;Any browser has its own engine that &lt;em&gt;renders&lt;/em&gt; what you see on its User Interface (UI). Roughly speaking, to render anything a browser uses &lt;em&gt;instructions&lt;/em&gt; in a markup language—HTML. &lt;/p&gt;

&lt;p&gt;Different browsers work differently. However, let me explain the process slightly generalizing: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When you enter a query in the browser's search bar (a URL), &lt;strong&gt;networking layer&lt;/strong&gt; of the browser delivers your "request" to a destination point that is retrieved from URL and bring back to browser a response which could be any type of file or data. Some file types can’t be displayed directly (for example, if the file is corrupted or has an unrecognized format), in which case the browser may display an error or prompt you to download it instead.&lt;/li&gt;
&lt;li&gt;If the received data is recognized as HTML, the browser parses it to build a Document Object Model (DOM) tree. This process involves reading the HTML and turning it into a structured hierarchy of nodes that represent the page’s elements. If the received data isn’t HTML but the browser can still display it (like plain text or PDF), the browser will process it in a specialized way. For plain text files, most browsers apply a minimal HTML “wrapper”; for PDFs, the browser’s PDF viewer is embedded in within an HTML context, so browser can position it, handle scrolling, zoom, and so on.&lt;/li&gt;
&lt;li&gt;The browser then creates or updates a render tree, which is the combination of the DOM and the CSSOM (the CSS Object Model). The layout step figures out the exact positions and sizes of all elements on the page.&lt;/li&gt;
&lt;li&gt;Finally, the browser paints (or “rasterizes”) the render tree onto your screen. This is what you actually see in the browser’s viewport.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There are many variables in the rendering process. Every element in the DOM tree is an object that the browser needs to render by calculating its position (this is where &lt;em&gt;responsive design&lt;/em&gt; comes in, since an element’s positioning depends heavily on the available space on the user’s device), appearance, and any dynamic changes. The DOM tree isn’t static; the browser continually re-renders it as elements move or change appearance—like those animations you see on some websites.&lt;/p&gt;

&lt;h4&gt;
  
  
  2.3 Document Object Model - DOM
&lt;/h4&gt;

&lt;p&gt;I want to elaborate more on the DOM tree. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The backbone of any HTML document is its tags. In the Document Object Model (DOM), every HTML tag is an object. Nested tags are considered “children” of the enclosing tag, and even the text inside a tag is treated as an object. The DOM represents HTML as a tree structure of tags. (&lt;a href="https://javascript.info/dom-nodes" rel="noopener noreferrer"&gt;Source&lt;/a&gt;)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;...&amp;lt;/h1&amp;gt;
    &amp;lt;div&amp;gt;...&amp;lt;/div&amp;gt;
    &amp;lt;...&amp;gt;...&amp;lt;/...&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can notice the nested structure of HTML document: each tag is an object, and the nesting forms a tree-like hierarchy.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;I'm repeating this: if you're aiming to be a front-end developer, the DOM is everything. In front-end development, almost everything ultimately revolves around DOM manipulation. I've shown how to manipulate the DOM using browser's dev tools—adding HTML elements, applying CSS, and most importantly, doing it through JS. Because in front-end development, JS is primarily about manipulating the objects in the DOM tree, which are (at their core) HTML elements.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;All these DOM objects are accessible via JavaScript. I’ve only shown a small part, and it might have seemed very easy. Well, it was easy because I was working &lt;em&gt;directly&lt;/em&gt; with those DOM objects—there were only a few, and &lt;em&gt;everything was clear&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;But remember this: the JS libraries you use to "simplify" certain features—and especially any framework (React, Angular, Vue, etc., though each to a different extent)—actually take you one step further away from direct manipulation of the DOM objects. &lt;/p&gt;

&lt;p&gt;Frameworks abstract direct DOM manipulation, so you write your code in a more &lt;em&gt;declarative&lt;/em&gt; way. Behind the scene, any framework still manipulates the DOM — but you interact with a framework’s abstractions instead of directly selecting or updating DOM elements.&lt;/p&gt;

&lt;p&gt;The key point is that these abstractions are not "bad"  - they make development more efficient and your code more maintainable, as long as you have a basic understanding of how the DOM works. If you rely on abstractions without knowing what’s happening underneath, you are cooked. No fancy framework will make you a good developer if you don’t at least keep the DOM tree in mind whenever you manipulate it with JS.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I’ve shown how to do manipulate DOM directly in the browser—not to promote this style of coding (obviously, no one codes like this for a full project). I just wanted to show you what your browser is truly capable of. &lt;strong&gt;Everything I did was handled entirely by the browser—there was no server behind it, no external tools, just the browser.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  2.4 Browser's JavaScript Interpreter
&lt;/h4&gt;

&lt;p&gt;And this leads to a very important point: I was able to execute JavaScript in the browser because browsers have a built-in JavaScript interpreter. Think about it—if you’re a Python developer, before you could run Python code, you had to install Python on your PC (unless you're on Linux, where it's usually pre-installed). That’s because Python isn't machine code, so your PC can’t understand it without an interpreter. The same goes for JavaScript. The key point is that modern browsers come with a JavaScript interpreter embedded in their engine.&lt;/p&gt;

&lt;p&gt;Sounds great, right? But here’s the cornerstone: &lt;strong&gt;different browsers have slightly different JavaScript interpreters inside their engines&lt;/strong&gt;. For basic, standard JavaScript usage, this isn’t a big deal. But when you get into non-standard usage, things get trickier. And every external library you add to your code potentially brings you slightly closer to using a JS in "non-standard" way that may be not supported by some browsers. And these browsers may be the favorite browser of potential users of your web applications :).  &lt;/p&gt;

&lt;p&gt;To conclude, here is a very &lt;strong&gt;generalized&lt;/strong&gt; scheme of &lt;em&gt;how browsers work&lt;/em&gt;: &lt;/p&gt;

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

&lt;p&gt;In the next article of this series on Web Development, I’ll elaborate on the format of the "query" you see in the browser’s search bar, as well as the role of "servers".&lt;/p&gt;

&lt;p&gt;This &lt;em&gt;query&lt;/em&gt; format is very important—I’d even call it crucial— because the routing in your future web apps result in these "queries".&lt;/p&gt;

&lt;p&gt;Jumping ahead, I should mention that "query" is actually a bit of an oversimplification of what appears in the address bar, because the correct technical term is URI (Uniform Resource Identifier). The true &lt;em&gt;query&lt;/em&gt; is just one part of a URI, and URIs are closely tied to the HTTP protocol in web development, which in turn is tied to networking.&lt;/p&gt;

&lt;p&gt;In the next article, I’ll do my best to simplify and clarify all these concepts!&lt;/p&gt;




&lt;p&gt;Summarizing the main points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Browsers have a built-in JS interpreter and they do run JS code.&lt;/li&gt;
&lt;li&gt;The browser’s JS interpreter is not the same as your PC (or server)’s JS interpreter—they’re different. Keep this in mind.&lt;/li&gt;
&lt;li&gt;The internal components of browsers are complex, and each browser implements them differently. &lt;strong&gt;In my humble opinion, that’s what makes the frontend part harder than the backend in modern architectures.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You need to &lt;em&gt;understand&lt;/em&gt; both backend and frontend to be a good web developer—you can’t just master one and completely ignore the other.&lt;/li&gt;
&lt;li&gt;In front-end development, almost everything ultimately revolves around DOM manipulation (= you have to understand the concept of DOM as clear as possible)&lt;/li&gt;
&lt;li&gt;If "network" for you only means "the Wi-Fi at home", you must invest your time in studying networking.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>react</category>
      <category>browser</category>
    </item>
    <item>
      <title>Virtualization on Debian with libvirt&amp;QEMU&amp;KVM — Networking beyond "default": Forwarding Incoming Connections to NAT'ed network</title>
      <dc:creator>aL</dc:creator>
      <pubDate>Mon, 27 Jan 2025 23:30:02 +0000</pubDate>
      <link>https://dev.to/dev-charodeyka/virtualization-on-debian-with-libvirtqemukvm-networking-beyond-default-bridged-networking-2ef4</link>
      <guid>https://dev.to/dev-charodeyka/virtualization-on-debian-with-libvirtqemukvm-networking-beyond-default-bridged-networking-2ef4</guid>
      <description>&lt;p&gt;This is the second part of "Networking Beyond "Default". All &lt;em&gt;must-have&lt;/em&gt; theory was explained in detail in the previous part, this article will be very practical.&lt;/p&gt;

&lt;p&gt;If your understanding of these concepts is very &lt;em&gt;wobbly&lt;/em&gt;, I strongly recommend reading the &lt;a href="https://dev.to/dev-charodeyka/virtualization-on-debian-with-libvirtqemukvm-networking-beyond-default-must-have-concepts-to-2ccn"&gt;previous article&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;nftables&lt;/code&gt; rules that perform network address translation&lt;/li&gt;
&lt;li&gt;The structure of IPv4 addresses and CIDR&lt;/li&gt;
&lt;li&gt;Physical vs Virtual Network Interfaces&lt;/li&gt;
&lt;li&gt;Virtual network switches a.k.a bridges&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Terminology I will use in this article:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Host&lt;/em&gt; – This refers to your PC, on which you set up virtualization and create virtual machines.&lt;br&gt;
&lt;em&gt;Guest&lt;/em&gt; – This is any virtual machine you create. VM/VMs – A virtual machine/Virtual Machines.&lt;br&gt;
&lt;em&gt;LAN&lt;/em&gt; – Local Area Network managed by my WiFi Router provided by ISP(Internet service Provider)&lt;br&gt;
&lt;em&gt;NAT&lt;/em&gt; – Network Address Translation.&lt;br&gt;
&lt;em&gt;&lt;strong&gt;DEFAULT&lt;/strong&gt; network&lt;/em&gt; – the Libvirt's virtual network that virtual machines are connected to by default (gets started with sudo virsh net-start default)&lt;br&gt;
&lt;em&gt;Packet&lt;/em&gt; - a network packet is a formatted unit of data carried by a network.&lt;/p&gt;



&lt;p&gt;As I have spitted the content on virtualization into 3+ articles, I’ll start with a quick overview of my current setup and the goals I want to achieve with different network configurations.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;My virtual machines are created under &lt;code&gt;qemu:///system&lt;/code&gt;, which means I run all &lt;code&gt;virsh&lt;/code&gt; commands with &lt;code&gt;sudo&lt;/code&gt;. Using &lt;code&gt;sudo&lt;/code&gt; privileges allows me to configure the network as I need, whereas &lt;code&gt;qemu:///session&lt;/code&gt; has very limited permissions in hardware virtualization (for more details, check &lt;a href="https://dev.to/dev-charodeyka/virtualization-on-debian-with-virshqemukvm-what-you-need-to-install-and-how-49oo"&gt;this article&lt;/a&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;My host machine runs Debian, with nftables managing network traffic and firewalld serving as the firewall management tool.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I have a virtual machine running &lt;a href="https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-debian/#std-label-install-mdb-community-debian" rel="noopener noreferrer"&gt;MongoDB&lt;/a&gt; (VM name: &lt;code&gt;deb-mongo&lt;/code&gt;). This VM was created in the most basic way and is attached to the &lt;strong&gt;DEFAULT&lt;/strong&gt; libvirt network. Here’s what that entails:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;deb-mongo&lt;/code&gt; VM can communicate with other VMs connected to the same &lt;strong&gt;DEFAULT&lt;/strong&gt; network, which operates in the NAT mode.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The VM can communicate with the host in both directions—whether the host initiates the connection (e.g., via SSH) or the VM initiates it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The VM has access to the internet, allowing me to install MongoDB, update system packages, use &lt;code&gt;ping&lt;/code&gt;, &lt;code&gt;curl&lt;/code&gt;, &lt;code&gt;wget&lt;/code&gt;, and so on.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No device on my local home network (the same network the host is on) can access this VM directly, and of course, nothing from the outside my home LAN can reach it either.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What I want to achieve, for demonstration purposes, is to make the &lt;code&gt;deb-mongo&lt;/code&gt; VM reachable from my laptop, which is on the same local home network as the host (My Desktop PC). I want to be able to connect to MongoDB running on the VM using the MongoDB Compass GUI client on my laptop.&lt;/strong&gt;&lt;/p&gt;



&lt;p&gt;In the previous article, I analyzed libvirt's &lt;strong&gt;DEFAULT&lt;/strong&gt; network  in detail and broke its configuration down into small pieces. &lt;em&gt;If you were paying close attention, you would have already understood which &lt;code&gt;nftables&lt;/code&gt; rules block connections from anything other than the host machine to the VMs connected to &lt;strong&gt;DEFAULT&lt;/strong&gt; network&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In the scope of this article I plan to enable access to the VM from devices connected to my LAN by modifying &lt;em&gt;nftables&lt;/em&gt; rules of &lt;strong&gt;DEFAULT&lt;/strong&gt; network, plus configure port forwarding on the host.&lt;/p&gt;

&lt;p&gt;This option is viable, however, it is about turning a well-defined libvirt’s &lt;strong&gt;DEFAULT&lt;/strong&gt; network into something different, which it was not meant to be. &lt;/p&gt;

&lt;p&gt;I wrote this tutorial for educational purposes as I noticed that the questions about how to do it can be found on all possible forums for different distros. Personally, I do not use this network configuration in my home lab, and later in this article it will become clear why.&lt;/p&gt;

&lt;p&gt;At some point, it’s better to define a new virtual network from scratch and define rules and NAT for it, rather than modify libvirt’s &lt;strong&gt;DEFAULT&lt;/strong&gt; one, if you are very “NATophile”. &lt;/p&gt;

&lt;p&gt;However, I can assume, that you are not, just maybe you think that is the only option to make VMs reachable for other devices connected to LAN. But it is not true. In the next article I will cover more suitable network configuration for the use case when VMs should be accessible members of LAN.&lt;/p&gt;

&lt;p&gt;Now, let's begin with &lt;strong&gt;DEFAULT&lt;/strong&gt; network tweaking!&lt;/p&gt;



&lt;p&gt;Here is the roadmap for this article:&lt;/p&gt;

&lt;p&gt;➀ DEFAULT libvirt network&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;➀.➀ &lt;code&gt;virbrN&lt;/code&gt; and &lt;code&gt;vnetN&lt;/code&gt; - what do they do?&lt;/li&gt;
&lt;li&gt;➀.➁ About DHCP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;➁ Configuring static IPv4 address on VM&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;➁.➀ Shrinking DHCP range &lt;/li&gt;
&lt;li&gt;➁.➁ Modifying &lt;code&gt;/etc/network/interfaces&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;➂ Connecting to a VM from Another Device on the LAN using the Port Forwarding Method:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;➂.➀ What is the Port Forwarding?&lt;/li&gt;
&lt;li&gt;➂.➁ &lt;code&gt;Firewalld&lt;/code&gt; and open ports&lt;/li&gt;
&lt;li&gt;➂.➂ Forwarding ports on the host with &lt;code&gt;firewalld&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;➂.➃ Adding a rule to &lt;code&gt;nftables&lt;/code&gt; ruleset to accept inbound network traffic to the VM&lt;/li&gt;
&lt;li&gt;➂.➄ Considerable drawbacks of this method&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  ➀ DEFAULT libvirt network
&lt;/h3&gt;

&lt;p&gt;First, I start the &lt;strong&gt;DEFAULT&lt;/strong&gt; network, then I start the VM &lt;code&gt;deb-mongo&lt;/code&gt; and enter its console.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# ip a serves to show when virtual network interfaces appear
$ ip a
1: lo
 ...
2: eno1:
    inet 192.168.1.X/24 brd 
3: wlx123456789kl:
    inet 192.168.1.Y/24

$ sudo virsh net-start default

$ ip a
1: lo
 ...
2: eno1:
    inet 192.168.1.X/24 brd 
3: wlx123456789kl:
    inet 192.168.1.Y/24
4: virbr0:
    inet 192.168.122.1/24

$ sudo virsh start deb-mongo

$ ip a
1: lo
 ...
2: eno1:
    inet 192.168.1.X/24 brd 
3: wlx123456789kl:
    inet 192.168.1.Y/24
4: virbr0:
    inet 192.168.122.1/24
5: vnet0: 
    inet ???

$ sudo virsh console deb-mongo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ➀.➀ &lt;code&gt;virbrN&lt;/code&gt; and &lt;code&gt;vnetN&lt;/code&gt; - what do they do?
&lt;/h4&gt;

&lt;p&gt;First, I want to schematize what these &lt;code&gt;virbr0&lt;/code&gt; and &lt;code&gt;vnet0&lt;/code&gt; network interfaces. They weren’t there initially (&lt;code&gt;ip link&lt;/code&gt;/&lt;code&gt;ip a&lt;/code&gt; before &lt;code&gt;sudo virsh net-start default&lt;/code&gt; did not contain them in outputs), but &lt;code&gt;virbr0&lt;/code&gt; showed up after I started the &lt;strong&gt;DEFAULT&lt;/strong&gt; network, and &lt;code&gt;vnet0&lt;/code&gt; appeared when I started the &lt;code&gt;deb-mongo&lt;/code&gt; VM.&lt;/p&gt;

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

&lt;p&gt;&lt;code&gt;virbr0&lt;/code&gt; is the virtual network switch, and while its name suggests it is a &lt;em&gt;bridge&lt;/em&gt; (and bridges are generally used to &lt;em&gt;connect different networks&lt;/em&gt;), in the case of the &lt;strong&gt;DEFAULT&lt;/strong&gt; libvirt network, &lt;code&gt;virbr0&lt;/code&gt; doesn't &lt;em&gt;bridge&lt;/em&gt; anything - it &lt;strong&gt;does not&lt;/strong&gt; bridge the &lt;strong&gt;DEFAULT&lt;/strong&gt; network (192.168.122.0/24) to any of the networks the host is connected to. In a more physical sense, no physical network interface is &lt;em&gt;plugged&lt;/em&gt; into this &lt;code&gt;virbr0&lt;/code&gt;. All network traffic between VM and host, between VM and the outside world is governed by NAT (Network Address Translation) configuration.&lt;/p&gt;

&lt;p&gt;This is partially why no VM connected to the &lt;strong&gt;DEFAULT&lt;/strong&gt; network can be reached from other devices on your local home network, which the host is connected to. The other reason lies in the rules that define NAT, which essentially translate and isolate the internal network traffic.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;vnet0&lt;/code&gt; is a network TUN device. TUN/TAP devices are kernel virtual network devices. Being network devices supported entirely in software, they differ from ordinary network devices which are backed by physical network adapters.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The guest VM will have an associated tun device created with a name of vnetN, which can also be overridden with the  element. The tun device will be attached to the bridge.This provides the guest VM full incoming &amp;amp; outgoing net access just like a physical machine. (&lt;a href="https://libvirt.org/formatdomain.html#network-interfaces" rel="noopener noreferrer"&gt;Libvirt:Network Interfaces&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's move on.&lt;/p&gt;

&lt;p&gt;If you check above the latest &lt;code&gt;ip a&lt;/code&gt; output from my host PC, you'll notice there is no IPv4 address associated with the &lt;code&gt;vnet0&lt;/code&gt; interface, which is the virtual network interface of my VM &lt;code&gt;deb-mongo&lt;/code&gt;. So, to proceed, I need to check the IP address of the VM directly from the VM itself.&lt;/p&gt;

&lt;p&gt;I want to connect to the MongoDB instance running on this VM using the MongoDB Compass GUI client installed &lt;em&gt;on my host machine&lt;/em&gt;. To do this, the first step is finding the IP address of the VM.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user-mongo@deb-mongo:~$ ip a
1: lo: 
...
2: enp1s0: 
    inet 192.168.122.196/24 brd 192.168.122.255
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's quite a strange IP address, especially considering this is the second VM created in this network. 192.168.122.1 is the address used by &lt;code&gt;virbr0&lt;/code&gt;, which acts as the router/gateway for this &lt;strong&gt;DEFAULT&lt;/strong&gt; network (I'll elaborate more on it later). So why is my VM's IP .196 instead of something like .2 or .3? Where did it get this address? The answer is that it got it from DHCP - the Dynamic Host Configuration Protocol. &lt;/p&gt;

&lt;h4&gt;
  
  
  ➀.➁ About DHCP
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;When you set up any network, any device connecting to this network needs to have certain information, such as the IP-address of its interface, the IP-address of at least one domain name server, and the IP-address of a server in the LAN that serves as a router to the internet. In the manual setup you have to type in this information for each client anew. With the Dynamic Host Configuration Protocol (DHCP) the computers can do that automatically for you. (&lt;a href="https://wiki.debian.org/DHCP_Server" rel="noopener noreferrer"&gt;Source&lt;/a&gt;)&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Each virtual network switch can be given a range of IP addresses, to be provided to guests through DHCP.&lt;br&gt;
Libvirt uses a program, dnsmasq, for this. An instance of dnsmasq is automatically configured and started by libvirt for each virtual network switch needing it. (&lt;a href="https://wiki.libvirt.org/VirtualNetworking.html#network-address-translation-nat" rel="noopener noreferrer"&gt;Libvirt: Network Address Translation-NAT&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;As stated in the libvirt documentation quoted above, I expect to find in the network configuration the range of IPs that the DHCP server is configured to assign. Indeed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo virsh net-dumpxml default

&amp;lt;network&amp;gt;
  &amp;lt;name&amp;gt;default&amp;lt;/name&amp;gt;
  &amp;lt;uuid&amp;gt;2becd6d0-5a0f-4b45-afff-5a518370fc8c&amp;lt;/uuid&amp;gt;
  &amp;lt;forward mode='nat'&amp;gt;
    &amp;lt;nat&amp;gt;
      &amp;lt;port start='1024' end='65535'/&amp;gt;
    &amp;lt;/nat&amp;gt;
  &amp;lt;/forward&amp;gt;
  &amp;lt;bridge name='virbr0' stp='on' delay='0'/&amp;gt;
  &amp;lt;mac address='MA:C:AD:DD:RE:SS'/&amp;gt;
  &amp;lt;ip address='192.168.122.1' netmask='255.255.255.0'&amp;gt;
    &amp;lt;dhcp&amp;gt;
      &amp;lt;range start='192.168.122.2' end='192.168.122.254'/&amp;gt;
    &amp;lt;/dhcp&amp;gt;
  &amp;lt;/ip&amp;gt;
&amp;lt;/network&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;DHCP automatically assigns IPs to the connected to &lt;strong&gt;DEFAULT&lt;/strong&gt; network virtual machines from the range 192.168.122.2 - 192.168.122.254, based on availability rather than strictly sequential allocation. That’s why my VM ended up with .196.&lt;/p&gt;

&lt;p&gt;What DHCP does respect when assigning IPs is that it keeps track of what’s happening in the network it manages. It won’t assign an IP that’s already &lt;em&gt;currently&lt;/em&gt; occupied to new VMs joining the network. However, unless configured otherwise, DHCP won’t intervene if two VMs somehow end up with the same IP, and that can create a mess.&lt;/p&gt;

&lt;p&gt;Why would this happen, you might ask? Well, it’s not uncommon to change the DHCP mode from dynamic IP allocation to static IPs (manually assigned). With the default setup, DHCP assigns dynamic addresses, so when a device connects to the network, it gets an IP like X. But if it disconnects and reconnects later, it &lt;em&gt;might&lt;/em&gt; be assigned a completely different IP like Y, which can be very inconvenient for certain services that need to be accessed remotely.&lt;/p&gt;




&lt;h3&gt;
  
  
  ➁ Configuring static IPv4 address on a VM
&lt;/h3&gt;

&lt;p&gt;NB! It’s not being said for sure that DHCP is some crazy guy randomly assigning IP addresses by just looking at new connections and completely ignoring whether a device (like a VM's network interface) was already connected before. In fact, it’s very common for the IP address assigned once to persist. This is because the VM’s network interface, even if virtual, has a consistent and unchanging MAC address - so, it can be identified.&lt;/p&gt;

&lt;p&gt;Anytime the VM reconnects to the same network, it will 99.9% get the same IP address as it was assigned the first time. However, sometimes certain factors can cause DHCP to reassign a different address.&lt;/p&gt;

&lt;p&gt;On top of that, you may want to bring some order to your VMs, especially if there’s a logical structure to them, and you’d like more control over their network configuration. Assigning static IP addresses can help with this. While static IPs introduce some network fragility, they also improve network security, as you can create firewall rules, nftables rules, etc., based on the fixed IPs.&lt;/p&gt;

&lt;p&gt;Of course, you can still set up such rules with DHCP-assigned dynamic IPs. However, the problem arises if the VM’s IP address changes for any reason. In that case, all the carefully imposed network traffic rules would collapse, and the setup would need to be reconfigured.&lt;/p&gt;

&lt;p&gt;Moreover, knowing how to configure VM's network interface in the way you want it to is very nice skill to have, IMHO. &lt;/p&gt;

&lt;p&gt;To manually assign a static IP to a VM, there are two things to respect: the range of network - I cannot assign IP address out of range; unique IP address - no any other VMs should have the same IP.&lt;/p&gt;

&lt;h4&gt;
  
  
  ➁.➀ Shrinking DHCP range
&lt;/h4&gt;

&lt;p&gt;To prevent, from the start, the possibility that DHCP assigns to a new VM an IP address I manually &lt;em&gt;glued&lt;/em&gt; to one of the VMs by reconfiguring its network interface, I have to modify the &lt;strong&gt;DEFAULT&lt;/strong&gt; network configuration and shrink the DHCP range—the set of available addresses that DHCP can use to assign.&lt;/p&gt;

&lt;p&gt;Currently, this range occupies all the addresses available in the 192.168.122.0/24 network—all 254 addresses, excluding those reserved for the gateway/router (&lt;code&gt;virbr0&lt;/code&gt;) and the broadcast address. By shrinking this range, I ensure there are IPs left outside the DHCP range that I can assign manually to specific VMs without interference.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#to prevent problems with VM connectivity
$ sudo virsh destroy deb-mongo

$ sudo virsh net-edit default
#I replace this:
&amp;lt;network&amp;gt;
...
  &amp;lt;ip address='192.168.122.1' netmask='255.255.255.0'&amp;gt;
    &amp;lt;dhcp&amp;gt;
---&amp;gt; &amp;lt;range start='192.168.122.2' end='192.168.122.254'/&amp;gt;
    &amp;lt;/dhcp&amp;gt;
  &amp;lt;/ip&amp;gt;
&amp;lt;/network&amp;gt;
#with:
&amp;lt;network&amp;gt;
...
  &amp;lt;ip address='192.168.122.1' netmask='255.255.255.0'&amp;gt;
    &amp;lt;dhcp&amp;gt;
---&amp;gt; &amp;lt;range start='192.168.122.101' end='192.168.122.254'/&amp;gt;
    &amp;lt;/dhcp&amp;gt;
  &amp;lt;/ip&amp;gt;
&amp;lt;/network&amp;gt;

# restart DEFAULT network
$ sudo virsh net-destroy default
$ sudo virsh net-start default

# check that changes came in power:
$ sudo virsh net-dumpxml default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ➁.➁ Modifying &lt;code&gt;/etc/network/interfaces&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;As it was found out earlier, the current IP of &lt;code&gt;deb-mongo&lt;/code&gt; VM is 192.168.122.196. To change this IP I will have to modify configurations of network interface stored in &lt;code&gt;/etc/network/interfaces&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user-mongo@deb-mongo:~$ sudo vim.tiny /etc/network/interfaces
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

source /etc/network/interfaces.d/*

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
allow-hotplug enp1s0
iface enp1s0 inet dhcp &amp;lt;--here is what I need to change

# I comment out the line: #iface enp1s0 inet dhcp and replace it with:
iface enp1s0 inet static
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this line (&lt;code&gt;iface enp1s0 inet static&lt;/code&gt;), I just switched the mode of interface configuration from &lt;code&gt;dhcp&lt;/code&gt; to &lt;code&gt;static&lt;/code&gt;, which means I now have to configure it manually. My goal is to assign an IP address of my choice, which can be set using the &lt;code&gt;address&lt;/code&gt; field. However, if you were attentive to the previous section, you’d know that DHCP doesn’t just assign an IP address—it also provides the device (the network interface of the VM) with crucial information about the network it’s connecting to. More specifically, it provides details such as the size of the network (netmask) and the IP address of the gateway. Since I switched to static mode, I need to manually provide the following details in the configuration: &lt;code&gt;address&lt;/code&gt;, &lt;code&gt;gateway&lt;/code&gt;, and &lt;code&gt;netmask&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;address 192.168.122.10&lt;/code&gt; is the IP address I want this VM to have persistently. You can choose any number outside the range .&lt;/p&gt;

&lt;p&gt;Information about the gateway and netmask is taken from this part of the network configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  &amp;lt;ip address='192.168.122.1' netmask='255.255.255.0'&amp;gt;
    &amp;lt;dhcp&amp;gt;
      &amp;lt;range start='192.168.122.101' end='192.168.122.254'/&amp;gt;
    &amp;lt;/dhcp&amp;gt;
  &amp;lt;/ip&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gateway is &lt;code&gt;ip address='192.168.122.1'&lt;/code&gt;, netmask is &lt;code&gt;netmask='255.255.255.0'&lt;/code&gt;. With the netmask, it’s pretty straightforward—it corresponds to the CIDR notation of the network. For example, /24 equals 255.255.255.0, with the last 0 indicating the part of the IP address range that is available for assignment. This defines the size of the network.&lt;/p&gt;

&lt;p&gt;But what about the gateway? Remember the previous article's explanation about network switches? In the &lt;strong&gt;DEFAULT&lt;/strong&gt; network, this job is handled by &lt;code&gt;virbr0&lt;/code&gt;. All VMs connected to this virtual network switch become part of the same network, and they are allowed to communicate with each other (as per the &lt;code&gt;nftables&lt;/code&gt; rules set by libvirt).&lt;/p&gt;

&lt;p&gt;It’s not just that by connecting to the same virtual network switch, VMs can magically communicate &lt;strong&gt;directly&lt;/strong&gt; with one another. No, there is a network switch in between EACH COMMUNICATION, acting like a &lt;em&gt;post office&lt;/em&gt;. When you want to send a letter to someone, you write the letter, put the destination address on it, and bring it to the post office—you don’t usually travel to the recipient’s address and deliver it manually. The post office handles everything, ensuring the letter is delivered.&lt;/p&gt;

&lt;p&gt;The same logic applies to the network switch, and in the case of the &lt;strong&gt;DEFAULT&lt;/strong&gt; network, this role is performed by &lt;code&gt;virbr0&lt;/code&gt;, which acts as the gateway. Gateways, as a technical term, are broader than just routers or switches because they can perform a variety of tasks. It is important for the VM to know where to find the gateway in order to communicate via it with other network devices and with the outside world (if allowed). Regardless, the gateway (&lt;code&gt;virbr0&lt;/code&gt;) will decide how to handle any communication sent from the VMs connected to it. virbr0 IP address is 192.168.122.1 (can validate with &lt;code&gt;ip a&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;This information ends up in the configuration file &lt;code&gt;/etc/network/interfaces&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;....
....
#iface enp1s0 inet dhcp
iface enp1s0 inet static
  address 192.168.122.10
  netmask 255.255.255.0
  gateway 192.168.122.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, I restart &lt;code&gt;networking systemd service&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user-mongo@deb-mongo:~$ sudo systemctl restart networking
user-mongo@deb-momgo:~$ ip a
1: lo: 
....
2: enp1s0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 qdisc fq_codel state UP ...
    link/ether ...
    inet 192.168.122.10/24 brd 192.168.122.255

# to check if nothing went messed up:
user-mongo@deb-momgo:~$ ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=112 time=17.4 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=112 time=17.0 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=112 time=16.5 ms
^C
--- 8.8.8.8 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms

#try to update system packages
user-mongo@deb-momgo:~$ sudo apt update &amp;amp;&amp;amp; upgrade
...
Fetched 485 kB in 1s (901 kB/s)
...
All packages are up to date.

#try to ssh from HOST MACHINE
ssh user-mongo@192.168.122.10
...
Are you sure you want to continue connecting (yes/no/[fingerprint])?
#It is requesting you again to accept the fingerprint, because you change "address" of a VM
#Anyway ssh is successful
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NB! If you experience any problems with VM's connectivity to the internet, try the following steps:&lt;/p&gt;

&lt;p&gt;Destroy the network with &lt;code&gt;sudo virsh net-destroy default&lt;/code&gt;; &lt;br&gt;
stop the VM with &lt;code&gt;sudo virsh destroy &amp;lt;vm-name&amp;gt;&lt;/code&gt;; restart the &lt;code&gt;libvirtd&lt;/code&gt; service with &lt;code&gt;sudo systemctl restart libvirtd&lt;/code&gt;; start the network with &lt;code&gt;sudo virsh net-start default&lt;/code&gt;; start the VM again with &lt;code&gt;sudo virsh start &amp;lt;vm-name&amp;gt;&lt;/code&gt;. Restarted VM should not have problems with connectivity. If connectivity still doesn’t work after these steps, then something got messed up wrong with the configuration.&lt;/p&gt;

&lt;p&gt;So now, I should be able to connect to the MongoDB running on my VM &lt;code&gt;deb-mongo&lt;/code&gt; from the host using the MongoDB Compass GUI client.&lt;/p&gt;

&lt;p&gt;For demonstration purposes, I didn’t configure any sophisticated RBAC for the database, so all I need to connect is the IP address and the port through which the database is accessible. Here is the MongoDB configuration on the VM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ user-mongo@deb-mongo:~$ cat /etc/mongof.conf
...
# network interfaces
net:
  port: 27017
  bindIp: 192.168.122.10
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Voila:&lt;/p&gt;

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

&lt;p&gt;Now, the objective is to modify configurations of the &lt;strong&gt;DEFAULT&lt;/strong&gt; network in such a way that I can connect to MongoDB on the VM from a laptop that is connected via Wi-Fi to the same local home network as my PC (host).&lt;/p&gt;

&lt;h3&gt;
  
  
  ➂ Connecting to a VM from Another Device on the LAN using the Port Forwarding Method:
&lt;/h3&gt;

&lt;p&gt;This is what Port Forwarding method is about:&lt;/p&gt;

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

&lt;p&gt;As I’ve pointed out more than once, 192.168.122.10 belongs to a different network than 192.168.1.0/24 (my home's LAN). &lt;code&gt;virbr0&lt;/code&gt; does not bridge any physical interface of my host PC that is connected to the LAN with the DEFAULT network. As a result, the VM’s address is unreachable for any device connected to the LAN.&lt;/p&gt;

&lt;p&gt;However, the devices connected to my home's LAN can communicate between themselves. &lt;/p&gt;

&lt;h4&gt;
  
  
  ➂.➀ What is the Port Forwarding?
&lt;/h4&gt;

&lt;p&gt;If you are a developer and work on remote servers using VSCode as IDE, most probably you’ve already used port forwarding &lt;em&gt;indirectly&lt;/em&gt;. For example, when you’re developing something like a web app during its early stages, it’s common for the app’s components—backend or frontend—to run on the server’s &lt;code&gt;localhost&lt;/code&gt; using specific development ports. However, when you connect to this server via VSCode and update the code, you can see in the browser the preliminary results on your development machine (let's call it laptop), not on the server’s browser.&lt;/p&gt;

&lt;p&gt;Here’s the point: let’s say your frontend app is running on the server’s localhost at port 5173. If you try to access it directly from your laptop’s browser, you won’t see anything. That’s because the app is running on the server, not your laptop. Without port forwarding, the server’s &lt;code&gt;localhost&lt;/code&gt; is inaccessible to your laptop.&lt;/p&gt;

&lt;p&gt;What happens, often with just two clicks—and sometimes even automatically in VSCode—is port forwarding. This forwards the server’s port (e.g., 5173) to a port on your laptop. As a result, you can open the browser on your laptop and see your app at an address like &lt;a href="http://localhost:5173" rel="noopener noreferrer"&gt;http://localhost:5173&lt;/a&gt;. What you’re actually seeing is the server’s localhost:5173, &lt;em&gt;forwarded&lt;/em&gt; to your laptop. &lt;/p&gt;

&lt;p&gt;So, I’m about to do something similar, bu with port forwarding on the host to the VM &lt;code&gt;deb-mongo&lt;/code&gt;. On my host machine, there’s no MongoDB installed, but it is running on the VM. I want to use the host machine as a kind of layover "airport" for the network packets traveling from my laptop to the VM to reach the database (the MongoDB Compass GUI client on my laptop exchanges packets with the MongoDB server and translates the packet payloads into the visualizations I see).&lt;/p&gt;

&lt;p&gt;The layover airport analogy fits perfectly here. In real life, layovers are a common practice, and the most interesting parallel is with &lt;strong&gt;visas&lt;/strong&gt;. For instance, let’s say you’re traveling to Brazil from your home country. Based on a bilateral agreement between your country and Brazil, you don’t need a visa to enter Brazil. However, your flight has a layover in the UK, and to enter the UK, you &lt;strong&gt;do need&lt;/strong&gt; a visa. Here’s where it gets interesting: if you’re just transiting through the UK and stay within the airport's transit zone, you don’t need a &lt;em&gt;true&lt;/em&gt; UK visa (let's leave aside transition visa). As long as you don’t exit the airport, everything works perfectly. But if you try to leave the transit zone, UK authorities will block you because you’re not permitted to &lt;em&gt;enter UK&lt;/em&gt; without a true visa. &lt;/p&gt;

&lt;p&gt;Similarly, in networking, my host machine acts as the "layover airport." Packets from my laptop need to travel through this transit point (the host) to reach the MongoDB instance running on the VM. As long as the host is correctly configured to forward packets (like a transit zone in an airport), everything flows smoothly. If, however, the laptop tries to directly access the VM (bypassing the host’s forwarding), the packets will fail because they are not permitted to "enter".&lt;/p&gt;

&lt;p&gt;Anyway, why do I speak so much about permissions? Because if you use &lt;code&gt;firewalld&lt;/code&gt; or any other firewall that’s up and running, they usually protect your device from unauthorized access. By default, all ports on your host are closed for &lt;strong&gt;NEW&lt;/strong&gt; connections. However, &lt;strong&gt;ESTABLISHED&lt;/strong&gt; and &lt;strong&gt;RELATED&lt;/strong&gt; connections are often allowed, meaning that network packets can navigate through them. &lt;strong&gt;ESTABLISHED&lt;/strong&gt; means the connection was initiated by the device itself (remember the distinction between inbound vs outbound traffic from the previous article?).&lt;/p&gt;

&lt;p&gt;What I want to do is use a random port, &lt;strong&gt;12345&lt;/strong&gt;, on my host machine as a "dummy port" that my laptop can connect via LAN to using the TCP protocol. I want that all network packets sent by my laptop to my host's IP:12345 will be redirected to the VM &lt;code&gt;deb-mongo&lt;/code&gt;, specifically to MongoDB’s default port, &lt;code&gt;27017&lt;/code&gt;, and the same for network packets with response from MongoDB.&lt;/p&gt;

&lt;h4&gt;
  
  
  ➂.➁ Firewalld and closed ports
&lt;/h4&gt;

&lt;p&gt;Now, for demonstration purposes, I’ll show you that a connection to this port (12345) on my host is not allowed by &lt;code&gt;firewalld&lt;/code&gt;. I’m using the &lt;code&gt;netcat-openbsd&lt;/code&gt; package to perform this simulation. It’s installed on both my laptop and my PC (the host).&lt;/p&gt;

&lt;p&gt;On the host, I start listening on port 12345. This is just a random listener; there isn’t any service running on this port. It’s not like the case with MongoDB, where MongoDB listens on its default port and expects specific syntax that it can understand. With &lt;code&gt;netcat&lt;/code&gt;, I can send any nonsense via TCP, and the listener will still accept it without validation—because it’s just a raw socket tool.&lt;/p&gt;

&lt;p&gt;On my host I start a listener on port 12345&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ nc -lv 12345
Listening on 0.0.0.0 12345
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From the laptop I try to send a message to the host private IP, port 12345:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ echo "hellow from the laptop" | nc -v 192.168.1.106 12345
nc: connect to 192.168.1.106 1235 (tcp) failed: No route to host!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The connection fails. I start by investigating the &lt;code&gt;firewalld&lt;/code&gt; rules because I’m sure it’s responsible for blocking the connection. And indeed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo firewall-cmd --query-port=12345/tcp
no
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This confirms that port 12345 is not open (read: no any device can connect to my host via this port). Next, I control which zones are being used for my network interfaces. &lt;code&gt;firewalld&lt;/code&gt; applies rules based on zones. However, explaining zones in detail is outside the scope of this article (you can check the documentation &lt;a href="https://firewalld.org/documentation/zone/" rel="noopener noreferrer"&gt;here&lt;/a&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo firewall-cmd --get-active-zones
libvirt
  interfaces: virbr0
public (default)
  interfaces: wlx123456789kl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not the best idea to modify firewall rules for the &lt;em&gt;public&lt;/em&gt; zone, so I switch to the home zone, which is actually the technically correct one because my host is connected to my home's LAN.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo firewall-cmd --zone=home --change-interface=wlx123456789kl
success
$ sudo firewall-cmd --zone=home --query-port=12345/tcp
no
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This port 12345 is also closed in the home zone, so I could just open it with &lt;code&gt;sudo firewall-cmd --zone=home --add-port=12345/tcp&lt;/code&gt;. But I want to open it only to be accessible by the private IP address of my laptop. So I just add a rich rule instead.&lt;/p&gt;

&lt;p&gt;Remember! Avoid, whenever you can, any wobbly rules for accepting something. Do not open ports to the whole world. Always try to minimize access and add precise, restrictive, and well-defined permissive rules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo firewall-cmd --zone=home --add-rich-rule='rule family="ipv4" source address="192.168.1.105" port protocol="tcp" port="12345" accept'
success
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And voilà: the message from my laptop successfully arrived at my host, port 12345:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo nc -lv 12345
Listening on 0.0.0.0 12345
Connection received on LAPTOP-1234567.station XXXXX
hellow from laptop!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NB! If you are unfamiliar with &lt;code&gt;firewalld&lt;/code&gt;, you might not have noticed that I didn’t add these rules permanently. This means that when I reboot or reload the &lt;code&gt;firewalld&lt;/code&gt; service, these rules will be lost—which is exactly what I wanted.&lt;/p&gt;

&lt;p&gt;For port forwarding, the rule is different and doesn’t require keeping the port open on the host which will be forwarded. This is where my earlier example with visas is valid—packets that are simply transiting through my host don’t need permission from the firewall because they don’t actually "enter" my host at the end.&lt;/p&gt;

&lt;p&gt;So, I’ve demonstrated and imposed these rules purely for educational purposes, and now I’ll flush them with &lt;code&gt;sudo firewall-cmd --reload&lt;/code&gt;, as they’re no longer needed.&lt;/p&gt;

&lt;h4&gt;
  
  
  ➂.➂ Forwarding ports on the host with &lt;code&gt;firewalld&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Now, I’ll forward the host’s port 12345 to the VM’s &lt;code&gt;deb-mongo&lt;/code&gt; default MongoDB port 27017. I’ll do this by adding a rule with &lt;code&gt;firewalld&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;According to the &lt;a href="https://firewalld.org/documentation/man-pages/firewall-cmd.html" rel="noopener noreferrer"&gt;firewalld documentation&lt;/a&gt;, this is how port forwarding should be done:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[--permanent] [--zone=zone] [--permanent] [--policy=policy] --add-forward-port=port=portid[-portid]:proto=protocol[:toport=portid[-portid]][:toaddr=address[/mask]] [--timeout=timeval]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As I flushed the rules from the example above, I first need to repeat the process of changing the zone for my WiFi interface from public to home. Once that’s done, I can apply the port forwarding rule. NB! I do not add &lt;code&gt;--permanent&lt;/code&gt; option to the commands, but if you plan to keep these &lt;code&gt;firewalld&lt;/code&gt; configurations, you will need to specify this option.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo firewall-cmd --zone=home --change-interface=wlx123456789kl
success
# THE RULE FOR PORT FORWARDING
$ sudo firewall-cmd --zone=home --add-forward-port=port=12345:proto=tcp:toport=27017:toaddr=192.168.122.10
success
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, can I connect now from my laptop to MongoDB running on the VM &lt;code&gt;deb-mongo&lt;/code&gt;? No. However, half the job is done.&lt;/p&gt;

&lt;p&gt;In the previous article, I described in very detailed way why any inbound traffic is blocked by rules specified in &lt;code&gt;nftables&lt;/code&gt; that are placed there by &lt;code&gt;libvirt&lt;/code&gt;. For the &lt;strong&gt;DEFAULT&lt;/strong&gt; network, these NAT rules are configured to isolate VMs, allowing them to connect outbound but blocking any inbound traffic from external devices (except Host, of course). This is why the connection still fails.&lt;/p&gt;

&lt;h4&gt;
  
  
  ➂.➃ Adding a rule to &lt;code&gt;nftables&lt;/code&gt; ruleset to accept inbound network traffic to the VM
&lt;/h4&gt;

&lt;p&gt;To fix this, I’ll need to adjust these &lt;code&gt;nftables&lt;/code&gt; rules. To see ALL the active &lt;code&gt;nftables&lt;/code&gt; rules, you can use the command &lt;code&gt;sudo nft list ruleset&lt;/code&gt;. To see only rules related to the &lt;strong&gt;DEFAULT&lt;/strong&gt; network, you can specify the &lt;em&gt;table&lt;/em&gt; and its family. Table name is &lt;code&gt;libvirt_network&lt;/code&gt;, and its family is &lt;code&gt;ip&lt;/code&gt; which stands for IPv4 - rules in this table are applicable ONLY to IPv4 network traffic! I prefer to use &lt;code&gt;sudo nft -a list table ip libvirt_network&lt;/code&gt;.&lt;br&gt;
The -a option adds a &lt;em&gt;handle&lt;/em&gt; to each rule. This handle acts like an index, allowing me to reference and manipulate any specific rule without having to retype the entire rule.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo nft -a list table ip libvirt_network
...
table ip libvirt_network { # handle 6
    chain forward { # handle 1
        type filter hook forward priority filter; policy accept;
        counter packets 728 bytes 637074 jump guest_cross # handle 7
        counter packets 728 bytes 637074 jump guest_input # handle 5
        counter packets 207 bytes 14639 jump guest_output # handle 3
    }

    chain guest_output { # handle 2
        ip saddr 192.168.122.0/24 iif "virbr0" counter packets 1 bytes 76 accept # handle 55
        iif "virbr0" counter packets 0 bytes 0 reject # handle 52
    }

    chain guest_input { # handle 4
        oif "virbr0" ip daddr 192.168.122.0/24 ct state established,related counter packets 1 bytes 76 accept # handle 56
        oif "virbr0" counter packets 9 bytes 468 reject # handle 53
    }

    chain guest_cross { # handle 6
        iif "virbr0" oif "virbr0" counter packets 0 bytes 0 accept # handle 54
    }

    chain guest_nat { # handle 8
        type nat hook postrouting priority srcnat; policy accept;
        ip saddr 192.168.122.0/24 ip daddr 224.0.0.0/24 counter packets 0 bytes 0 return # handle 61
        ip saddr 192.168.122.0/24 ip daddr 255.255.255.255 counter packets 0 bytes 0 return # handle 60
        meta l4proto tcp ip saddr 192.168.122.0/24 ip daddr != 192.168.122.0/24 counter packets 0 bytes 0 masquerade to :1024-65535 # handle 59
        meta l4proto udp ip saddr 192.168.122.0/24 ip daddr != 192.168.122.0/24 counter packets 1 bytes 76 masquerade to :1024-65535 # handle 58
        ip saddr 192.168.122.0/24 ip daddr != 192.168.122.0/24 counter packets 0 bytes 0 masquerade # handle 57
    }
}
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you’ve already dag all the internet searching for an answer about &lt;em&gt;how to add port forwarding to the &lt;strong&gt;DEFAULT&lt;/strong&gt; libvirt network in NAT mode&lt;/em&gt;, or &lt;em&gt;how to use the port forwarding method to access NAT'ed virtual networks&lt;/em&gt;, I imagine that your attention is attached to the &lt;code&gt;chain guest_nat&lt;/code&gt; in the displayed above &lt;code&gt;libvirt_network table&lt;/code&gt;, because it handles NAT. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;However, this is misleading!&lt;/strong&gt; The rule that is actually blocking inbound connections to the VM (&lt;code&gt;deb-mongo&lt;/code&gt;, in my case) from a laptop connected to the LAN is not there - the blocking rule is in the &lt;code&gt;chain guest_input&lt;/code&gt;!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;chain guest_input {
          oif "virbr0" ip daddr 192.168.122.0/24 ct state established,related counter packets 1 bytes 76 accept # handle 56
          oif "virbr0" counter packets 9 bytes 468 reject # handle 53
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These two rules, #56 and #53, are the culprits behind the issue. Rule #56 accepts ONLY the traffic with destination address in the &lt;strong&gt;DEFAULT&lt;/strong&gt; network 192.168.122.0/24 &lt;em&gt;which is part of &lt;strong&gt;ESTABLISHED&lt;/strong&gt; and &lt;strong&gt;RELATED&lt;/strong&gt; network connections&lt;/em&gt;. This is why connections between the host and the VMs work seamlessly, as those are ESTABLISHED or RELATED.&lt;br&gt;
Similarly, &lt;strong&gt;outbound traffic&lt;/strong&gt; from any VM on this network (e.g., downloading packages or connecting to external resources) also falls under this rule.&lt;/p&gt;

&lt;p&gt;However, when I try to connect to any VM on this network (192.168.122.0/24) from my laptop, which is on the LAN (a "neighbor" of the host), this traffic is a part of NEW connection. This is where Rule #53 comes into picture and REJECTS any traffic that is part of NEW connections targeting the virtual network devices.&lt;/p&gt;

&lt;p&gt;The intuitive solution is to modify rule #56, and using its handle, I can do something like this :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo nft replace rule libvirt_network guest_input handle 56 oif "virbr0" ip daddr 192.168.122.0/24 ct state new,established,related counter accept
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;HOWEVER! Remember, it’s wiser to &lt;strong&gt;avoid adding overly permissive rules&lt;/strong&gt;. So, instead, I’ll specify that traffic that is part of NEW connections AND:&lt;br&gt;
A) ONLY from the private IP of my laptop &lt;br&gt;
B) ONLY to the VM &lt;code&gt;deb-mongo&lt;/code&gt;&lt;br&gt;
c) ONLY to the port 27017 of the VM &lt;code&gt;deb-mongo&lt;/code&gt;&lt;br&gt;
should be accepted, while the rest should fall into rule #53—the rejection rule. &lt;/p&gt;

&lt;p&gt;To do this, I need to add &lt;em&gt;a new rule&lt;/em&gt; to handle NEW traffic.&lt;/p&gt;

&lt;p&gt;Please NOTE! Rule #56, which accepts traffic from ESTABLISHED and RELATED connections, is the &lt;em&gt;first&lt;/em&gt; in the &lt;code&gt;chain guest_input&lt;/code&gt;. Rule #53, which rejects anything that is not accepted by Rule #56, is the &lt;em&gt;second&lt;/em&gt; rule in the chain. If you add a new rule using &lt;code&gt;sudo nft add rule ...&lt;/code&gt;, it will be appended to the end of the chain. This means it will never participate in filtering traffic from NEW connections because all these packets will already be rejected by Rule #53. &lt;strong&gt;The order of rules in chains matters!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of using &lt;code&gt;sudo nft add rule...&lt;/code&gt;, I will use &lt;code&gt;sudo nft insert rule ...&lt;/code&gt;. This command always inserts the rule at the top of the chain, which is not always ideal for different network logic but, in this case, is completely fine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo nft insert rule libvirt_network guest_input \
    oif "virbr0" ip saddr 192.168.1.10 ip daddr 192.168.122.10 tcp dport 27017 \
    ct state new counter accept
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;VOILA! the connection on my laptop is installed right away after this new rule is added:&lt;/p&gt;

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

&lt;p&gt;As you may notice, if you are following my steps, nothing needs to be restarted with &lt;code&gt;sudo virsh&lt;/code&gt;- nor the &lt;strong&gt;DEFAULT&lt;/strong&gt; network, nor the VM, nor the &lt;code&gt;systemd networking service&lt;/code&gt; on the host, nor the same service on the VM. That’s the superpower of network traffic management and mangling.&lt;/p&gt;

&lt;p&gt;As I mentioned earlier, &lt;em&gt;&lt;code&gt;virbr0&lt;/code&gt; is not connected in any way to any physical network interfaces of your host&lt;/em&gt;. All the connectivity is managed through the &lt;code&gt;nftables&lt;/code&gt; ruleset in the &lt;code&gt;libvirt_network table&lt;/code&gt; for IPv4 traffic.&lt;/p&gt;

&lt;h4&gt;
  
  
  ➂.➄ Considerable drawbacks of this method
&lt;/h4&gt;

&lt;p&gt;In the end, to enable the Port Forwarding on the NAT'ed DEFAULT network it’s just two commands one: to add the &lt;code&gt;firewalld&lt;/code&gt; rule to impose port forwarding on the HOST. Second command is to add a rule to the guest_input chain in the libvirt_netwrok ip table in &lt;code&gt;nftables&lt;/code&gt; ruleset.&lt;/p&gt;

&lt;p&gt;Quite elegant and simple, no? So, what’s the drawback then, besides the complexity of &lt;code&gt;nftables&lt;/code&gt; rules for people completely unfamiliar with them? WELL… the drawback is the persistence of this configuration—or, to be more accurate, zero persistence.&lt;/p&gt;

&lt;p&gt;When I added the port forwarding rule with &lt;code&gt;firewalld&lt;/code&gt;, I noted that I didn’t make it permanent, but you can preserve the rules by using the &lt;code&gt;--permanent&lt;/code&gt; option. What about the &lt;code&gt;nftables&lt;/code&gt; rule I added? How do you preserve it? No way.&lt;/p&gt;

&lt;p&gt;If you remember, at the start, I showed you (using &lt;code&gt;ip link&lt;/code&gt;/&lt;code&gt;ip a&lt;/code&gt; outputs) that &lt;code&gt;virbr0&lt;/code&gt; doesn’t exist as an interface before you start the &lt;strong&gt;DEFAULT&lt;/strong&gt; network that is based on it. The same logic applies to the &lt;code&gt;nftables&lt;/code&gt; rules related to this virtual interface. If you list all the rules when the &lt;strong&gt;DEFAULT&lt;/strong&gt; network is down, there will be no &lt;code&gt;libvirt_network table&lt;/code&gt; in &lt;code&gt;nftables&lt;/code&gt; ruleset. But the moment you start the &lt;strong&gt;DEFAULT&lt;/strong&gt; network, the table immediately appears—it is added automatically by &lt;code&gt;libvirt&lt;/code&gt; itself.&lt;/p&gt;

&lt;p&gt;So, any custom rules for the &lt;code&gt;DEFAULT&lt;/code&gt; network you add to &lt;code&gt;nftables&lt;/code&gt; will be lost the moment this network stops and restarts. This is the main drawback of this otherwise clean and straightforward approach. &lt;/p&gt;

&lt;p&gt;There is a workaround, though—a script that can automate the re-adding of the &lt;code&gt;nftables&lt;/code&gt; rule. Even though it’s still a workaround, it is presented in the &lt;a href="https://wiki.libvirt.org/Networking.html#forwarding-incoming-connections." rel="noopener noreferrer"&gt;libvirt documentation on how to forward connections for the DEFAULT network&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;However, there’s an important note: the documentation’s example is written for &lt;code&gt;iptables&lt;/code&gt;, not &lt;code&gt;nftables&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here’s a tiny reminder: you cannot just drop &lt;code&gt;iptables&lt;/code&gt; rules out of the blue on your Debian system if you have &lt;code&gt;nftables&lt;/code&gt; up and running. At best, the rules will simply not work. At worst, you will mess up all the network traffic on your host :3.&lt;/p&gt;

&lt;p&gt;Here is this script:&lt;br&gt;
&lt;/p&gt;

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

# IMPORTANT: Change the "VM NAME" string to match your actual VM Name.
# In order to create rules to other VMs, just duplicate the below block and configure
# it accordingly.
if [ "${1}" = "VM NAME" ]; then

   # Update the following variables to fit your setup
   GUEST_IP=
   GUEST_PORT=
   HOST_PORT=

   if [ "${2}" = "stopped" ] || [ "${2}" = "reconnect" ]; then
    /sbin/iptables -D FORWARD -o virbr0 -p tcp -d $GUEST_IP --dport $GUEST_PORT -j ACCEPT
    /sbin/iptables -t nat -D PREROUTING -p tcp --dport $HOST_PORT -j DNAT --to $GUEST_IP:$GUEST_PORT
   fi
   if [ "${2}" = "start" ] || [ "${2}" = "reconnect" ]; then
    /sbin/iptables -I FORWARD -o virbr0 -p tcp -d $GUEST_IP --dport $GUEST_PORT -j ACCEPT
    /sbin/iptables -t nat -I PREROUTING -p tcp --dport $HOST_PORT -j DNAT --to $GUEST_IP:$GUEST_PORT
   fi
fi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;em&gt;workaround&lt;/em&gt; is actually a &lt;strong&gt;hook script&lt;/strong&gt;— libvirt's hooks are quite handy for specific system management needs. These hooks can trigger various scripts based on VM-related events like start, shutdown, etc. You can definitely create a similar hook for adding the &lt;code&gt;nftables&lt;/code&gt; rule, and it would even be much shorter than this monstrosity of &lt;code&gt;iptables&lt;/code&gt; rules.  &lt;/p&gt;

&lt;p&gt;However, I will not be doing it, nor will I use this configuration for my home lab. That’s because the &lt;strong&gt;DEFAULT network&lt;/strong&gt; is designed for a different purpose—it’s meant to provide a plug-and-play or out-of-the-box experience for new VMs. You create a VM, start it, and everything works from the networking side without additional configuration.  &lt;/p&gt;

&lt;p&gt;I prefer to keep it that way. For accessibility to the VMs from my LAN devices, I use other network configurations that are better suited for this purpose.&lt;/p&gt;

</description>
      <category>libvirt</category>
      <category>debian</category>
      <category>networking</category>
      <category>nftable</category>
    </item>
    <item>
      <title>Virtualization on Debian with libvirt&amp;QEMU&amp;KVM — Networking beyond "default": Must Have Concepts to Start</title>
      <dc:creator>aL</dc:creator>
      <pubDate>Thu, 23 Jan 2025 00:12:56 +0000</pubDate>
      <link>https://dev.to/dev-charodeyka/virtualization-on-debian-with-libvirtqemukvm-networking-beyond-default-must-have-concepts-to-2ccn</link>
      <guid>https://dev.to/dev-charodeyka/virtualization-on-debian-with-libvirtqemukvm-networking-beyond-default-must-have-concepts-to-2ccn</guid>
      <description>&lt;p&gt;Initially, I planned to publish all &lt;em&gt;how-to&lt;/em&gt; s for advanced network configurations for virtual machines in one article. However, I am unstoppable when it comes to writing, and the article became quite long. So, I decided to split the theory from the hands-on configurations, XML/Shell scripting, etc.&lt;/p&gt;

&lt;p&gt;You might think you don’t need to read this, but I strongly discourage you from jumping directly to the next article and blindly copying my configurations without understanding what you’re doing.&lt;/p&gt;




&lt;p&gt;NB! I’m not a network engineer, nor have I taken courses on this. I’m actually &lt;em&gt;just a dev&lt;/em&gt; who’s just passionate about Debian and spends way too much time on my PC. So, please &lt;em&gt;don’t throw slippers at me&lt;/em&gt; if I end up sharing any misleading information. I’m simply documenting and describing some of my experiments in this field.&lt;/p&gt;




&lt;p&gt;Terminology I will use in this article, sometimes in acronym form – I try to avoid it, but sometimes it just happens automatically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Host&lt;/em&gt; – This refers to your PC, on which you set up virtualization and create virtual machines.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Guest&lt;/em&gt; – This is any virtual machine you create.
VM/VMs – A virtual machine/Virtual Machines.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;ISP&lt;/em&gt; – Internet Service Provider.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;LAN&lt;/em&gt; – Local Area Network.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;NAT&lt;/em&gt; – Network Address Translation.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;&lt;strong&gt;DEFAULT&lt;/strong&gt;&lt;/em&gt; network – This refers to the Libvirt's virtual network that virtual machines are connected to by default, within the scope of &lt;code&gt;qemu:///system&lt;/code&gt;. If you do not understand the difference between &lt;code&gt;qemu:///system&lt;/code&gt; and &lt;code&gt;qemu:///session&lt;/code&gt; and how to switch between them, please refer to &lt;a href="https://dev.to/dev-charodeyka/virtualization-on-debian-with-virshqemukvm-what-you-need-to-install-and-how-49oo"&gt;the previous article&lt;/a&gt;. In this article I will be creating/configuring VMs that are in the scope of &lt;code&gt;qemu:///system&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Packet&lt;/em&gt; - A network packet is a formatted unit of data carried by a network.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;When it comes to networking, it’s really hard to decide where to even start explaining. To avoid turning this article into some kind of networking handbook—which I don’t have the expertise to write anyway—I’ll probably have to skip over some fundamental concepts.&lt;/p&gt;

&lt;p&gt;However, I’ll do my best to simplify things and provide schemes for the main concepts so you can (hopefully!) follow along.&lt;/p&gt;

&lt;p&gt;Here is the road-map for this article:&lt;/p&gt;

&lt;p&gt;➀ About Public IP address and LAN&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;➀.➀ Inbound vs Outbound traffic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;➁ Understanding your LAN and private IP address(s) of your host machine&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;➁.➀ Network Interfaces: general information&lt;/li&gt;
&lt;li&gt;➁.➁ Network Interfaces: MAC addresses&lt;/li&gt;
&lt;li&gt;➁.➂ IPv4 vs IPv6 addresses&lt;/li&gt;
&lt;li&gt;➁.➃ IPv4 address ranges reserved for private networks&lt;/li&gt;
&lt;li&gt;➁.➄ IPv4 addresses structure and CIDR&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;➂ Libvirt's &lt;strong&gt;DEFAULT&lt;/strong&gt; Network: about NAT mode&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;➂.➀ About virtual network switches&lt;/li&gt;
&lt;li&gt;➂.➁ Libvirt wants &lt;code&gt;iptables&lt;/code&gt;, Debian has &lt;code&gt;nftables&lt;/code&gt;: what to do?&lt;/li&gt;
&lt;li&gt;➂.➂ DEFAULT Libvirt's network: what's under the hood?&lt;/li&gt;
&lt;li&gt;➂.➃ Nftables rulesets: tables and chains explained&lt;/li&gt;
&lt;li&gt;➂.➄ About how NAT works&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's start!&lt;/p&gt;




&lt;p&gt;I’ve already written some explanations of how your home network works, about public IP address, and ports in &lt;a href="https://dev.to/dev-charodeyka/debian-12-is-amazing-how-to-create-your-custom-codehouse-part-3b4-2ca5"&gt;this article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is the schematic representation of the most common setup of local "home" network with WiFi router playing the central role in it:&lt;/p&gt;

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

&lt;h3&gt;
  
  
  ➀ About Public IP address and LAN
&lt;/h3&gt;

&lt;p&gt;The most important takeaway from this scheme is that all your devices—whether they connect to your Wi-Fi router wirelessly or physically (via Ethernet cable)—are part of a local network, your home network. This LAN (Local Area Network) is created and managed by your Wi-Fi router.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Most likely&lt;/em&gt;*, only your router has a public IP address! Your devices do not (I wrote &lt;em&gt;most likely&lt;/em&gt; because if you’ve configured your Home Lab to use IPv6, the story is quite different. But I’m guessing you wouldn’t be reading this article if you were capable of doing so! :) ) Instead, each device connected to your Wi-Fi network is assigned a private IP address—one private address per device. If, for some reason, something gets messed up with your router and it assigns the same IP address to two devices, those devices will start having problems accessing the internet (they will start losing some packets - for example, pinging will be unstable, with some percentage of packets lost).&lt;/p&gt;

&lt;p&gt;You can check your public IP address using website like &lt;a href="https://www.dnsleaktest.com/" rel="noopener noreferrer"&gt;DNS Leak Test&lt;/a&gt;, which will show you IP address with which you visited this site (what is DNS and DNS "leaking" I will explain later in this article). Your Public IP address is discovered by servers hosting the websites you visit. For example, when I upload an image here in articles, the Dev.to servers see the request coming from my public IP address that’s pushing the data.&lt;/p&gt;

&lt;h4&gt;
  
  
  ➀.➀ Inbound vs Outbound traffic
&lt;/h4&gt;

&lt;p&gt;For now, what you know for sure is that a Wi-Fi router is the magic box that allows you to access internet resources from your devices. This is quite obvious because you paid for exactly that. But how does it actually work?&lt;/p&gt;

&lt;p&gt;In the previous part of this series, I mentioned that I couldn’t host anything on my PC - a website - without tweaking the router. In general, when someone tries to reach you via your public IP address, they send you packets with some data/requests. These packets first reach your Wi-Fi router as it is the one having public IP address, not your devices connected to WiFi! What happens next depends on your router’s configuration.&lt;/p&gt;

&lt;p&gt;Will it drop the packets, effectively blocking them, or will it redirect them to the appropriate member of your local network (for example, your PC)? If it does redirect, the router uses the private IP address (as he knows all the private addresses of connected devices) of the target device (your PC) to forward the packets. Router will also have to forward the packets to the correct port. However, for all of this to happen, the router needs to be explicitly configured. By default, most Home-purpose routers will just block incoming traffic, and the packets will simply be dropped. This is why I cannot host any website on my PC without modifying the configurations of WiFi router.&lt;/p&gt;

&lt;p&gt;However, I can download whatever I want by default. When I download something, it comes to my computer in form of &lt;em&gt;packets&lt;/em&gt; with data as well. And the stuff that I am downloading is &lt;em&gt;coming in&lt;/em&gt;, not &lt;em&gt;out&lt;/em&gt;. So, it is also a sort of incoming traffic, and it does not get blocked by WiFi router at all.&lt;/p&gt;

&lt;p&gt;The key difference in these two examples is &lt;strong&gt;who initializes the communication&lt;/strong&gt;. When you download something—i.e using &lt;code&gt;wget&lt;/code&gt;—it’s your device that sends the request, initializing the connection. The server responds, sends packets with requested data, they arrive to your WiFi router, it redirects them to the device that made a "request" - and that’s why it works - WiFi router does not impede this process, because it is &lt;em&gt;outbound&lt;/em&gt; traffic.&lt;/p&gt;

&lt;p&gt;Inbound traffic, on the other hand, refers to situations where the connection isn’t initiated by your device—like when uninvited guests show up at the door of your house. In those cases, the &lt;em&gt;firewall rules&lt;/em&gt; of your router sends them away. These rules protect your local home network from unexpected or unwanted traffic.&lt;/p&gt;

&lt;p&gt;In the scope of this article on virtualization, the focus will be on &lt;em&gt;networking "locally"&lt;/em&gt;. It won’t be about creating networks that make your VMs publicly accessible, vulnerable, or anything of that sort. I won’t touch my router configurations, and all configurations will be done on networks that operate &lt;em&gt;behind&lt;/em&gt; a Wi-Fi router.&lt;/p&gt;




&lt;h3&gt;
  
  
  ➁ Understanding your LAN and private IP address(s) of your host machine
&lt;/h3&gt;

&lt;p&gt;As I mentioned, the Wi-Fi router creates a LAN (local area network). Any devices you connect to the Wi-Fi join this LAN, and their communication—both with each other (if configured so) and with the outside world (web) —is managed by the Wi-Fi router.&lt;/p&gt;

&lt;p&gt;The first thing to do is to get familiar with the private IP addresses of your host machine - what do they mean, why they are like this, how to manage them.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you’re using ONLY Network Manager to handle your network connections... well, put it aside for now. Your new best friend is the &lt;code&gt;iproute2&lt;/code&gt;. It should already be installed by default on Debian. The thing is, when you install Network Manager, it can start conflicting with &lt;code&gt;iproute2&lt;/code&gt; configurations. However, on Debian, this is partially resolved by the default setup, where Network Manager only manages wireless network interfaces.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  ➁.➀ Network Interfaces: general information
&lt;/h4&gt;

&lt;p&gt;Now, let’s take a look at what’s going on with the networks on my host machine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ip link show
1: lo: &amp;lt;LOOPBACK,UP,LOWER_UP&amp;gt; ...
....
2: eno1: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; ... state UP ....
   ....
3: wlxXXXXXX43643754XX: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; ... state UP 
   ...
4: virbr0: &amp;lt;NO-CARRIER,BROADCAST,MULTICAST,UP&amp;gt; .... state DOWN ...
    ....
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What I have:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;lo&lt;/code&gt;: This is the loopback interface. It’s a virtual network interface used by my OS to communicate with itself. It’s always there—no need to touch it! It won’t participate in the networking configurations I’ll be covering in this article.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;eno1&lt;/code&gt;: This is my network interface that comes with the Ethernet cable that physically links my PC to the Wi-Fi router. *This guy will be playing the key role in the networking setups for my VMs.&lt;br&gt;
*&lt;/p&gt;

&lt;p&gt;&lt;code&gt;wlxXXXXXX43643754XX&lt;/code&gt;: This is the wireless network interface that comes from my USB Wi-Fi adapter. In my setup, it has higher priority - this interface is used for communications with WiFi router and within the LAN. However, this wireless network interface &lt;strong&gt;WILL NOT participate in SOME networking setup (bridged networking) for the virtualization process because it’s problematic.&lt;/strong&gt; Wireless network devices use different drivers, different protocols, and it isn’t trivial to make them to participate in bridged networks. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;virbr0&lt;/code&gt;: The last guy in the list, which is currently DOWN, is a virtual bridge. It’s managed by libvirt/qemu. It is created when the &lt;code&gt;libvirt&lt;/code&gt; daemon is first installed and started. However, it remains down unless any libvirt's network that uses this network interface starts (i.e &lt;code&gt;sudo virsh net-start default&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Let's look into more details about each network interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ip a
1: lo: &amp;lt;LOOPBACK,UP,LOWER_UP&amp;gt; ...
....
2: eno1: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; ... state UP ....
   link/ether 12:ab:c3:d4:ef:56
   inet 192.168.1.X/24 brd 192.168.1.255
   inet6 fe80::278e:1234:l678:123/64 
3: wlxXXXXXX43643754XX: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; ... state UP 
   link/ether 98:zy:xb:67:kl:34
   inet 192.168.1.Y/24 brd 192.168.1.255
   inet6 fe80::kre7:b5b5:z8y6:987/64 
4: virbr0: &amp;lt;NO-CARRIER,BROADCAST,MULTICAST,UP&amp;gt; .... state DOWN ...
    link/ether 77:mo:5g:k9:r0:46
    inet 192.168.122.1/24 brd 192.168.122.255
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this article section, I will explain what are these values: &lt;code&gt;link/ether&lt;/code&gt;, &lt;code&gt;inet&lt;/code&gt; and &lt;code&gt;inet6&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  ➁.➁ Network Interfaces: MAC addresses (&lt;code&gt;link/ether&lt;/code&gt;)
&lt;/h4&gt;

&lt;p&gt;First, I want to point your attention to the fact that &lt;code&gt;eno1&lt;/code&gt; and &lt;code&gt;wlxXXXXXX43643754XX&lt;/code&gt; Network Interfaces are providing the connection to the same network, my home local network (LAN), managed by my Wi-Fi router. Just the origin of &lt;code&gt;eno1&lt;/code&gt; is the cable (Ethernet), and the origin of &lt;code&gt;wlxXXXXXX43643754XX&lt;/code&gt; is the Wi-Fi USB adapter, so a wireless connection. Why do they have different IP addresses even if they are "attached" to the same network? &lt;/p&gt;

&lt;p&gt;All other devices connected to my house Wi-Fi have private IP addresses - like the laptop at 192.168.1.A, the phone at 192.168.1.B, the PlayStation at 192.168.1.C, etc. So why does my single desktop PC have two addresses in the same network? Does this cause confusion?&lt;/p&gt;

&lt;p&gt;My PC can connect to a home local network (and to any network that is not virtualized by the PC itself=existing only "inside" the PC) through a NIC—Network Interface Card/Contrtoller. This is a hardware component that is often integrated into the motherboard.&lt;/p&gt;

&lt;p&gt;If you’re using a laptop that doesn’t have a port for an Ethernet cable, it most likely doesn’t have a NIC either. So, even if you imagine buying something like a USB-to-Lightning-to-Type-C adapter just to connect an Ethernet cable to port of Type C, it won’t work. This is because it’s not just about the "shape" of the cable, but the capability to process the type of communication that comes through it - and this capability is granted by NIC.&lt;/p&gt;

&lt;p&gt;The solution is to purchase a proper USB hub with a built-in NIC and Ethernet port. The laptop example is a good one because it shows that an Ethernet cable is not the cornerstone of connectivity to networks. Such laptops indeed connect wirelessly, if a network of interest allows so. In this case, they do not use a default NIC but rather a WNIC—a Wireless Network Interface Card.&lt;/p&gt;

&lt;p&gt;A WNIC in a desktop computer often needs to be purchased separately—either as a PCIe network card (to be attached to the motherboard's PCI slot) or as part of a USB Wi-Fi adapter.&lt;/p&gt;

&lt;p&gt;In my case, I have them both, integrated NIC and WNIC of WiFi USB adapter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#list of Ethernet controllers
$ lspci | grep -i ethernet
04:00.0 Ethernet controller: Realtek Semiconductor Co., Ltd. Controller
#list of WNIC
$ lsusb
Bus 002 Device 002: ID 1234:5678 TP-Link 802.11ac NIC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My Wi-Fi router perceives connection from each (W)NICs as a separate initialization of a connection and assigns a distinct private IP address to each. That’s why my PC, which has two network interfaces, ends up with two IP addresses—one for each (W)NIC. The router cannot identify that both NICs &lt;em&gt;physically&lt;/em&gt; belong to the same device (my PC) because the &lt;em&gt;physical&lt;/em&gt; identifiers are the NICs themselves (not a CPU, not a motherboard ecc)!&lt;/p&gt;

&lt;p&gt;Each NIC has its own unique MAC address (Media Access Control address), which serves as the &lt;em&gt;unique permanent identifier&lt;/em&gt;. Not only network interfaces, but every piece of network-connected hardware, &lt;em&gt;MUST HAVE&lt;/em&gt; a unique MAC address in a network. This is different from an IP address because a MAC address is permanent: every NIC has one and only one MAC address, hardcoded by the manufacturer (though, sometimes it is possible to change it). You can see the MAC addresses of your network interfaces using &lt;code&gt;ip link&lt;/code&gt; or &lt;code&gt;ip a&lt;/code&gt; commands—in the lines labeled &lt;code&gt;link/ether&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;While MAC addresses are permanent, an IP address is temporary and can change quite often - every time a device connects to a network, it is possible that WiFi router can assign to it a new private IP address (the probability of it depends on the network configuration and the device’s own settings).&lt;/p&gt;

&lt;p&gt;You can think of a MAC address as a device’s permanent name, while an IP address is more like instructions for other devices on how to communicate with it. For example, your device might always be MAC number A, but at any given moment, it can be located at IP address B or IP Address C ecc.&lt;/p&gt;

&lt;p&gt;Now that I’ve covered MAC addresses, it’s time to explain IP addresses.&lt;/p&gt;

&lt;h4&gt;
  
  
  ➁.➂ IPv4 vs IPv6 addresses and NAT
&lt;/h4&gt;

&lt;p&gt;To see the IP addresses of your PC's network interface(s) (roughly speaking Private IP address(es) of your PC) you can execute &lt;code&gt;ip a&lt;/code&gt; command. You can scroll up and take another look at my output of this command. Now you know that the &lt;code&gt;link/ether&lt;/code&gt; field tells you the MAC address(es) of network interfaces. If you’re familiar with IP addresses, you can guess that &lt;code&gt;inet&lt;/code&gt; field exactly contains IP address information (the address though which other devices on the same network can communicate with PC). However, 'inet' field is not the one that shows you IP address of a network interface. &lt;code&gt;inet6&lt;/code&gt; field (if you have it) shows you another address and it is also IP address (even if it looks more like a MAC address)!&lt;/p&gt;

&lt;p&gt;My PC's &lt;code&gt;eno1&lt;/code&gt; interface (Ethernet) has &lt;code&gt;inet&lt;/code&gt; 192.168.1.X/24 and &lt;code&gt;inet6&lt;/code&gt; fe80::278e:1234:l678:123/64; &lt;code&gt;wlxXXXXXX43643754XX&lt;/code&gt; interface (wireless) has &lt;code&gt;inet&lt;/code&gt; 192.168.1.Y/24 and &lt;code&gt;inet6&lt;/code&gt; fe80::kre7:b5b5:z8y6:987/64; &lt;code&gt;virbr0&lt;/code&gt; virtual network interface managed by &lt;code&gt;libvirt&lt;/code&gt; has only &lt;code&gt;inet&lt;/code&gt; 192.168.122.1/24. &lt;/p&gt;

&lt;p&gt;Both &lt;code&gt;inet&lt;/code&gt; and &lt;code&gt;inet6&lt;/code&gt; fields of &lt;code&gt;ip a&lt;/code&gt; output contain completely valid IP addresses - it’s not like what you see in &lt;code&gt;inet6&lt;/code&gt; field is just an encrypted, altered, or transformed version of &lt;code&gt;inet&lt;/code&gt; field value, nor does it indicate a completely different network. The value of &lt;code&gt;inet&lt;/code&gt; field is the IPv4 address, while &lt;code&gt;inet6&lt;/code&gt; displays the IPv6 address.&lt;/p&gt;

&lt;p&gt;IP stands for Internet Protocol and it has two major versions: IPv4 and IPv6. IPv6 is the newer, more advanced version, offering enhanced features, greater capabilities, and significant potential for addressing future network demands.&lt;/p&gt;

&lt;p&gt;In a previous article, I already touched on the topic of IPv4 vs. IPv6 differences. I’ll share the most relevant takeaway as it relates to this article:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;A unique public IP address is a scarce resource! Actually, a unique public IPv4 address is in deficit. Internet Protocol version 4 (IPv4) forms the foundation of most Global Internet traffic today. An IP Address represented under IPv4 is composed of four sets of numbers ranging from 0 to 255, separated by periods(.).&lt;br&gt;
If you do the straightforward math - total four numbers in an IPv4 address; each number can be in range between 0 and 255 (256 possible values) - 256 * 256 * 256 * 256 = 4,294,967,296 total addresses.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first distinction is that IPv6 addresses are not just numerical; they are alphanumeric. IPv6 can provide many more unique addresses than the IPv4 system (340 trillion trillion trillion unique addresses vs 4 billion). But this is not the only difference. IPv6, as a protocol, is more modern and from the beginning was designed to be more secure. &lt;/p&gt;

&lt;p&gt;It is not like IPv4 was designed without security in mind, just when it was created, the internet had far fewer users, and the number of devices worldwide was significantly lower. Let me illustrate how it affects the security mechanisms.&lt;/p&gt;

&lt;p&gt;Security mechanisms, such as encryption, inevitably introduce additional computational load. For example, imagine my PC is communicating with your PC using an encryption mechanism. All the &lt;em&gt;packets&lt;/em&gt; we exchange are heavily encrypted, and our devices communicate using public IP addresses, which act like the destination addresses for our data packets (like mailing letters). My device with its public IP address communicates with your device at its public IP address.&lt;/p&gt;

&lt;p&gt;This scenario demonstrates the end-to-end principle of communication: my device can communicate with yours securely, with security mechanisms implemented directly in the communicating end nodes (our PCs, that have encryption/decryption keys). The &lt;em&gt;intermediary nodes&lt;/em&gt; (like gateways and routers) don’t take part in our secure communication.&lt;/p&gt;

&lt;p&gt;So, the Gateways and Routers are bad? Not at all! However, in this example, gateways and routers can A) introduce computational overhead B) make some encryption mechanisms impossible. &lt;/p&gt;

&lt;p&gt;Getting closer to the topic... This idealized scenario of end-to-end communication is possible today, but only if we use IPv6 and our devices are properly configured to support it. Why? Because IPv6 adreesses poll is really huuuge, so every device can have a public IPv6 address, enabling direct communication between them without needing intermediary &lt;em&gt;translation&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Each packet that we exchange in my example as any network packet consists of control information (headers) and user data (payload). Control information provides data for delivering the payload (e.g., source and destination network addresses, error detection codes, or sequencing information). &lt;/p&gt;

&lt;p&gt;So, in case of IPv4. My PC sends to my WiFi router a packet that it is meant for you. But my PC does not have a Public IP address! So the source IP address in the headers is abracadabra for the web scope and it needs to be_ translated_, according to some rules. This job is done by NAT - Network Address Translation mechanism of my WiFi router. And here it is why this intermediary node in our communication is a baddie - NAT introduces processing overhead because it rewrites packet headers &lt;strong&gt;for every packet&lt;/strong&gt; that passes through the router. And this happens not only on my side, but also on your side!&lt;/p&gt;

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

&lt;p&gt;Some encryption techniques are meant for end-to-end communication only with consistent IP addresses - so they are not possible if NAT (in its default form) is in the middle.&lt;/p&gt;

&lt;p&gt;With IPv4, gateways, routers become unavoidable due to the scarcity of public IPv4 addresses, and so does NAT. I started introducing NAT right here; however, I will elaborate more on this in the next sections, as the &lt;code&gt;libvirt&lt;/code&gt;'s &lt;strong&gt;DEFAULT&lt;/strong&gt; network is based on the NAT mechanism.&lt;/p&gt;

&lt;p&gt;_A little thought experiment: how do you think the internet would be if IPv6 were fully adopted and replaced IPv4? What would happen to the prices for internet service? Would the speed of the internet change? PS. you can check out what is CGNAT :) _&lt;/p&gt;

&lt;p&gt;In this article, I will use IPv4 addresses for any network configuration. Unfortunately, Internet Protocol version 4 (IPv4) still forms the foundation of most global internet traffic today. Plus, as I stated earlier, this article is focused on creating a local network rather than configuring any VM to be remotely reachable from the outside.&lt;/p&gt;

&lt;p&gt;The scope of IPv4 reserved addresses for private networks is quite large for a home setup, so let's move to it.&lt;/p&gt;

&lt;h4&gt;
  
  
  ➁.➃ IPv4 address ranges reserved for private networks
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;According to standards set forth in Internet Engineering Task Force (IETF) document RFC-1918 , the following IPv4 address ranges are reserved by the IANA for private internets, and are not publicly routable on the global internet:&lt;br&gt;
10.0.0.0/8 IP addresses: 10.0.0.0 – 10.255.255.255&lt;br&gt;
172.16.0.0/12 IP addresses: 172.16.0.0 – 172.31.255.255&lt;br&gt;
192.168.0.0/16 IP addresses: 192.168.0.0 – 192.168.255.255&lt;br&gt;
Note that only a portion of the “172” and the “192” address ranges are designated for private use. The remaining addresses are considered “public,” and thus are routable on the global Internet.(&lt;a href="https://www.arin.net/reference/research/statistics/address_filters/" rel="noopener noreferrer"&gt;Source&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Key takeaway is that there is set of IPv4 IP addresses that are reserved for private networks. The reservation ensures that there will be no conflicts with global - public IP addresses.&lt;/p&gt;

&lt;h4&gt;
  
  
  ➁.➄ IPv4 addresses structure and CIDR
&lt;/h4&gt;

&lt;p&gt;All IPv4 IP addresses has the same structure - 4 numbers divided by dots: x.x.x.x. The trailing slash with a number after it is not a part of IP address, it is CIDR (Classless Inter-Domain Routing) notation.&lt;/p&gt;

&lt;p&gt;Take a detailed look at this again:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;10.0.0.0/8 IP addresses: 10.0.0.0 – 10.255.255.255&lt;/li&gt;
&lt;li&gt;172.16.0.0/12 IP addresses: 172.16.0.0 – 172.31.255.255&lt;/li&gt;
&lt;li&gt;192.168.0.0/16 IP addresses: 192.168.0.0 – 192.168.255.255&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The largest network range (with the most IP addresses) is represented by 10.0.0.0/8. The CIDR number after the slash (e.g., "/8") is not random; it refers to the number of bits allocated for the network portion of the IP address.&lt;/p&gt;

&lt;p&gt;A "/8" means that the first 8 bits are reserved for the network, leaving 24 bits for the host part. This allows for 16,777,216 available addresses for devices (hosts) within the network. The smaller the CIDR number (like "/8"), the larger the number of available IP addresses, because fewer bits are used for the network portion, and more are left for devices.&lt;/p&gt;

&lt;p&gt;The IP IPv4 address is 32 bits long, divided into four groups (octets) of 8 bits each. For example, 192.168.1.0 is written as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;192    .    168    .     1    .    0
11000000.10101000.00000001.00000000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each number is an 8-bit block (each block represents one of the four octets). The CIDR prefix tells how many of these 32 bits are used for the network portion.&lt;/p&gt;

&lt;p&gt;Lets return to my outputs from ip a:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;eno1: inet 192.168.1.X/24 brd 192.168.1.255&lt;/li&gt;
&lt;li&gt;wlxXXXXXX43643754XX: inet 192.168.1.Y/24 brd 192.168.1.255&lt;/li&gt;
&lt;li&gt;virbr0: inet 192.168.122.1/24 brd 192.168.122.255&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;eno1&lt;/code&gt; and &lt;code&gt;wlxXXXXXX43643754XX&lt;/code&gt; network interfaces connect my PC to the local network managed by my WiFi router. Router assigned them private IP address 192.168.1.X and 192.168.1.Y. These IP addresses were vacant, so they were assigned by router's DHCP (Dynamic Host Configuration Protocol). CIDR /24 tells me that in this local network there are 256 "spots" for devices:&lt;/p&gt;

&lt;p&gt;/24 means the first 24 bits are for the network. This leaves 8 bits for the host (the devices within the network). Network bits: 24 bits; host bits: 8 bits; 2^8 = 256 possible IP addresses in this network (but in practice, some are reserved for special purposes).&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;virbr0&lt;/code&gt; network interface is a different case. First, it is managed by &lt;code&gt;libvirt/QEMU&lt;/code&gt;, as I mentioned, not by my WiFi router, and it is a virtual bridge. The Network to which this network interface connects also has a CIDR of /24, meaning there are 256 available addresses.&lt;/p&gt;

&lt;p&gt;However, remember the last information I shared in the previous part of this series on virtualization? RECAP: I created a VM connected to the &lt;strong&gt;DEFAULT&lt;/strong&gt; network, and I mentioned that even though it can reach the internet, no device connected to my local home network can access this VM via SSH, ping, or anything else (except HOST!). This is because any 192.168.122.X address is not part of my local home's 192.168.1.X/24 network! The 192.168.1.X/24 range covers addresses from 192.168.1.0 to 192.168.1.255, but any 192.168.122.X address is outside of that range. As a result, there’s no connection, no communication.&lt;/p&gt;

&lt;p&gt;After this long introductory session, let’s get back to virtualization with virsh, QEMU, and KVM, focusing on network configurations.&lt;/p&gt;




&lt;h3&gt;
  
  
  ➂ Libvirt's &lt;strong&gt;DEFAULT&lt;/strong&gt; Network: about NAT mode
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;libvirt&lt;/code&gt; uses the concept of a virtual network switch. The network interface you saw in my &lt;code&gt;ip a&lt;/code&gt; outputs in the previous section, called &lt;code&gt;virbr0&lt;/code&gt;, is nothing more than a virtual network switch automatically created and managed by libvirt.&lt;/p&gt;

&lt;h4&gt;
  
  
  ➂.➀ About virtual network switches
&lt;/h4&gt;

&lt;p&gt;I don’t know if you’re familiar with &lt;code&gt;physical&lt;/code&gt; network switches, but these guys look like this (not so friendly for trypophobic folks, hehe):&lt;/p&gt;

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

&lt;p&gt;Getting closer to understanding networks: my PC has an Ethernet cable that’s plugged into the Wi-Fi router. But what if I plug it into my laptop instead? &lt;em&gt;Haha, I can’t do that because my laptop doesn’t have an Ethernet port&lt;/em&gt;. However, if I could, that connection would create a small network of two devices (my PC and my laptop), allowing them to communicate with each other.&lt;/p&gt;

&lt;p&gt;But what if I wanted to attach something else—a second laptop? It would be pretty hard to do with the scarcity of Ethernet ports (even if both laptops had one Ethernet port).&lt;/p&gt;

&lt;p&gt;If I had a network switch, though, I could plug in all the devices I wanted—up to the number of available ports on the switch. The network switch would handle the communication between them by forwarding packets between connected devices!&lt;/p&gt;

&lt;p&gt;So, the virtual network switch works kinda the same way, just for VMs. The default &lt;code&gt;virbr0&lt;/code&gt; virtual network switch is used when VMs are connect to the &lt;strong&gt;DEFAULT&lt;/strong&gt; network. This virtual network switch enables them to communicate easily with each other.&lt;/p&gt;

&lt;p&gt;Here is the &lt;strong&gt;DEFAULT&lt;/strong&gt; network:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo virsh net-list --all

 Name      State    Autostart   Persistent
--------------------------------------------
 default   active   no          yes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;strong&gt;DEFAULT&lt;/strong&gt; network operates in Network Address Translation (NAT) mode (the one I introduced above discussing IPv4 vs IPv6).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;By default, a virtual network switch operates in NAT mode (using IP masquerading rather than SNAT or DNAT).&lt;br&gt;
This means any guests connected through it, use the host IP address for communication to the outside world. Computers external to the host can't initiate communications to the guests inside, when the virtual network switch is operating in NAT mode. (&lt;a href="https://wiki.libvirt.org/VirtualNetworking.html" rel="noopener noreferrer"&gt;Libvirt: Virtual Networking&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi8hioepoxd1u1e9fis75.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi8hioepoxd1u1e9fis75.png" alt=" " width="682" height="389"&gt;&lt;/a&gt;&lt;/p&gt;
DEFAULT network in the NAT mode&lt;a href="https://wiki.libvirt.org/VirtualNetworking.html" rel="noopener noreferrer"&gt;Libvirt documentation&lt;/a&gt;



&lt;p&gt;Libvirt's documentation specifies, that &lt;strong&gt;the NAT is set up using &lt;em&gt;iptables&lt;/em&gt; rules&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And here we arrive at another important player in the whole networking process – &lt;code&gt;iptables&lt;/code&gt;. It is often perceived juts as a tool that protects your system from unauthorized access/traffic based on some rules. However, the reality is that it is much, much more than that, as it can also be used for traffic manipulation, forwarding, NAT (Network Address Translation), and more.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Iptables provides packet filtering, network address translation (NAT) and other packet mangling. &lt;br&gt;
NOTE: &lt;code&gt;iptables&lt;/code&gt; was replaced by &lt;code&gt;nftables&lt;/code&gt; starting in Debian 10 Buster. (&lt;a href="https://wiki.debian.org/iptables" rel="noopener noreferrer"&gt;Debian Wiki: iptables&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I am using Debian Sid, so I definitely have &lt;code&gt;nftables&lt;/code&gt; and not the legacy &lt;code&gt;iptables&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Is &lt;code&gt;nftables&lt;/code&gt; just a modern version of &lt;code&gt;iptables&lt;/code&gt; with fixed vulnerabilities, faster performance, and so on, but still it is just the&lt;code&gt;iptables&lt;/code&gt; under the hood? NO. They are two different frameworks designed to do the same job—'mangling' network traffic. However, think of 'different frameworks' in this context as you would if you have experience with Python: it's like TensorFlow and PyTorch. In web development, it's like React and Angular. You cannot write a neural network using PyTorch and then expect to just copy-paste the network source code into TensorFlow and have it work. The same goes for &lt;code&gt;nftables&lt;/code&gt; and &lt;code&gt;iptables&lt;/code&gt;. They are different, with different syntax, and different logic, especially when it comes to IPv6 traffic.                  &lt;/p&gt;

&lt;p&gt;It's better not to mix the two, thinking, 'Oh, for an issue Y I'll write and add some rules in &lt;code&gt;iptables&lt;/code&gt;, but then for the issue X I found a tutorial for &lt;code&gt;nftables&lt;/code&gt;, so I'll add rules this way'. (&lt;em&gt;Played Witcher 3? Remember what happened to Geralt when he was courting both Triss and Yennefer? Well, the same can happen to your network traffic if you start playing around with different tools for network traffic management)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;However, even if you do so (use both the &lt;code&gt;nftables&lt;/code&gt; and the legacy &lt;code&gt;iptables&lt;/code&gt; tool at the same time), Debian has you covered in a certain way. First, the &lt;code&gt;iptables&lt;/code&gt; utility is not installed on the system by default. If it is installed, the &lt;code&gt;iptables&lt;/code&gt; utility will, by default, use the &lt;code&gt;nftables&lt;/code&gt; backend. But, again:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Should I mix nftables and iptables/ebtables/arptables rulesets?&lt;br&gt;
No, unless you know what you are doing. (&lt;a href="https://wiki.debian.org/nftables" rel="noopener noreferrer"&gt;Debian Wiki: nftables&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  ➂.➁ Libvirt wants &lt;code&gt;iptables&lt;/code&gt;, Debian has &lt;code&gt;nftables&lt;/code&gt;: what to do?
&lt;/h4&gt;

&lt;p&gt;As I mentioned before, &lt;code&gt;libvirt&lt;/code&gt;'s &lt;strong&gt;DEFAULT&lt;/strong&gt; network functions in NAT mode, and this NAT mode is defined using &lt;code&gt;iptables&lt;/code&gt; rules. So, when you installed &lt;code&gt;libvirt&lt;/code&gt; tools, it most probably pulled in &lt;code&gt;iptables&lt;/code&gt; as a dependency. Indeed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ aptitude why iptables
i   libvirt-daemon-system          Depends libvirt-daemon-driver-nwfilter (= 10.10.0-3)
i A libvirt-daemon-driver-nwfilter Depends iptables
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But I already discouraged you from using &lt;code&gt;iptables&lt;/code&gt;, hehe. So, what's the plan? First, most likely, you don't even have &lt;code&gt;nftables&lt;/code&gt; up and running yet :D. Because if you did, and it was running as a &lt;code&gt;systemd&lt;/code&gt; service, you would have encountered some troubles starting VMs with the &lt;strong&gt;DEFAULT&lt;/strong&gt; network. I'll show you why:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#if for some reason you do not have nftables installed:
# $ sudo apt install nftables
$ sudo systemctl status nftables
#is it active? No? Then, start it
$ sudo systemctl startnftables
# and ENABLE it so it will start on the Boot
$ sudo systemctl enable nftables.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;FYI: Take a look at the &lt;code&gt;nftables&lt;/code&gt; ruleset "in use" using command &lt;code&gt;sudo nft list ruleset&lt;/code&gt; when &lt;code&gt;nftables.service&lt;/code&gt; is stopped and when it's started (or before starting it and after). Compare the two and try to find libvirt's NAT configuration :).&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#And now I bring UP DEFAULt libvirt network
$ sudo virsh net-start default
error: Failed to start network default
error: internal error: Failed to apply firewall command 'nft -ae insert rule ip libvirt_network guest_output iif virbr0 counter reject': Error: Could not process rule: No such file or directory
insert rule ip libvirt_network guest_output iif virbr0 counter reject
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OOPS! I broke everything. Libvirt wants &lt;code&gt;iptables&lt;/code&gt;, not &lt;code&gt;nftables&lt;/code&gt;. &lt;br&gt;
Whatever wants Libvirt, but in Russian there is saying: "eat what is given". There’s a legit solution to make libvirt work well with &lt;code&gt;nftables&lt;/code&gt;. And it’s not just some workaround. &lt;/p&gt;

&lt;p&gt;Maybe you are familiar with UFW—Uncomplicated Firewall—this tool is built on top of &lt;code&gt;iptables&lt;/code&gt;, making it easier to manage &lt;code&gt;iptables&lt;/code&gt; rules. You write simplified rules, and it translates them into &lt;code&gt;iptables&lt;/code&gt; rules in the underground. A similar interface also exists for &lt;code&gt;nftables&lt;/code&gt;! It’s called &lt;code&gt;firewalld&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;First, let’s clarify why this isn’t just a workaround that adds extra software to your system just to make something work for VMs:"&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;You should consider using a wrapper instead of writing your own firewalling scripts&lt;/strong&gt;. It is recommended to run &lt;code&gt;firewalld&lt;/code&gt;, which integrates pretty well into the system. See also &lt;a href="https://firewalld.org/" rel="noopener noreferrer"&gt;https://firewalld.org/&lt;/a&gt; (&lt;a href="https://wiki.debian.org/nftables" rel="noopener noreferrer"&gt;Debian Wiki: nftables&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And...:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The firewalld software takes control of all the firewalling setup in your system, so you don't have to know all the details of what is happening in the underground. There are many other system components that can integrate with firewalld, like NetworkManager, libvirt, podman, fail2ban, docker, etc.(&lt;a href="https://wiki.debian.org/nftables" rel="noopener noreferrer"&gt;Debian Wiki: nftables&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, &lt;code&gt;firewalld&lt;/code&gt;, besides all its perks in firewall configuration and network traffic management, is the bro that will make &lt;code&gt;libvirt&lt;/code&gt; function correctly with &lt;code&gt;nftables&lt;/code&gt;. It will ensure that all the NAT rules, written with love by the &lt;code&gt;libvirt&lt;/code&gt; devs, are active. That means the DEFAULT network based on NAT rules will work again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt install firewalld
$ sudo systemctl start firewalld
$ sudo systemctl enable firewalld

#have a look on the new ruleset firewalld brought with it to nftables
$ sudo nft list ruleset
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What about libvirt's ruleset for NAT?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo nft list ruleset | grep libvirt

#nothing 

#now i start DEFAULT network
$ sudo virsh net-start default
Network default started
$ sudo nft list ruleset | grep libvirt
        iifname "virbr0" jump mangle_PRE_libvirt
        iifname "virbr0" jump nat_PRE_libvirt
        iifname "virbr0" oifname "virbr0" jump nat_POST_libvirt
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo nft list tables

table inet filter
table inet firewalld
table ip libvirt_network
table ip6 libvirt_network
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, there's a separate "table" for &lt;code&gt;libvirt_network&lt;/code&gt;. This table is managed by libvirt, and libvirt fills this table with rules when its virtual network switch becomes active (&lt;code&gt;virbr0&lt;/code&gt;). This happens only if at least one network using the virtual bridge &lt;code&gt;virbr0&lt;/code&gt; is started.&lt;/p&gt;

&lt;h4&gt;
  
  
  ➂.➂ DEFAULT Libvirt's network: what's under the hood?
&lt;/h4&gt;

&lt;p&gt;As I mentioned before, the &lt;code&gt;libvirt&lt;/code&gt; &lt;strong&gt;DEFAULT&lt;/strong&gt; network's NAT is set up using &lt;em&gt;iptables&lt;/em&gt; rules. And in my case Libvirt is forced to use what is available on my Debian - &lt;code&gt;nftables&lt;/code&gt;. So, if I explore existing rules in nftables, I for sure should find there Libvirt's default network rules, according to which traffic circulate between VMs and FROM VMs TO the "outside world". &lt;/p&gt;

&lt;p&gt;And here's how the network magic happens for VMs: when they interact with each other and with the host, and access the internet to fetch whatever they're commanded to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo nft -a list ruleset
table ip libvirt_network { # handle 6
    chain forward { # handle 1
        type filter hook forward priority filter; policy accept;
        counter packets 0 bytes 0 jump guest_cross # handle 7
        counter packets 0 bytes 0 jump guest_input # handle 5
        counter packets 0 bytes 0 jump guest_output # handle 3
    }

    chain guest_output { # handle 2
        ip saddr 192.168.122.0/24 iif "virbr0" counter packets 0 bytes 0 accept # handle 13
        iif "virbr0" counter packets 0 bytes 0 reject # handle 10
    }

    chain guest_input { # handle 4
        oif "virbr0" ip daddr 192.168.122.0/24 ct state established,related counter packets 0 bytes 0 accept # handle 14
        oif "virbr0" counter packets 0 bytes 0 reject # handle 11
    }

    chain guest_cross { # handle 6
        iif "virbr0" oif "virbr0" counter packets 0 bytes 0 accept # handle 12
    }

    chain guest_nat { # handle 8
        type nat hook postrouting priority srcnat; policy accept;
        ip saddr 192.168.122.0/24 ip daddr 224.0.0.0/24 counter packets 1 bytes 40 return # handle 21
        ip saddr 192.168.122.0/24 ip daddr 255.255.255.255 counter packets 0 bytes 0 return # handle 20
        meta l4proto tcp ip saddr 192.168.122.0/24 ip daddr != 192.168.122.0/24 counter packets 0 bytes 0 masquerade to :1024-65535 # handle 19
        meta l4proto udp ip saddr 192.168.122.0/24 ip daddr != 192.168.122.0/24 counter packets 0 bytes 0 masquerade to :1024-65535 # handle 18
        ip saddr 192.168.122.0/24 ip daddr != 192.168.122.0/24 counter packets 0 bytes 0 masquerade # handle 17
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h4&gt;
  
  
  ➂.➃ Nftables rulesets: Libvirt Network's table and chains explained
&lt;/h4&gt;

&lt;p&gt;Let’s break it down:&lt;/p&gt;

&lt;p&gt;First, &lt;code&gt;libvirt_network&lt;/code&gt; rules have their own &lt;em&gt;table&lt;/em&gt;. In &lt;code&gt;nftables&lt;/code&gt;, tables act as "containers" within the overall ruleset (to see the full list: &lt;code&gt;sudo nft list ruleset&lt;/code&gt;). &lt;em&gt;Tables&lt;/em&gt; contain &lt;em&gt;chains&lt;/em&gt;, sets, maps, flowtables, and stateful objects.&lt;/p&gt;

&lt;p&gt;Each table belongs to &lt;strong&gt;exactly one&lt;/strong&gt; &lt;em&gt;family&lt;/em&gt;. If you want to apply a specific set of rules to some network traffic, you must first &lt;strong&gt;define the table by specifying its type&lt;/strong&gt;. The type determines that only traffic of this specific type will be filtered by the rules in that table.&lt;/p&gt;

&lt;p&gt;For example, the &lt;em&gt;table ip libvirt_network&lt;/em&gt; rules &lt;strong&gt;only filter IPv4 traffic/packets&lt;/strong&gt; because its table is assigned the &lt;strong&gt;ip&lt;/strong&gt; family:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;table ip libvirt_network

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;em&gt;chain&lt;/em&gt; is essentially a list of rules. In the case of &lt;code&gt;libvirt_network&lt;/code&gt; ruleset, everything starts with the &lt;em&gt;forward chain&lt;/em&gt;, which is responsible for filtering incoming traffic packets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;table ip libvirt_network {
    chain forward { #rules}
        chain guest_output {#rules}
    chain guest_input {#rules}
        chain guest_cross {#rules}
    chain guest_nat {#rules}
}

  ______         A |-----------------|F
 |packet|  ---&amp;gt;  C |  chain forward  |I ---|
  ¯¯¯¯¯¯         C |  type: filter   |L    | 
                 E |  hook: forward  |T    |
                 P |  policy: accept |E    |
                 T |-----------------|R    |
                     -----packet----&amp;gt;      | 
                                           |
                                Routing decision:
                     &amp;lt;---?---  | |----?----&amp;gt; 
             |-----------------| | -----------------|
             |                 | ?                  |
|------------------------|     | |    |------------------------| 
|    chain guest_input   |     | V    |   chain guest_output   |    
|    inbound packets     |     |      |    outbound packets    |
|   destined for guests  |     |      |       from guests      |   
|           ?            |     |      |             ?          |
|------------------------|     |      |------------------------| 
            |                  |                    |  
|-----------|------------|     |      |-------------|----------|  
|                        |     |      |                        | 
+ACCEPT             REJECT-    |      +ACCEPT             REJECT-
+If:              Anything-    |      +If:              Anything-
+outgoing             else-    |      +source IP:           else-
+interface:               -    |      +192.168.122.0/24         -
+virbr0                   -    |      +                         - 
+                         -    |      +incoming                 -
+connection               -    |      +interface:               -
+state:                   -    |      +virbr0                   -     
+established OR           -    |      +++++++++++++++++++++++++++ 
+related                  -    | 
+                         -    |      |--------------------------|
+destination:             -    |      |    chain guest_cross     | 
+in 192.168.122.0/24      -    |------|    traffic between       |
+++++++++++++++++++++++++++           |    guests on virbr0      |                                            
                                      |             ?            |        
                                      |--------------------------| 
                                      +ACCEPT              REJECT-
                                      +If:                No rule-
                                      +incoming interface:       -
                                      +virbr0                    - 
                                      +                          - 
                                      +outgoing interface:       -
                                      +virbr0                    -
                                      ++++++++++++++++++++++++++++ 

 ##########################################################  
 #                Routing decision is made!               #
 #                            IF:                         #
 #                 outbound packets from guests           #
 ##########################################################
    :==========:      ______                      :==========:
    :guest VM  : --&amp;gt; |packet|                     : Other VM : 
    :==========:      ¯¯¯¯¯¯                      :==========:
                        |                                  ^ 
                        V                                  |
 |------------------------|    |--IF traffic FROM:      NO NAT
 |                        |    |                    (NO masquerade)
 |   chain guest_nat      |---&amp;gt;| guest subnet TO:          ^
 |   type: nat            |    | (224.0.0.0/24) OR --------|
 |   hook: postrouting    |    | (255.255.255.255)
 |   priority: srcnat;    |    |==================================
 |   policy: accept;      |---&amp;gt;| IF traffic FROM:   
 |                        |    | guest subnet TO: ---------|
 | -----------------------|    | DIFFERENT subnet          V
                               |           MASQUERADE traffic (NAT)
                                                           |
                                          INTERNET &amp;lt;--------

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I hope this ASCII scheme brought some clarity to how VMs attached to the &lt;strong&gt;DEFAULT&lt;/strong&gt; libvirt's network communicate with the host, with each other, and with the outside world. Here’s some more info.&lt;/p&gt;

&lt;p&gt;A network packet pops up on the &lt;code&gt;virbr0&lt;/code&gt; interface and then gets &lt;em&gt;filtered&lt;/em&gt; (by &lt;code&gt;chain forward&lt;/code&gt;) and &lt;em&gt;routed&lt;/em&gt; or &lt;em&gt;dropped&lt;/em&gt;  - based on the &lt;code&gt;nftables&lt;/code&gt; rules.&lt;/p&gt;

&lt;p&gt;For example, when one VM sends a packet to another VM (if both are connected to &lt;strong&gt;DEFAULT&lt;/strong&gt; network, it’s routed to that VM. The rules in &lt;code&gt;chain guest_cross&lt;/code&gt; are triggered—there’s no rejection, so the packet is delivered without any special &lt;em&gt;mangling&lt;/em&gt;, since it goes from &lt;code&gt;virbr0&lt;/code&gt; to &lt;code&gt;virbr0&lt;/code&gt; network interface. &lt;code&gt;Chain guest_nat&lt;/code&gt; also participates, but NAT does not apply here because it’s an internal connection, and &lt;em&gt;the rules say that when it’s from the same subnet to the same subnet, there’s no NAT&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Another example: the host sends a packet to a VM. This triggers the &lt;code&gt;chain guest_input&lt;/code&gt;. The &lt;em&gt;connection tracking state&lt;/em&gt; is &lt;em&gt;established&lt;/em&gt; in this case (the VM has already initiated a connection to the host). And here it is! This is why you cannot connect to the VM remotely (&lt;strong&gt;even from another device connected to the same local home network as host and configured port forwarding!&lt;/strong&gt;)—it will be &lt;strong&gt;new&lt;/strong&gt; connection, not &lt;strong&gt;established&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;Also, if you try to connect to the VM from a laptop on the same Wi-Fi as the host, it gets dropped because the VM is on a different network (remember, 192.168.122.x is not a part of 192.168.1.0/24 network!). But you configured &lt;em&gt;port forwarding&lt;/em&gt;? Still nope. Packets will be dropped. Because again, it’s a &lt;strong&gt;new&lt;/strong&gt; connection.&lt;/p&gt;

&lt;p&gt;Yet another example: the VM wants to run &lt;code&gt;sudo apt update &amp;amp;&amp;amp; sudo apt upgrade&lt;/code&gt;. It needs to fetch data from the Debian repositories. This is governed by &lt;code&gt;chain guest_output&lt;/code&gt;, so the rules are triggered and they don’t cause the drop of packets. Then the &lt;code&gt;guest_nat chain&lt;/code&gt; is activated because traffic is going from the guest subnet to an external subnet. Without NAT, it wouldn’t work at all—the VM network is different from your local home network (WiFi), so they don’t directly communicate. They’re isolated from each other, and &lt;code&gt;virbr0&lt;/code&gt; doesn’t &lt;em&gt;bridge&lt;/em&gt; these two networks!&lt;/p&gt;

&lt;p&gt;To summarize: there are only two possible outcomes for the network traffic you initialize - FROM and TO your virtual machines that are attached to the &lt;strong&gt;DEFAULT&lt;/strong&gt; network—it will either reach its destination (ACCEPTED) or be dropped (REJECTED).&lt;/p&gt;

&lt;p&gt;I hope it’s clear when it comes to host-to-VM, VM-to-host traffic, and communication between VMs. 1) There are no specific &lt;code&gt;nftables&lt;/code&gt; network filtering/mangling rules blocking it. 2) These communications are possible by the specifically configured and virtualized &lt;code&gt;virbr0&lt;/code&gt; virtual network switch.&lt;/p&gt;

&lt;p&gt;However, HOW &lt;em&gt;does&lt;/em&gt; traffic TO the internet—and especially FROM other networks (like your home LAN) &lt;em&gt;work&lt;/em&gt; — can still be challenging to understand. NAT (Network Address Translation) might still appear like a black box. So, in the final section of this article, I’ll try to simplify things and explain it in detail.&lt;/p&gt;

&lt;p&gt;Understanding NAT is crucial for all IPv4 traffic, as NAT has become a widespread networking configuration. This is due to the inherent limitations IPv4 faces in the modern world, making NAT a kind of symbiotic solution for IPv4 networks.&lt;/p&gt;

&lt;h4&gt;
  
  
  ➂.➄ About how NAT works
&lt;/h4&gt;

&lt;p&gt;Let’s get back to the &lt;code&gt;NAT chain&lt;/code&gt; of the &lt;code&gt;libvirt_network ip table&lt;/code&gt;. This chain doesn’t just apply filtering rules to network traffic—it handles &lt;em&gt;postrouting&lt;/em&gt; rules, meaning it &lt;strong&gt;sees all packets after routing, right before they leave the local system&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, imagine your VM wants to connect to the internet to update itself when you run &lt;code&gt;sudo apt update&lt;/code&gt;. This command fetches the data about the versions of your system’s packages against the repository versions. The packets from the VM are sent to &lt;a href="http://deb.debian.org/debian/" rel="noopener noreferrer"&gt;http://deb.debian.org/debian/&lt;/a&gt; (the Debian package repo source link).&lt;/p&gt;

&lt;p&gt;These packets (fetching request) end up on &lt;code&gt;virbr0&lt;/code&gt;, which acts like an airport for them. It decides if these “passengers” (packets) need to take an “international flight” (outside the guest VM network) or a “domestic flight” (within the guest VM network).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NB! I will cover DNS servers and gateways that act as routers in detail in the next article! For now, let’s simplify things (even though it’s not fully technically accurate) and say that the &lt;code&gt;virbr0&lt;/code&gt; virtual network switch handles this &lt;em&gt;somehow&lt;/em&gt;, so I can focus on explaining NAT (otherwise I will never finish this article T_T).&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's get back to the postrouting rules specified in the guest_nat chain and disaamble them to see how they worl&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo nft -a list ruleset
...
chain guest_nat { # handle 8
        type nat hook postrouting priority srcnat; policy accept;
        ip saddr 192.168.122.0/24 ip daddr 224.0.0.0/24 counter packets 1 bytes 40 return # handle 21
        ip saddr 192.168.122.0/24 ip daddr 255.255.255.255 counter packets 0 bytes 0 return # handle 20
        meta l4proto tcp ip saddr 192.168.122.0/24 ip daddr != 192.168.122.0/24 counter packets 0 bytes 0 masquerade to :1024-65535 # handle 19
        meta l4proto udp ip saddr 192.168.122.0/24 ip daddr != 192.168.122.0/24 counter packets 0 bytes 0 masquerade to :1024-65535 # handle 18
        ip saddr 192.168.122.0/24 ip daddr != 192.168.122.0/24 counter packets 0 bytes 0 masquerade # handle 17
    }
}
..
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Handles&lt;/em&gt; are used natively by &lt;code&gt;nftables&lt;/code&gt; to make it easier to reference specific rules when you need to modify or transform them, so they’re quite &lt;strong&gt;handy&lt;/strong&gt; for me right now—I don’t have to retype rules.&lt;/p&gt;

&lt;p&gt;Handles #20 and #21 aren’t what I’m looking for to illustrate NAT in action. These handles specifically ignore packets traveling within the same guest network.&lt;/p&gt;

&lt;p&gt;But handles #19, #18, and #17 are exactly where NAT is in action! These three handles are categorized based on the communication protocol in use—TCP (#19), UDP (#18), and all other protocols (#17).&lt;/p&gt;

&lt;p&gt;All three handles process traffic in the same way. The key word in all these rules is &lt;strong&gt;masquerade&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The term masquerade hints what happens to the &lt;em&gt;source address&lt;/em&gt; of the network traffic—it gets "masked".&lt;/p&gt;

&lt;p&gt;Let’s follow the network packets from a VM that executed &lt;code&gt;sudo apt update&lt;/code&gt;. First, since network communication of this type uses the TCP protocol, I have to look into the rule of handle #19:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ip saddr 192.168.122.0/24 ip daddr != 192.168.122.0/24 --&amp;gt;  
--&amp;gt; This is like a programming `if` condition:
If the **s**ource **addr**ess (saddr) is within the 192.168.122.0/24 subnet 
AND the **d**estination **addr**ess (daddr) is outside of this subnet:
     masquerade to :1024-65535
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Masquerade&lt;/em&gt; replaces the original source address with something else. In this case, it replaces it with the private IP address of the host. More specifically, it replace it with the private IP of the network interface with the highest priority (if there’s more than one, as in your case).&lt;/p&gt;

&lt;p&gt;And what’s with the numbers after the colon? Those are ports—specifically the ephemeral port range (1024-65535). Why such a big range? And aren’t these ports busy on the host?&lt;/p&gt;

&lt;p&gt;This port range is the ephemeral port range, which is designed to avoid conflicts with privileged ports (0–1023). Privileged ports are reserved for well-known system services, so this range ensures the masqueraded traffic doesn’t interfere with them.&lt;/p&gt;

&lt;p&gt;Oh I left alone the packets from &lt;code&gt;sudo apt update&lt;/code&gt;. Here they are: after masquerading:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The packets originally departed from the VM at 192.168.122.10 on port 80 (since it’s HTTP traffic)&lt;/li&gt;
&lt;li&gt;They reached &lt;code&gt;virbr0&lt;/code&gt; (the virtual network switch).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;virbr0&lt;/code&gt; routed them to go outside the &lt;strong&gt;DEFAULT&lt;/strong&gt; network (where VM belongs to).&lt;/li&gt;
&lt;li&gt;Before leaving, they were masqueraded: each packet’s header was modified: the source address was replaced from 192.168.122.10:80 → to 192.168.1.5:12345. Here, 192.168.1.5 is the private IP address of the host (my PC), and 12345 is an ephemeral port assigned dynamically for this communication. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is the peculiar schema:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feu4wdfc7kt8zoxb814pi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feu4wdfc7kt8zoxb814pi.png" alt=" " width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;a href="https://www.gettyimages.it/detail/foto/woman-with-black-hair-and-eyes-peeking-behind-immagine-royalty-free/182885155?adppopup=true" rel="noopener noreferrer"&gt;Source of Original Image&lt;/a&gt;



&lt;p&gt;And that’s how NAT works. That’s all. Hahaha, just kidding. &lt;/p&gt;

&lt;p&gt;That’s how the FIRST NAT worked. Now, these poor little packets have to "embark" on yet another "plane". They’re off to their next layover, where they’ll get NAT'd (masqueraded) AGAIN— new masks for everyone! This time, the layover is at the WiFi router.&lt;/p&gt;

&lt;p&gt;The NAT’d packets leave the host with source address 192.168.1.5:12345. They arrive to my Wi-Fi router on the LAN side.&lt;br&gt;
Wi-Fi Router NAT&lt;/p&gt;

&lt;p&gt;The WiFi router sees packets from 192.168.1.5:1234 → to 123.123.7.132:80 (I am sooo bad, I do not know the IP address of Debian Servers &amp;amp; lazy to check).&lt;/p&gt;

&lt;p&gt;NAT rewrites the source IP AGAIN from 192.168.1.5 to my router’s public IP (111.111.111.111) and change the port to another ephemeral one (54321).&lt;/p&gt;

&lt;p&gt;NB! This is important! This is the cornerstone of weak routers (from the point of hardware):&lt;/p&gt;

&lt;p&gt;Network packets don’t just travel to the Debian servers for a one-way trip; it’s always a round trip! At some point, response packets will come back. In the case of success, the VM expects to receive information about the versions of the packages it requested, to figure out what’s outdated.&lt;/p&gt;

&lt;p&gt;Now, with all this masquerading, we end up in a situation much like those in movies—a classic masquerade ball drama (someone gets kissed because they were mistaken for someone else under their mask). To prevent these kinds of situationships, the WiFi router steps in as the responsible dude in charge. The router keeps track of &lt;strong&gt;who&lt;/strong&gt; is going &lt;strong&gt;where&lt;/strong&gt;, and if something comes back from "there," it makes sure it gets sent to the right &lt;strong&gt;who&lt;/strong&gt;. To do this, router keeps updated a sort of table (thankfully it’s .xlsx). This table looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;|Private IP    | Public IP      | Source Port | Destination Port |
|192.168.1.5   | 123.193.7.162  | 54321       | 80               |
|192.168.1.20  | 111.153.6.132  | 35465       | 443              |
|192.168.1.11  | 125.161.7.162  | 34564       | 27017            |
|192.168.1.16  | 3.193.5.132    | 1224        | 22               |
.......................
#This table can be quite long if many devices are connected to the same WiFi and actively use Internet!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then, the router keeps this information... in the table.&lt;/p&gt;

&lt;p&gt;Here’s where an important point comes in: this table can grow very quickly. If your router is fast and powerful, no big deal—but if you cheaped out on it, you might run into trouble. A router that can’t handle more than a certain number of rows (based on its hardware limitations) will start to slow down significantly as the table grows beyond.&lt;/p&gt;

&lt;p&gt;Again I left alone travelling network packets. Here they are:&lt;/p&gt;

&lt;p&gt;The packets departed to the Debian server with my public IP and an ephemeral port: 111.111.111.111:54321.&lt;/p&gt;

&lt;p&gt;The Debian server responds, and the response arrives from 123.123.7.132:80 to the router. The router then checks its table and says, "Ah, here it is!" It finds the matching entry in its tracking table and figures out which device the response is meant for. At this point, the router rewrites the headers of the packet AGAIN, removing its public IP address (111.111.111.111) and restoring the private IP and port from the table. Finally, it sends the packet back to my PC (the host) at 192.168.1.5:12345.&lt;/p&gt;

&lt;p&gt;The host sees in the packets the destination 192.168.1.5:12345, checks its NAT table from the &lt;em&gt;guest_nat chain&lt;/em&gt;, and recognizes this was originally from 192.168.122.10:80.&lt;/p&gt;

&lt;p&gt;It rewrites AGAIN the destination from 192.168.1.5:12345 back to the VM’s IP and port: 192.168.122.10:80.&lt;/p&gt;

&lt;p&gt;The packet is finally forwarded to the VM at 192.168.122.10:80.&lt;br&gt;
The VM sees a response from &lt;a href="http://deb.debian.org/debian/" rel="noopener noreferrer"&gt;http://deb.debian.org/debian/&lt;/a&gt; to 192.168.122.10:80, completes the TCP handshake, and gets the repository data.&lt;/p&gt;

&lt;p&gt;And now, that’s truly that's all! This is how NAT works. Enjoying IPv4? Still thinking IPv6 is difficult and scary? &lt;/p&gt;

&lt;p&gt;For a little fun (and for little support for my emotional state after writing this article), try counting and writing in the comments how many times the network packets from the VM, headed to the internet, were rewritten along the way :).&lt;/p&gt;




&lt;p&gt;Now after this TINY introduction to networking, it is time to get to the hands-on configurations. How can I make virtual machines accessible from other devices connected to the same LAN as the host—or even remotely? While I won’t be covering remote access here, making VMs accessible from other devices on the same home network can be quite convenient.&lt;/p&gt;

&lt;p&gt;For example, I have a pretty powerful Desktop PC in terms of specs, and maybe sometimes I’m lazy and want to work from my laptop, which is like a toy in comparison to my PC. When I say I am lazy, I mean I don't want to sit properly at my desk; I want to loaf on the sofa with my laptop. However, I still want to use the resources of my PC. I can, of course, connect via SSH to my host machine, but it may be that I just want to connect to the VM with MongoDB to do something or check on it.&lt;/p&gt;

&lt;p&gt;There are three main ways to do so:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Port forwarding &amp;amp; Custom NAT&lt;/li&gt;
&lt;li&gt;Bridged networking (aka "shared physical device")&lt;/li&gt;
&lt;li&gt;PCI Passthrough of host network devices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of them I will cover in the next article.&lt;/p&gt;

</description>
      <category>libvirt</category>
      <category>debian</category>
      <category>networking</category>
      <category>ipv6</category>
    </item>
    <item>
      <title>Virtualization on Debian with virsh&amp;QEMU&amp;KVM — Installation of virtualization tools and first VM creation</title>
      <dc:creator>aL</dc:creator>
      <pubDate>Sun, 12 Jan 2025 13:00:56 +0000</pubDate>
      <link>https://dev.to/dev-charodeyka/virtualization-on-debian-with-virshqemukvm-what-you-need-to-install-and-how-49oo</link>
      <guid>https://dev.to/dev-charodeyka/virtualization-on-debian-with-virshqemukvm-what-you-need-to-install-and-how-49oo</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3tnq2vci8jxtzzq95liw.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3tnq2vci8jxtzzq95liw.gif" alt=" " width="8" height="4"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;In this article, I will not cover the basics of &lt;em&gt;virtualization&lt;/em&gt;—what it is and when you might need it. This article is for those who are more or less familiar with the concept, but don’t know how to get started with it on Debian. &lt;/p&gt;




&lt;p&gt;Here’s the road map for this article:&lt;/p&gt;

&lt;p&gt;➀ Virtualization and self-hosting&lt;br&gt;
➁ Virtualization as a tool for resource-Constrained application development&lt;br&gt;
➂ Virtualization on Debian: how it works&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;➂.➀ CPU virtualization support&lt;/li&gt;
&lt;li&gt;➂.➁ Hypervisor&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;➃ KVM &amp;amp; QEMU&lt;br&gt;
➄ Libvirt&lt;br&gt;
➅ Validation of virtualization tools installation&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;➅.➀ Important! &lt;code&gt;qemu:///system&lt;/code&gt; vs &lt;code&gt;qemu:///session&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;➆ Creation of first VM:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;➆.➀ OS image file&lt;/li&gt;
&lt;li&gt;➆.➁ Preparing storage&lt;/li&gt;
&lt;li&gt;➆.➂ VM creation with virt-install&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;➇ Let the Networking begin!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;➇.➀ Userspace (SLIRP or passt) connection&lt;/li&gt;
&lt;li&gt;➇.➁ NAT forwarding (aka "virtual networks")&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;First, I will introduce my use case - why do I need virtual machines on my personal PC.&lt;/p&gt;

&lt;p&gt;Long story short, I’m an "on-premise" girl (read "not very pro with cloud infrastructures"). I have an experience with dealing with on-prem infrastructure, and now I’m facing the need to deploy SOMEWHERE my personal project—a web app—so it sees the real world and real world sees it. And no, this isn’t a static website; it has a backend and a database. And hypothetically, some components will need horizontal scaling in the future.&lt;/p&gt;


&lt;h3&gt;
  
  
  ➀ Virtualization and self-hosting
&lt;/h3&gt;

&lt;p&gt;My personal PC isn’t bad at all in terms of specs—perfectly capable for development purposes and even, &lt;strong&gt;theoretically&lt;/strong&gt;, for serving all the needs of my small app in production. However, hosting anything exposed to the web on a personal PC is out of the question. If your first thought is that the only obstacle is my PC needing to run 24/7, it is not about it. Using a machine that has some personal data for hosting of something exposed to the web is a VERY BAD IDEA. If you don’t understand why, you can check out &lt;a href="https://dev.to/dev-charodeyka/debian-12-is-amazing-how-to-create-your-custom-codehouse-part-3b4-2ca5"&gt;this article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If I create Virtual Machine(s) on my personal machine, and configure very well the networks, does that solve the problem? NO! And here’s why. &lt;strong&gt;The main impediment to hosting anything from "home"&lt;/strong&gt;—even if you bought a proper server for it—&lt;strong&gt;is your router and internet provider&lt;/strong&gt;. Is this about internet speed? Nope. It’s about...your public IP address. &lt;/p&gt;

&lt;p&gt;Let's say you move into a new house, and it doesn’t have Wi-Fi. So you contact the internet providers in your city, check the prices, select the most advantageous offer, and… sign the contract. If you’re just an average user and didn’t specify otherwise, the contract gives you “internet for your house” from the chosen provider.&lt;/p&gt;

&lt;p&gt;A technician arrives at the scheduled appointment, brings some cables, and a plastic box—which is the router. They deal with the cables, connect them to the router, hand you a manual along with the Wi-Fi network name and password, and voilà! You can connect all your home devices and enjoy browsing the web.&lt;/p&gt;

&lt;p&gt;When you just use the internet, you’re most likely never even aware of your public IP address. But chances are, it’s not static at all. It changes periodically, and this is done by your internet provider—because that’s how they often manage their clients with “home” use.&lt;/p&gt;

&lt;p&gt;When it comes to hosting something, like a website, even if you buy a domain name like &lt;em&gt;my-cool-site.it&lt;/em&gt;, how will people find it? Who will bind YOUR PC WITH CODE of this site (where all the site’s needs and dependencies reside) to that domain? Domain name of your web app needs to be resolved in such a way that the correct IP address behind it is revealed.&lt;/p&gt;

&lt;p&gt;Theoretically, you don’t even need to buy a domain name; your site can work perfectly fine with just an IP address like &lt;em&gt;&lt;a href="https://12.34.56.78/home" rel="noopener noreferrer"&gt;https://12.34.56.78/home&lt;/a&gt;&lt;/em&gt;. But it’s not a top if you want your site to be searchable on Google and not just accessible by people who already have the link.&lt;/p&gt;

&lt;p&gt;If your internet provider changes your public IP address periodically, it’s like frequently moving houses. People trying to send you letters would still send them to your old address unless you keep updating them, and the letters would never reach you. The same logic applies to the hosting having a dynamic IP. You could, of course, manually update everything and rebind the domain to your new public IP address, but that’s hardly convenient. &lt;/p&gt;

&lt;p&gt;If you want to have a static public IP address, you should contact your internet provider and find out the conditions under which you can get it. &lt;em&gt;It will probably come with an increased payment for internet service&lt;/em&gt;. Is sticking a certain IP address to your Wi-Fi router that hard and does it require extra effort to keep it like this to cover "technical" costs? No. The increased payment is not even very related to the fact that the need for fixed public IP address can hint that the internet access is for business use, so it’s about earning something, and therefore why not to charge you more. Well... it’s because a &lt;em&gt;unique&lt;/em&gt; public IP address is a scarce resource! Actually, a &lt;em&gt;unique&lt;/em&gt; public &lt;strong&gt;IPv4&lt;/strong&gt; address is in deficit. Internet Protocol version 4 (IPv4) forms the foundation of most &lt;em&gt;Global Internet traffic&lt;/em&gt; today. An IP Address represented under IPv4 is composed of four sets of numbers ranging from 0 to 255, separated by periods(.). &lt;/p&gt;

&lt;p&gt;If you do the straightforward math - total four numbers in an IPv4 address; each number can be in range between 0 and 255 (256 possible values) - 256 * 256 * 256 * 256 = 4,294,967,296 total addresses.&lt;/p&gt;

&lt;p&gt;So here, on the &lt;em&gt;market&lt;/em&gt; for internet service, a basic economic rule comes into play: demand is growing with increasing digitalization around the world, but the supply is restricted by the very nature (mathematical) of the good (unique IPv4 address), so the prices for this good are increasing. In the next article, I will cover more details on IPv4, explain a bit about IPv6 (the solution for this IP deficit situationship), and also cover some interesting aspects of networking that are consequences of this IPv4 address deficit (NAT).&lt;/p&gt;

&lt;p&gt;Plus, another obstacle for self-hosting is a router, provided by your internet provider. They often have very restricting measures in terms of incoming https/s traffic (it gets blocked), and those restrictions (thankfully) will impede any hosting attempts. I say thankfully, because if you truly do not understand how it works, it is better that these restriction, firewall rules, are up and protecting you. &lt;/p&gt;

&lt;p&gt;However, keep in mind, hosting on the **same network **you use for any personal device is not a perfect idea if you are unable to configure all the security mechanisms, firewalls, and configure networks properly.&lt;/p&gt;

&lt;p&gt;Summing this up, currently, it is not an option for me to "selfhost".&lt;/p&gt;


&lt;h3&gt;
  
  
  ➁ Virtualization as a tool for resource-Constrained application development
&lt;/h3&gt;

&lt;p&gt;So, virtualization is not a solution for my problem with deployment of the web-app. Then where it can be deployed? The cloud. I can choose a cloud provider, rent the instances that match my app's needs, configure them, and deploy my app. Simple, right? Well, not so fast—because every instance, every service, comes with a price. And those prices... For someone like me, who’s built a pretty powerful PC for around $1,000, seeing cloud pricing for "little server" instances can be a bit confusing. To give you an idea, you can explore pricing on AWS using &lt;a href="https://calculator.aws/#/" rel="noopener noreferrer"&gt;their calculator&lt;/a&gt;. I’ll share some screenshots of EC2 instance pricing:&lt;/p&gt;

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

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

&lt;p&gt;2 vCPUs, 4 GB of RAM, and storage for an additional cost. All yours for around $30 per month if you want to host something that has a server side operations. &lt;/p&gt;

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

&lt;p&gt;Actually, 2 &lt;strong&gt;v&lt;/strong&gt;CPUs, not CPUs. vCPUs stand for virtual CPUs because they aren’t real physical CPUs—they’re virtualized. And an EC2 instances are essentially just virtual machines.&lt;/p&gt;

&lt;p&gt;Now as we are back to the word virtualization, let’s talk about my use case—my needs. When it comes to the development of my app on my PC, even though I could install everything needed directly (since I’m always using the same stack as a developer), it’s far from optimal. Why clutter my PC with installations os stuff like Nginx and MongoDB, leaving them hanging around unnecessarily when the project is finished?&lt;/p&gt;

&lt;p&gt;To keep everything tidy for development purposes, virtual machines hosted on my PC is the great solution. However, the real issue with development directly on my PC is this: when I develop on a machine with 20 CPU cores of the latest generation, 64 GB of RAM, and 12 GB of GPU memory, how can I be sure that what I’ve developed will actually run on a small EC2 instances? Or more importantly, how can I evaluate the resource requirements for my app in general? (let's leave code-based evaluation aside for now)&lt;/p&gt;

&lt;p&gt;This is where virtualization will really help me. I can evaluate my code’s performance right from the start by creating VMs with small resources attached and placing my app's components there!&lt;/p&gt;
&lt;h4&gt;
  
  
  Note for the Dockerists/Dockerphiles/Containerphiles
&lt;/h4&gt;

&lt;p&gt;I can already foresee the "Gosh, just learn Docker—&lt;em&gt;it’s easy!&lt;/em&gt; Developing on bare metal is dinosauric; containerization is the key!" argument. I have no doubt Docker can handle everything. In fact, I personally enjoy Docker Swarm quite a bit (can’t say the same for Kubernetes, though). &lt;/p&gt;

&lt;p&gt;However, let’s not forget that Docker has under a virtualization technology. And as I mentioned earlier, EC2 instances are nothing more than virtual machines. So, when you spin up an EC2 instance, you’re essentially getting a VM—a &lt;strong&gt;virtual layer&lt;/strong&gt;. Then, when you install Docker on top of that, you’re adding… yet another virtual layer! And all of this is happening on a modest machine with just a few CPUs and some RAM. &lt;/p&gt;

&lt;p&gt;You know what happens when you pile on more and more virtualization layers? They take you farther and farther away from the bare-metal performance of the hardware. &lt;/p&gt;

&lt;p&gt;And Kubernetes for small apps? That’s like using a bazooka to kill a fly. Sure, I know Docker apps can be deployed in various ways on AWS (not only on top of EC2), but that’s not the point. My small-scope web app doesn’t need any of the "perks" Docker van bring.&lt;/p&gt;

&lt;p&gt;"with Docker, my app can run everywhere"—because it’s no longer tied to OS. But I don’t plan to run my app anywhere except on Debian. I know how my app component's VMs work; I will set them up myself and I will know exactly what’s there. &lt;br&gt;
"Docker provides an isolated environment" Sure, but isolated from what? Separate VMs already provide plenty of isolation.&lt;/p&gt;

&lt;p&gt;As for bundling and isolating software of different components, and managing version conflicts. If it is your primary need for Docker even in small projects...Naughty, naughty - did you give up on pure TypeScript/Python and relied on external libraries a lot? Not my case, by the way.&lt;/p&gt;

&lt;p&gt;Why would one follow the containerization hype just because everyone else is doing it?&lt;/p&gt;

&lt;p&gt;That said, I’m not completely throwing Docker out of my stack. But for me, dockerization is something I’ll consider only when everything else is ready. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Moreover, Docker/Containerization is as same easy as virtualization/Virtual Machines. Different sintaxis, some different concepts, but the logic behind is more or less the same. Docker is easy when it comes to setting up everything in a default way, but if you need something more advanced, most probably you will get very frustrated if you do not know anything about virtualization of hardware and virtual machines. Docker is not a rocket science at all for those who have some experience with Virtual Machines.&lt;/strong&gt;      &lt;/p&gt;

&lt;p&gt;So let's start with virtualization on Debian!&lt;/p&gt;


&lt;h3&gt;
  
  
  ➂ Virtualization on Debian: how it works
&lt;/h3&gt;

&lt;p&gt;Virtualization process is happening under the "instructions" of your PC's &lt;em&gt;physical&lt;/em&gt; CPU, so it is important that your CPU is supporting it. Yes, virtual machines can access (if allowed so) various hardware components of your PC, but it is exactly the CPU that is responsible for isolation of process running on guest VMs from the host (your physical PC). If your CPU supports the virtualization, first, it needs to be enabled on your PC:&lt;/p&gt;
&lt;h4&gt;
  
  
  ➂.➀ CPU virtualization support
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;To know if you have virtualization support enabled, you can check if the relevant flag is enabled with grep. If the following command for your processor returns some text, you already have virtualization support enabled:&lt;br&gt;
For Intel processors you can execute &lt;code&gt;grep vmx /proc/cpuinfo&lt;/code&gt; to check for Intel's Virtual Machine Extensions.&lt;br&gt;
For AMD processors you can execute &lt;code&gt;grep svm /proc/cpuinfo&lt;/code&gt; to check for AMD's Secure Virtual Machine. (&lt;a href="https://www.debian.org/doc/manuals/debian-handbook/sect.virtualization.en.html" rel="noopener noreferrer"&gt;The Debian Administrator's Handbook: Virtualization&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In my case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#this command is counting the number of times 'vmx flags' is mentioned in the output of /proc/cpuinfo. It is equal to 20, meaning that all my CPU cores support virtualization (I have 20 cores totale)
$ egrep -c '(vmx flags)' /proc/cpuinfo
20
#additional command
$ lscpu | grep Virtualization
Virtualization:       VT-x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If in your case the output of command &lt;code&gt;grep vmx /proc/cpuinfo&lt;/code&gt; is empty, but your CPU is quite modern and is supposed to support virtualization, you’ll have to enter the BIOS during boot and enable it there. The steps to follow in BIOS are something like described &lt;a href="https://support.hp.com/in-en/document/ish_5637142-5637191-16" rel="noopener noreferrer"&gt;in this guide&lt;/a&gt;. The interface of your BIOS depends on the brand of your motherboard, so if you are lost you’ll need to check instructions on how to enable virtualization on your PC in web.&lt;/p&gt;

&lt;p&gt;All the CPU cores are ready to virtualize something! Who starts?&lt;/p&gt;

&lt;h4&gt;
  
  
  ➂.➁ Hypervisor
&lt;/h4&gt;

&lt;p&gt;A hypervisor! hypervisor is a bit generic term:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;A hypervisor, also known as a virtual machine monitor (VMM) or virtualizer, is a type of computer software, firmware or hardware that creates and runs virtual machines. A computer on which a hypervisor runs one or more virtual machines is called a host machine, and each virtual machine is called a guest machine. (&lt;a href="https://en.wikipedia.org/wiki/Hypervisor" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are different types of hypervisors. To simplify their perception, they can be divided into two types (left and right images of the scheme below):&lt;/p&gt;

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

&lt;p&gt;The second type of hypervisors might be familiar to you if you’ve ever used VirtualBox. It runs on top on Windows, just like any other app on Windows. On the other hand, Type 1 hypervisors &lt;strong&gt;run directly on bare metal&lt;/strong&gt;. They often have their own OS, specifically tuned for virtualization purposes. And they are often used for enterprise scope.&lt;/p&gt;

&lt;p&gt;I included Proxmox in the category of hypervisors type 2, because it comes as a Debian-based OS. To use it, you’ll need to replace your current desktop Debian with Proxmox OS. &lt;em&gt;By the way, Proxmox is pretty great—easy to use and functionality-rich. I use this hypervisor for work, and I find it awesome&lt;/em&gt;. But it is not fully technically correct that Proxmox is hypervisor of type 1, as it is based on KVM&amp;amp;QEMU.&lt;/p&gt;

&lt;p&gt;Let's get to KVM, that is schematized in the middle on the image above. Is it a hypervisor? Well... yes and no. The term "hypervisor" is generic, so you could call it that. But technically, KVM is a Linux kernel module. You don’t have to build it yourself—it comes shipped with the Linux kernel that is core part of your Debian, just like other kernel modules (for example, drivers).&lt;/p&gt;

&lt;p&gt;In this article, I’ll be using KVM to set up virtualization tools on my PC. A popular alternative to KVM on Debian is Xen. Xen is a &lt;strong&gt;truly&lt;/strong&gt; Type 1 hypervisor, even though it can also run alongside Debian OS for personal use.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Xen is a “paravirtualization” solution. It introduces a thin abstraction layer, called a “hypervisor”, between the hardware and the upper systems; this acts as a referee that controls access to hardware from the virtual machines. (&lt;a href="https://www.debian.org/doc/manuals/debian-handbook/sect.virtualization.en.html" rel="noopener noreferrer"&gt;The Debian Administrator's Handbook: Virtualization&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As Xen runs &lt;em&gt;between the hardware and the upper systems&lt;/em&gt; it qualifies as a Type 1 hypervisor. VMware ESXi is another example of a Type 1 hypervisor.&lt;/p&gt;

&lt;p&gt;I’ll be using a KVM-based virtualization setup instead of Xen—just a personal preference.&lt;/p&gt;




&lt;h3&gt;
  
  
  ➃ KVM &amp;amp; QEMU
&lt;/h3&gt;

&lt;p&gt;But what exactly is KVM, besides being a kernel module?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The Kernel Virtual Machine, or KVM, is a full virtualization solution for Linux on x86 (64-bit included) and ARM hardware containing virtualization extensions (Intel VT or AMD-V). It consists of a loadable kernel module, kvm.ko, which provides the core virtualization infrastructure and a processor specific module, kvm-intel.ko or kvm-amd.ko. (&lt;a href="https://wiki.debian.org/KVM" rel="noopener noreferrer"&gt;Debian Wiki: KVM&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;While KVM is providing most of the infrastructure that can be used by a virtualizer, but it is not a virtualizer by itself. Actual control for the virtualization is handled by a QEMU-based application.&lt;br&gt;
Unlike other virtualization systems, KVM was merged into the Linux kernel right from the start. Its developers chose to take advantage of the processor instruction sets dedicated to virtualization (Intel-VT and AMD-V), which keeps KVM lightweight, elegant and not resource-hungry. The counterpart, of course, is that KVM doesn't work on any computer but only on those with appropriate processors. &lt;br&gt;
Unlike such tools as VirtualBox, KVM itself doesn't include any user-interface for creating and managing virtual machines.(&lt;a href="https://www.debian.org/doc/manuals/debian-handbook/sect.virtualization.en.html" rel="noopener noreferrer"&gt;The Debian Administrator's Handbook: Virtualization&lt;/a&gt;)&lt;/em&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before, I showed how to check if the virtualization is enabled on your PC, the following command will show you if you have KVM kernel module and it can be used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# checking for presence of KVM kernel modules
$ lsmod | grep kvm
kvm_intel             327680  0
kvm                   983040  1 kvm_intel
#Additional check with cpu-checker package:
$ sudo apt install cpu-checker 
$ sudo kvm-ok
INFO: /dev/kvm exists
KVM acceleration can be used
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As the Debian Manual on virtualization stated in the quote above KVM alone goes alongside with qemu for virualization porocesses.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;QEMU (stands for Quick Emulator) is a generic and open source machine emulator and virtualizer.&lt;br&gt;
When used as a machine emulator, QEMU can run OSes and programs made for one machine (e.g. an ARM board) on a different machine (e.g. your own PC). By using dynamic translation, it achieves very good performance.&lt;br&gt;
When used as a virtualizer, QEMU achieves near native performance by executing the guest code directly on the host CPU. QEMU supports virtualization when executing under the Xen hypervisor or using the KVM kernel module in Linux. When using KVM, QEMU can virtualize x86, server and embedded PowerPC, 64-bit POWER, S390, 32-bit and 64-bit ARM, and MIPS guests. (&lt;a href="https://wiki.qemu.org/Main_Page" rel="noopener noreferrer"&gt;QEMU Wiki&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Is it possible to virtualize using only KVM? THEORETICALLY yes, but KVM has neither GUI nor CLI, so one has to write code in C in order to virtualize something, but KVM alone will not emulate virtual CPUs or virtual RAM.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Can you use QEMU without KVM? Yes. QEMU alone can &lt;em&gt;emulate&lt;/em&gt; a full system with its built-in binary translator Tiny Code Generator (TCG), which is purely emulated (= compute-intensive) and overall performance of fully emulated system can be slow. Thus, using QEMU without an accelerator is inefficient and generally best for experimental purposes (e.g if your CPU has an architecture A, but you’re curious about exploring how it all works on CPU architecture B). To “accelerate” the emulated system that run on the same architecture to the host’s one QEMU is using accelerators; and KVM is one of them. However, QEMU can use alternative accelerators like XEN (&lt;a href="https://www.qemu.org/docs/master/system/introduction.html" rel="noopener noreferrer"&gt;QEMU: Virtualisation Accelerators&lt;/a&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;QEMU is not a software package that comes pre-installed on Debian, so you’ll need to install it manually. And here’s where confusion can arise. If you Google around, you’ll most likely find something like this for Debian-based systems &lt;code&gt;sudo apt install qemu-kvm virt-manager bridge-utils&lt;/code&gt;. At first glance, this seems fine— you actually need QEMU to work with KVM. But here’s the tricky part: &lt;code&gt;qemu-kvm&lt;/code&gt; isn’t even a real package. It’s a virtual package, which actually point to something else:&lt;/p&gt;

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

&lt;p&gt;For my case, it’s fine because I plan to have all my guests using this architecture. But if you want to use a different architecture for your guest VMs, &lt;code&gt;qemu-kvm&lt;/code&gt;will bring in redundant package. There are other packages that will install QEMU besides &lt;code&gt;qemu-system-x86,&lt;/code&gt; like &lt;code&gt;qemu-system-arm&lt;/code&gt;, &lt;code&gt;qemu-system-misc&lt;/code&gt;, &lt;code&gt;qemu-system-ppc&lt;/code&gt; and &lt;code&gt;qemu-system&lt;/code&gt;, which will bring you dependencies to virtualize/emulate various architectures with qemu.&lt;/p&gt;

&lt;p&gt;I will install QEMU in this way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt install qemu-system-x86
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To shape your choice a bit, according to QEMU documentation on virtualization with KVM:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;QEMU can make use of KVM when running a target architecture that is the same as the host architecture. For instance, when running &lt;code&gt;qemu-system-x86&lt;/code&gt; on an x86 compatible processor, you can take advantage of the KVM acceleration — giving you benefit for your host and your guest system (&lt;a href="https://wiki.qemu.org/Features/KVM" rel="noopener noreferrer"&gt;QEMU: features KVM&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Technically, if you try to create a guest with a CPU architecture different from your host machine’s CPU, KVM won’t be used, because, remember, QEMU can fully emulate machines (I haven’t tested this myself, though).&lt;/p&gt;

&lt;p&gt;QEMU is installed, KVM is ready to virtualize, so everything is technically set up. However, the QEMU CLI syntax is far from simple and pretty particular. I would prefer to use a syntax which is more familiar to me. And this is where Libvirt will help me.&lt;/p&gt;




&lt;h3&gt;
  
  
  ➄ Libvirt
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Libvirt is collection of software that provides a convenient way to manage virtual machines and other virtualization functionality, such as storage and network interface management.&lt;br&gt;
An primary goal of libvirt is to provide a single way to manage multiple different virtualization providers/hypervisors. No need to learn the hypervisor specific tools! (&lt;a href="https://wiki.libvirt.org/FAQ.html" rel="noopener noreferrer"&gt;Libvirt FAQ&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Libvirt is a bundle of software that includes an API library, a daemon (&lt;code&gt;libvirtd&lt;/code&gt;), and a command line utility (&lt;code&gt;virsh&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Libvirt tools for management of virtual machines are 'virsh', 'virt-manager', and 'virt-install', which are all built around libvirt functionality.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;virt-manager&lt;/code&gt; is a GUI tool for creation and management of VMs entirely through a graphical user interface (GUI) &amp;lt;-- can be a viable option in the beginning.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;virt-install&lt;/code&gt; is a CLI tool that enables the creation and management of VMs via commands and parameters. If created VMs are supposed to have a display and graphical sessions, they can be accessed with &lt;code&gt;virt-viewer&lt;/code&gt; (for a display).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;virsh&lt;/code&gt; is a command-line utility that can do a lot of stuff - staring from very simple tasks like VM creation up to advanced virtualization. virsh works tightly with XML configuration files, that can be used to configure domains, virtual machine specs, networks ecc. Virsh gives an option to connect to existing VMs remotely via SSH &amp;lt;--I will be using this tool.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I install &lt;code&gt;libvirt&lt;/code&gt; with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt install libvirt-daemon-system
$ systemctl status libvirtd
● libvirtd.service - libvirt legacy monolithic daemon
     Loaded: loaded (/usr/lib/systemd/system/libvirtd.service; enabled; preset: enabled)
     Active: active (running) since Fri 2025-01-10 22:39:29 CET; 2min 17s ago
#if not enabled in your case:
# $ sudo systemctl enable libvirtd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this way, I will have &lt;code&gt;libvirt-clients&lt;/code&gt; installed as well, as it is a dependency of this package.&lt;/p&gt;

&lt;p&gt;Everything needed is supposed to be installed for now. First, I want to validate that everything is OK, and then I can proceed with first VM creation.&lt;/p&gt;




&lt;h3&gt;
  
  
  ➅ Validation of installed virtualization tools
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ virt-host-validate
  QEMU: Checking for hardware virtualization  : PASS
  QEMU: Checking if device '/dev/kvm' exists  : PASS
  QEMU: Checking if device '/dev/kvm' is accessible : PASS
  ...
$ virsh version
Compiled against library: libvirt 10.10.0
Using library: libvirt 10.10.0
Using API: QEMU 10.10.0
Running hypervisor: QEMU 9.2.0

#command to check, which guest machines you can emulate with QEMU features you have installed:
$ virsh capabilities
...
  &amp;lt;guest&amp;gt;
    &amp;lt;os_type&amp;gt;hvm&amp;lt;/os_type&amp;gt;
    &amp;lt;arch name='i686'&amp;gt; &amp;lt;---
      &amp;lt;wordsize&amp;gt;32&amp;lt;/wordsize&amp;gt; &amp;lt;---
      &amp;lt;emulator&amp;gt;/usr/bin/qemu-system-i386&amp;lt;/emulator&amp;gt;
      ...
      &amp;lt;domain type='qemu'/&amp;gt; &amp;lt;---
      &amp;lt;domain type='kvm'/&amp;gt;  &amp;lt;---
    &amp;lt;/arch&amp;gt;
  &amp;lt;/guest&amp;gt;

  &amp;lt;guest&amp;gt;
    &amp;lt;os_type&amp;gt;hvm&amp;lt;/os_type&amp;gt;
    &amp;lt;arch name='x86_64'&amp;gt; &amp;lt;---
      &amp;lt;wordsize&amp;gt;64&amp;lt;/wordsize&amp;gt; &amp;lt;---
      &amp;lt;emulator&amp;gt;/usr/bin/qemu-system-x86_64&amp;lt;/emulator&amp;gt;
      ...
      &amp;lt;domain type='qemu'/&amp;gt; &amp;lt;---
      &amp;lt;domain type='kvm'/&amp;gt;  &amp;lt;---
    &amp;lt;/arch&amp;gt;
  &amp;lt;/guest&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ➅.➀ Important! qemu:///system vs qemu:///session
&lt;/h4&gt;

&lt;p&gt;Here is the command I want you to pay attention to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ virsh uri
qemu:///session

$ sudo virsh uri
qemu:///system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, there’s a difference between running &lt;code&gt;virsh&lt;/code&gt; with &lt;code&gt;sudo&lt;/code&gt; or without it. When you run &lt;code&gt;virsh&lt;/code&gt; with &lt;code&gt;sudo&lt;/code&gt;, it connects  to the system &lt;code&gt;libvirtd&lt;/code&gt; service, the one launched by &lt;code&gt;systemd&lt;/code&gt;. &lt;code&gt;libvirtd&lt;/code&gt; is running as root, so has access to all host resources. Daemon config is in &lt;code&gt;/etc/libvirt&lt;/code&gt;, VM logs and other bits are stored in &lt;code&gt;/var/lib/libvirt&lt;/code&gt;. &lt;br&gt;
On the contrary, if you run &lt;code&gt;virsh&lt;/code&gt; without &lt;code&gt;sudo&lt;/code&gt;, it connects to &lt;code&gt;qemu:///session&lt;/code&gt;, that is a session &lt;code&gt;libvirtd&lt;/code&gt; service running as the app user, the daemon is auto-launched if it's not already running. &lt;code&gt;libvirt&lt;/code&gt; and all VMs run as the user. All config and logs and disk images are stored in &lt;code&gt;$HOME&lt;/code&gt; directory of a user. This means each user has their own &lt;code&gt;qemu:///session&lt;/code&gt; VMs, separate from all other users. Details are taken &lt;a href="https://blog.wikichoon.com/2016/01/qemusystem-vs-qemusession.html" rel="noopener noreferrer"&gt;from here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If, for some reason, your output of &lt;code&gt;virsh uri&lt;/code&gt; is empty, you can connect manually. And if you mess up between the session or system, you’ll be informed about it in the output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ virsh
virsh # connect qemu:///system
==== AUTHENTICATING FOR org.libvirt.unix.manage ====
System policy prevents management of local virtualized systems

#The correct way if you want to connect to user-space session:
virsh # connect qemu:///session
#The correct way if you want to connect to system wide session:
$ sudo virsh
virsh # connect qemu:///system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;THIS INFO IS VERY IMPORTANT:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;With qemu:///session, libvirtd and VMs run as your unprivileged user. This integrates better with desktop use cases since permissions aren't an issue, no root password is required, and each user has their own separate pool of VMs.&lt;br&gt;
However because nothing in the chain is privileged, any VM setup tasks that need host admin privileges aren't an option. Unfortunately this includes most general purpose networking options.&lt;br&gt;
The default qemu network mode when running unprivleged is usermode networking (or SLIRP). This is an IP stack implemented in userspace. This has many drawbacks: the VM can not easily be accessed by the outside world, the VM can talk to the outside world but only over a limited number of networking protocols, and it's very slow. (&lt;a href="https://blog.wikichoon.com/2016/01/qemusystem-vs-qemusession.html" rel="noopener noreferrer"&gt;Source&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If this quote does not tell you much, here is the key takeaway: &lt;code&gt;qemu:///session&lt;/code&gt; integrates better &lt;strong&gt;with desktop use cases&lt;/strong&gt;. Any &lt;strong&gt;VM setup&lt;/strong&gt; tasks &lt;strong&gt;that need host admin privileges aren't an option&lt;/strong&gt;. &lt;strong&gt;This means that your VMs in the scope of &lt;code&gt;qemu:///session&lt;/code&gt; will have ONLY general purpose networking options&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  ➆ Creation of first VM
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;NB! For the demonstration purposes, I will be creating first VM in the scope of &lt;code&gt;qemu:///session&lt;/code&gt;. In this way, I will be able to demonstrate the constraints of created VM. Then, I will show you, how to move create VM under &lt;code&gt;qemu:///system&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  ➆.➀ OS image file
&lt;/h4&gt;

&lt;p&gt;To create my first virtual machine, which will, of course, be Debian Stable (Bookworm), I need an &lt;code&gt;.iso&lt;/code&gt; file. I’ll go for the minimal &lt;a href="https://www.debian.org/CD/netinst/" rel="noopener noreferrer"&gt;netinstall image&lt;/a&gt; to keep the system tidy and install later only tools I will need.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cd #to teleport to $HOME directory
$ mkdir -p .local/share/libvirt/images/
$ cd .local/share/libvirt/images/
$ wget https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-12.9.0-amd64-netinst.iso
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  ➆.➁ Preparing storage
&lt;/h4&gt;

&lt;p&gt;Then, I want to specify the storage device I plan to use for all my virtual machines (as they will share portions of it). Since I manage all storage devices on my PC using LVM, the first step is to create a new logical volume using the available free space in my existing logical volume group.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo vgs
  VG            #PV #LV #SN Attr   VSize    VFree
  MY-vg       1   5   0 wz--n- &amp;lt;372.53g &amp;lt;129.02g
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How will my virtual machines use the storage space I plan to create for them? Each machine will have one or more virtual disks, and these virtual disks are essentially "disk images." As everything is a file on Linux OS, these disk images are files. And they can have different formats. The &lt;code&gt;.qcow&lt;/code&gt; format is a file format for disk image files used by QEMU. Its updated version, &lt;code&gt;.qcow2&lt;/code&gt;, offers better optimization to the original &lt;code&gt;.qcow&lt;/code&gt;. I can also create disk images for VMs in the &lt;code&gt;.raw&lt;/code&gt; format. However, &lt;code&gt;.qcow2&lt;/code&gt; is generally more space-efficient and can be snapshot-ed and compressed.&lt;/p&gt;

&lt;p&gt;So the task is the following: I need to create a &lt;code&gt;.qcow2&lt;/code&gt; disk image for my to be created VM. And I want to use available space in my logical volume group.&lt;/p&gt;

&lt;p&gt;There are two options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;I can create a fairly large new logical volume to provide space for multiple virtual machines. In this case, I need to place a file system on top of the new logical volume, mount it, and then create &lt;code&gt;.qcow2&lt;/code&gt; disk images over it. Why? Because a logical volume without a file system is a single, contiguous block of storage. A single &lt;code&gt;.qcow2&lt;/code&gt; can occupy the entire block device, but there’s no mechanism to store multiple files on the same device unless a file system is present.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The second option is to create separate logical volumes sized to the needs of each virtual machine, with each logical volume fully allocated to a single &lt;code&gt;.qcow2&lt;/code&gt; image. By the way, you can also use physical partitions for your VMs, as logical volumes are just my preferred method of managing storage space. However, even if under each &lt;code&gt;.qcow2&lt;/code&gt; image there is its "personal"  logical volume, this doesn’t mean you can expand the size of &lt;code&gt;.qcow2&lt;/code&gt; image by expanding &lt;br&gt;
logical volume. No, not at all. If my VM runs out of space, I’ll need to attach a new "virtual disk", create an additional logical volume, create new &lt;code&gt;.qcow2&lt;/code&gt; image over it.... This quickly becomes a mess.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, I prefer the first option: a single logical volume as a kind of storage pool for all my virtual machines disk images. &lt;/p&gt;

&lt;p&gt;If your understanding of LVM terminology is a bit &lt;em&gt;wobbly&lt;/em&gt; and you still confuse &lt;em&gt;logical volume groups&lt;/em&gt; with &lt;em&gt;logical volumes&lt;/em&gt;, I recommend to read &lt;a href="https://dev.to/dev-charodeyka/debian-12-is-amazing-how-to-create-your-custom-codehouse-part-24-2m32"&gt;this article&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#I create new logical volume with name 'virt-machines' inside of the existing volume group
$ sudo lvcreate -L 100G -n virt-machines MY-vg
# I create filesystem on top of it 
$ sudo mkfs.ext4 /dev/MY-vg/virt-machines
# I create a mounting point for it
$ sudo mkdir -p /mnt/virt-machines
# I mount it
$ sudo mount /dev/MY-vg/virt-machines /mnt/virt-machines
# i add automounting option on boot with by modifying /etc/fstab
$ sudo vim.tiny /etc/fstab
# I add this line 
/dev/mapper/MY--vg-virt--machines /mnt/virt-machines ext4 defaults 0 0
# to validate syntax:
$ sudo mount -a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, I can create a &lt;code&gt;.qcow2&lt;/code&gt; disk image in this directory. Since I plan to create and run VMs in my user space, I’ve given ownership of this directory to my user to avoid any permission issues later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# I create a .qcow2 disk image
$ sudo qemu-img create -f qcow2 /mnt/virt-machines/deb-nginx.qcow2 10G
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have an &lt;code&gt;.iso&lt;/code&gt; file from which the VM will boot (which will be attached as a virtual CDROM), and I have a virtual disk where the new system will be installed. Now, I just need to create a VM and allocate CPU cores and RAM to it. For the first VM creation, I will use &lt;code&gt;virt-install&lt;/code&gt; instead of &lt;code&gt;virsh&lt;/code&gt; to demonstrate the logic, and then proceed with XML configuration explanations. The &lt;code&gt;virt-install&lt;/code&gt; CLI is part of the &lt;code&gt;virtinst&lt;/code&gt; package and is not included in the &lt;code&gt;libvirt-clients&lt;/code&gt; package. It needs to be installed separately.&lt;/p&gt;

&lt;h4&gt;
  
  
  ➆.➂ VM creation with &lt;code&gt;virt-install&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;I will be using the default options of the &lt;code&gt;virt-install&lt;/code&gt; command, with two exceptions: -&lt;code&gt;-graphics none&lt;/code&gt; and &lt;code&gt;--extra-args='console=ttyS0'&lt;/code&gt;. My VMs don’t need any graphical interface as they will not have display servers; I will access them via the console. Debian offers not only a graphical installer but also a terminal user interface (TUI) installer, which will guide through the installation process.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt install virtinst
$ virt-install \
  --connect qemu:///session \
  --name deb-nginx \
  --ram 4096 \
  --vcpus 2 \
  --disk path=/mnt/virt-machines/deb-nginx.qcow2,size=10 \
  --location $HOME/.local/share/libvirt/images/debian-12.8.iso \
  --os-variant debian12 \
  --graphics none \
  --extra-args='console=ttyS0'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you encounter an error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Traceback (most recent call last):
  File "/usr/bin/virt-install", line 6, in &amp;lt;module&amp;gt;
    from virtinst import virtinstall
  File "/usr/share/virt-manager/virtinst/__init__.py", line 8, in &amp;lt;module&amp;gt;
    import gi
ModuleNotFoundError: No module named 'gi'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check if you are currently not in any active Python environment. I use anaconda, so the &lt;code&gt;base&lt;/code&gt; conda environment is always activated. I just deactivate it with &lt;code&gt;conda deactivate&lt;/code&gt; command before executing &lt;code&gt;virt-install&lt;/code&gt; command.&lt;br&gt;
You will see this:&lt;/p&gt;

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

&lt;p&gt;And then installation of Debian (in TUI only) should pop up. &lt;/p&gt;

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

&lt;p&gt;After installation is finished, VM is rebooted, I close active console ad reconnect with &lt;code&gt;virsh&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ virsh --connect qemu:///session console deb-nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Actually, everything is ready, VM is usable, so technically I can try to go into SSH...&lt;/p&gt;




&lt;h3&gt;
  
  
  ➇ Let the Networking begin!
&lt;/h3&gt;

&lt;p&gt;To SSH into the VM, I need to know the private IP address it was assigned (and I believe it was, as I expect some default network to have been configured and the VM joined it during the creation process via QEMU). I will leave the details about how SSH works from a networking perspective for now and will cover it in the next article of this virtualization series.&lt;/p&gt;

&lt;p&gt;To find out IP of created VM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;root@deb-nginx:~# ip a
1: lo: &amp;lt;LOOPBACK,UP,LOWER_UP&amp;gt; mtu qdisc noqueue state UNKNOWN 
....
2: enp1s0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether MA:CA:DD:RE:SS:VM brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic enp1s0
       valid_lft 77570sec preferred_lft 77570sec
    inet6 XXXXXXXXXXXXXX/64 scope site dynamic mngtmpaddr
       valid_lft 86291sec preferred_lft 14291sec
    inet6 XXXXXXXXXXXXXXXX/64 scope link
       valid_lft forever preferred_lft forever
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The network interface is enp1s0, and the IPv4 address is 10.0.2.15. So, let's try ssh!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#from Host machine!
$ ssh 10.0.2.15

Nothing!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ➇.➀ Userspace (SLIRP or passt) connection
&lt;/h3&gt;

&lt;p&gt;However, as I mentioned earlier, &lt;code&gt;qemu:///session&lt;/code&gt; VMs are primarily intended for desktop use, such as trying out a new distro. The network used under &lt;code&gt;qemu:///session&lt;/code&gt; is somewhat primitive and restrictive—it does not allow incoming connections to the VMs and cannot be properly modified. For instance, you cannot configure more sophisticated network settings without &lt;code&gt;sudo&lt;/code&gt; privileges to create network components like bridges, change their states, etc.&lt;/p&gt;

&lt;p&gt;I can check which network is configured for this VM, and in general, I can review the full configuration of created VM. When I used &lt;code&gt;virt-install&lt;/code&gt;, I simply passed some options during the creation process to specify &lt;em&gt;how&lt;/em&gt; I wanted my VM, and those parameters were translated into configuration file. This file is much more detailed and "technical" than the option list I provided when I was creating VM. QEMU thoroughly translated my requirements into technical specifications, allocated the necessary hardware, and configured other components for my VM to work. The configuration format used by &lt;code&gt;virsh&lt;/code&gt; for almost everything is XML.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ virsh dumpxml deb-nginx

&amp;lt;domain type='kvm' id='3'&amp;gt;
  &amp;lt;name&amp;gt;deb-nginx&amp;lt;/name&amp;gt;
  &amp;lt;uuid&amp;gt;0057aa74-392a-4b4b-ac89-8557d7b9312d&amp;lt;/uuid&amp;gt;
  ...
  &amp;lt;memory unit='KiB'&amp;gt;4194304&amp;lt;/memory&amp;gt;
  &amp;lt;currentMemory unit='KiB'&amp;gt;4194304&amp;lt;/currentMemory&amp;gt;
  &amp;lt;vcpu placement='static'&amp;gt;2&amp;lt;/vcpu&amp;gt; &amp;lt;--interesting
  &amp;lt;os&amp;gt;
    &amp;lt;type arch='x86_64' machine='pc-q35-9.2'&amp;gt;hvm&amp;lt;/type&amp;gt;
  &amp;lt;/os&amp;gt;
  &amp;lt;features&amp;gt;
   ...
  &amp;lt;/features&amp;gt;
  &amp;lt;cpu mode='host-passthrough' check='none' migratable='on'/&amp;gt; &amp;lt;--interesting
   ...
  &amp;lt;devices&amp;gt;
    &amp;lt;emulator&amp;gt;/usr/bin/qemu-system-x86_64&amp;lt;/emulator&amp;gt;
    &amp;lt;disk type='file' device='disk'&amp;gt;
      &amp;lt;driver name='qemu' type='qcow2' discard='unmap'/&amp;gt;
      &amp;lt;source file='/mnt/virt-machines/deb-nginx.qcow2' index='2'/&amp;gt;
      &amp;lt;backingStore/&amp;gt;
      &amp;lt;target dev='vda' bus='virtio'/&amp;gt;
      &amp;lt;alias name='virtio-disk0'/&amp;gt;
      &amp;lt;address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/&amp;gt;
    &amp;lt;/disk&amp;gt;
    &amp;lt;disk type='file' device='cdrom'&amp;gt;
      &amp;lt;driver name='qemu'/&amp;gt;
      &amp;lt;target dev='sda' bus='sata'/&amp;gt;
      &amp;lt;readonly/&amp;gt;
      &amp;lt;alias name='sata0-0-0'/&amp;gt;
      &amp;lt;address type='drive' controller='0' bus='0' target='0' unit='0'/&amp;gt;
    &amp;lt;/disk&amp;gt;
    ....
--------------&amp;gt; HERE IT IS, NETWORK INTERFACE &amp;lt;-------------------
    &amp;lt;interface type='user'&amp;gt;
      &amp;lt;mac address='MA:CA:DD:RE:SS:VM'/&amp;gt;
      &amp;lt;model type='virtio'/&amp;gt;
      &amp;lt;alias name='net0'/&amp;gt;
      &amp;lt;address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/&amp;gt;
    &amp;lt;/interface&amp;gt;
    ....
 ---&amp;gt; O! Mouse and keyboard: &amp;lt;---------
    &amp;lt;input type='mouse' bus='ps2'&amp;gt;
      &amp;lt;alias name='input0'/&amp;gt;
    &amp;lt;/input&amp;gt;
    &amp;lt;input type='keyboard' bus='ps2'&amp;gt;
      &amp;lt;alias name='input1'/&amp;gt;
    &amp;lt;/input&amp;gt;
    ....
&amp;lt;/domain&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Created VM has a network interface, only one. But in XML configuration file I see that this interface has type "user" &lt;code&gt;&amp;lt;interface type='user'&amp;gt;&lt;/code&gt;, and normally the type should be "network".&lt;/p&gt;

&lt;p&gt;I can check for existing alternatives (other network interfaces). Under &lt;code&gt;qemu:///session&lt;/code&gt;, as expected, there is nothing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ virsh net-list --all

 Name   State   Autostart   Persistent
----------------------------------------
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, it seems that this userspace network thingy can now be configured quite extensively, because newer versions of &lt;code&gt;libvirt&lt;/code&gt; have introduced more advanced features and options:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Since 9.0.0 an alternate backend implementation of the user interface type can be selected by setting the interface's  subelement type attribute to passt. In this case, the passt transport (&lt;a href="https://passt.top" rel="noopener noreferrer"&gt;https://passt.top&lt;/a&gt;) is used. Similar to SLIRP, passt has an internal DHCP server that provides a requesting guest with one ipv4 and one ipv6 address; it then uses userspace proxies and a separate network namespace to provide outgoing UDP/TCP/ICMP sessions, and optionally redirect incoming traffic destined for the host toward the guest instead.(&lt;a href="https://libvirt.org/formatdomain.html#userspace-slirp-or-passt-connection" rel="noopener noreferrer"&gt;Libvirt: Userspace connection&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;However, configuration of userspace connection is beyond the scope of this article (and the next article on networking as well). For my use case, I don’t actually need port forwarding—I need something different.&lt;/p&gt;

&lt;h4&gt;
  
  
  ➇.➁ NAT forwarding (aka "virtual networks")
&lt;/h4&gt;

&lt;p&gt;However, &lt;code&gt;qemu:///system&lt;/code&gt; has one default interface (NAT):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; $ sudo virsh net-list --all

 Name      State    Autostart   Persistent
--------------------------------------------
 default   active   no          yes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, I just recreate a VM into &lt;code&gt;qemu:///system&lt;/code&gt; scope and this VM will be using by default this 'default' network. &lt;/p&gt;

&lt;p&gt;First, I have to destroy and undefine VM in &lt;code&gt;qemu:///session&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ virsh destroy deb-nginx
$ virsh undefine deb-nginx
#(optionally, recreare disk image)
$ sudo rm /mnt/virt-machines/deb-nginx.qcow2
$ sudo qemu-img create -f qcow2 /mnt/virt-machines/deb-nginx.qcow2 10G
#IMPORTANT! Start dafault network if it is not started yet
$ sudo virsh net-start default

$ sudo virt-install \
  --connect qemu:///system \
  --name test \
  --ram 4096 \
  --vcpus 2 \
  --disk path=/mnt/virt-machines/deb-nginx.qcow2,size=10 \
  --location /var/lib/libvirt/images/debian-12.9.iso \
  --os-variant debian12 --graphics none \
--extra-args='console=ttyS0'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Please note&lt;/strong&gt; where I placed the ISO file. If you place it inside &lt;code&gt;/etc/libvirt/&lt;/code&gt;in some folder, as it might seem like the right place, you could encounter a weird and misleading error, such as:&lt;br&gt;
&lt;code&gt;error: internal error cannot load AppArmor profile 'libvirt-9cb01efc-ed3b-ff8e-4de5-7227d311dd15'.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you put the ISO file somewhere under your &lt;code&gt;$HOME&lt;/code&gt; directory, you might see a warning like this:&lt;br&gt;
WARNING /home/..../debian-12.8.iso may not be accessible by the hypervisor. You will need to grant the 'libvirt-qemu' user search permissions for the following directories: ['/home/...', '/home/....local', '/home/.../.local/share'].&lt;/p&gt;

&lt;p&gt;Both errors are related to the fact that the VM creation process cannot access the ISO file.&lt;/p&gt;

&lt;p&gt;Meanwhile I proceed with new Installation via TUI. I setup LVM on this VM, and I put &lt;code&gt;/var&lt;/code&gt; on separate logical volume, because this VM is meant for NGINX and NGINX can be very talkative in its logs, especially if configured badly. If you do not know how to do it, refer to &lt;a href="https://dev.to/dev-charodeyka/debian-12-is-amazing-how-to-create-your-custom-codehouse-part-24-2m32"&gt;this article&lt;/a&gt;. I also installed SSH server, so I can ssh into this VM from host.&lt;/p&gt;

&lt;p&gt;NB if you have &lt;code&gt;ufw&lt;/code&gt; up! During installation, Debian should auto-configure the network. If it fails and you see this:&lt;/p&gt;

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

&lt;p&gt;...disable &lt;code&gt;ufw&lt;/code&gt; &lt;strong&gt;temporarily&lt;/strong&gt; for the process of installation, then &lt;strong&gt;enable it again afterward&lt;/strong&gt;. It's not the best solution, the best approach is to adjust &lt;code&gt;ufw&lt;/code&gt; rules so it doesn't block DHCP requests and DNS resolution, which &lt;code&gt;libvirt&lt;/code&gt; uses to configure the VM's network through the default NAT setup.&lt;/p&gt;

&lt;p&gt;When Installation is completed, I reopen the console with virsh and login into the freshly created VM. First, lets I check connectivity, disccover the IP address, try to ssh from host:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo virsh console deb-nginx
# ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=112 time=15.5 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=112 time=17.3 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=112 time=16.3 ms
# ip a
1: lo: &amp;lt;LOOPBACK,UP,LOWER_UP&amp;gt;
.....
2: enp1s0: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether XXXXXXXXXXXXXXXX brd ff:ff:ff:ff:ff:ff
    inet 192.168.122.125/24 brd 192.168.122.255 scope global dynamic enp1s0
       valid_lft 3217sec preferred_lft 3217sec
    inet6 XXXXXXXXXXXXXXXX scope link
       valid_lft forever preferred_lft forever
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, the IP is 192.168.122.125.&lt;br&gt;
*&lt;em&gt;NB! If you try to execute &lt;code&gt;ssh 192.168.122.125&lt;/code&gt; from the host, the login will fail because you will automatically be attempting to ssh as &lt;code&gt;root&lt;/code&gt;, and &lt;code&gt;root&lt;/code&gt; login via ssh is disabled by default on Debian.&lt;br&gt;
*&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ssh user@192.168.122.125
-&amp;gt;yes
user@192.168.122.125's password:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, what can I do from this VM from the network standpoint:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I can access the Internet (e.g., run &lt;code&gt;apt update&lt;/code&gt; and &lt;code&gt;apt upgrade&lt;/code&gt;).
&lt;/li&gt;
&lt;li&gt;I can SSH into this VM from the host machine.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What I cannot do:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I have a laptop connected to the same home network as my PC (via WiFi). Can I SSH into this VM from the laptop? No. This is why:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ip a
.....
5. virbr0 ...
  inet 192.168.122.0/24
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;You may have many questions and few answers about network configuration, but this article is already quite long, so I’ll move the networking setups to the second part of this series&lt;/p&gt;

</description>
      <category>debian</category>
      <category>kvm</category>
      <category>virtualmachine</category>
      <category>linux</category>
    </item>
    <item>
      <title>Debian 12 … is amazing! How to: Create your custom codehouse #6 [Giving Voice to Debian: Wireless Audio Devices configuration]</title>
      <dc:creator>aL</dc:creator>
      <pubDate>Mon, 30 Dec 2024 12:34:07 +0000</pubDate>
      <link>https://dev.to/dev-charodeyka/debian-12-is-amazing-how-to-create-your-custom-codehouse-6-giving-voice-to-debian-wireless-32kc</link>
      <guid>https://dev.to/dev-charodeyka/debian-12-is-amazing-how-to-create-your-custom-codehouse-6-giving-voice-to-debian-wireless-32kc</guid>
      <description>&lt;p&gt;In the &lt;a href="https://dev.to/dev-charodeyka/debian-12-is-amazing-how-to-create-your-custom-codehouse-part-4a4-4dfh"&gt;previous article&lt;/a&gt;, I demonstrated all the steps required to set up a custom UI from scratch, including the display server, window manager, status bar and additional useful apps. Additionally, I shared my choices for fundamental apps, such as a terminal emulator and a browser.&lt;/p&gt;

&lt;p&gt;Now, it’s time to complete the setup. What I configured previously was primarily focused on output devices—like the monitor, which handles what you see—and input devices like the keyboard and mouse, which allow interaction with applications. However, input and output audio devices remain untouched (in my case, a Marshall wireless Bluetooth headset with both a microphone for input and speakers for output).&lt;/p&gt;




&lt;p&gt;My input/output audio device is only one—a Marshall IV headset (which I use mostly in wireless Bluetooth mode, but it also has a wired option). I don’t have separate speakers or a microphone. So, the first thing I need to ensure is that my Bluetooth dongle is working properly. I have written a detailed &lt;a href="https://dev.to/dev-charodeyka/why-is-it-when-something-happens-it-is-always-you-two-troubleshooting-bluetooth-and-wi-fi-2ofn"&gt;article dedicated to troubleshooting Wi-Fi and Bluetooth devices&lt;/a&gt;, so if your Bluetooth device gives you any troubles, please refer to that article.&lt;/p&gt;

&lt;p&gt;My Bluetooth dongle works perfectly on Debian—truly plug and play. There’s no need for configuration, installation of drivers/firmware, or any tweaks; it just works. This Bluetooth dongle is from the brand Edimax, purchased on Amazon for around $10 (Edimax BT-8500). It supports the Bluetooth 5.0 protocol.&lt;/p&gt;

&lt;p&gt;Therefore, my Debian system is ready to be configured to use my headset seamlessly for audio playback and, if needed, microphone input.&lt;/p&gt;




&lt;p&gt;Here’s the road map for this article:&lt;/p&gt;

&lt;p&gt;➀ Sound on your PC: where it starts (hardware-side)?&lt;/p&gt;

&lt;p&gt;➁ Sound on your PC: where it starts (software-side)?&lt;/p&gt;

&lt;p&gt;➂ ALSA, PipeWire or Pulseaudio: what is better*? (*trick question)&lt;/p&gt;

&lt;p&gt;➃ Bluetooth devices management tool&lt;/p&gt;

&lt;p&gt;➄ PipeWire vs Pulseaudio&lt;/p&gt;

&lt;p&gt;➅ PipeWire: Installation&lt;/p&gt;

&lt;p&gt;➆ About XDG Desktop Portal&lt;/p&gt;

&lt;p&gt;➇ Pipewire audio profiles: Understanding the difference between device profile headset-head-unit and a2dp&lt;/p&gt;




&lt;h3&gt;
  
  
  ➀ Sound on your PC: where it starts?
&lt;/h3&gt;

&lt;p&gt;Let’s start by understanding what is the crucial component of your PC which determines whether you can enjoy any audio or not. It’s not just about having speakers/microphone, a headset, or anything else that can actually play/register sound. Your PC needs a hardware piece capable of processing audio at a very low level. That job is done by sound cards. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;A sound card is a computer expansion card that can input and output sound under program control. (&lt;a href="https://wiki.debian.org/SoundCard" rel="noopener noreferrer"&gt;Source&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you have a laptop or a PC—or if you built a PC manually and you are worried that you might have forgotten some piece—there’s nothing to worry about in 99% of cases; your machine has a sound card. Modern machines typically have on-board sound cards integrated into the motherboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxz22mymihs0h3cxqpwz3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxz22mymihs0h3cxqpwz3.png" alt=" " width="800" height="683"&gt;&lt;/a&gt;&lt;/p&gt;
Zoomed area of sound card on motherboard with 3 audio jacks



&lt;p&gt;Depending on your motherboard’s specifications, the quality of the integrated sound card will vary, and that directly affects the quality of the sound your PC can produce or record. Some people who work with professional audio may find the capacities of integrated sound cards unsatisfactory, so they might opt for an additional sound card that can be attached via PCIe or USB (not all motherboards have PCIe slots for sound cards, though). However, for everyday use, an integrated sound card is usually very sufficient.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ lspci -d ::0403 #::0403 here means Audio device subclass
00:1f.3 Audio device: Intel Corporation Raptor Lake High Definition Audio Controller (rev 11)
01:00.1 Audio device: NVIDIA Corporation GA106 High Definition Audio Controller (rev a1)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first audio device is my motherboard’s integrated sound card. But what about NVIDIA Corporation High Definition Audio Controller? Why is it there? Can I use it?&lt;/p&gt;

&lt;p&gt;Nvidia High Definition Audio allows NVIDIA GPU to transmit audio to any display (monitor or TV) with built-in speakers, provided it has an audio-capable connector (most commonly HDMI or DisplayPort). By using Nvidia HD Audio, you can enjoy various uncompressed and lossless audio formats, which can significantly enhance your overall listening experience—especially if you’re using modern displays with integrated speakers.&lt;/p&gt;

&lt;p&gt;However, my monitor is pretty simple and it does not have any audio device. So, I will be using on-board soundcard.&lt;/p&gt;




&lt;h3&gt;
  
  
  ➁ Sound on your PC: where it starts (software-side)?
&lt;/h3&gt;

&lt;p&gt;As with any hardware device, a sound card needs firmware and drivers to function. On Debian, you usually don’t have to worry about these, except in cases where something is messed up with the kernel modules—this can often happen if Debian is running on a virtual machine. Yes, like most drivers, sound card drivers are part of Linux kernel, so it’s the Linux kernel that provides them to your Debian.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ lspci -k -d ::0403
00:1f.3 Audio device: Intel Corporation Raptor Lake High Definition Audio Controller (rev 11)
    DeviceName: Intel HD Audio
    Subsystem: ASUSTeK Computer Inc. Raptor Lake High Definition Audio Controller
    Kernel driver in use: snd_hda_intel
    Kernel modules: snd_hda_intel, snd_soc_avs, snd_sof_pci_intel_tgl
01:00.1 Audio device: NVIDIA Corporation GA106 High Definition Audio Controller (rev a1)
    Subsystem: ASUSTeK Computer Inc. GA106 High Definition Audio Controller
    Kernel driver in use: snd_hda_intel
    Kernel modules: snd_hda_intel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything is alright with sound card's driver in my case - &lt;code&gt;snd_hda_intel&lt;/code&gt; driver is present and in use.&lt;/p&gt;

&lt;h3&gt;
  
  
  ➂ ALSA, PipeWire or Pulseaudio: what is better*? (trick question)
&lt;/h3&gt;

&lt;p&gt;I have a sound card, I have drivers and firmware, and &lt;strong&gt;I have an audio output device—my headset connected via cable&lt;/strong&gt; (let’s leave aside the Bluetooth management part for now).&lt;/p&gt;

&lt;p&gt;So, I go to YouTube to listen to my favourite music band, &lt;a href="https://www.youtube.com/watch?v=4S9ZMmctmMA&amp;amp;list=OLAK5uy_mShYKzbbtk8KHC9apyQTS43XrMR4zcD7A&amp;amp;index=6" rel="noopener noreferrer"&gt;Public Memory&lt;/a&gt;...&lt;/p&gt;

&lt;p&gt;Well, I can’t hear anything (I can’t provide a screenshot as proof, though XD). So, what am I missing?&lt;/p&gt;

&lt;p&gt;If you’ve ever Googled/troubleshooted about audio on Linux, most probably you’ve encountered the three names ALSA, PulseAudio, and PipeWire, regardless of your distro. Maybe, these names can be confusing. Are they alternatives to each other, do you need all of them, or which one is better? That’s why I gave this section its title. I want to answer these questions by exploring why I can’t listen to Public Memory - which software I am missing? &lt;/p&gt;

&lt;h3&gt;
  
  
  ALSA (Advanced Linux Sound Architecture)
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;_ Advanced Linux Sound Architecture (ALSA) is a software framework and part of the Linux kernel that provides an application programming interface (API) for sound card device drivers.&lt;br&gt;
Put simply, ALSA can be divided into two components: The kernel API that provides access to your sound card for higher-level sound servers and applications, and a userspace library that provides more general functions (like effects, mixing, routing, etc.)&lt;br&gt;
There is no way to "replace" ALSA, with regards to the kernel API. Previously, there was also OSS (Open Sound System), but that's been deprecated for nearly 20 years. The same is not true of ALSA's userspace library, which can be replaced. (&lt;a href="https://wiki.debian.org/ALSA" rel="noopener noreferrer"&gt;Debian Wiki: ALSA&lt;/a&gt;)_&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here we are! The first answer is: &lt;strong&gt;ALSA is a must-have&lt;/strong&gt;. No ALSA = no audio. Moreover, you already have it on Debian, because it is the part of the Linux kernel, the same as sound card drivers. So, since I have the Linux kernel, I should already have ALSA installed.&lt;/p&gt;

&lt;p&gt;Indeed, I have it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# I check for presense of package that contains the ALSA userspace library and its standard plugins, as well as the required configuration files:
$ dpkg -l | grep libasound2
ii  libasound2:amd64                     1.2.8-1+b1                           amd64        shared library for ALSA applications
ii  libasound2-data                      1.2.8-1                              all          Configuration files and profiles for ALSA drivers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why I cannot listen to Public Memory, then?? T-T. &lt;/p&gt;

&lt;p&gt;Well, if you haven’t been reading this series of articles from the first part, you might not know that when I installed my Debian, I used a super minimal installation: Debian Stable with only the standard system utilities. This setup is usually perfect for server environments, and the last thing you’d typically do on a server is play music. So, I do have ALSA, I have the drivers, and everything is ready for me, but it needs to be initialised. By default, it’s not initialised because Debian (if you choose this option during installation) doesn’t run/initialise anything you didn’t explicitly request (and I love it). &lt;/p&gt;

&lt;p&gt;The initialization is quite simple: &lt;code&gt;sudo alsactl init&lt;/code&gt;. However, I cannot use it because this command is part of the &lt;a href="https://packages.debian.org/bookworm/alsa-utils" rel="noopener noreferrer"&gt;alsa-utils package&lt;/a&gt;, which is not installed by default on my setup and is not included in the standard system utilities. Therefore, I need to install it first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt install alsa-utils
$ sudo alsactl init
Found hardware: "HDA-Intel" "Realtek ALC897" "HDA:10ec0897,104387fb,00100402" "0x1043" "0x87fb"
Hardware is initialized using a generic method
Found hardware: "HDA-Intel" "Nvidia GPU 9f HDMI/DP" "HDA:10de009f,1043881d,00100100" "0x1043" "0x881d"
Hardware is initialized using a generic method
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aaaannd magic! Public Memory divine music is in my ears!&lt;/p&gt;

&lt;p&gt;Is ALSA restrictive in some way? Does it work only for one audio stream at a time? &lt;/p&gt;

&lt;p&gt;For development purposes, I have multiple browsers installed, so I opened another Public Memory song in a different browser and played it simultaneously. It works—I hear the overlaying audio of both songs.&lt;/p&gt;

&lt;p&gt;I think it’s time to set up Bluetooth management tools and try listening to songs using my wireless headset.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Regarding the question posed in the section title, I haven’t fully explored it yet. For now, it’s clear that ALSA is the key to audio input/output working on Debian, and it’s functioning well for little tests I performed. I’ll leave advanced tests—like using the microphone, testing apps for calls, etc.—for later, as my priority now is setting up Bluetooth to use my headset wirelessly.&lt;/p&gt;




&lt;h3&gt;
  
  
  ➃ Bluetooth devices management tool
&lt;/h3&gt;

&lt;p&gt;I have a Bluetooth adapter (dongle Edimax BT-8500) and a Bluetooth device (Marshall IV headset). &lt;br&gt;
Basically, I just need to install the &lt;a href="https://packages.debian.org/bookworm/bluetooth" rel="noopener noreferrer"&gt;&lt;code&gt;bluetooth&lt;/code&gt; metapackage&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Package: bluetooth (5.66-1+deb12u2)&lt;br&gt;
This package provides all of the different plugins supported by the Bluez bluetooth stack&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt install bluetooth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I won't be using any GUI for now; I'll use command-line tool &lt;code&gt;bluetoothctl&lt;/code&gt; instead. First, I'll check if the bluetooth service is running.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ systemctl status bluetooth
# if it is not running
$ sudo systemctl start bluetooth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, I run &lt;code&gt;bluetoothctl&lt;/code&gt; and enter its CLI&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ bluetoothctl
[bluetooth]# power on
# i set the agent to handle pairing and set it to default mode:
[bluetooth]# agent on
[bluetooth]# default-agent
# start scanning devices
[bluetooth]# scan on
[NEW] Device XX:XX:XX:XX:XX:XX MAJOR IV
[bluetooth]# scan off
#numbers you see is unique MAC address of your audio device
# once I have the MAC address of my Marshall headset, I can initiate pairing:
# I PUT INTO PAIRING MODE MY MARSHALL HEADSET
[bluetooth]# pair XX:XX:XX:XX:XX:XX
Attempting to pair with 1C:6E:4C:84:CF:A7
[CHG] Device XX:XX:XX:XX:XX:XX Connected: yes
[CHG] Device XX:XX:XX:XX:XX:XX Bonded: yes
...
[CHG] Device XX:XX:XX:XX:XX:XX ServicesResolved: yes
[CHG] Device XX:XX:XX:XX:XX:XX Paired: yes
Pairing successful
[CHG] Device XX:XX:XX:XX:XX:XX ServicesResolved: no
[CHG] Device XX:XX:XX:XX:XX:XX Connected: no
[bluetooth]# scan off
##Hmm, pairing is successful, but connection is no...
[bluetooth]# connect XX:XX:XX:XX:XX:XX
Attempting to connect to XX:XX:XX:XX:XX:XX
Failed to connect: org.bluez.Error.Failed br-connection-profile-unavailable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The error isn’t very self-explanatory. Something seems off. &lt;a href="https://github.com/bluez/bluez/blob/bb12ef4a9f71550ba84033f565a27773d893d8bf/doc/errors.txt#L26-L30" rel="noopener noreferrer"&gt;Bluez's corresponding error description&lt;/a&gt; still not entirely clear:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Failed to find connectable services or the target service.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;However, nothing to worry, this isn’t really about troubleshooting or debugging. I just wanted to gently point you back to the unanswered question in the previous section - is having only ALSA enough?&lt;/p&gt;

&lt;p&gt;First off, there are two guides for Debian on Bluetooth usage: one for &lt;a href="https://wiki.debian.org/BluetoothUser" rel="noopener noreferrer"&gt;non-audio Bluetooth devices&lt;/a&gt;, and &lt;a href="https://wiki.debian.org/BluetoothUser/a2dp" rel="noopener noreferrer"&gt;another specifically for Bluetooth audio devices&lt;/a&gt;. The second one, the right one for my case, explicitly states:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Pre-configuration&lt;br&gt;
In short: To connect to a given device, you need Bluetooth hardware on your PC (either built-in, or in the form of a USB dongle), the Bluez daemon, and a compatible audio server (either PulseAudio or PipeWire). Alternatively Bluetooth ALSA available since Debian 12 bookworm allows to avoid running of a high-level sound server. (&lt;a href="https://wiki.debian.org/BluetoothUser/a2dp" rel="noopener noreferrer"&gt;Debian Wiki: Bluetooth Audio&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, there are two options here:&lt;/p&gt;

&lt;p&gt;You stick with ALSA only, using an additional package &lt;a href="https://wiki.debian.org/Bluetooth/Alsa" rel="noopener noreferrer"&gt;Bluetooth ALSA&lt;/a&gt;. In this case, you don’t need PulseAudio or PipeWire for Bluetooth audio devices to work.&lt;br&gt;
Both PulseAudio and PipeWire, as mentioned above, are audio servers.&lt;/p&gt;

&lt;p&gt;However, it may still be unclear why I can't simply use ALSA if it worked for wired music playback, and what's missing from my system. The answer is that, without additional software, the Bluetooth codec used by my Marshall headset isn't supported.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;A codec determines how Bluetooth transmits from the source device to your headphones. It encodes and decodes digital audio data into a specific format. In an ideal world, a high-fidelity signal would be possible at the minimum specified bit rate, resulting in the least amount of space and bandwidth required for storage and transmission. Lower bitrates actually mean better compression but often mean worse sound quality, a high bitrate usually means better sound quality and worse compression. (&lt;a href="https://www.soundguys.com/understanding-bluetooth-codecs-15352/" rel="noopener noreferrer"&gt;Source&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are more advanced codecs and less advanced ones. However, the use of codecs is somewhat bilateral. I mean, the choice I mentioned above—that you can go for just an additional package like Bluetooth ALSA or choose one of the sound servers—can be partially guided by the support of audio codecs out of the box. In this case, the winner is PipeWire. However, if your audio input/output doesn't support advanced codecs, there's no way to utilize them. &lt;br&gt;
Interestingly, despite using Bluetooth 5.0, the my Marshall Major IV supports only the default SBC audio codec. This codec is adequate and the connection is stable, however it’s not ideal (&lt;a href="https://zmarshall.zendesk.com/hc/en-us/articles/22570444942481-Major-IV-Specifications" rel="noopener noreferrer"&gt;source&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;So, actually for my setup just additional package for bluettohb audio devices support for ALSA is viable version:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The &lt;code&gt;bluez-alsa-utils&lt;/code&gt;package supports the LDAC and SBC codecs. AAC support is not compiled into the official package, because the required library is only available in the non-free repository.&lt;br&gt;
This will also automatically configure a "bluez-alsa" systemd service, and install the proper ALSA configuration files to glue everything together. No further steps should be necessary. If you run into issues after installation though, a reboot might help. (&lt;a href="https://wiki.debian.org/Bluetooth/Alsa" rel="noopener noreferrer"&gt;Source&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;However, I would prefer to use a sound server on my setup.&lt;/p&gt;


&lt;h3&gt;
  
  
  ➄ PipeWire vs Pulseaudio
&lt;/h3&gt;

&lt;p&gt;Before comparing these two audio servers, it would be helpful to first understand what they do and why they’re needed except for the Bluetooth connection of audio devices.&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://dev.to/dev-charodeyka/debian-12-is-amazing-how-to-create-your-custom-codehouse-part-4a4-4dfh"&gt;previous article&lt;/a&gt; of this series, I installed and configured a display server as the first step in building my custom UI. The reason for this is that all graphical applications "live" on the display server, which handles all inputs and outputs. Moreover, there is an inter-process communication, the desktop bus, used by applications to communicate with each other, creating a kind of "graphical ecosystem".&lt;/p&gt;

&lt;p&gt;A similar parallel can be drawn for sound servers. While ALSA is sufficient for many use cases, you might need a sound server for more advanced and dynamic audio usage. This includes not just playing music but also audio and video streaming, sharing screens with audio during calls, and leveraging advanced audio technologies and codecs. For most of these scenarios, a sound server becomes essential.&lt;/p&gt;

&lt;p&gt;The difference between PulseAudio and PipeWire is somewhat analogous to the difference between the X11 display protocol and Wayland. The latter is a much newer technology, designed and developed to replace PulseAudio while addressing all of its shortcomings.&lt;/p&gt;

&lt;p&gt;There’s nothing inherently wrong with PulseAudio—it’s neither abandoned nor outdated. However, PipeWire is more modern and, in some cases, offers out-of-the-box support for advanced audio devices without the need for tweaks or extra configuration. This isn’t relevant in my case since, as I mentioned, my Marshall Major IV headset only supports the very basic SBC Bluetooth codec. Nonetheless, I’ve decided to go with PipeWire.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;PipeWire is a server and API for handling multimedia on Linux. Its most common use is for Wayland and Flatpak applications to implement screensharing, remote desktop, and other forms of audio and video routing between different pieces of software. Per the official FAQ, "you can think of it as a multimedia routing layer on top of the drivers that applications and libraries can use."&lt;br&gt;
As opposed to PulseAudio's focus on consumer audio and JACK's focus on professional audio, PipeWire aims to work for all users at all levels. Among other techniques, PipeWire achieves this with its ability to dynamically switch between different buffer sizes, for adapting to the different latency requirements of different audio applications.&lt;br&gt;
In Debian 12, PipeWire 0.3.65 is available, and is considerably more reliable, and is a comfortable drop-in replacement for many use-cases. PipeWire is the default sound server with GNOME Desktop. (&lt;a href="https://wiki.debian.org/PipeWire" rel="noopener noreferrer"&gt;Debian Wiki: PipeWire&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h3&gt;
  
  
  ➅ PipeWire: Installation
&lt;/h3&gt;

&lt;p&gt;In the &lt;a href="https://dev.to/dev-charodeyka/debian-12-is-amazing-how-to-create-your-custom-codehouse-part-4a4-4dfh"&gt;previous article&lt;/a&gt;, while configuring the BSPWM window manager and display server, it was necessary to set up D-Bus for the X session. This is because, when initiating the graphical user interface with &lt;code&gt;startx&lt;/code&gt; from the console, an X session managed by X11 is started, and it exists with BSPWM which executes all configurations specified in its setup.&lt;/p&gt;

&lt;p&gt;Similarly, PipeWire requires a session manager to function effectively.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Two session managers are now available. The one pulled in by default (wireplumber) is the one recommended by pipewire's developers. The other one (pipewire-media-session) is primitive, and is best when using PipeWire just for its basic functionality like screensharing. When using PipeWire as your system's sound server, the maintainer recommends installing the more advanced WirePlumber instead. This command will install WirePlumber while removing the old session manager:_&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, I just need to install WirePlumber, and as a dependency, it will bring PipeWire to my system. There is this recommendation from Debian developers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;It is recommended to install the metapackage &lt;code&gt;pipewire-audio&lt;/code&gt; which depends on &lt;code&gt;wireplumber&lt;/code&gt; (the recommended session manager), &lt;code&gt;pipewire-pulse&lt;/code&gt; (to replace PulseAudio), &lt;code&gt;pipewire-alsa&lt;/code&gt; (ALSA) and &lt;code&gt;libspa-0.2-bluetooth&lt;/code&gt; (for Bluetooth support). Moreover, installing this metapackage will remove &lt;code&gt;pulseaudio&lt;/code&gt;to prevent any conflicts between both sound server. (&lt;a href="https://wiki.debian.org/PipeWire#Bluetooth-1" rel="noopener noreferrer"&gt;Source&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;*&lt;em&gt;NB! The Debian 12 Bookworm stable repository has quite outdated versions of the PipeWire sound server (v. 0.3.65) and its dependencies. Since PipeWire is young and evolving quickly, it may be important to have a more up-to-date version installed. So, the option to install it from Bookworm backports may be viable (v. 1.2.5), and later on, I will show you how. *&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;However, even if you decide to go for it, make sure to read the text  below and check the logs, because otherwise, you might not understand the &lt;code&gt;XDG Desktop Portal&lt;/code&gt;. A version of WirePlumber and PipeWire from Bookworm backports doesn't seem to complain about it, but trust me, you'll most likely need the &lt;code&gt;XDG Desktop Portal&lt;/code&gt;."&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt install pipewire-audio
$ systemctl --user start wireplumber
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, I’ll check if everything is okay with wireplumber service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ systemctl --user status  wireplumber
● wireplumber.service - Multimedia Service Session Manager
     Loaded: loaded (/usr/lib/systemd/user/wireplumber.service; enabled; preset: enabled)
     Active: active (running) since Mon 2024-12-30 13:04:27 CET; 13s ago
....

Dec 30 13:04:27 wonderland systemd[1017]: Started wireplumber.service - Multimedia Service Session Manager.
Dec 30 13:04:27 wonderland wireplumber[6190]: Can't find org.freedesktop.portal.Desktop. Is xdg-desktop-portal running?
Dec 30 13:04:27 wonderland wireplumber[6190]: found session bus but no portal
Dec 30 13:04:27 wonderland wireplumber[6190]: Failed to set scheduler settings: Operation not permitted
Dec 30 13:04:27 wonderland wireplumber[6190]: SPA handle 'api.libcamera.enum.manager' could not be loaded; is it installed?
Dec 30 13:04:27 wonderland wireplumber[6190]: PipeWire's libcamera SPA missing or broken. libcamera not supported.
Dec 30 13:04:28 wonderland wireplumber[6190]: Trying to use legacy bluez5 API for LE Audio - only A2DP will be supported. Please upgrade bluez5.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wireplumber is running, but there are a couple of things that aren't okay. Let's go through them one by one with the errors.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;wireplumber: Failed to set scheduler settings: Operation not permitted&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is definitely something about permissions. It might be related to the fact that PipeWire version available on Debian Bookworm is low, so the permission issue is most likely due to the absence of a specific user group (pipewire) created with the necessary permissions when PipeWire is installed. The default group is used, but PipeWire needs more. Personally, I would leave it as is and only debug if something really doesn't work.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;PipeWire's libcamera SPA missing or broken. libcamera not supported.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is just about a PipeWire plugin, and depending on whether you need it or not, you can install it. I don't need it, so I’ll leave PipeWire to complain about the missing plugin. &lt;a href="https://packages.debian.org/bookworm/mips64el/pipewire-libcamera" rel="noopener noreferrer"&gt;Here is&lt;/a&gt; the package for this plugin.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Trying to use legacy bluez5 API for LE Audio - only A2DP will be supported. Please upgrade bluez5&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
Well, here I am completely okay with it since I don't have devices that use legacy codecs. A2DP is exactly what I need:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Advanced Audio Distribution Profile (A2DP)&lt;br&gt;
A standard for how Bluetooth devices that can stream high-quality audio to remote devices. This is most commonly used for linking wireless headphones and speakers to your PC. (&lt;a href="https://wiki.debian.org/BluetoothUser/a2dp" rel="noopener noreferrer"&gt;Source&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What’s left is quite interesting, and it’s something you should pay attention to:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Can't find org.freedesktop.portal.Desktop. Is xdg-desktop-portal running?&lt;/code&gt;&lt;br&gt;
&lt;code&gt;found session bus but no portal&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pipewire&lt;/code&gt; service reports the same issue in slightly different manner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ systemctl --user  status pipewire
● pipewire.service - PipeWire Multimedia Service
     Loaded: loaded (/usr/lib/systemd/user/pipewire.service; enabled; preset: enabled)
     Active: active (running) since Mon 2024-12-30 13:04:27 CET; 12min ago
TriggeredBy: ● pipewire.socket
....

Dec 30 13:04:27 wonderland systemd[1017]: Started pipewire.service - PipeWire Multimedia Service.
Dec 30 13:04:27 wonderland pipewire[6189]: mod.rt: Can't find org.freedesktop.portal.Desktop. Is xdg-desktop-portal running?
Dec 30 13:04:27 wonderland pipewire[6189]: mod.rt: found session bus but no portal
alisa@wonderland:~$
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, DBus User Message Bus is running correctly (I dedicated a very detailed attention to configuring it &lt;a href="https://dev.to/dev-charodeyka/debian-12-is-amazing-how-to-create-your-custom-codehouse-part-4a4-4dfh"&gt;in the previous article&lt;/a&gt;). But something related to the desktop is missing...&lt;/p&gt;

&lt;p&gt;What even is this?&lt;/p&gt;




&lt;h3&gt;
  
  
  ➆ About XDG Desktop Portal
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;XDG Desktop Portal&lt;br&gt;
A portal frontend service for Flatpak and other desktop containment frameworks.&lt;br&gt;
xdg-desktop-portal works by exposing a series of D-Bus interfaces known as portals under a well-known name (org.freedesktop.portal.Desktop) and object path (/org/freedesktop/portal/desktop). (&lt;a href="https://flatpak.github.io/xdg-desktop-portal/" rel="noopener noreferrer"&gt;Source&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Mostly, it’s needed for containerized software like Flatpaks, as the quote above says. However, it’s not only for this, and even if you don’t use Flatpak, you could still run into problems. A very trivial case could be with a browser like Mozilla or any other when you try to upload something, and the file chooser dialog is needed. It won’t work without the portal.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Portals were designed for use with applications sandboxed through Flatpak, but any application can use portals to provide uniform access to features independent of desktops and toolkits. This is commonly used, for example, to allow screen sharing on Wayland via PipeWire, or to use file open and save dialogs on Firefox that use the same toolkit as your current desktop environment. (&lt;a href="https://wiki.archlinux.org/title/XDG_Desktop_Portal" rel="noopener noreferrer"&gt;Arch Wiki: XDG Desktop Portal&lt;/a&gt;)&lt;/em&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;XDG Desktop Portal can be installed with &lt;code&gt;sudo apt install xdg-desktop-portal&lt;/code&gt; on Debian. HOWEVER, THE COMMON MISTAKE IS TO INSTALL ONLY THIS PACKAGE. XDG Desktop Portal needs at least one backend (can have also more). There are different backends available on Debian Bookworm:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;xdg-desktop-portal-gnome&lt;/li&gt;
&lt;li&gt;xdg-desktop-portal-gtk&lt;/li&gt;
&lt;li&gt;xdg-desktop-portal-kde&lt;/li&gt;
&lt;li&gt;xdg-desktop-portal-wlr&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I will proceed with installation of xdg-desktop-portal first.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Package: xdg-desktop-portal &lt;br&gt;
desktop integration portal for Flatpak and Snap&lt;br&gt;
xdg-desktop-portal provides a portal frontend service for Flatpak, Snap, and possibly other desktop containment/sandboxing frameworks. This service is made available to the sandboxed application, and provides mediated D-Bus interfaces for file access, URI opening, printing and similar desktop integration features. (&lt;a href="https://packages.debian.org/bookworm/xdg-desktop-portal" rel="noopener noreferrer"&gt;Source&lt;/a&gt;)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt install xdg-desktop-portal
$ systemctl --user start xdg-desktop-portal
$ systemctl --user status xdg-desktop-portal
● xdg-desktop-portal.service - Portal service
     Loaded: loaded (/usr/lib/systemd/user/xdg-desktop-portal.service; static)
     Active: active (running) since Mon 2024-12-30 13:29:28 CET; 1s ago
 ....

Dec 30 13:29:28 wonderland systemd[1017]: Starting xdg-desktop-portal.service - Portal service...
Dec 30 13:29:28 wonderland xdg-desktop-por[7132]: No skeleton to export
Dec 30 13:29:28 wonderland systemd[1017]: Started xdg-desktop-portal.service - Portal service.

$ systemctl --user  restart wireplumber

$ systemctl --user  status wireplumber
● wireplumber.service - Multimedia Service Session Manager
     Loaded: loaded (/usr/lib/systemd/user/wireplumber.service; enabled; preset: enabled)
     Active: active (running) since Mon 2024-12-30 13:29:46 CET; 7s ago
   ...

Dec 30 13:29:46 wonderland systemd[1017]: Started wireplumber.service - Multimedia Service Session Manager.
Dec 30 13:29:46 wonderland wireplumber[7157]: Failed to set scheduler settings: Operation not permitted
Dec 30 13:29:46 wonderland wireplumber[7157]: SPA handle 'api.libcamera.enum.manager' could not be loaded; is it installed?
Dec 30 13:29:46 wonderland wireplumber[7157]: PipeWire's libcamera SPA missing or broken. libcamera not supported.
Dec 30 13:29:47 wonderland wireplumber[7157]: Trying to use legacy bluez5 API for LE Audio - only A2DP will be supported. Please upgrade bluez5.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;WirePlumber just wanted the XDG Desktop Portal available, without the specific backend. So, after installing the XDG Desktop Portal and starting it as a service, the errors in the WirePlumber and PipeWire services related to the XDG portal disappeared (after restarting both of them).&lt;/p&gt;

&lt;p&gt;However, the XDG Desktop Portal service itself is reporting that no backend was found: &lt;code&gt;xdg-desktop-portal[7132]: No skeleton to export.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I already mentioned that even if you don’t use Flatpaks, you can still encounter problems with other apps, like browsers. The backend is easy to choose when you have the default DE: if it’s GNOME, then &lt;code&gt;xdg-desktop-portal-gnome&lt;/code&gt;; if it’s KDE, then &lt;code&gt;xdg-desktop-portal-kde&lt;/code&gt;. These are usually installed automatically if you have installed one of default DEs. I would install the GTK backend (&lt;code&gt;xdg-desktop-portal-gtk&lt;/code&gt;). Why? For example, Firefox depends on GTK, and I also use GIMP, which depends on GTK. In general, GTK will be completely enough for me. &lt;strong&gt;But please note, some Flatpaks depend on other services that require a specific backend, like GNOME for XDG Desktop Portal. If a Flatpak doesn’t work with GTK, you’ll have to check the logs to figure out what it needs and install it if necessary, possibly another backend.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt install xdg-desktop-portal-gtk

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NB! You need to understand this sequence in order to decide how and when to launch the aforementioned services (&lt;code&gt;wireplumber&lt;/code&gt; and &lt;code&gt;xdg-dektop-portal&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;wireplumber&lt;/code&gt; service manages the sound server &lt;code&gt;pipewire&lt;/code&gt; sessions.&lt;br&gt;
If you enable it with &lt;code&gt;systemctl --user enable wireplumber&lt;/code&gt;, it will start automatically on boot.&lt;br&gt;
However, you cannot enable &lt;code&gt;xdg-desktop-portal&lt;/code&gt; service  in the same way! Moreover, even if you can do it manually, keep in mind that it will result in an error without the &lt;code&gt;DISPLAY&lt;/code&gt; environment variable, just like &lt;code&gt;dunst&lt;/code&gt; &lt;a href="https://dev.to/dev-charodeyka/debian-12-is-amazing-how-to-create-your-custom-codehouse-part-4a4-4dfh"&gt;in the previous article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So, I do not enable &lt;code&gt;wireplumber&lt;/code&gt; because it will need to be restarted after &lt;code&gt;xdg-desktop-portal&lt;/code&gt; is launched. Instead, I modify the &lt;code&gt;bspwmrc&lt;/code&gt; config, so it handles it for me.&lt;/p&gt;

&lt;p&gt;I add these lines (&lt;code&gt;systemctl --user start xdg-dektop-portal&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;systemctl --user restart wireplumber&lt;/code&gt; and &lt;code&gt;systemctl --user restart pipewire&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo vim.tiny /etc/xdg/bspwm/bspwmrc
#! /bin/sh

#SXHKD Launching
pgrep -x sxhkd &amp;gt; /dev/null || sxhkd -c "/etc/xdg/bspwm/sxhkdrc" &amp;amp;

#DISPLAY env import
systemctl --user import-environment DISPLAY

#Starting XDG Desktop portal
systemctl --user start xdg-dektop-portal

#ReStarting Wireplumber
systemctl --user restart wireplumber
systemctl --user restart pipewire

#DUNST Launching
systemctl --user start dunst

#POLYBAR Launching
polybar-msg cmd quit

echo "---" | tee -a /tmp/polybar.log
polybar 2&amp;gt;&amp;amp;1 | tee -a /tmp/polybar.log &amp;amp; disown


bspc monitor -d I II III IV V

bspc config border_width         2
bspc config window_gap          12

bspc config split_ratio          0.52
bspc config borderless_monocle   true
bspc config gapless_monocle      true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can do &lt;code&gt;sudo reboot&lt;/code&gt; and then &lt;code&gt;startx&lt;/code&gt; to check the success of modifications with:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;systemctl --user status wireplumber&lt;/code&gt; &lt;code&gt;systemctl --user status xdg-desktop-portal&lt;/code&gt; and &lt;code&gt;systemctl --user status pipewire&lt;/code&gt;. Here is what I have:&lt;/p&gt;

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

&lt;p&gt;Everything seems alrighty!&lt;br&gt;
Time to test Bluetooth!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ bluetoothctl
[bluetooth]# scan on
[NEW] Device XX:XX:XX:XX:XX:XX MAJOR IV
[bluetooth]# pair XX:XX:XX:XX:XX:XX
Attempting to pair with XX:XX:XX:XX:XX:XX
[CHG] Device XX:XX:XX:XX:XX:XX Connected: yes
[CHG] Device XX:XX:XX:XX:XX:XX Bonded: yes
Pairing successful
[bluetooth]# connect XX:XX:XX:XX:XX:XX
Attempting to connect to XX:XX:XX:XX:XX:XX
Connection successful
[MAJOR IV]# scan off
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Connection successful! Start enjoying &lt;a href="https://www.youtube.com/watch?v=rsI1gv_BcnM" rel="noopener noreferrer"&gt;Public Memory - Zig Zag&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now I can use the WirePlumber CLI to check if the device is registered there.&lt;/p&gt;




&lt;h3&gt;
  
  
  ➇ Pipewire audio profiles: Understanding the difference between device profile headset-head-unit and a2dp
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ wpctl status
PipeWire 'pipewire-0' [0.3.65, alisa@wonderland, cookie:1612476836]
 └─ Clients:
 ...

Audio
 ├─ Devices:
 │      39. GA106 High Definition Audio Controller [alsa]
 │      40. Built-in Audio                      [alsa]
 │      51. MAJOR IV                            [bluez5]
 │
 ├─ Sinks:
 │      41. Built-in Audio Digital Stereo (IEC958) [vol: 0.40]
 │  *   52. MAJOR IV                            [vol: 0.60]
 │
 ├─ Sink endpoints:
 │
 ├─ Sources:
 │      42. Built-in Audio Analog Stereo        [vol: 1.00]
 │
 ├─ Source endpoints:
 │
 └─ Streams:

...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sinks are audio output devices, and sources are audio input devices. As you can see, &lt;code&gt;wpctl status&lt;/code&gt; shows that my Marshall headset is registered as a sink (so, output) and not as a source, so I can't use the microphone of my headset.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;wpctl&lt;/code&gt; is similar to &lt;code&gt;pactl&lt;/code&gt;, which is the PulseAudio CLI. However, you can use it with PipeWire without installing the PulseAudio server—just the utilities. So, I’ll do that because &lt;code&gt;pactl&lt;/code&gt; is easier to use for me. &lt;code&gt;pactl list&lt;/code&gt; output will help me explain why my headset is attached only as an output device.&lt;code&gt;pactl&lt;/code&gt; is part of the &lt;code&gt;pulseaudio-utils&lt;/code&gt; package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt install pulseaudio-utils 
$ pactl list
Card #52
    Name: bluez_card.XX_XX_XX_XX_XX_XX
    Driver: module-bluez5-device.c
    Owner Module: n/a
    Properties:
                ....
        api.bluez5.connection = "connected"
        api.bluez5.device = ""
        bluez5.auto-connect = "[ hfp_hf hsp_hs a2dp_sink ]"
                ...
        device.alias = "MAJOR IV"
        device.api = "bluez5"
        device.bus = "bluetooth"
        device.description = "MAJOR IV"
        device.form_factor = "headphone"
                ....
    Profiles:
        off: Off (sinks: 0, sources: 0, priority: 0, available: yes)
        a2dp-sink: High Fidelity Playback (A2DP Sink) (sinks: 1, sources: 0, priority: 16, available: yes)
        headset-head-unit: Headset Head Unit (HSP/HFP) (sinks: 1, sources: 1, priority: 1, available: yes)
        a2dp-sink-sbc: High Fidelity Playback (A2DP Sink, codec SBC) (sinks: 1, sources: 0, priority: 18, available: yes)
        a2dp-sink-sbc_xq: High Fidelity Playback (A2DP Sink, codec SBC-XQ) (sinks: 1, sources: 0, priority: 17, available: yes)
        headset-head-unit-cvsd: Headset Head Unit (HSP/HFP, codec CVSD) (sinks: 1, sources: 1, priority: 2, available: yes)
        headset-head-unit-msbc: Headset Head Unit (HSP/HFP, codec mSBC) (sinks: 1, sources: 1, priority: 3, available: yes)
    Active Profile: a2dp-sink-sbc

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Input is quite long, but you just have to find the card of your audio device. What’s important in the output are the profiles. For my Marshall IV headset, the available profiles (which my Marshall supports, and as I mentioned above, it’s not the highest-end codec it supports in terms of quality) are a2dp-sink (only output), a2dp-sink-sbc (only output), a2dp-sink-sbc_xq (only output), headset-head-unit (both input and output) and headset-head-unit-xvsd (both input and output).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Active Profile: a2dp-sink-sbc&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;a2dp-sink-sbc: High Fidelity Playback (A2DP Sink, codec SBC) (sinks: 1, sources: 0, priority: 18, available: yes)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You can see that there are no sources for this profile, only sinks! So, THIS IS THE REASON WHY I can use my Marshall only as an output device.&lt;/p&gt;

&lt;p&gt;However, I am not bound to only one codec, automatically selected. I can switch it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ pactl set-card-profile bluez_card.XX_XX_XX_XX_XX_XX headset-head-unit
#I can change it back
$ pactl set-card-profile bluez_card.XX_XX_XX_XX_XX_XX a2dp-sink-sbc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NB! When I use the profile &lt;code&gt;headset-head-unit&lt;/code&gt; to use the microphone, the sound quality is quite horrid. I guess this is connected to the maturity of PipeWire and the overall performance of Bluetooth audio devices on Linux, which is evolving rapidly but still has room for improvement. You can tweak the configuration, and if I find a solution to improve the audio quality, I will definitely share it. Most likely, it will be related to the configuration of PipeWire&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  (Optional)
&lt;/h4&gt;

&lt;p&gt;Upgrading the PipeWire sound server version could be a partial remedy. For Debian Bookworm, the way to upgrade it is through the Bookworm backports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#add bookworm-backports repo
$ sudo vim.tiny /etc/apt/sources.list
deb http://deb.debian.org/debian/ bookworm-backports main contrib non-free
$ sudo apt update
$ sudo apt install --reinstall -t bookworm backports pipewire-audio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;That's all! See you in the next, conclusive part, which will be dedicated to having fun with Polybar, shell cuxtomization and installing some cool apps.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>debian</category>
    </item>
  </channel>
</rss>
