<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Docker | 空垠尘</title><link>/en/tags/docker/</link><description>Minimalist Static · Boundless Edge</description><generator>Hugo -- gohugo.io</generator><language>en</language><managingEditor>Dukang Xu</managingEditor><webMaster>Dukang Xu</webMaster><copyright>© 2026 空垠尘</copyright><lastBuildDate>Sun, 13 Sep 2026 10:00:00 +0800</lastBuildDate><atom:link href="/en/tags/docker/index.xml" rel="self" type="application/rss+xml"/><item><title>Raspberry Pi HomeLab Setup and Tuning</title><link>/en/raspberry-pi/</link><pubDate>Sun, 13 Sep 2026 10:00:00 +0800</pubDate><author>Dukang Xu</author><guid isPermaLink="true">/en/raspberry-pi/</guid><description>A complete hands-on guide covering headless OS flashing, mirror optimization, external SSD persistent mounting with UUID, GPIO-based PWM smart fan thermal control, and Docker HomeLab environment setup.</description><content:encoded><![CDATA[<h2 id="introduction-why-the-raspberry-pi-is-the-ideal-mini-home-server">Introduction: Why the Raspberry Pi is the Ideal Mini Home Server</h2>
<p>The Raspberry Pi, with its ultra-low power consumption (2–5W at idle), silent operation, and extensive GPIO expansion capabilities, is the top choice for developers and tinkerers building a 24/7 <strong>HomeLab / Mini Server</strong>.</p>
<p>However, setting up a reliable Raspberry Pi server from scratch often involves common pitfalls:</p>
<ul>
<li>Operating without an external monitor or keyboard (<strong>Headless Setup</strong>);</li>
<li>Slow package downloads and tricky mirror configurations;</li>
<li>External drives changing drive letters or losing permissions after reboot;</li>
<li>Thermal throttling under load or loud fans running at 100% all day.</li>
</ul>
<p>This comprehensive guide brings together battle-tested practices covering <strong>OS Installation</strong>, <strong>Storage Expansion</strong>, <strong>Thermal Management</strong>, and <strong>Docker Infrastructure</strong>.</p>
<pre tabindex="0"><code class="language-mermaid" data-lang="mermaid">flowchart TD
    A[&#34;1. Headless OS Flashing&lt;br/&gt;(SSH &amp; Wi-Fi Preconfiguration)&#34;] --&gt; B[&#34;2. System Mirror &amp; Network Tuning&lt;br/&gt;(APT Acceleration / Static IP)&#34;]
    B --&gt; C[&#34;3. External SSD Mounting&lt;br/&gt;(UUID / fstab / Permissions)&#34;]
    C --&gt; D[&#34;4. GPIO Thermal Fan Control&lt;br/&gt;(PWM Regulation / Silent Operation)&#34;]
    D --&gt; E[&#34;5. Docker &amp; HomeLab Stack&lt;br/&gt;(Compose / Container Matrix)&#34;]
</code></pre><hr>
<h2 id="1-headless-os-flashing--setup-no-monitor-needed">1. Headless OS Flashing &amp; Setup (No Monitor Needed)</h2>
<h3 id="1-flash-the-os-raspberry-pi-os-lite--ubuntu-server">1. Flash the OS (Raspberry Pi OS Lite / Ubuntu Server)</h3>
<p>Use the official <strong><a href="https://www.raspberrypi.com/software/">Raspberry Pi Imager</a></strong>:</p>
<ol>
<li><strong>Choose OS</strong>: Select <code>Raspberry Pi OS Lite (64-bit)</code> or <code>Ubuntu Server (64-bit)</code>. The Lite version is headless and takes up only ~100MB of RAM.</li>
<li><strong>Advanced Settings</strong>: Click the gear icon or press <code>Ctrl + Shift + X</code> (<code>Cmd + Shift + X</code> on macOS):
<ul>
<li>Check <strong>Set hostname</strong> (e.g., <code>rpi-server</code>);</li>
<li>Check <strong>Enable SSH</strong> (set username/password or inject your SSH public key);</li>
<li>Check <strong>Configure wireless LAN</strong> (enter your Wi-Fi SSID and password);</li>
<li>Set Timezone.</li>
</ul>
</li>
<li>Flash the image onto your microSD card or USB SSD.</li>
</ol>
<h3 id="2-first-boot--ssh-remote-access">2. First Boot &amp; SSH Remote Access</h3>
<p>Insert the card and power on your Raspberry Pi. Wait 1–2 minutes, then connect from your terminal:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># Connect using mDNS hostname</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">ssh your_username@rpi-server.local
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># Or connect via the assigned local IP</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">ssh your_username@192.168.1.100
</span></span></code></pre></div><hr>
<h2 id="2-package-mirror-acceleration--basic-utilities">2. Package Mirror Acceleration &amp; Basic Utilities</h2>
<p>Update your APT package sources to a fast nearby mirror to speed up installations:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># Backup original sources</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># Update repositories and upgrade packages</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">sudo apt update <span class="o">&amp;&amp;</span> sudo apt upgrade -y
</span></span><span class="line"><span class="ln">6</span><span class="cl">
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"># Install essential admin tools</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl">sudo apt install -y curl wget git vim htop zsh tmux net-tools ufw
</span></span></code></pre></div><hr>
<h2 id="3-persistent-external-ssd--hdd-mounting-via-uuid">3. Persistent External SSD / HDD Mounting via UUID</h2>
<p>microSD cards have limited write endurance and random I/O throughput. Connecting an external USB SSD or HDD for persistent data storage is highly recommended.</p>
<pre tabindex="0"><code class="language-mermaid" data-lang="mermaid">sequenceDiagram
    autonumber
    participant USB as External Drive / SSD
    participant Kernel as Linux Kernel
    participant Fstab as /etc/fstab Config
    participant Mount as Mount Point (/mnt/data)

    Kernel-&gt;&gt;USB: Reads Unique Device UUID
    Fstab-&gt;&gt;Kernel: Parses mount rules on boot (nofail, UUID, ext4)
    Kernel-&gt;&gt;Mount: Mounts directory &amp; assigns permissions
</code></pre><h3 id="1-find-the-drives-unique-uuid">1. Find the Drive&rsquo;s Unique UUID</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># List all block devices and filesystems</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">lsblk -f
</span></span></code></pre></div><p>Example output:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">NAME        FSTYPE FSVER LABEL  UUID                                 MOUNTPOINT
</span></span><span class="line"><span class="ln">2</span><span class="cl">sda
</span></span><span class="line"><span class="ln">3</span><span class="cl">└─sda1      ext4   1.0   DATA   e58f2371-d41a-4d7a-8b8a-927164b18210
</span></span></code></pre></div><blockquote>
<p>⚠️ <strong>Warning</strong>: Never use device names like <code>/dev/sda1</code> in <code>fstab</code>. If other USB devices are plugged in, device paths can change and cause boot failures. Always use the <strong>UUID</strong>.</p>
</blockquote>
<h3 id="2-create-mount-point--edit-etcfstab">2. Create Mount Point &amp; Edit <code>/etc/fstab</code></h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 1. Create target mount directory</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">sudo mkdir -p /mnt/data
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 2. Edit fstab configuration</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">sudo vim /etc/fstab
</span></span></code></pre></div><p>Append the following line to the end of the file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">UUID=e58f2371-d41a-4d7a-8b8a-927164b18210     /mnt/data       ext4    defaults,nofail,noatime 0       2
</span></span></code></pre></div><blockquote>
<p>💡 <strong>Key Options</strong>:</p>
<ul>
<li><code>defaults</code>: Standard read/write privileges.</li>
<li><code>nofail</code>: <strong>Crucial</strong>! If the drive is disconnected, the system will continue booting normally without hanging.</li>
<li><code>noatime</code>: Disables access timestamp updates on reads, prolonging drive life.</li>
</ul>
</blockquote>
<h3 id="3-apply-mount--fix-permissions">3. Apply Mount &amp; Fix Permissions</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># Test mount configuration without rebooting</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">sudo mount -a
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># Assign directory ownership to your user</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">sudo chown -R <span class="nv">$USER</span>:<span class="nv">$USER</span> /mnt/data
</span></span></code></pre></div><hr>
<h2 id="4-hardware-cooling-smart-gpio-fan-temperature-control">4. Hardware Cooling: Smart GPIO Fan Temperature Control</h2>
<p>To avoid thermal throttling while keeping your HomeLab whisper-quiet, use a simple Python script to regulate fan speed via GPIO.</p>
<h3 id="1-wiring-schema">1. Wiring Schema</h3>
<ul>
<li><strong>Fan Positive (Red)</strong> $\rightarrow$ 5V (Pin 4) or 3.3V (Pin 1)</li>
<li><strong>Fan Negative (Black)</strong> $\rightarrow$ Collector of NPN Transistor (e.g., S8050) to GND (Pin 6)</li>
<li><strong>Transistor Base</strong> $\rightarrow$ GPIO 14 (Pin 8, TXD)</li>
</ul>
<h3 id="2-smart-fan-daemon-script-temp_controlpy">2. Smart Fan Daemon Script (<code>temp_control.py</code>)</h3>
<p>Create <code>/usr/local/bin/temp_control.py</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="ch">#!/usr/bin/env python3</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"># -*- coding: utf-8 -*-</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="kn">import</span> <span class="nn">time</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="kn">import</span> <span class="nn">os</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="kn">import</span> <span class="nn">RPi.GPIO</span> <span class="k">as</span> <span class="nn">GPIO</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="n">FAN_PIN</span> <span class="o">=</span> <span class="mi">14</span>          <span class="c1"># GPIO Pin BCM 14</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="n">TEMP_HIGH</span> <span class="o">=</span> <span class="mf">55.0</span>      <span class="c1"># Turn on fan above 55°C</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="n">TEMP_LOW</span> <span class="o">=</span> <span class="mf">45.0</span>       <span class="c1"># Turn off fan below 45°C</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="n">GPIO</span><span class="o">.</span><span class="n">setwarnings</span><span class="p">(</span><span class="kc">False</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="n">GPIO</span><span class="o">.</span><span class="n">setmode</span><span class="p">(</span><span class="n">GPIO</span><span class="o">.</span><span class="n">BCM</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="n">GPIO</span><span class="o">.</span><span class="n">setup</span><span class="p">(</span><span class="n">FAN_PIN</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">OUT</span><span class="p">,</span> <span class="n">initial</span><span class="o">=</span><span class="n">GPIO</span><span class="o">.</span><span class="n">LOW</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">def</span> <span class="nf">get_cpu_temp</span><span class="p">():</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s2">&#34;/sys/class/thermal/thermal_zone0/temp&#34;</span><span class="p">,</span> <span class="s2">&#34;r&#34;</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">        <span class="k">return</span> <span class="nb">float</span><span class="p">(</span><span class="n">f</span><span class="o">.</span><span class="n">read</span><span class="p">())</span> <span class="o">/</span> <span class="mf">1000.0</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="n">fan_state</span> <span class="o">=</span> <span class="kc">False</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">    <span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">        <span class="n">temp</span> <span class="o">=</span> <span class="n">get_cpu_temp</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">        <span class="k">if</span> <span class="n">temp</span> <span class="o">&gt;=</span> <span class="n">TEMP_HIGH</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">fan_state</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">            <span class="n">GPIO</span><span class="o">.</span><span class="n">output</span><span class="p">(</span><span class="n">FAN_PIN</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">HIGH</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">            <span class="n">fan_state</span> <span class="o">=</span> <span class="kc">True</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">        <span class="k">elif</span> <span class="n">temp</span> <span class="o">&lt;=</span> <span class="n">TEMP_LOW</span> <span class="ow">and</span> <span class="n">fan_state</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">            <span class="n">GPIO</span><span class="o">.</span><span class="n">output</span><span class="p">(</span><span class="n">FAN_PIN</span><span class="p">,</span> <span class="n">GPIO</span><span class="o">.</span><span class="n">LOW</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">            <span class="n">fan_state</span> <span class="o">=</span> <span class="kc">False</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">        <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="k">except</span> <span class="ne">KeyboardInterrupt</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">    <span class="n">GPIO</span><span class="o">.</span><span class="n">cleanup</span><span class="p">()</span>
</span></span></code></pre></div><h3 id="3-register-as-a-systemd-service">3. Register as a Systemd Service</h3>
<p>Create <code>/etc/systemd/system/fan-control.service</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">[Unit]</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="na">Description</span><span class="o">=</span><span class="s">Raspberry Pi Smart Fan Temperature Control</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="na">After</span><span class="o">=</span><span class="s">multi-user.target</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">[Service]</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="na">Type</span><span class="o">=</span><span class="s">simple</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="na">ExecStart</span><span class="o">=</span><span class="s">/usr/bin/python3 /usr/local/bin/temp_control.py</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="na">Restart</span><span class="o">=</span><span class="s">always</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="na">RestartSec</span><span class="o">=</span><span class="s">10</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">[Install]</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="na">WantedBy</span><span class="o">=</span><span class="s">multi-user.target</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">sudo systemctl daemon-reload
</span></span><span class="line"><span class="ln">2</span><span class="cl">sudo systemctl <span class="nb">enable</span> --now fan-control.service
</span></span></code></pre></div><hr>
<h2 id="5-docker--modern-container-matrix-setup">5. Docker &amp; Modern Container Matrix Setup</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 1. Install Docker via official convenience script</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">curl -fsSL https://get.docker.com <span class="p">|</span> bash
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 2. Add current user to docker group</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">sudo usermod -aG docker <span class="nv">$USER</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># 3. Install Docker Compose plugin</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">sudo apt install -y docker-compose-plugin
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># 4. Configure Docker log rotation to protect disk space</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">sudo mkdir -p /etc/docker
</span></span><span class="line"><span class="ln">12</span><span class="cl">sudo tee /etc/docker/daemon.json <span class="s">&lt;&lt;-&#39;EOF&#39;
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="s">{
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="s">  &#34;log-driver&#34;: &#34;json-file&#34;,
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="s">  &#34;log-opts&#34;: {
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="s">    &#34;max-size&#34;: &#34;20m&#34;,
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="s">    &#34;max-file&#34;: &#34;3&#34;
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="s">  }
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="s">}
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="s">EOF</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl">sudo systemctl restart docker
</span></span></code></pre></div><hr>
<h2 id="conclusion">Conclusion</h2>
<p>With these five steps, your Raspberry Pi transforms into an <strong>ultra-low-power, whisper-quiet, reliable, and production-ready mini server</strong> ready to host your media, backup, and self-hosted tools.</p>
]]></content:encoded><category>Raspberry Pi</category><category>HomeLab</category><category>Linux</category><category>Hardware</category><category>Docker</category><category>Server</category></item></channel></rss>