<?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: pizidavi</title>
    <description>The latest articles on DEV Community by pizidavi (@pizidavi).</description>
    <link>https://dev.to/pizidavi</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%2F3758152%2F170a4330-bac4-4b58-ac8b-efaa4222c90b.jpeg</url>
      <title>DEV Community: pizidavi</title>
      <link>https://dev.to/pizidavi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pizidavi"/>
    <language>en</language>
    <item>
      <title>Minimal Homelab Server with Alpine Linux</title>
      <dc:creator>pizidavi</dc:creator>
      <pubDate>Sat, 01 Aug 2026 17:37:05 +0000</pubDate>
      <link>https://dev.to/pizidavi/minimal-alpine-linux-homelab-server-n7i</link>
      <guid>https://dev.to/pizidavi/minimal-alpine-linux-homelab-server-n7i</guid>
      <description>&lt;p&gt;I wanted a minimal homelab server with full control over every component - no bloated NAS distros, no web UIs I didn't ask for, just a lean Linux box with pooled storage, parity protection, VPN access, containers, and automated backups. Here's how I built one with Alpine Linux on my &lt;a href="https://www.zimaspace.com/products/blade-personal-nas" rel="noopener noreferrer"&gt;Zimablade&lt;/a&gt; server.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ This isn't a tutorial; this is how I created my system. Yours may be different.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  1. Install Alpine Linux
&lt;/h2&gt;

&lt;p&gt;Download the ISO from &lt;a href="https://alpinelinux.org/wiki/Installation" rel="noopener noreferrer"&gt;alpinelinux.org&lt;/a&gt;, flash it to a USB drive with &lt;a href="https://balena.io/etcher/" rel="noopener noreferrer"&gt;Balena Etcher&lt;/a&gt;, and boot from it.&lt;/p&gt;

&lt;p&gt;Login as &lt;code&gt;root&lt;/code&gt; (no password) and launch the installer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;setup-alpine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Walk through the prompts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Keyboard layout&lt;/strong&gt; - pick yours&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hostname&lt;/strong&gt; - whatever you want&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network interface&lt;/strong&gt; - select your adapter&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DNS&lt;/strong&gt; - leave domain blank, set DNS as &lt;code&gt;1.1.1.1 8.8.8.8&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Root password&lt;/strong&gt; - set a strong one&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timezone&lt;/strong&gt; - pick "UTC" or your local timezone&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proxy&lt;/strong&gt; - &lt;code&gt;none&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NTP&lt;/strong&gt; - &lt;code&gt;busybox&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;APK mirror&lt;/strong&gt; - don't skip this one

&lt;ul&gt;
&lt;li&gt;Press &lt;code&gt;c&lt;/code&gt; to enable community packages&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;f&lt;/code&gt; to auto-select the fastest mirror&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a user&lt;/strong&gt; - I used &lt;code&gt;admin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSH server&lt;/strong&gt; - &lt;code&gt;openssh&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Disk&lt;/strong&gt; - select your drive, choose &lt;code&gt;lvm&lt;/code&gt; then &lt;code&gt;sys&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;lvm&lt;/strong&gt; (Logical Volume Manager) lets you manage disk space flexibly - resize, add, or remove partitions without rebooting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sys&lt;/strong&gt; means a full disk installation&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Reboot when it's done.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. First Boot: Baseline Setup
&lt;/h2&gt;

&lt;p&gt;SSH in from your workstation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-copy-id &lt;span class="nt"&gt;-i&lt;/span&gt; .ssh/id_rsa.pub admin@192.168.1.X
ssh admin@192.168.1.X
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Switch to Bash
&lt;/h3&gt;

&lt;p&gt;Alpine ships with &lt;code&gt;ash&lt;/code&gt; by default. Swap it for &lt;code&gt;bash&lt;/code&gt; for the admin user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apk add bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit &lt;code&gt;/etc/passwd&lt;/code&gt; and change the &lt;code&gt;admin&lt;/code&gt; user's shell from &lt;code&gt;/bin/ash&lt;/code&gt; to &lt;code&gt;/bin/bash&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then create &lt;code&gt;~/.bash_profile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Aliases&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;l&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ls -la"&lt;/span&gt;
&lt;span class="nb"&gt;alias sudo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"doas"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; Alpine uses &lt;code&gt;doas&lt;/code&gt; instead of &lt;code&gt;sudo&lt;/code&gt;. The alias makes the transition painless.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Install Base Packages
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apk add util-linux lsblk xfsprogs curl git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Fix APK Repositories
&lt;/h3&gt;

&lt;p&gt;Edit &lt;code&gt;/etc/apk/repositories&lt;/code&gt; and make sure you have main, community, and testing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;https&lt;/span&gt;://&lt;span class="n"&gt;dl&lt;/span&gt;-&lt;span class="n"&gt;cdn&lt;/span&gt;.&lt;span class="n"&gt;alpinelinux&lt;/span&gt;.&lt;span class="n"&gt;org&lt;/span&gt;/&lt;span class="n"&gt;alpine&lt;/span&gt;/&amp;lt;&lt;span class="n"&gt;version&lt;/span&gt;&amp;gt;/&lt;span class="n"&gt;main&lt;/span&gt;
&lt;span class="n"&gt;https&lt;/span&gt;://&lt;span class="n"&gt;dl&lt;/span&gt;-&lt;span class="n"&gt;cdn&lt;/span&gt;.&lt;span class="n"&gt;alpinelinux&lt;/span&gt;.&lt;span class="n"&gt;org&lt;/span&gt;/&lt;span class="n"&gt;alpine&lt;/span&gt;/&amp;lt;&lt;span class="n"&gt;version&lt;/span&gt;&amp;gt;/&lt;span class="n"&gt;community&lt;/span&gt;
@&lt;span class="n"&gt;testing&lt;/span&gt; &lt;span class="n"&gt;https&lt;/span&gt;://&lt;span class="n"&gt;dl&lt;/span&gt;-&lt;span class="n"&gt;cdn&lt;/span&gt;.&lt;span class="n"&gt;alpinelinux&lt;/span&gt;.&lt;span class="n"&gt;org&lt;/span&gt;/&lt;span class="n"&gt;alpine&lt;/span&gt;/&lt;span class="n"&gt;edge&lt;/span&gt;/&lt;span class="n"&gt;testing&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; Replace &lt;code&gt;&amp;lt;version&amp;gt;&lt;/code&gt; with your current Alpine version (e.g. &lt;code&gt;v3.21&lt;/code&gt;). To install testing packages, append &lt;code&gt;@testing&lt;/code&gt; like: &lt;code&gt;apk add mergerfs@testing&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  3. MergerFS + SnapRAID
&lt;/h2&gt;

&lt;p&gt;This is the heart of the homelab. &lt;a href="https://github.com/trapexit/mergerfs" rel="noopener noreferrer"&gt;MergerFS&lt;/a&gt; pools multiple drives into a single mount point, and &lt;a href="https://www.snapraid.it/" rel="noopener noreferrer"&gt;SnapRAID&lt;/a&gt; provides parity protection - think software RAID without the lock-in. Read more: &lt;a href="https://thenomadcode.tech/mergerfs-snapraid-is-the-new-raid-5" rel="noopener noreferrer"&gt;MergerFS + SnapRAID is the new RAID 5&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prepare the Disks
&lt;/h3&gt;

&lt;p&gt;List available disks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lsblk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For each data disk (&lt;code&gt;sda&lt;/code&gt;, &lt;code&gt;sdb&lt;/code&gt;, &lt;code&gt;sdc&lt;/code&gt;, ...), create a partition and filesystem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;fdisk /dev/sda
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside &lt;code&gt;fdisk&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;g&lt;/code&gt; - create a new GPT partition table&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;n&lt;/code&gt; - create a new partition (accept all defaults)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;w&lt;/code&gt; - write changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then format with XFS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;mkfs.xfs /dev/sda1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repeat for every drive. If &lt;code&gt;mkfs.xfs&lt;/code&gt; complains about the device being busy, reboot and try again.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mount the Disks
&lt;/h3&gt;

&lt;p&gt;Get the UUIDs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;blkid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create mount points:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; /mnt/disk&lt;span class="o"&gt;{&lt;/span&gt;1,2,3&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; /mnt/parity
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; /mnt/storage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit &lt;code&gt;/etc/fstab&lt;/code&gt; - replace the UUIDs with your own:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Data drives
UUID="..."    /mnt/disk1    xfs    defaults    0    2
UUID="..."    /mnt/disk2    xfs    defaults    0    2
UUID="..."    /mnt/disk3    xfs    defaults    0    2

# Parity drive
UUID="..."    /mnt/parity    xfs    defaults    0    2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;mount &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Set Up MergerFS
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apk add fuse@testing mergerfs@testing

&lt;span class="c"&gt;# Enable FUSE&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;modprobe fuse
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'fuse'&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/modules-load.d/fuse.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add MergerFS to &lt;code&gt;/etc/fstab&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;# MergerFS pool
/mnt/disk*    /mnt/storage    fuse.mergerfs    defaults,allow_other,use_ino,cache.files=partial,dropcacheonclose=true,category.create=pfrd    0    0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mount and verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;mount &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see &lt;code&gt;/mnt/storage&lt;/code&gt; with the combined capacity of all data drives.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set Up SnapRAID
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apk add snapraid@testing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit &lt;code&gt;/etc/snapraid.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;# Parity disk location
&lt;/span&gt;&lt;span class="n"&gt;parity&lt;/span&gt; /&lt;span class="n"&gt;mnt&lt;/span&gt;/&lt;span class="n"&gt;parity&lt;/span&gt;/&lt;span class="n"&gt;snapraid&lt;/span&gt;.&lt;span class="n"&gt;parity&lt;/span&gt;

&lt;span class="c"&gt;# Content file locations (one per data disk + one in /var)
&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt; /&lt;span class="n"&gt;var&lt;/span&gt;/&lt;span class="n"&gt;snapraid&lt;/span&gt;/&lt;span class="n"&gt;snapraid&lt;/span&gt;.&lt;span class="n"&gt;content&lt;/span&gt;
&lt;span class="n"&gt;content&lt;/span&gt; /&lt;span class="n"&gt;mnt&lt;/span&gt;/&lt;span class="n"&gt;disk1&lt;/span&gt;/.&lt;span class="n"&gt;snapraid&lt;/span&gt;.&lt;span class="n"&gt;content&lt;/span&gt;
&lt;span class="n"&gt;content&lt;/span&gt; /&lt;span class="n"&gt;mnt&lt;/span&gt;/&lt;span class="n"&gt;disk2&lt;/span&gt;/.&lt;span class="n"&gt;snapraid&lt;/span&gt;.&lt;span class="n"&gt;content&lt;/span&gt;
&lt;span class="n"&gt;content&lt;/span&gt; /&lt;span class="n"&gt;mnt&lt;/span&gt;/&lt;span class="n"&gt;disk3&lt;/span&gt;/.&lt;span class="n"&gt;snapraid&lt;/span&gt;.&lt;span class="n"&gt;content&lt;/span&gt;

&lt;span class="c"&gt;# Data disks
&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="n"&gt;d1&lt;/span&gt; /&lt;span class="n"&gt;mnt&lt;/span&gt;/&lt;span class="n"&gt;disk1&lt;/span&gt;/
&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="n"&gt;d2&lt;/span&gt; /&lt;span class="n"&gt;mnt&lt;/span&gt;/&lt;span class="n"&gt;disk2&lt;/span&gt;/
&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="n"&gt;d3&lt;/span&gt; /&lt;span class="n"&gt;mnt&lt;/span&gt;/&lt;span class="n"&gt;disk3&lt;/span&gt;/

&lt;span class="c"&gt;# Exclude patterns
&lt;/span&gt;&lt;span class="n"&gt;exclude&lt;/span&gt; *.&lt;span class="n"&gt;unrecoverable&lt;/span&gt;
&lt;span class="n"&gt;exclude&lt;/span&gt; /&lt;span class="n"&gt;tmp&lt;/span&gt;/
&lt;span class="n"&gt;exclude&lt;/span&gt; /&lt;span class="n"&gt;lost&lt;/span&gt;+&lt;span class="n"&gt;found&lt;/span&gt;/
&lt;span class="n"&gt;exclude&lt;/span&gt; .&lt;span class="n"&gt;AppleDouble&lt;/span&gt;
&lt;span class="n"&gt;exclude&lt;/span&gt; .&lt;span class="err"&gt;_&lt;/span&gt;*
&lt;span class="n"&gt;exclude&lt;/span&gt; .&lt;span class="n"&gt;DS_Store&lt;/span&gt;
&lt;span class="n"&gt;exclude&lt;/span&gt; &lt;span class="n"&gt;Thumbs&lt;/span&gt;.&lt;span class="n"&gt;db&lt;/span&gt;
&lt;span class="n"&gt;exclude&lt;/span&gt; &lt;span class="n"&gt;desktop&lt;/span&gt;.&lt;span class="n"&gt;ini&lt;/span&gt;
&lt;span class="n"&gt;exclude&lt;/span&gt; *.&lt;span class="n"&gt;tmp&lt;/span&gt;
&lt;span class="n"&gt;exclude&lt;/span&gt; *.&lt;span class="n"&gt;temp&lt;/span&gt;
&lt;span class="n"&gt;exclude&lt;/span&gt; .&lt;span class="n"&gt;fseventsd&lt;/span&gt;/
&lt;span class="n"&gt;exclude&lt;/span&gt; .&lt;span class="n"&gt;Spotlight&lt;/span&gt;-&lt;span class="n"&gt;V100&lt;/span&gt;/
&lt;span class="n"&gt;exclude&lt;/span&gt; .&lt;span class="n"&gt;Trash&lt;/span&gt;-&lt;span class="m"&gt;1000&lt;/span&gt;/
&lt;span class="n"&gt;exclude&lt;/span&gt; */.&lt;span class="n"&gt;Trash&lt;/span&gt;-&lt;span class="m"&gt;1000&lt;/span&gt;/
&lt;span class="n"&gt;exclude&lt;/span&gt; .&lt;span class="n"&gt;recycling&lt;/span&gt;/

&lt;span class="c"&gt;# Auto-save content every 100 changes
&lt;/span&gt;&lt;span class="n"&gt;autosave&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the content directory and run the first sync:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /var/snapraid
&lt;span class="nb"&gt;sudo &lt;/span&gt;snapraid &lt;span class="nb"&gt;sync&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; The first &lt;code&gt;sync&lt;/code&gt; can take a while depending on how much data you have.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Automate SnapRAID with Cron
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;[IMPORTANT] SnapRAID is not a real-time RAID solution. It only protects against disk failures when you run &lt;code&gt;snapraid sync&lt;/code&gt;. You should run it daily or more frequently depending on your data change rate.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;crontab &lt;span class="nt"&gt;-e&lt;/span&gt;
&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;0 1 * * * snapraid sync &amp;amp;&amp;amp; snapraid scrub -p 4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;scrub -p 4&lt;/code&gt; checks 4% of your data each day for bit rot - this means a full check completes roughly once a month.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Tailscale
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://tailscale.com/" rel="noopener noreferrer"&gt;Tailscale&lt;/a&gt; gives you a private VPN to access your homelab from anywhere - no port forwarding, no dynamic DNS headaches.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://tailscale.com/install.sh | sh
&lt;span class="nb"&gt;sudo &lt;/span&gt;tailscale up &lt;span class="nt"&gt;--accept-dns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Authenticate with the link it prints, then you're in.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tailscale Serve
&lt;/h3&gt;

&lt;p&gt;Expose local services over your Tailscale network with a clean HTTPS URL. Read more: &lt;a href="https://tailscale.com/docs/features/tailscale-serve" rel="noopener noreferrer"&gt;Tailscale Serve&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;tailscale serve &lt;span class="nt"&gt;--service&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;svc:&amp;lt;service-name&amp;gt; &lt;span class="nt"&gt;--https&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;443 &amp;lt;local-port&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, to expose a web app running on port 8080:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;tailscale serve &lt;span class="nt"&gt;--service&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;svc:webapp &lt;span class="nt"&gt;--https&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;443 8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Access it at &lt;code&gt;https://webapp.&amp;lt;your-machine&amp;gt;.ts.net&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Docker
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apk add docker docker-cli-compose
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable and start the Docker service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;rc-update add docker default
&lt;span class="nb"&gt;sudo &lt;/span&gt;service docker start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create stack directories for your containers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /mnt/storage/stacks &lt;span class="c"&gt;# This is where the compose.yml files will live&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /mnt/storage/data &lt;span class="c"&gt;# This is where the container data will be stored&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. Backup with Backrest
&lt;/h2&gt;

&lt;p&gt;I use &lt;a href="https://github.com/garethgeorge/backrest" rel="noopener noreferrer"&gt;Backrest&lt;/a&gt;, a web UI for &lt;a href="https://restic.net/" rel="noopener noreferrer"&gt;Restic&lt;/a&gt;, to back up to &lt;a href="https://www.backblaze.com/cloud-storage" rel="noopener noreferrer"&gt;Backblaze B2&lt;/a&gt;. Everything runs in Docker.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a Backblaze B2 Bucket
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Create a &lt;a href="https://www.backblaze.com/" rel="noopener noreferrer"&gt;Backblaze&lt;/a&gt; account&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;B2 Cloud Storage&lt;/strong&gt; → &lt;strong&gt;Create a Bucket&lt;/strong&gt; (e.g. &lt;code&gt;homelab-backup&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;App Keys&lt;/strong&gt; → &lt;strong&gt;Create New Application Key&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Give it a name (e.g. &lt;code&gt;backrest&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Note down the &lt;strong&gt;KeyID&lt;/strong&gt; and &lt;strong&gt;Application Key&lt;/strong&gt; - you won't see the key again&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Docker Compose
&lt;/h3&gt;

&lt;p&gt;Create a directory for Backrest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /mnt/storage/data/backrest
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /mnt/storage/stacks/backrest
&lt;span class="nb"&gt;cd&lt;/span&gt; /mnt/storage/stacks/backrest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create &lt;code&gt;compose.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;backrest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;garethgeorge/backrest:latest&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backrest&lt;/span&gt;
    &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backrest&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;9898:9898"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/mnt/storage/data/backrest/data:/data&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/mnt/storage/data/backrest/config:/config&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/mnt/storage/data/backrest/cache:/cache&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/mnt/storage/data/backrest/tmp:/tmp&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/mnt/storage/data/backrest/rclone:/root/.config/rclone&lt;/span&gt; &lt;span class="c1"&gt;# Mount for rclone config (needed when using rclone remotes)&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/mnt/storage/stacks:/stacks&lt;/span&gt;  &lt;span class="c1"&gt;# Mount local paths to backup&lt;/span&gt;
      &lt;span class="c1"&gt;# - /path/to/local/repos:/repos     # Mount local repos (optional for remote storage)&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;TZ=UTC&lt;/span&gt; &lt;span class="c1"&gt;# Set your timezone, e.g. "UTC"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;BACKREST_DATA=/data&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;BACKREST_CONFIG=/config/config.json&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;XDG_CACHE_HOME=/cache&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;TMPDIR=/tmp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start it up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configure Backrest
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;code&gt;http://&amp;lt;your-server-ip&amp;gt;:9898&lt;/code&gt; in your browser&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;Add Repository&lt;/strong&gt; and fill in:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Type:&lt;/strong&gt; &lt;code&gt;S3&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repo URL:&lt;/strong&gt; &lt;code&gt;s3:&amp;lt;region&amp;gt;.backblazeb2.com/&amp;lt;bucket-name&amp;gt;&lt;/code&gt; - check your bucket's endpoint in the Backblaze dashboard (e.g. &lt;code&gt;s3:eu-central-003.backblazeb2.com/homelab-backup&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Password:&lt;/strong&gt; choose a strong Restic repo password&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Under &lt;strong&gt;Environment Variables&lt;/strong&gt;, add:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;   &lt;span class="py"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;your-key-id&amp;gt;&lt;/span&gt;
   &lt;span class="py"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;your-application-key&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Add Plan&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repository:&lt;/strong&gt; select the one you just created&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Paths:&lt;/strong&gt; the directories to back up (e.g. &lt;code&gt;/mnt/storage/stacks&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schedule:&lt;/strong&gt; set a backup frequency (e.g. daily at 2 AM)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retention:&lt;/strong&gt; configure how many snapshots to keep&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Final Checklist
&lt;/h2&gt;

&lt;p&gt;At this point you should have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Alpine Linux installed and hardened with SSH key access&lt;/li&gt;
&lt;li&gt;A pooled storage filesystem via MergerFS with parity protection from SnapRAID&lt;/li&gt;
&lt;li&gt;Automated daily SnapRAID sync and scrub&lt;/li&gt;
&lt;li&gt;VPN access to your homelab via Tailscale&lt;/li&gt;
&lt;li&gt;Docker running with your user in the docker group&lt;/li&gt;
&lt;li&gt;Automated backups to Backblaze B2 via Backrest&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://wiki.alpinelinux.org/wiki/Installation" rel="noopener noreferrer"&gt;Alpine Linux Wiki - Installation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/trapexit/mergerfs" rel="noopener noreferrer"&gt;MergerFS GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.snapraid.it" rel="noopener noreferrer"&gt;SnapRAID - Official Site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tailscale.com/docs" rel="noopener noreferrer"&gt;Tailscale - Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/garethgeorge/backrest" rel="noopener noreferrer"&gt;Backrest GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>linux</category>
      <category>alpinelinux</category>
      <category>selfhosted</category>
      <category>docker</category>
    </item>
    <item>
      <title>Minimal Fedora install with Niri + DankMaterialShell</title>
      <dc:creator>pizidavi</dc:creator>
      <pubDate>Tue, 28 Apr 2026 16:19:56 +0000</pubDate>
      <link>https://dev.to/pizidavi/minimal-fedora-install-with-niri-dankmaterialshell-34md</link>
      <guid>https://dev.to/pizidavi/minimal-fedora-install-with-niri-dankmaterialshell-34md</guid>
      <description>&lt;p&gt;I wanted a &lt;em&gt;minimal Fedora base&lt;/em&gt; without giving up a modern Wayland desktop.&lt;/p&gt;

&lt;p&gt;This guide shows a reproducible flow to install Fedora from the &lt;strong&gt;Everything ISO&lt;/strong&gt;, then layer on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Niri&lt;/strong&gt; as the Wayland window manager&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DankMaterialShell (DMS)&lt;/strong&gt; as the desktop shell (Quickshell + Go) via the &lt;strong&gt;DankLinux&lt;/strong&gt; installer&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ This isn't a tutorial; this is how I created my system. Yours may be different.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;0) Before you start&lt;/li&gt;
&lt;li&gt;1) Install Fedora minimal&lt;/li&gt;
&lt;li&gt;2) First boot: baseline setup&lt;/li&gt;
&lt;li&gt;3) Install Niri and DankMaterialShell&lt;/li&gt;
&lt;li&gt;4) Post-install: drivers and core functionality&lt;/li&gt;
&lt;li&gt;5) Daily packages&lt;/li&gt;
&lt;li&gt;Final checklist&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  0) Before you start
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A USB drive with the &lt;strong&gt;Fedora Everything ISO&lt;/strong&gt; written to it&lt;/li&gt;
&lt;li&gt;Basic familiarity with the Fedora installer&lt;/li&gt;
&lt;li&gt;Internet connection (for updates + packages)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re installing on a laptop, keep an Ethernet adapter/hotspot handy — Wi‑Fi firmware is the most common “minimal install” snag.&lt;/p&gt;

&lt;h2&gt;
  
  
  1) Install Fedora minimal
&lt;/h2&gt;

&lt;p&gt;Boot from the USB and follow these steps in the Fedora installer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select your language&lt;/li&gt;
&lt;li&gt;Set your keyboard layout&lt;/li&gt;
&lt;li&gt;In &lt;strong&gt;Software Selection&lt;/strong&gt;, pick the minimal/custom option (keep it minimal)&lt;/li&gt;
&lt;li&gt;Configure the disk (choose your layout — Btrfs is a solid default)&lt;/li&gt;
&lt;li&gt;Create a user account and keep &lt;strong&gt;root disabled&lt;/strong&gt; (recommended)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At this point you should boot into a minimal Fedora shell environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  2) First boot: baseline setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Maximize DNF download speed
&lt;/h3&gt;

&lt;p&gt;Edit DNF’s configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/dnf/dnf.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add (or edit) this option:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[main]&lt;/span&gt;
&lt;span class="py"&gt;max_parallel_downloads&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;10&lt;/span&gt;
&lt;span class="py"&gt;defaultyes&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Enable RPM Fusion repositories
&lt;/h3&gt;

&lt;p&gt;RPM Fusion is needed for some codecs and (commonly) NVIDIA drivers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-&lt;span class="si"&gt;$(&lt;/span&gt;rpm &lt;span class="nt"&gt;-E&lt;/span&gt; %fedora&lt;span class="si"&gt;)&lt;/span&gt;.noarch.rpm &lt;span class="se"&gt;\&lt;/span&gt;
  https://mirrors.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-&lt;span class="si"&gt;$(&lt;/span&gt;rpm &lt;span class="nt"&gt;-E&lt;/span&gt; %fedora&lt;span class="si"&gt;)&lt;/span&gt;.noarch.rpm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update the system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf group upgrade core
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf upgrade &lt;span class="nt"&gt;--refresh&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3) Install Niri and DankMaterialShell
&lt;/h2&gt;

&lt;p&gt;Run the installer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://install.danklinux.com | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the wizard I selected:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Niri&lt;/strong&gt; as Window Manager&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alacritty&lt;/strong&gt; as terminal emulator&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;dms-greeter&lt;/strong&gt; as display manager/greeter (a &lt;code&gt;greetd&lt;/code&gt; greeter)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ensure a graphical boot target:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl set-default graphical.target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now reboot!&lt;/p&gt;

&lt;h2&gt;
  
  
  4) Post-install: drivers and core functionality
&lt;/h2&gt;

&lt;p&gt;Now that Fedora + Niri + DMS are installed, validate the “core stuff” (networking, graphics, portals, audio). The goal is to catch driver/firmware issues early.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install core software and firmware packages
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;which wget pciutils usbutils linux-firmware
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Optional utils:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;plocate autoconf bison re2c libxml2-devel sqlite-devel oniguruma-devel libcurl-devel gd-devel libpq-devel libzip-devel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: &lt;code&gt;pciutils&lt;/code&gt; provides &lt;code&gt;lspci&lt;/code&gt; for hardware identification; &lt;code&gt;linux-firmware&lt;/code&gt; provides firmware blobs for many peripherals.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create XDG user directories
&lt;/h3&gt;

&lt;p&gt;This sets up the standard home folders (Downloads, Documents, etc.) using &lt;code&gt;xdg-user-dirs&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;xdg-user-dirs
xdg-user-dirs-update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Wi‑Fi (NetworkManager)
&lt;/h3&gt;

&lt;p&gt;Install and enable NetworkManager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;NetworkManager NetworkManager-wifi
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; NetworkManager
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nmcli device status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;/p&gt;
  If Wi‑Fi doesn’t work: diagnose driver/firmware
  &lt;p&gt;Troubleshooting commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Identify the Wi‑Fi adapter + kernel driver&lt;/span&gt;
lspci &lt;span class="nt"&gt;-nnk&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-iA3&lt;/span&gt; net

&lt;span class="c"&gt;# Check if common Wi‑Fi modules are loaded&lt;/span&gt;
lsmod | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"iwl|rtl|ath9k|ath10k|mt76|b43|bcma"&lt;/span&gt;

&lt;span class="c"&gt;# Scan logs for firmware/driver errors&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;dmesg | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-iE&lt;/span&gt; &lt;span class="s2"&gt;"firmware|wifi|wlan|iwl|rtl|ath|error"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;br&gt;
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  Intel Wi‑Fi firmware manual install (if your adapter needs it)
  &lt;p&gt;Download firmware from the upstream linux-firmware repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/plain/intel/iwlwifi/iwlwifi-so-a0-gf-a0-89.ucode
wget https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/plain/intel/iwlwifi/iwlwifi-so-a0-gf-a0.pnvm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Move into &lt;code&gt;/lib/firmware/&lt;/code&gt; and fix ownership/permissions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo mv &lt;/span&gt;iwlwifi-so-a0-gf-a0-89.ucode /lib/firmware/
&lt;span class="nb"&gt;sudo mv &lt;/span&gt;iwlwifi-so-a0-gf-a0.pnvm /lib/firmware/

&lt;span class="nb"&gt;sudo chown &lt;/span&gt;root:root /lib/firmware/iwlwifi-so-a0-gf-a0-89.ucode /lib/firmware/iwlwifi-so-a0-gf-a0.pnvm
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;644 /lib/firmware/iwlwifi-so-a0-gf-a0-89.ucode /lib/firmware/iwlwifi-so-a0-gf-a0.pnvm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart the driver:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;modprobe &lt;span class="nt"&gt;-r&lt;/span&gt; iwlwifi
&lt;span class="nb"&gt;sudo &lt;/span&gt;modprobe iwlwifi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;br&gt;
&lt;p&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  GPU (Intel + NVIDIA)
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Intel iGPU (Mesa)
&lt;/h4&gt;

&lt;p&gt;Install Mesa drivers and the &lt;code&gt;glxinfo&lt;/code&gt; tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;mesa-dri-drivers mesa-vulkan-drivers mesa-libGL mesa-demos
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;glxinfo | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"OpenGL renderer"&lt;/span&gt;
&lt;span class="c"&gt;# Expected: "Mesa Intel(R) ..."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  NVIDIA dGPU
&lt;/h4&gt;

&lt;p&gt;Install NVIDIA drivers from RPM Fusion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;akmod-nvidia xorg-x11-drv-nvidia-cuda
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify PRIME offload rendering:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;__NV_PRIME_RENDER_OFFLOAD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nv"&gt;__GLX_VENDOR_LIBRARY_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nvidia glxinfo | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"OpenGL renderer"&lt;/span&gt;
&lt;span class="c"&gt;# Expected: "NVIDIA GeForce RTX ..."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;/p&gt;
  Quick tip
  &lt;p&gt;Add a shell alias for easier PRIME offload testing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;prime-run&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"__NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;br&gt;
&lt;p&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  XDG Desktop Portals (Flatpak + screenshots/screencast)
&lt;/h3&gt;

&lt;p&gt;Install portal backends:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;xdg-desktop-portal xdg-desktop-portal-gnome xdg-desktop-portal-gtk xdg-desktop-portal-wlr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add or edit the configuration in &lt;code&gt;~/.config/xdg-desktop-portal/portal.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[preferred]&lt;/span&gt;
&lt;span class="py"&gt;default&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;gnome;gtk&lt;/span&gt;
&lt;span class="py"&gt;org.freedesktop.impl.portal.Screenshot&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;wlr&lt;/span&gt;
&lt;span class="py"&gt;org.freedesktop.impl.portal.ScreenCast&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;wlr&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This uses &lt;code&gt;xdg-desktop-portal-gnome&lt;/code&gt; as default and keeps &lt;code&gt;gtk&lt;/code&gt;/&lt;code&gt;wlr&lt;/code&gt; available for Wayland-specific functionality.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keyring
&lt;/h3&gt;

&lt;p&gt;Install &lt;code&gt;GNOME Keyring&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;gnome-keyring gnome-keyring-pam seahorse
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set the portal Secret provider in &lt;code&gt;~/.config/xdg-desktop-portal/portal.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[preferred]&lt;/span&gt;
&lt;span class="c"&gt;# ... existing config&lt;/span&gt;
&lt;span class="py"&gt;org.freedesktop.impl.portal.Secret&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;gnome-keyring&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Enable keyring auto-unlock in PAM
&lt;/h4&gt;

&lt;p&gt;Enable fingerprint for sudo access:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;authselect enable-feature with-pam-gnome-keyring
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Automatically change keyring password with user password
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;This only affects the login keyring&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Append or uncomment to &lt;code&gt;/etc/pam.d/passwd&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;password    optional    pam_gnome_keyring.so use_authtok
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Audio and microphone (PipeWire)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf group upgrade multimedia &lt;span class="nt"&gt;--exclude&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;PackageKit-gstreamer-plugin
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;pipewire pipewire-pulseaudio pipewire-alsa wireplumber alsa-sof-firmware

systemctl &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; pipewire pipewire-pulse wireplumber
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Bluetooth
&lt;/h3&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rfkill list
rfkill unblock bluetooth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Battery power profiles
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;power-profiles-daemon
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Fingerprint
&lt;/h3&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;fprintd fprintd-pam
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enroll / list fingerprints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fprintd-list &amp;lt;user&amp;gt;
fprintd-enroll
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Enable fingerprint for sudo access
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;This will make &lt;strong&gt;keyring auto-unlock&lt;/strong&gt; not work as expected&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;authselect enable-feature with-fingerprint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Printer
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;cups

&lt;span class="c"&gt;# Start cups service&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;cups.service
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start cups.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://localhost:631&lt;/code&gt; to get CUPS dashboard&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Search for local/network available printers&lt;/span&gt;
lpinfo &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now configure the printers from DMS "Printers" settings&lt;/p&gt;

&lt;h4&gt;
  
  
  HP Printers
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;hplip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  "Open With" / URL handler
&lt;/h3&gt;

&lt;p&gt;Install the common helper:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;xdg-utils
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set DankMaterialShell’s browser picker (&lt;code&gt;dms-open.desktop&lt;/code&gt;) as the default browser handler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xdg-settings &lt;span class="nb"&gt;set &lt;/span&gt;default-web-browser dms-open.desktop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Other quick checks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Keyboard function keys (volume, mute, brightness)&lt;/li&gt;
&lt;li&gt;Camera&lt;/li&gt;
&lt;li&gt;Screen sharing / recording&lt;/li&gt;
&lt;li&gt;External monitor&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5) Daily packages
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Nautilus file manager + quick preview
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;nautilus sushi nautilus-python
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: &lt;code&gt;sushi&lt;/code&gt; provides quick previews inside Nautilus.&lt;/p&gt;

&lt;h3&gt;
  
  
  FFmpeg (H.264 codecs)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf swap ffmpeg-free ffmpeg &lt;span class="nt"&gt;--allowerasing&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Flatpak + Flathub
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;flatpak

&lt;span class="c"&gt;# Add Flathub repository for Flatpak packages&lt;/span&gt;
flatpak remote-add &lt;span class="nt"&gt;--if-not-exists&lt;/span&gt; flathub https://dl.flathub.org/repo/flathub.flatpakrepo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Disks
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;gnome-disk-utility
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Btrfs Assistant
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;btrfs-assistant
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Fedora Everything ISO: &lt;a href="https://fedoraproject.org/misc/#everything" rel="noopener noreferrer"&gt;https://fedoraproject.org/misc/#everything&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;DankLinux docs: &lt;a href="https://danklinux.com/docs" rel="noopener noreferrer"&gt;https://danklinux.com/docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Arch Wiki (for general Linux concepts): &lt;a href="https://wiki.archlinux.org/" rel="noopener noreferrer"&gt;https://wiki.archlinux.org/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;AI is your best friend!&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>linux</category>
      <category>fedora</category>
      <category>niri</category>
      <category>dankmaterialshell</category>
    </item>
    <item>
      <title>Jurnal: A Voice-First Journal App with On-Device Whisper + LLM Note</title>
      <dc:creator>pizidavi</dc:creator>
      <pubDate>Sun, 15 Feb 2026 21:18:42 +0000</pubDate>
      <link>https://dev.to/pizidavi/jurnal-a-voice-first-journal-app-with-on-device-whisper-llm-note-36le</link>
      <guid>https://dev.to/pizidavi/jurnal-a-voice-first-journal-app-with-on-device-whisper-llm-note-36le</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/github-2026-01-21"&gt;GitHub Copilot CLI Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Jurnal is a simple mobile journaling app that makes it easy to capture "what happened today" using your voice. Instead of starting from a blank page, you record a short voice note, and the app turns it into a clean, structured journal entry you can skim later.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Jurnal&lt;/code&gt; is a &lt;em&gt;Romanian&lt;/em&gt; term that translates into English as &lt;em&gt;diary&lt;/em&gt; or &lt;em&gt;journal&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The core flow is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Record audio in-app&lt;/li&gt;
&lt;li&gt;Transcribe locally on-device using a Whisper model &lt;/li&gt;
&lt;li&gt;Send the transcription to a larger LLM to turn it into a real note, with sections like summary/highlights/intentions in Mardown&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What I like about this approach is the combination of privacy-first transcription with the "writing assistant" part happening after: I still speak naturally, and the app handles organizing the content into something readable.&lt;/p&gt;

&lt;p&gt;Teck stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React Native with Expo&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;react-native-executorch&lt;/code&gt; to handle local transcription model&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@tanstack/ai&lt;/code&gt; to provide online LLM utility&lt;/li&gt;
&lt;li&gt;TailwindCSS by Uniwind for styling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Future improvements I'm excited about:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Custom templates for different styles&lt;/li&gt;
&lt;li&gt;Choosing custom models for transcription and enrichment&lt;/li&gt;
&lt;li&gt;Manual editing of the final note in-app&lt;/li&gt;
&lt;li&gt;Smarter search with local &lt;strong&gt;RAG&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;A background queue for long-running processing&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;🔗 Repo: &lt;a href="https://github.com/pizidavi/jurnal-app" rel="noopener noreferrer"&gt;https://github.com/pizidavi/jurnal-app&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Home&lt;/th&gt;
&lt;th&gt;Note&lt;/th&gt;
&lt;th&gt;Recording&lt;/th&gt;
&lt;th&gt;Settings&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&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%2Fcz8ebp3czyt3zzx445jp.jpg" alt="Home screen" width="591" height="1280"&gt;&lt;/td&gt;
&lt;td&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%2Fmxfkui33xnbu950cm9xz.jpg" alt="Recording new note" width="591" height="1280"&gt;&lt;/td&gt;
&lt;td&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%2Fey8t58jk4uyx6vvpu9t5.jpg" alt="Note screen" width="591" height="1280"&gt;&lt;/td&gt;
&lt;td&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%2Ftzwu5vzs7sykxbqhcrvh.jpg" alt="Setting screen" width="591" height="1280"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  My Experience with GitHub Copilot CLI
&lt;/h2&gt;

&lt;p&gt;Copilot CLI was most helpful when I treated planning as a first-class step, not a formality. The biggest productivity boost came from writing a tight plan, implementing in small slices, and then letting the AI validate (and correct) itself.&lt;/p&gt;

&lt;p&gt;To support that workflow, I made the project intentionally strict: ESLint rules are restrictive, TypeScript is in strict mode with other custom strict types, and I pushed the assistant to run &lt;code&gt;lint&lt;/code&gt; and &lt;code&gt;typecheck&lt;/code&gt; command before considering anything as done. That created fast feedback loops and prevents subtle regressions from piling up.&lt;/p&gt;

&lt;p&gt;I also built a custom &lt;strong&gt;Orchestrator&lt;/strong&gt; agent that follows a &lt;code&gt;plan → implement → review&lt;/code&gt; cycle and repeats until the plan is completed. It was really fun to see the agents correct themselves when they missed a step or introduced a bug.&lt;/p&gt;

&lt;p&gt;I'm extremely positive about the future of Copilot CLI: the UI is good and the interaction model fits real development.&lt;br&gt;
One thing I'd love to see next is a persistent &lt;strong&gt;allowlist for safe commands&lt;/strong&gt; (like lint/typecheck/format), so that I don't have to approve them every time.&lt;/p&gt;

&lt;p&gt;Have fun!&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>githubchallenge</category>
      <category>cli</category>
      <category>githubcopilot</category>
    </item>
  </channel>
</rss>
