summaryrefslogtreecommitdiff
path: root/minimalnetworkinglaptop.html
diff options
context:
space:
mode:
Diffstat (limited to 'minimalnetworkinglaptop.html')
-rw-r--r--minimalnetworkinglaptop.html212
1 files changed, 106 insertions, 106 deletions
diff --git a/minimalnetworkinglaptop.html b/minimalnetworkinglaptop.html
index f9ca405..bc07c9b 100644
--- a/minimalnetworkinglaptop.html
+++ b/minimalnetworkinglaptop.html
@@ -1,52 +1,49 @@
<!DOCTYPE html>
<html>
-<head>
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <head>
+ <title>Minimal Laptop Networking with NixOS</title>
+ <meta property="article:published_time" content="2026-07-29">
+ <script src="article.js"></script>
+ </head>
+ <body>
+ <article>
+ <header></header>
- <title>Minimal Laptop Networking with NixOS</title>
- <link rel="stylesheet" href="prose.css">
- <meta property="article:published_time" content="2026-07-29">
- <script src="article.js"></script>
-</head>
-<body>
- <article>
- <header></header>
+ <p>
+ The default networking stack on NixOS uses NetworkManager.
+ While it's a great choice for many users, I prefer a lighter setup built around systemd-networkd, systemd-resolved, and iwd. It integrates well with the rest of the systemd stack, has fewer moving pieces, and exposes powerful networking features without an additional network management daemon.
+ </p>
- <p>
- The default networking stack on NixOS uses NetworkManager.
- While it's a great choice for many users, I prefer a lighter setup built around systemd-networkd, systemd-resolved, and iwd. It integrates well with the rest of the systemd stack, has fewer moving pieces, and exposes powerful networking features without an additional network management daemon.
- </p>
+ <p class="comment">
+ This article focuses on laptops that connect to wifi.
+ If you're configuring a server, you probably don't need iwd at all; systemd-networkd and systemd-resolved are usually sufficient.
+ </p>
- <p class="comment">
- This article focuses on laptops that connect to wifi.
- If you're configuring a server, you probably don't need iwd at all; systemd-networkd and systemd-resolved are usually sufficient.
- </p>
+ <div id="toc"></div>
- <div id="toc"></div>
+ <h2>The default stack</h2>
+ <p>If not configured otherwise, NixOS uses the following stack:</p>
+ <ul>
+ <li>NetworkManager for wifi</li>
+ <li>dhcpcd for DHCP</li>
+ <li>resolvconf for DNS</li>
+ <li>a "legacy script based system" for network configuration<sup><a href="https://search.nixos.org/options?channel=26.05&query=useNetworkd&type=options#show=option%253Anetworking.useNetworkd:~:text=legacy%20script%20based%20system">[1]</a></sup></li>
+ </ul>
+ <p>We'll be replacing it with:</p>
+ <ul>
+ <li>iwd for wifi</li>
+ <li>systemd-networkd for DHCP and network configuration</li>
+ <li>systemd-resolved for DNS</li>
+ </ul>
+ <p>
+ This has a few benefits.
+ Firstly, switching to resolved allows you to enable features like encrypted DNS over TLS (DoT) and DNSSEC validation, which reduces the potential for DNS spoofing and man-in-the-middle attacks<sup><a href="https://whynothugo.nl/journal/2026/06/24/ignore-dnssec-if-you-like-mitm-attacks/#distributions-and-oss">[2]</a></sup>.
+ Additionally, the default stack uses 3 extra packages (NM, dhcpcd, resolvconf) and some bash scripts, while this setup only uses one extra package (iwd).
+ Also, I'm kind of a systemd nerd, and this is a way to dig into systemd a bit more.
+ </p>
- <h2>The default stack</h2>
- <p>If not configured otherwise, NixOS uses the following stack:</p>
- <ul>
- <li>NetworkManager for wifi</li>
- <li>dhcpcd for DHCP</li>
- <li>resolvconf for DNS</li>
- <li>a "legacy script based system" for network configuration<sup><a href="https://search.nixos.org/options?channel=26.05&query=useNetworkd&type=options#show=option%253Anetworking.useNetworkd:~:text=legacy%20script%20based%20system">[1]</a></sup></li>
- </ul>
- <p>We'll be replacing it with:</p>
- <ul>
- <li>iwd for wifi</li>
- <li>systemd-networkd for DHCP and network configuration</li>
- <li>systemd-resolved for DNS</li>
- </ul>
- <p>
- This has a few benefits.
- Firstly, switching to resolved allows you to enable features like encrypted DNS over TLS (DoT) and DNSSEC validation, which reduces the potential for DNS spoofing and man-in-the-middle attacks<sup><a href="https://whynothugo.nl/journal/2026/06/24/ignore-dnssec-if-you-like-mitm-attacks/#distributions-and-oss">[2]</a></sup>.
- Additionally, the default stack uses 3 extra packages (NM, dhcpcd, resolvconf) and some bash scripts, while this setup only uses one extra package (iwd).
- Also, I'm kind of a systemd nerd, and this is a way to dig into systemd a bit more.
- </p>
-
- <p>So the first thing we can do is disable all of the default stuff:</p>
- <pre><code>{
+ <p>So the first thing we can do is disable all of the default stuff:</p>
+ <pre><code>{
<prop>networking</prop> = {
<prop>networkmanager</prop>.<prop>enable</prop> = <kw>false</kw>;
<prop>dhcpcd</prop>.<prop>enable</prop> = <kw>false</kw>;
@@ -55,14 +52,14 @@
};
}</code></pre>
- <p>And let's get to configuring!</p>
+ <p>And let's get to configuring!</p>
- <h2>iwd</h2>
- <p>
- First up is iwd.
- The important parts of the configuration are delegating network configuration to networkd, delegating name resolution (DNS) to resolved, and configuring MAC randomization for some extra privacy.
- </p>
- <pre><code>{
+ <h2>iwd</h2>
+ <p>
+ First up is iwd.
+ The important parts of the configuration are delegating network configuration to networkd, delegating name resolution (DNS) to resolved, and configuring MAC randomization for some extra privacy.
+ </p>
+ <pre><code>{
<prop>networking</prop>.<prop>wireless</prop>.<prop>iwd</prop> = {
<prop>enable</prop> = <kw>true</kw>;
<prop>settings</prop> = {
@@ -76,28 +73,28 @@
};
}</code></pre>
- <p>
- At this point, we don't have any network configuration yet, so internet access won't work. However, you can verify that iwd is functioning correctly:
- </p>
- <ul>
- <li><code>iwctl station wlan0 scan</code>: Scans for new wifi networks</li>
- <li><code>iwctl station wlan0 get-networks</code>: lists the networks that it found</li>
- <li><code>iwctl station wlan0 connect "&lt;SSID&gt;"</code>: connects to the network, prompting for a PSK if necessary</li>
- </ul>
+ <p>
+ At this point, we don't have any network configuration yet, so internet access won't work. However, you can verify that iwd is functioning correctly:
+ </p>
+ <ul>
+ <li><code><kw>iwctl</kw> <prop>station</prop> <prop>wlan0</prop> <prop>scan</prop></code>: Scans for new wifi networks</li>
+ <li><code><kw>iwctl</kw> <prop>station</prop> <prop>wlan0</prop> <prop>get-networks</prop></code>: lists the networks that it found</li>
+ <li><code><kw>iwctl</kw> <prop>station</prop> <prop>wlan0</prop> <prop>connect</prop> <str>"&lt;SSID&gt;"</str></code>: connects to the network, prompting for a PSK if necessary</li>
+ </ul>
- <h2>systemd-networkd</h2>
- <p>
- Next up is systemd-networkd.
- Networkd has lots of advanced options for things like static IP config, Wireguard tunnels, VLANs, domain-based DNS routing, and more.
- However, here we'll just do a basic configuration to enable DHCP and deny DHCP-advertised DNS.
- </p>
- <details class="comment">
- <summary>why disable DHCP-advertised DNS?</summary>
- By default, most networks advertise their own DNS resolver via DHCP.
- Since we'll configure our own encrypted upstream DNS servers in the next section, we tell networkd to ignore the ones provided by the network.
- </details>
+ <h2>systemd-networkd</h2>
+ <p>
+ Next up is systemd-networkd.
+ Networkd has lots of advanced options for things like static IP config, Wireguard tunnels, VLANs, domain-based DNS routing, and more.
+ However, here we'll just do a basic configuration to enable DHCP and deny DHCP-advertised DNS.
+ </p>
+ <details class="comment">
+ <summary>why disable DHCP-advertised DNS?</summary>
+ By default, most networks advertise their own DNS resolver via DHCP.
+ Since we'll configure our own encrypted upstream DNS servers in the next section, we tell networkd to ignore the ones provided by the network.
+ </details>
- <pre><code>{
+ <pre><code>{
<prop>systemd</prop>.<prop>network</prop> = {
<prop>enable</prop> = <kw>true</kw>;
@@ -113,27 +110,28 @@
<prop>networking</prop>.<prop>useNetworkd</prop> = <kw>true</kw>;
}</code></pre>
- <p>
- The <code>name = "wl*"</code> refers to the network interface name that this configuration applies to.
- According to the systemd docs, wireless LAN devices should start with the two-character prefix "wl"<sup><a href="https://www.freedesktop.org/software/systemd/man/latest/systemd.net-naming-scheme.html#:~:text=wl,WLAN">[3]</a></sup>.
- Here we use a glob (<code>*</code>) to match all wireless interfaces.
- </p>
+ <p>
+ The <code>name = "wl*"</code>
+ refers to the network interface name that this configuration applies to.
+ According to the systemd docs, wireless LAN devices should start with the two-character prefix "wl"<sup><a href="https://www.freedesktop.org/software/systemd/man/latest/systemd.net-naming-scheme.html#:~:text=wl,WLAN">[3]</a></sup>.
+ Here we use a glob (<code>*</code>) to match all wireless interfaces.
+ </p>
- <p>You can verify that your configuration worked with a few commands:</p>
- <ul>
- <li><code>ip addr</code>: make sure that there's an IP address associated with your wireless interface. That means DHCP is working.</li>
- <li><code>networkctl status</code>: make sure there's no significant errors in the logs.</li>
- </ul>
+ <p>You can verify that your configuration worked with a few commands:</p>
+ <ul>
+ <li><code><kw>ip</kw> <prop>addr</prop></code>: make sure that there's an IP address associated with your wireless interface. That means DHCP is working.</li>
+ <li><code><kw>networkctl</kw> <prop>status</prop></code>: make sure there's no significant errors in the logs.</li>
+ </ul>
- <h2>systemd-resolved</h2>
- <p>
- Now all we have left is DNS configuration, then we're done.
- This is probably the part where you can nerd out the hardest, but I try to keep it pretty minimal.
- The important parts are selecting upstream DNS servers that support secure DNS protocols (like DNS over TLS and DNSSEC) and that respect your privacy.
- For these reasons, I use <a href="https://quad9.net">quad9</a>, but do your own research and find the best upstream DNS server for you!
- Some other options include <a href="https://www.cloudflare.com/products/dns/">Cloudflare DNS</a>, <a href="https://developers.google.com/speed/public-dns/">Google's DNS</a>, or <a href="https://adguard-dns.io/en/public-dns.html">AdGuard Public DNS</a>.
- </p>
- <pre><code>{
+ <h2>systemd-resolved</h2>
+ <p>
+ Now all we have left is DNS configuration, then we're done.
+ This is probably the part where you can nerd out the hardest, but I try to keep it pretty minimal.
+ The important parts are selecting upstream DNS servers that support secure DNS protocols (like DNS over TLS and DNSSEC) and that respect your privacy.
+ For these reasons, I use <a href="https://quad9.net">quad9</a>, but do your own research and find the best upstream DNS server for you!
+ Some other options include <a href="https://www.cloudflare.com/products/dns/">Cloudflare DNS</a>, <a href="https://developers.google.com/speed/public-dns/">Google's DNS</a>, or <a href="https://adguard-dns.io/en/public-dns.html">AdGuard Public DNS</a>.
+ </p>
+ <pre><code>{
<prop>networking</prop>.<prop>nameservers</prop> = [
<str>"9.9.9.9#dns.quad9.net"</str>
<str>"149.112.112.112#dns.quad9.net"</str>
@@ -151,23 +149,25 @@
};
}</code></pre>
- <p>You can verify your DNS configuration by using the <code>resolvectl status</code> command or by just opening a website.</p>
+ <p>You can verify your DNS configuration by using the <code><kw>resolvectl</kw> <prop>status</prop></code>
+ command or by just opening a website.</p>
- <h2>P.S. Captive Portals</h2>
- <p>
- Captive portals are those wifi login pages that pop up when you connect to some public wifi networks.
- Some captive portals don't behave correctly when you're using an encrypted third-party DNS server instead of the network's advertised resolver.
- To get around this, I use <a href="https://github.com/FiloSottile/captive-browser">Captive Browser</a> (<a href="https://search.nixos.org/packages?channel=26.05&query=captive-browser#show=captive-browser">nixpkgs</a>), which launches an instance of Chromium through a SOCKS5 proxy, avoiding the system DNS configuration altogether.
- This allows you to access a captive portal for long enough to log in and gain internet access.
- Pretty neat!
- </p>
+ <h2>P.S. Captive portals</h2>
+ <p>
+ Captive portals are those wifi login pages that pop up when you connect to some public wifi networks.
+ Some captive portals don't behave correctly when you're using an encrypted third-party DNS server instead of the network's advertised resolver.
+ To get around this, I use <a href="https://github.com/FiloSottile/captive-browser">Captive Browser</a>
+ (<a href="https://search.nixos.org/packages?channel=26.05&query=captive-browser#show=captive-browser">nixpkgs</a>), which launches an instance of Chromium through a SOCKS5 proxy, avoiding the system DNS configuration altogether.
+ This allows you to access a captive portal for long enough to log in and gain internet access.
+ Pretty neat!
+ </p>
- <h2>P.P.S. I miss nm-applet</h2>
- <p>
- Probably the biggest downside to this setup is the lack of desktop integration.
- GNOME and Plasma only support NetworkManager in their network configuration menu, so you're kinda out of luck.
- For tiling window manager people, I can recommend <a href="https://github.com/e-tho/iwmenu">iwmenu</a>, which is a launcher (dmenu, rofi, fuzzel)-based menu for connecting to wifi networks via iwd.
- </p>
- </article>
-</body>
-</html> \ No newline at end of file
+ <h2>P.P.S. I miss nm-applet</h2>
+ <p>
+ Probably the biggest downside to this setup is the lack of desktop integration.
+ GNOME and Plasma only support NetworkManager in their network configuration menu, so you're kinda out of luck.
+ For tiling window manager people, I can recommend <a href="https://github.com/e-tho/iwmenu">iwmenu</a>, which is a launcher (dmenu, rofi, fuzzel)-based menu for connecting to wifi networks via iwd.
+ </p>
+ </article>
+ </body>
+</html>