<?xml version="1.0" encoding="utf-8" standalone="no"?><feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://u.sb/</id>
    <title>烧饼博客</title>
    <updated>2026-05-21T13:20:40.793Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <author>
        <name>Showfom</name>
        <email>i@m.ac</email>
        <uri>https://u.sb</uri>
    </author>
    <link href="https://u.sb/" rel="alternate"/>
    <link href="https://u.sb/atom.xml" rel="self"/>
    <subtitle>烧饼博客是一个专注于服务器运维、专业域名投资分析的网站。</subtitle>
    <rights>© 2026 烧饼博客</rights>
    <entry>
        <title type="html"><![CDATA[Debian / Ubuntu 下使用 nginx-acme 自动签发并配置 SSL 证书]]></title>
        <id>https://u.sb/debian-nginx-acme/</id>
        <link href="https://u.sb/debian-nginx-acme/"/>
        <link href="https://s.bh.sb/images/debian-nginx-acme.webp" rel="enclosure" type="image/webp"/>
        <updated>2026-05-20T17:00:00.000Z</updated>
        <summary type="html"><![CDATA[本文将介绍在 Debian 或 Ubuntu 下使用 nginx-acme 自动签发并配置 SSL 证书的方法。]]></summary>
        <content type="html"><![CDATA[<p><img src="https://s.bh.sb/images/debian-nginx-acme.webp" alt="Debian / Ubuntu 下使用 nginx-acme 自动签发并配置 SSL 证书" title="Debian / Ubuntu 下使用 nginx-acme 自动签发并配置 SSL 证书" /></p>
<p>本文将介绍在 Debian 或 Ubuntu 下使用 nginx-acme 自动签发并配置 SSL 证书的方法。</p>
<p>本文适合 Debian Stable 和 Ubuntu LTS，请使用 root 用户进行操作。</p>
<h2>1、什么是 nginx-acme</h2>
<p><a href="https://github.com/nginx/nginx-acme">nginx-acme</a> 是 Nginx 官方开发的基于 ACME 协议自动签发 SSL 证书的模块，隔壁 Caddy 都出了几百年的功能， Nginx 也终于赶上了。</p>
<p>和 Nginx 使用 C 语言开发不同，这个模块是用 Rust 语言开发的，这里就不做评价。</p>
<p>这个模块支持 <a href="https://datatracker.ietf.org/doc/html/rfc8555">RFC8555</a>、<a href="https://datatracker.ietf.org/doc/html/rfc8737">RFC8737</a>、<a href="https://datatracker.ietf.org/doc/html/rfc8738">RFC8738</a> 和 <a href="https://datatracker.ietf.org/doc/draft-ietf-acme-profiles/">draft-ietf-acme-profiles</a> 等规范，目前我实际测试下来已经基本可以用于生产环境。</p>
<h2>2、安装 N.WTF</h2>
<p>这里我们使用<a href="https://u.sb/">烧饼博客</a>打包的 <a href="https://n.wtf/">N.WTF</a>，这个项目已经集成了 <code>nginx-acme</code> 模块，可以做到开箱即用。</p>
<p>首先，安装一些必要的软件包：</p>
<pre><code class="language-bash">sudo apt update
sudo apt upgrade -y
sudo apt install curl vim wget gnupg dpkg apt-transport-https lsb-release ca-certificates
</code></pre>
<p>然后加入 N.WTF 的 GPG 公钥和 apt 源：</p>
<pre><code class="language-bash">curl -sSL https://n.wtf/public.key | sudo bash -c &#39;gpg --dearmor &gt; /usr/share/keyrings/n.wtf.gpg&#39;
sudo bash -c &#39;echo &quot;deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/n.wtf.gpg] https://mirror-cdn.xtom.com/sb/nginx/ $(lsb_release -sc) main&quot; &gt; /etc/apt/sources.list.d/n.wtf.list&#39;
</code></pre>
<p>国内机器可以用<a href="https://mirrors.tuna.tsinghua.edu.cn/">清华 TUNA</a> 的国内源：</p>
<pre><code class="language-bash">curl -sSL https://mirrors.tuna.tsinghua.edu.cn/n.wtf/public.key | sudo bash -c &#39;gpg --dearmor &gt; /usr/share/keyrings/n.wtf.gpg&#39;
sudo bash -c &#39;echo &quot;deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/n.wtf.gpg] https://mirrors.tuna.tsinghua.edu.cn/n.wtf/ $(lsb_release -sc) main&quot; &gt; /etc/apt/sources.list.d/n.wtf.list&#39;
</code></pre>
<p>Debian 下也可以直接使用 <a href="https://u.sb/debian-extrepo/">extrepo</a>:</p>
<pre><code class="language-sh">sudo apt update
sudo apt install extrepo -y
sudo extrepo enable n.wtf
</code></pre>
<p>接着更新系统并安装 Nginx：</p>
<pre><code class="language-sh">sudo apt update
sudo apt install nginx-extras -y
</code></pre>
<h2>3、nginx-acme 准备工作</h2>
<p>准备工作很简单，我们需要建立一个目录来存放 SSL 证书并给予正确的权限，这里我们以 <code>/var/cache/nginx/letsencrypt</code> 为演示：</p>
<pre><code class="language-sh">sudo mkdir -p /var/cache/nginx
sudo mkdir -p /var/cache/nginx/letsencrypt
sudo chown 33:33 /var/cache/nginx -R
</code></pre>
<p>然后别忘了把域名解析到你的服务器哦！</p>
<h2>4、配置 Nginx 站点</h2>
<p>我们以 <code>example.com</code> 为例，假设你的邮箱是 <code>user@example.com</code>，需要配置的域名是 <code>example.com</code> 和 <code>www.example.com</code>，并且希望访问 <code>www.example.com</code> 跳转到 <code>example.com</code>：</p>
<p>直接修改 <code>/etc/nginx/sites-enabled/default</code> 文件：</p>
<pre><code class="language-nginx">resolver 8.8.8.8:53 ipv6=off valid=5s;

acme_issuer letsencrypt {
    uri         https://acme-v02.api.letsencrypt.org/directory;
    contact     user@example.com;
    state_path  /var/cache/nginx/letsencrypt;
    accept_terms_of_service;
    ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
    ssl_verify off;
}

acme_shared_zone zone=ngx_acme_shared:1M;

server {
    # Listen on port 80 for all IPv4 and IPv6 addresses
    listen 80 default_server;
    listen [::]:80 default_server;
   
    # Match all domain names
    server_name _;

    location /.well-known/ {
        return 404;
    }

    location / {
        # Redirect all other HTTP requests to HTTPS using 301 permanent redirect
        return 301 https://$host$request_uri;
    }
}

server {
    # Standard TLS listening
    listen 443 ssl default_server;
    listen [::]:443 ssl default_server;

    # HTTP/2 protocol support
    http2 on;
   
    # HTTP/3 QUIC protocol support
    listen 443 quic reuseport;
    listen [::]:443 quic reuseport;
    add_header Alt-Svc &#39;h3=&quot;:443&quot;; ma=86400&#39; always;
    add_header X-Protocol $server_protocol always;

    server_name example.com;
    root /var/www/html;
    index index.html;

    # modern configuration
    ssl_protocols TLSv1.3;
    ssl_ecdh_curve X25519:prime256v1:secp384r1;
    ssl_prefer_server_ciphers off;

    acme_certificate letsencrypt;

    ssl_certificate       $acme_certificate;
    ssl_certificate_key   $acme_certificate_key;

    # do not parse the certificate on each request
    ssl_certificate_cache max=2;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;

    http2 on;

    listen 443 quic;
    listen [::]:443 quic;

    add_header Alt-Svc &#39;h3=&quot;:443&quot;; ma=86400&#39; always;
    add_header X-Protocol $server_protocol always;

    server_name www.example.com;
    return 301 https://example.com$request_uri;

    ssl_protocols TLSv1.3;
    ssl_ecdh_curve X25519:prime256v1:secp384r1;
    ssl_prefer_server_ciphers off;

    acme_certificate letsencrypt;

    ssl_certificate       $acme_certificate;
    ssl_certificate_key   $acme_certificate_key;

    # do not parse the certificate on each request
    ssl_certificate_cache max=2;
}
</code></pre>
<p>然后验证 Nginx 配置并重新加载：</p>
<pre><code class="language-bash">sudo nginx -t
sudo nginx -s reload
</code></pre>
<p>此时，在默认的 Nginx 日志文件 <code>/var/log/nginx/access.log</code> 中可以看到 Let&#39;s Encrypt 验证服务器的请求记录：</p>
<pre><code class="language-ini">23.178.112.210 - - [15/Jan/2026:16:08:18 +0000] &quot;GET /.well-known/acme-challenge/blablablablablablablablablablablablablablab HTTP/1.1&quot; 200 87 &quot;-&quot; &quot;Mozilla/5.0 (compatible; Let&#39;s Encrypt validation server; +https://www.letsencrypt.org)&quot;
34.212.137.78 - - [15/Jan/2026:16:08:18 +0000] &quot;GET /.well-known/acme-challenge/blablablablablablablablablablablablablablab HTTP/1.1&quot; 200 87 &quot;-&quot; &quot;Mozilla/5.0 (compatible; Let&#39;s Encrypt validation server; +https://www.letsencrypt.org)&quot;
18.222.179.58 - - [15/Jan/2026:16:08:18 +0000] &quot;GET /.well-known/acme-challenge/blablablablablablablablablablablablablablab HTTP/1.1&quot; 200 87 &quot;-&quot; &quot;Mozilla/5.0 (compatible; Let&#39;s Encrypt validation server; +https://www.letsencrypt.org)&quot;
16.171.19.61 - - [15/Jan/2026:16:08:18 +0000] &quot;GET /.well-known/acme-challenge/blablablablablablablablablablablablablablab HTTP/1.1&quot; 200 87 &quot;-&quot; &quot;Mozilla/5.0 (compatible; Let&#39;s Encrypt validation server; +https://www.letsencrypt.org)&quot;
13.228.72.222 - - [15/Jan/2026:16:08:18 +0000] &quot;GET /.well-known/acme-challenge/blablablablablablablablablablablablablablab HTTP/1.1&quot; 200 87 &quot;-&quot; &quot;Mozilla/5.0 (compatible; Let&#39;s Encrypt validation server; +https://www.letsencrypt.org)&quot;
2600:3000:2710:200::81 - - [15/Jan/2026:16:08:20 +0000] &quot;GET /.well-known/acme-challenge/blablablablablablablablablablablablablablab HTTP/1.1&quot; 200 87 &quot;-&quot; &quot;Mozilla/5.0 (compatible; Let&#39;s Encrypt validation server; +https://www.letsencrypt.org)&quot;
2600:1f14:804:fd02:f7fd:3c68:dec7:a062 - - [15/Jan/2026:16:08:20 +0000] &quot;GET /.well-known/acme-challenge/blablablablablablablablablablablablablablab HTTP/1.1&quot; 200 87 &quot;-&quot; &quot;Mozilla/5.0 (compatible; Let&#39;s Encrypt validation server; +https://www.letsencrypt.org)&quot;
2600:1f16:269:da00:c9ce:508c:ea69:9c2 - - [15/Jan/2026:16:08:20 +0000] &quot;GET /.well-known/acme-challenge/blablablablablablablablablablablablablablab HTTP/1.1&quot; 200 87 &quot;-&quot; &quot;Mozilla/5.0 (compatible; Let&#39;s Encrypt validation server; +https://www.letsencrypt.org)&quot;
2a05:d016:39f:3101:8b83:62b8:2603:d15d - - [15/Jan/2026:16:08:20 +0000] &quot;GET /.well-known/acme-challenge/blablablablablablablablablablablablablablab HTTP/1.1&quot; 200 87 &quot;-&quot; &quot;Mozilla/5.0 (compatible; Let&#39;s Encrypt validation server; +https://www.letsencrypt.org)&quot;
2406:da18:85:1401:1f69:967a:a988:cdc8 - - [15/Jan/2026:16:08:20 +0000] &quot;GET /.well-known/acme-challenge/blablablablablablablablablablablablablablab HTTP/1.1&quot; 200 87 &quot;-&quot; &quot;Mozilla/5.0 (compatible; Let&#39;s Encrypt validation server; +https://www.letsencrypt.org)&quot;
</code></pre>
<p>等待数秒后即可访问 <code>https://example.com/</code> 了。</p>
<p>如果要给 IP 地址签发证书，则需要在 <code>acme_issuer letsencrypt {}</code> 段里添加一行 <code>profile shortlived;</code>，并且 <code>server_name</code> 必须写入完整的 IP 地址，不能直接用 <code>server_name _</code> 哦。</p>
<p><del>不过目前 nginx-acme 最新版本 0.3.1 签发 IP 证书还是会失败，开发版已经修复，应该需要等下一个版本才能使用。</del></p>
<p>目前最新版本已经支持 IP 地址证书的签发，可以在 <code>accept_terms_of_service;</code> 下方加入一条 <code>profile shortlived;</code> 并且自行修改 <code>server_name example.com;</code> 为你的 IP 地址，比如 <code>server_name 192.0.2.2 2001:db8::2;</code> 即可。</p>
<p>读者们在使用过程中如果遇到问题，可以在 V2EX 交流讨论：</p>
<p><a href="https://be.st/Mv7j">https://be.st/Mv7j</a></p>
]]></content>
        <author>
            <name>Showfom</name>
            <email>i@m.ac</email>
            <uri>https://u.sb</uri>
        </author>
        <published>2026-01-16T03:05:21.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[Ubuntu 24.04 Noble 升级 Ubuntu 26.04 Resolute]]></title>
        <id>https://u.sb/ubuntu-upgrade-2604/</id>
        <link href="https://u.sb/ubuntu-upgrade-2604/"/>
        <link href="https://s.bh.sb/images/ubuntu-upgrade-2604.webp" rel="enclosure" type="image/webp"/>
        <updated>2026-05-19T05:55:00.000Z</updated>
        <summary type="html"><![CDATA[本文将指导如何升级 Ubuntu 24.04 Noble Numbat 到 Ubuntu 26.04 Resolute Raccoon。]]></summary>
        <content type="html"><![CDATA[<p><img src="https://s.bh.sb/images/ubuntu-upgrade-2604.webp" alt="Ubuntu 24.04 Noble 升级 Ubuntu 26.04 Resolute" title="Ubuntu 24.04 Noble 升级 Ubuntu 26.04 Resolute" /></p>
<p>本文将指导如何升级 Ubuntu 24.04 Noble Numbat 到 Ubuntu 26.04 Resolute Raccoon。</p>
<p>相关教程：<a href="https://u.sb/ubuntu-upgrade-2404/">Ubuntu 22.04 Jammy 升级 Ubuntu 24.04 Noble</a>。</p>
<h2>准备工作</h2>
<p>除非你是物理服务器，以及没有用过奇奇怪怪定制或修改的内核的 KVM 构架的 VPS 和云主机，否则升级大版本更新内核是有一定机率导致 Grub 加载失败的，切记备份重要数据！</p>
<p><em>OpenVZ 和 LXC 构架的 VPS 是无法升级的，因为他们没有自己独立的内核</em></p>
<p>再强调一遍，一定要备份重要数据！</p>
<p>Ubuntu 26.04 LTS 已经正式发布，不过 Ubuntu LTS 版本的自动升级提示通常会等到第一个小版本发布后才会开放。如果你在 Ubuntu 26.04.1 LTS 发布前从 Ubuntu 24.04 LTS 升级，<code>do-release-upgrade</code> 需要加上 <code>-d</code> 参数。</p>
<p>以下操作需要在 root 用户下完成，请使用 <code>sudo -i</code> 或 <code>su root</code> 切换到 root 用户进行操作</p>
<h2>更新系统</h2>
<p>首先需要更新你当前的系统</p>
<pre><code class="language-bash">apt update
apt upgrade -y
apt dist-upgrade -y
apt autoclean
apt autoremove -y
</code></pre>
<p>如果内核更新了，可以重启让最新的内核生效，也可以直接进行升级。</p>
<h2>升级系统</h2>
<p>这里有两种升级系统的方法，第一种是使用 <code>do-release-upgrade</code> 命令，第二种是手动更新 <code>apt</code> 源文件。</p>
<h3>方法一：使用 <code>do-release-upgrade</code> 命令</h3>
<p>首先需要安装 <code>ubuntu-release-upgrader-core</code> 包：</p>
<pre><code class="language-bash">apt install ubuntu-release-upgrader-core
</code></pre>
<p>然后修改 <code>/etc/update-manager/release-upgrades</code> 文件，确保 <code>Prompt</code> 值为 <code>lts</code>：</p>
<pre><code class="language-bash">cat /etc/update-manager/release-upgrades | grep lts
</code></pre>
<p>显示如下内容即可：</p>
<pre><code class="language-bash">root@ubuntu ~ # cat /etc/update-manager/release-upgrades | grep lts
#  lts    - Check to see if a new LTS release is available.  The upgrader
Prompt=lts
</code></pre>
<p>最后执行以下命令升级系统：</p>
<pre><code class="language-bash">do-release-upgrade -d
</code></pre>
<p>等 Ubuntu 26.04.1 LTS 发布并开放 LTS 自动升级后，可以直接使用：</p>
<pre><code class="language-bash">do-release-upgrade
</code></pre>
<h3>方法二：手动更新 <code>apt</code> 源文件</h3>
<p>Ubuntu 24.04 的默认软件源配置文件已经变更为 <code>DEB822</code> 格式，路径为 <code>/etc/apt/sources.list.d/ubuntu.sources</code>。我们可以直接替换 <code>noble</code> 为 <code>resolute</code>：</p>
<pre><code class="language-bash">sed -i &#39;s/noble/resolute/g&#39; /etc/apt/sources.list.d/ubuntu.sources
</code></pre>
<p>如果你的系统里仍然有传统 One-Line-Style 的源文件，也可以一并替换：</p>
<pre><code class="language-bash">sed -i &#39;s/noble/resolute/g&#39; /etc/apt/sources.list
sed -i &#39;s/noble/resolute/g&#39; /etc/apt/sources.list.d/*.list
</code></pre>
<p>如果没有对应文件会提示诸如 <code>sed: can&#39;t read /etc/apt/sources.list: No such file or directory</code> 的错误，忽略即可。</p>
<p>或者直接一行命令：</p>
<pre><code class="language-bash">sed -i &#39;s/noble/resolute/g&#39; /etc/apt/sources.list /etc/apt/sources.list.d/*.{list,sources} 2&gt;/dev/null
</code></pre>
<p>使用 <code>DEB822</code> 格式的源文件 <code>/etc/apt/sources.list.d/ubuntu.sources</code> 应该是类似这样的：</p>
<pre><code class="language-bash">Types: deb
URIs: https://archive.ubuntu.com/ubuntu
Suites: resolute resolute-updates resolute-backports
Components: main restricted universe multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg

Types: deb
URIs: http://security.ubuntu.com/ubuntu
Suites: resolute-security
Components: main restricted universe multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
</code></pre>
<p><em>国内服务器可以替换 <code>archive.ubuntu.com</code> 和 <code>security.ubuntu.com</code> 为 <code>mirrors.tuna.tsinghua.edu.cn</code></em></p>
<p>如果你有第三方源或 PPA，建议先备份并根据情况临时禁用，升级完成后再确认是否支持 Ubuntu 26.04：</p>
<pre><code class="language-bash">mkdir -p /root/apt-sources-backup
cp -a /etc/apt/sources.list.d /root/apt-sources-backup/
</code></pre>
<p>第三方源通常需要单独检查，不能简单把 <code>noble</code> 替换成 <code>resolute</code>。</p>
<p>然后我们再次执行更新系统：</p>
<pre><code class="language-bash">apt update
apt upgrade -y
apt full-upgrade -y
</code></pre>
<p><em>更新过程中会提示一些软件是否需要自动重启，选 Yes 即可，以及一些软件的配置文件是否需要更新，按照自己的情况选择即可，默认回车即视为使用旧的配置文件，一般会出现在 OpenSSH 等软件的更新上。</em></p>
<p>更新后删除不必要的软件和依赖：</p>
<pre><code class="language-bash">apt autoclean
apt autoremove -y
</code></pre>
<p>然后我们使用 <code>reboot</code> 命令重启系统，耐心等待后，查看最新的系统版本：</p>
<pre><code class="language-bash">root@ubuntu ~ # lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 26.04 LTS
Release:	26.04
Codename:	resolute
</code></pre>
<pre><code class="language-bash">root@ubuntu ~ # uname -a
Linux Nana 7.0.0-15-generic #15-Ubuntu SMP PREEMPT_DYNAMIC Wed Apr 22 16:06:43 UTC 2026 x86_64 GNU/Linux
</code></pre>
<p>这时我们就已经更新到了最新的 Ubuntu 26.04 Resolute 和内核了。</p>
]]></content>
        <author>
            <name>Showfom</name>
            <email>i@m.ac</email>
            <uri>https://u.sb</uri>
        </author>
        <published>2026-05-19T19:12:04.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[WSL 2 使用 Docker 桥接模式网络访问 HTTPS 超时的解决方法]]></title>
        <id>https://u.sb/fix-wsl-2-docker-network/</id>
        <link href="https://u.sb/fix-wsl-2-docker-network/"/>
        <link href="https://s.bh.sb/images/fix-wsl-2-docker-network.webp" rel="enclosure" type="image/webp"/>
        <updated>2026-01-21T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[本文介绍在 WSL 2 中使用 Docker 的桥接模式（bridge network）访问 HTTPS 时出现超时问题的解决方法。]]></summary>
        <content type="html"><![CDATA[<p><img src="https://s.bh.sb/images/fix-wsl-2-docker-network.webp" alt="WSL 2 使用 Docker 桥接模式网络访问 HTTPS 超时的解决方法" title="WSL 2 使用 Docker 桥接模式网络访问 HTTPS 超时的解决方法" /></p>
<p>本文介绍在 WSL 2 中使用 Docker 的桥接模式（bridge network）访问 HTTPS 时出现超时问题的解决方法。</p>
<p>最近一直在 Windows 下使用基于 WSL 2 的 Debian 进行开发。许多场景下需要使用 Docker 构建镜像，但过程中遇到一个奇怪的问题，在 Docker 容器内部访问 HTTP 站点时一切正常：</p>
<pre><code class="language-bash">$ docker run --rm curlimages/curl time curl -s http://ip.gs
192.0.2.2
real    0m 0.02s
user    0m 0.00s
sys     0m 0.00s
</code></pre>
<p>一旦切换为 HTTPS，访问就会明显变慢，并且有一定概率出现超时：</p>
<pre><code class="language-bash">$ docker run --rm curlimages/curl time curl -s https://ip.gs
192.0.2.2
real    0m 6.52s
user    0m 0.00s
sys     0m 0.00s
</code></pre>
<p>然而，如果将 Docker 切换到 Host 模式（<code>--network=host</code>），访问又恢复正常：</p>
<pre><code class="language-bash">$ docker run --rm --network=host curlimages/curl time curl -s https://ip.gs
192.2.0.2
real    0m 0.28s
user    0m 0.00s
sys     0m 0.00s
</code></pre>
<p>在排查了 WSL 2 的 Debian 系统配置后，最终请教 ChatGPT 得到结论：</p>
<blockquote>
<blockquote>
<p>Path MTU 与防火墙流量检查（inspection）不兼容导致问题</p>
</blockquote>
</blockquote>
<p>解决方法很简单：将 WSL 2 虚拟网络的 MTU 下调为 <code>1400</code> 即可。</p>
<h2>1、添加 Docker 配置</h2>
<p>在 WSL 2 的系统里，使用 root 用户修改 <code>/etc/docker/daemon.json</code> 文件：</p>
<pre><code class="language-json">{
    &quot;log-driver&quot;: &quot;json-file&quot;,
    &quot;log-opts&quot;: {
        &quot;max-size&quot;: &quot;20m&quot;,
        &quot;max-file&quot;: &quot;3&quot;
    },
    &quot;mtu&quot;: 1400,
    &quot;dns&quot;: [
      &quot;8.8.8.8&quot;,
      &quot;1.1.1.1&quot;
    ]
}
</code></pre>
<p>其中 <code>&quot;mtu&quot;: 1400,</code> 是关键配置。</p>
<p>然后在 Windows 下把 WSL 2 关闭：</p>
<pre><code class="language-powershell">wsl --shutdown
</code></pre>
<h2>2、修改 WSL 2 网卡配置</h2>
<p>在 Windows 下使用管理员身份运行 Powershell，检查网卡名称：</p>
<pre><code class="language-powershell">PS C:\Users\showfom&gt; Get-NetAdapter | Where-Object { $_.Name -like &quot;*WSL*&quot; }

Name                      InterfaceDescription                    ifIndex Status       MacAddress             LinkSpeed
----                      --------------------                    ------- ------       ----------             ---------
vEthernet (WSL (Hyper-V … Hyper-V Virtual Ethernet Adapter #3          41 Up           12-34-56-78-AB-CD        10 Gbps

PS C:\Users\showfom&gt; Get-NetAdapter | Format-Table -AutoSize

Name                               InterfaceDescription                      ifIndex Status       MacAddress        Lin
                                                                                                                    kSp
                                                                                                                    eed
----                               --------------------                      ------- ------       ----------        ---
vEthernet (Default Switch)         Hyper-V Virtual Ethernet Adapter               35 Up           12-34-56-78-AB-AB …ps
vEthernet (WSL (Hyper-V firewall)) Hyper-V Virtual Ethernet Adapter #3            41 Up           12-34-56-78-AB-CD …ps
</code></pre>
<p>输出可以看到完整名称为 <code>vEthernet (WSL (Hyper-V firewall))</code>，然后为其设置 MTU：</p>
<pre><code class="language-powershell">netsh interface ipv4 set subinterface &quot;vEthernet (WSL (Hyper-V firewall))&quot; mtu=1400 store=persistent
</code></pre>
<p>出现 <code>Ok.</code> 字样即代表设置成功。</p>
<h2>3、测试 WSL 2 中的 Docker 网络</h2>
<p>然后重新运行 WSL 2 虚拟机：</p>
<pre><code class="language-powershell">wsl -d debian
</code></pre>
<p>再次测试：</p>
<pre><code class="language-bash">$ docker run --rm curlimages/curl time curl -s https://ip.gs
192.0.2.2
real    0m 0.28s
user    0m 0.00s
sys     0m 0.00s
</code></pre>
<p>问题已完美解决，可以继续愉快地 Vibe Coding 了！ <em>★,°</em>:.☆(￣▽￣)/$:<em>.°★</em> 。</p>
]]></content>
        <author>
            <name>Showfom</name>
            <email>i@m.ac</email>
            <uri>https://u.sb</uri>
        </author>
        <published>2026-01-21T04:53:46.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[Debian / Ubuntu 使用源安装 LAMP 教程]]></title>
        <id>https://u.sb/debian-install-apache-php-mysql/</id>
        <link href="https://u.sb/debian-install-apache-php-mysql/"/>
        <link href="https://s.bh.sb/images/debian-install-apache-php-mysql.webp" rel="enclosure" type="image/webp"/>
        <updated>2026-01-18T00:00:00.002Z</updated>
        <summary type="html"><![CDATA[本文将介绍使用官方源和第三方源在 Debian 和 Ubuntu 安装最新版 Apache 2 + PHP + MySQL 的教程，并且可以自行选择 PHP 版本。]]></summary>
        <content type="html"><![CDATA[<p><img src="https://s.bh.sb/images/debian-install-apache-php-mysql.webp" alt="Debian / Ubuntu 使用源安装 LAMP 教程" title="Debian / Ubuntu 使用源安装 LAMP 教程" /></p>
<p>本文将介绍使用官方源和第三方源在 Debian 和 Ubuntu 安装最新版 Apache 2 + PHP + MySQL 的教程，并且可以自行选择 PHP 版本。</p>
<p><strong>PS：本文适用于 Debian Stable 以及 Ubuntu LTS</strong></p>
<p>以下操作需要在 root 用户下完成，请使用 <code>sudo -i</code> 或 <code>su root</code> 切换到 root 用户进行操作。</p>
<h2>1、更新系统并安装部分必要软件</h2>
<pre><code class="language-bash">apt update
apt upgrade -y
apt dist-upgrade -y
apt install curl vim wget gnupg dpkg apt-transport-https lsb-release ca-certificates
</code></pre>
<p><strong>如果您通过 iso 方式安装 Debian 并且设置了 root 密码，则默认不带 <code>sudo</code> 包，使用 <code>apt install sudo</code> 安装即可</strong></p>
<h2>2、增加 Ondřej Surý 大神打包的 PHP 源并安装 PHP 8.x</h2>
<p>和 <a href="https://u.sb/debian-install-nginx-php-mysql/">LEMP</a> 安装方法一样，我们还是使用 <a href="https://deb.sury.org/">Ondřej Surý</a> 大佬打包的 PHP 源。</p>
<p>至于为啥先装 PHP 再装 Apache 2，因为装了 PHP 以后 Apache 2 会识别你 PHP 版本然后生成对应的配置文件，所以顺序不要搞反。</p>
<h3>2.1 Debian 和 Ubuntu 安装 LAMP 区别</h3>
<p>唯一区别就是 PHP 和 Apache 2 的安装添加源方法不一样，其他的步骤都一毛一样。</p>
<h3>2.2 加入大神做好的源</h3>
<div data-code-group>
<div data-code-title="Debian"><pre><code class="language-bash">wget -O /usr/share/keyrings/php.gpg https://packages.sury.org/php/apt.gpg
echo &quot;deb [signed-by=/usr/share/keyrings/php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main&quot; &gt; /etc/apt/sources.list.d/php.list
</code></pre>
</div>
<div data-code-title="Ubuntu"><pre><code class="language-bash">add-apt-repository ppa:ondrej/php
</code></pre>
</div>
</div><p>Debian 下也可以直接使用 <a href="https://u.sb/debian-extrepo/">extrepo</a>:</p>
<pre><code class="language-sh">sudo apt update
sudo apt install extrepo -y
sudo extrepo enable sury
</code></pre>
<h3>2.3 更新系统源</h3>
<pre><code class="language-bash">apt update
apt upgrade -y
</code></pre>
<h3>2.4 安装自己需要的 PHP 版本</h3>
<p>这个源目前默认的 PHP 是 8.5.x，如果您需要其他版本，那么请修改对应的 PHP 版本号 (注意配置文件哦)。</p>
<p>这里举例 WordPress 需要的部分 PHP 包</p>
<p>安装 PHP 8.5.x (从 PHP 8.5 开始 OPcache 已经强制集成，不需要单独安装):</p>
<pre><code class="language-sh">apt install php8.5-{fpm,cli,mysql,curl,gd,mbstring,xml,zip,imap,soap,gmp,bcmath} -y
</code></pre>
<p>安装 PHP 8.4.x：</p>
<pre><code class="language-sh">apt install php8.4-{fpm,cli,mysql,curl,gd,mbstring,xml,zip,imap,opcache,soap,gmp,bcmath} -y
</code></pre>
<p>安装 PHP 8.3.x：</p>
<pre><code class="language-sh">apt install php8.3-{fpm,cli,mysql,curl,gd,mbstring,xml,zip,imap,opcache,soap,gmp,bcmath} -y
</code></pre>
<p>安装 PHP 8.2.x：</p>
<pre><code class="language-sh">apt install php8.2-fpm php8.2-cli php8.2-mysql php8.2-curl php8.2-gd php8.2-mbstring php8.2-xml php8.2-zip php8.2-imap php8.2-opcache php8.2-soap php8.2-gmp php8.2-bcmath -y
</code></pre>
<p><strong>以下版本 PHP 已经 EOL，PHP 官方不再提供支持，请尽快更新您的程序兼容最新的 PHP，如果您的程序还未兼容，建议鞭策开发者</strong></p>
<p>安装 PHP 8.1.x</p>
<pre><code class="language-bash">apt install php8.1-fpm php8.1-cli php8.1-mysql php8.1-curl php8.1-gd php8.1-mbstring php8.1-xml php8.1-zip php8.1-imap php8.1-opcache php8.1-soap php8.1-gmp php8.1-bcmath -y
</code></pre>
<p>安装 PHP 8.0.x</p>
<pre><code class="language-bash">apt install php8.0-fpm php8.0-cli php8.0-mysql php8.0-curl php8.0-gd php8.0-mbstring php8.0-xml php8.0-zip php8.0-imap php8.0-opcache php8.0-soap php8.0-gmp php8.0-bcmath -y
</code></pre>
<p>安装 PHP 7.4.x</p>
<pre><code class="language-bash">apt install php7.4-fpm php7.4-cli php7.4-mysql php7.4-curl php7.4-gd php7.4-mbstring php7.4-xml php7.4-xmlrpc php7.4-zip php7.4-json php7.4-imap php7.4-opcache php7.4-soap php7.4-gmp php7.4-bcmath -y
</code></pre>
<p>安装 PHP 7.3.x</p>
<pre><code class="language-bash">apt install php7.3-fpm php7.3-mysql php7.3-curl php7.3-gd php7.3-mbstring php7.3-xml php7.3-xmlrpc php7.3-zip php7.3-opcache
</code></pre>
<p>安装 PHP 7.2.x (PHP 7.2 开始已经不支持 mcrypt 组件)</p>
<pre><code class="language-bash">apt install php7.2-fpm php7.2-mysql php7.2-curl php7.2-gd php7.2-mbstring php7.2-xml php7.2-xmlrpc php7.2-zip php7.2-opcache
</code></pre>
<p>安装 PHP 7.1.x</p>
<pre><code class="language-bash">apt install php7.1-fpm php7.1-mysql php7.1-curl php7.1-gd php7.1-mbstring php7.1-mcrypt php7.1-xml php7.1-xmlrpc php7.1-zip php7.1-opcache
</code></pre>
<p>安装 PHP 7.0.x</p>
<pre><code class="language-bash">apt install php7.0-fpm php7.0-mysql php7.0-curl php7.0-gd php7.0-mbstring php7.0-mcrypt php7.0-xml php7.0-xmlrpc php7.0-zip php7.0-opcache
</code></pre>
<p>安装 PHP 5.6.x</p>
<pre><code class="language-bash">apt install php5.6-fpm php5.6-mysql php5.6-curl php5.6-gd php5.6-mbstring php5.6-mcrypt php5.6-xml php5.6-xmlrpc php5.6-zip php5.6-opcache
</code></pre>
<p>如果希望安装其他组件，可以通过搜索看看有没有对应的包</p>
<pre><code class="language-bash">apt-cache search php8.5* | grep php8.5
</code></pre>
<p>修改 <code>php.ini</code> 防止跨目录攻击，如果安装的 PHP 8.5.x 请相应修改 <code>/etc/php/8.5/fpm/php.ini</code> PHP 7.4.x 请相应修改 <code>/etc/php/7.4/fpm/php.ini</code></p>
<pre><code class="language-bash">sed -i &#39;s/;cgi.fix_pathinfo=1/cgi.fix_pathinfo=0/&#39; /etc/php/8.5/fpm/php.ini 
</code></pre>
<p>修改 <code>php.ini</code> 增加上传大小限制</p>
<pre><code class="language-bash">sed -i &#39;s/upload_max_filesize = 2M/upload_max_filesize = 10M/&#39; /etc/php/8.5/fpm/php.ini
sed -i &#39;s/post_max_size = 8M/post_max_size = 10M/&#39; /etc/php/8.5/fpm/php.ini
</code></pre>
<p>您也可以同时安装多个 PHP 版本，然后使用以下命令选择系统默认的 PHP 版本：</p>
<pre><code class="language-sh">update-alternatives --config php
</code></pre>
<h2>3、增加 Ondřej Surý 大神打包的 Apache 2 源并安装</h2>
<p>这里我们推荐 <a href="https://deb.sury.org/">Ondřej Surý</a> 打包的 Apache 2 源。</p>
<h3>3.1 Debian 和 Ubuntu 安装 LAMP 区别</h3>
<p>唯一区别就是 Apache 2 和 PHP 的安装添加源方法不一样，其他的步骤都一毛一样。</p>
<h3>3.2 首先增加 Apache 源</h3>
<div data-code-group>
<div data-code-title="Debian"><pre><code class="language-bash">wget -O /usr/share/keyrings/apache2.gpg https://packages.sury.org/apache2/apt.gpg
echo &quot;deb [signed-by=/usr/share/keyrings/apache2.gpg] https://packages.sury.org/apache2/ $(lsb_release -sc) main&quot; &gt; /etc/apt/sources.list.d/apache2.list
</code></pre>
</div>
<div data-code-title="Ubuntu"><pre><code class="language-bash">add-apt-repository ppa:ondrej/apache2
</code></pre>
</div>
</div><p>Debian 下也可以直接使用 <a href="https://u.sb/debian-extrepo/">extrepo</a>:</p>
<pre><code class="language-sh">sudo extrepo enable sury_apache2
</code></pre>
<h3>3.3 接着更新并安装 Apache 2.4</h3>
<pre><code class="language-bash">apt update
apt upgrade -y
apt install apache2 -y
</code></pre>
<p>安装完毕后，我们可以使用 <code>apachectl -v</code> 命令看到 Apache 2 已经是最新的 2.4 版本了</p>
<pre><code class="language-bash">root@debian ~ # apachectl -v
Server version: Apache/2.4.66 (Debian)
Server built:   2025-12-05T14:35:42
</code></pre>
<h3>3.4 Apache 2 开启 PHP-FPM 支持</h3>
<p>首先，需要开启 Apache 2 的 PHP-FPM 支持，我们以 PHP 8.5 为例，按照自己的需求，可以开启如下模块</p>
<pre><code class="language-bash">a2enconf php8.5-fpm
a2enmod proxy_fcgi
a2enmod headers
a2enmod http2
a2enmod remoteip
a2enmod ssl
a2enmod rewrite
a2enmod expires
a2enmod deflate
a2enmod mime
a2enmod setenvif
</code></pre>
<p>然后我们重启 PHP-FPM 服务</p>
<pre><code class="language-bash">systemctl restart php8.5-fpm
</code></pre>
<p>对应 PHP 7.4.x 命令如下</p>
<pre><code class="language-bash">systemctl restart php7.4-fpm
</code></pre>
<p>Apache 2 参考配置文件如下，因为默认 Debian 的 Apache 2 默认配置已经使用了 <code>example.com</code> 这个域名，所以我们以 <code>example.org</code> 为例，新建立个 <code>/etc/apache2/sites-available/example.org.conf</code></p>
<pre><code class="language-bash">cat &gt;&gt; /etc/apache2/sites-available/example.org.conf &lt;&lt; EOF
&lt;VirtualHost *:80&gt;
	ServerName example.org
	DocumentRoot /var/www/example.org
	DirectoryIndex index.php index.html index.htm
	
	ErrorLog ${APACHE_LOG_DIR}/example.org.error.log
	CustomLog ${APACHE_LOG_DIR}/example.org.access.log combined

	&lt;Directory /var/www/example.org&gt;
		Options FollowSymLinks
		AllowOverride All
		Require all granted
	&lt;/Directory&gt;
&lt;/VirtualHost&gt;
EOF
</code></pre>
<p>然后使用 <code>a2ensite</code> 命令使其生效</p>
<pre><code class="language-bash">a2ensite example.org.conf
</code></pre>
<p><em>如果不需要这个 vhost 的时候可以使用 <code>a2dissite example.org.conf</code> 命令移除</em></p>
<p>检测是否已经软链接到 <code>/etc/apache2/sites-enabled</code> 目录</p>
<pre><code class="language-bash">root@debian ~ # ls /etc/apache2/sites-enabled
000-default.conf  example.org.conf
</code></pre>
<p>到这里基本没有问题，我们可以执行 <code>apache2ctl configtest</code> 命令检查配置文件是否出错</p>
<pre><code class="language-bash">root@debian ~ # apache2ctl configtest 
Syntax OK
</code></pre>
<p>显示 <code>Syntax OK</code> 则说明所有配置文件均无问题，可以重启 Apache 2 使我们的配置生效</p>
<pre><code class="language-bash">systemctl restart apache2
</code></pre>
<p>我们的目录在 <code>/var/www/example.org</code>，我们先创建这个目录</p>
<pre><code class="language-bash">mkdir -p /var/www/example.org
</code></pre>
<p>然后创建一个 <code>phpinfo.php</code> 并输入 <code>phpinfo()</code> 函数</p>
<pre><code class="language-bash">cat &gt;&gt; /var/www/example.org/phpinfo.php &lt;&lt; EOF
&lt;?php phpinfo(); ?&gt;
EOF
</code></pre>
<p>好了，此时在浏览器输入 <code>http://example.org/phpinfo.php</code>，如果看到经典的 <code>phpinfo</code> 页面则说明安装成功，如果不成功，请仔细对比步骤查找哪里出错或在烧饼博客下方留言。</p>
<p>最终效果如下：</p>
<p><img src="https://s.bh.sb/uploads/2022/01/29/uXMprq5Wh6oQOaK.png" alt="image.png"></p>
<h2>4、安装 MariaDB</h2>
<p>这里我们使用 MariaDB 作为 MySQL 的代替</p>
<h3>4.1 首先，添加并导入 Mariadb 的官方源</h3>
<p>按照官方的<a href="https://mariadb.com/kb/en/gpg/">教程</a>导入 GPG</p>
<p>下载 GPG Key：</p>
<pre><code class="language-bash">curl -sSL https://supplychain.mariadb.com/MariaDB-Server-GPG-KEY | gpg --dearmor &gt; /usr/share/keyrings/mariadb.gpg
</code></pre>
<p>然后添加 MariaDB 的源</p>
<div data-code-group>
<div data-code-title="Debian"><pre><code class="language-sh">echo &quot;deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/mariadb.gpg] https://dlm.mariadb.com/repo/mariadb-server/11.8/repo/debian $(lsb_release -sc) main&quot; &gt; /etc/apt/sources.list.d/mariadb.list
</code></pre>
</div>
<div data-code-title="Ubuntu"><pre><code class="language-sh">echo &quot;deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/mariadb.gpg] https://dlm.mariadb.com/repo/mariadb-server/11.8/repo/ubuntu $(lsb_release -sc) main&quot; &gt; /etc/apt/sources.list.d/mariadb.list
</code></pre>
</div>
</div><p>Debian 下也可以直接使用 <a href="https://u.sb/debian-extrepo/">extrepo</a>:</p>
<pre><code class="language-sh">sudo extrepo enable mariadb-11.8
</code></pre>
<p>国内可以用清华 TUNA 的源</p>
<div data-code-group>
<div data-code-title="Debian"><pre><code class="language-sh">echo &quot;deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/mariadb.gpg] https://mirrors.tuna.tsinghua.edu.cn/mariadb/repo/11.8/debian $(lsb_release -sc) main&quot; &gt; /etc/apt/sources.list.d/mariadb.list
</code></pre>
</div>
<div data-code-title="Ubuntu"><pre><code class="language-sh">echo &quot;deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/mariadb.gpg] https://mirrors.tuna.tsinghua.edu.cn/mariadb/repo/11.8/ubuntu $(lsb_release -sc) main&quot; &gt; /etc/apt/sources.list.d/mariadb.list
</code></pre>
</div>
</div><p>您也可以<a href="https://mariadb.com/kb/en/mirror-sites-for-mariadb/">在这儿</a>找到更多的 MariaDB 源</p>
<h3>4.2 接着更新一下系统</h3>
<pre><code class="language-bash">apt update
</code></pre>
<h3>4.3 然后直接安装最新稳定版 MariaDB</h3>
<pre><code class="language-bash">apt install mariadb-server mariadb-client
</code></pre>
<p>安装完毕后强烈推荐使用 <code>mariadb-secure-installation</code> 命令做一次安全设置</p>
<h3>4.4 创建数据库并测试</h3>
<p><em>开启数据库之前，您可以使用 <code>pwgen</code> 这个小工具或者<a href="https://ip.sb/password/">随机密码生成器</a>生成一个强大的随机密码，比如 32 位，然后随意挑选一个使用</em></p>
<pre><code class="language-bash">apt install pwgen
pwgen 32
</code></pre>
<p>使用 MySQL root 用户登陆，因为默认使用 Unix domain socket 模式，所以本机不需要 MySQL root 密码即可登录</p>
<pre><code class="language-sql">mariadb -u root
</code></pre>
<p>创建数据库 <code>example_database</code></p>
<pre><code class="language-sql">CREATE DATABASE example_database DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
</code></pre>
<p>创建用户名 <code>example_user</code> 并赋予权限</p>
<pre><code class="language-sql">GRANT ALL ON example_database.* TO &#39;example_user&#39;@&#39;localhost&#39; IDENTIFIED BY &#39;这里改成您要设置的强大的没人能猜出来的随机的密码&#39;;
</code></pre>
<p>刷新 MySQL 权限</p>
<pre><code class="language-sql">FLUSH PRIVILEGES;
</code></pre>
<p>没问题以后就可以退出了</p>
<pre><code class="language-sql">EXIT;
</code></pre>
<p>新建立一个 <code>/var/www/example.org/mysql-test.php</code> 文件并测试</p>
<pre><code class="language-bash">cat &gt;&gt; /var/www/example.org/mysql-test.php &lt;&lt; EOF
&lt;?php
\$dbname = &#39;example_database&#39;;    //MySQL 数据库名
\$dbuser = &#39;example_user&#39;;   //MySQL 用户名
\$dbpass = &#39;您的强大的没人可以猜出来的密码&#39;;
\$dbhost = &#39;localhost&#39;;  //安装在本地就用 localhost
\$link = mysqli_connect(\$dbhost, \$dbuser, \$dbpass) or die(&quot;Unable to Connect to &#39;\$dbhost&#39;&quot;);
mysqli_select_db(\$link, \$dbname) or die(&quot;Could not open the db &#39;\$dbname&#39;&quot;);
\$test_query = &quot;SHOW TABLES FROM \$dbname&quot;;
\$result = mysqli_query(\$link, \$test_query);
\$tblCnt = 0;
while(\$tbl = mysqli_fetch_array(\$result)) {
  \$tblCnt++;
  #echo \$tbl[0].&quot;&amp;lt;br /&amp;gt;\n&quot;;
}
if (!\$tblCnt) {
  echo &quot;MySQL is working fine. There are no tables.&quot;;
} else {
  echo &quot;MySQL is working fine. There are \$tblCnt tables.&quot;;
}
?&gt;
EOF
</code></pre>
<p>创建完毕后访问 <code>http://example.org/mysql-test.php</code> 如果出现 <code>MySQL is working fine. There are no tables.</code> 则说明 MySQL 工作正常。</p>
<h2>5、安装 MySQL (可选)</h2>
<p>如果您必须使用某些 MySQL 才有的功能，那么可以按照 MySQL <a href="https://dev.mysql.com/doc/mysql-apt-repo-quick-guide/en/#apt-repo-fresh-install">官网的教程</a>安装 MySQL。</p>
<p><em>注意：除非您知道您在做什么，否则不要同时安装 MySQL 和 MariaDB。</em></p>
<h3>5.1 添加 apt 源</h3>
<pre><code class="language-sh">wget https://repo.mysql.com/mysql-apt-config_0.8.34-1_all.deb
dpkg -i mysql-apt-config_0.8.34-1_all.deb
</code></pre>
<p>国内的机器可以在添加完成后修改为清华 TUNA 源，您可以修改 <code>/etc/apt/sources.list.d/mysql-community.list</code> 文件，替换成如下内容：</p>
<pre><code class="language-sh">deb https://mirrors.tuna.tsinghua.edu.cn/mysql/apt/debian $(lsb_release -sc) mysql-8.0 mysql-8.4-lts mysql-apt-config mysql-tools
</code></pre>
<p>Debian 下也可以直接使用 <a href="https://u.sb/debian-extrepo/">extrepo</a>:</p>
<pre><code class="language-sh">sudo extrepo enable mysql-lts
</code></pre>
<h3>5.2 安装 MySQL</h3>
<pre><code class="language-sh">apt update
apt install mysql-server -y
</code></pre>
<p>默认 MySQL 会安装最新的 8.4 版本，如果您需要更低的版本，比如 8.0，可以使用如下命令：</p>
<pre><code class="language-sh">dpkg-reconfigure mysql-apt-config
</code></pre>
<p>您可能需要设置一个强大的 root 密码，接下来的步骤和 MariaDB 基本相同，把 <code>mariadb</code> 命令换成 <code>mysql</code> 命令即可，本文不再赘述。</p>
<p>好了，以上就是基本的 Debian 和 Ubuntu 安装最新版 LAMP 的教程，如有问题可以随时发评论留言讨论。</p>
]]></content>
        <author>
            <name>Showfom</name>
            <email>i@m.ac</email>
            <uri>https://u.sb</uri>
        </author>
        <published>2022-01-23T19:49:04.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[Debian / Ubuntu 使用源安装 LEMP 教程]]></title>
        <id>https://u.sb/debian-install-nginx-php-mysql/</id>
        <link href="https://u.sb/debian-install-nginx-php-mysql/"/>
        <link href="https://s.bh.sb/images/debian-install-nginx-php-mysql.webp" rel="enclosure" type="image/webp"/>
        <updated>2026-01-18T00:00:00.001Z</updated>
        <summary type="html"><![CDATA[本文将介绍使用官方源和第三方源在 Debian 和 Ubuntu 安装最新版 Nginx + PHP + MySQL 的教程，并且可以自行选择 PHP 版本。]]></summary>
        <content type="html"><![CDATA[<p><img src="https://s.bh.sb/images/debian-install-nginx-php-mysql.webp" alt="Debian / Ubuntu 使用源安装 LEMP 教程" title="Debian / Ubuntu 使用源安装 LEMP 教程" /></p>
<p>本文将介绍使用官方源和第三方源在 Debian 和 Ubuntu 安装最新版 Nginx + PHP + MySQL 的教程，并且可以自行选择 PHP 版本。</p>
<p><strong>PS：本文适用于 Debian Stable 以及 Ubuntu LTS</strong></p>
<p>以下操作需要在 root 用户下完成，请使用 <code>sudo -i</code> 或 <code>su root</code> 切换到 root 用户进行操作。</p>
<h2>1、更新系统并安装部分必要软件</h2>
<pre><code class="language-sh">apt update
apt upgrade -y
apt install curl vim wget gnupg dpkg apt-transport-https lsb-release ca-certificates
</code></pre>
<p><strong>如果您通过 iso 方式安装 Debian 并且设置了 root 密码，则默认不带 <code>sudo</code> 包，使用 <code>apt install sudo</code> 安装即可</strong></p>
<h2>2、增加烧饼博客打包的 Nginx 源并安装</h2>
<p>这里我们推荐<a href="https://n.wtf/">烧饼博客</a>团队打包的 Nginx 源，这货是在<a href="https://salsa.debian.org/nginx-team">官方 Nginx 打包组</a>的基础上，保持更新最新版本的 Nginx 以及 OpenSSL。</p>
<h3>2.1 首先增加 GPG Key</h3>
<pre><code class="language-sh">curl -sSL https://n.wtf/public.key | gpg --dearmor &gt; /usr/share/keyrings/n.wtf.gpg
</code></pre>
<h3>2.2 然后增加 Nginx 源</h3>
<pre><code class="language-sh">echo &quot;deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/n.wtf.gpg] https://mirror-cdn.xtom.com/sb/nginx/ $(lsb_release -sc) main&quot; &gt; /etc/apt/sources.list.d/n.wtf.list
</code></pre>
<p>国内机器可以用<a href="https://mirrors.tuna.tsinghua.edu.cn/">清华 TUNA</a> 的国内源：</p>
<pre><code class="language-sh">echo &quot;deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/n.wtf.gpg] https://mirrors.tuna.tsinghua.edu.cn/n.wtf/ $(lsb_release -sc) main&quot; &gt; /etc/apt/sources.list.d/n.wtf.list
</code></pre>
<p>Debian 下也可以直接使用 <a href="https://u.sb/debian-extrepo/">extrepo</a>:</p>
<pre><code class="language-sh">sudo apt update
sudo apt install extrepo -y
sudo extrepo enable n.wtf
</code></pre>
<h3>2.3 接着更新并安装 Nginx</h3>
<pre><code class="language-sh">apt update
apt install nginx-extras -y
</code></pre>
<p>安装完毕后，我们可以使用 <code>nginx -V</code> 命令看到 Nginx 已经是最新的 1.29.4 主线版了：</p>
<pre><code class="language-sh">root@debian ~ # nginx -V
nginx version: nginx-n.wtf/1.29.4
built by gcc 14.2.0 (Debian 14.2.0-19) 
built with OpenSSL 3.6.0 1 Oct 2025
TLS SNI support enabled
</code></pre>
<h2>3、增加 Ondřej Surý 大神打包的 PHP 源并安装 PHP 8.x</h2>
<p><a href="https://deb.sury.org/">Ondřej Surý</a> 大佬打包的 PHP 源更好用，Ubuntu 的 <a href="https://launchpad.net/~ondrej/+archive/ubuntu/php">PPA for PHP</a> 就是这位大佬做的，当然少不了 Debian 的源了，下面一步一步来。</p>
<h3>3.1 Debian 和 Ubuntu 安装 LEMP 区别</h3>
<p>唯一区别就是 PHP 的安装添加源方法不一样，其他的步骤都一毛一样。</p>
<h3>3.2 加入大神做好的源</h3>
<div data-code-group>
<div data-code-title="Debian"><pre><code class="language-sh">wget -O /usr/share/keyrings/php.gpg https://packages.sury.org/php/apt.gpg
echo &quot;deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main&quot; &gt; /etc/apt/sources.list.d/php.list
</code></pre>
</div>
<div data-code-title="Ubuntu"><pre><code class="language-sh">add-apt-repository ppa:ondrej/php
</code></pre>
</div></div><p>这个大神的 GPG 密钥每两年会更新一个新的，如果 GPG 密钥失效，重新下载 GPG 密钥即可。</p>
<p>Debian 下也可以直接使用 <a href="https://u.sb/debian-extrepo/">extrepo</a>:</p>
<pre><code class="language-sh">sudo extrepo enable sury
</code></pre>
<p>国内机器可以用<a href="https://mirror.nju.edu.cn/">南京大学</a> 的国内源：</p>
<pre><code class="language-sh">wget -O /usr/share/keyrings/php.gpg https://mirror.nju.edu.cn/sury/php/apt.gpg
echo &quot;deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/php.gpg] https://mirror.nju.edu.cn/sury/php/ $(lsb_release -sc) main&quot; &gt; /etc/apt/sources.list.d/php.list
</code></pre>
<p>Ubuntu 的 PPA 暂时没有国内镜像，可以使用 USTC 反代的方式：</p>
<pre><code class="language-sh">curl &quot;https://keyserver.ubuntu.com/pks/lookup?op=get&amp;search=0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c&quot; | gpg --dearmor &gt; /usr/share/keyrings/php.gpg
echo &quot;deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/php.gpg] https://launchpad.proxy.ustclug.org/ondrej/php/ubuntu $(lsb_release -sc) main&quot; &gt; /etc/apt/sources.list.d/php.list
</code></pre>
<p>如果 GPG 密钥失效，请<a href="https://launchpad.net/~ondrej/+archive/ubuntu/php/">查看最新的 GPG 密钥</a>。</p>
<h3>3.3 更新系统</h3>
<pre><code class="language-sh">apt update
apt upgrade -y
</code></pre>
<h3>3.4 安装自己需要的 PHP 版本</h3>
<p>这个源目前默认的 PHP 是 8.5.x，如果您需要其他版本，那么请修改对应的 PHP 版本号 (注意配置文件哦)。</p>
<p>这里举例 WordPress 需要的部分 PHP 包：</p>
<p>安装 PHP 8.5.x (从 PHP 8.5 开始 OPcache 已经强制集成，不需要单独安装):</p>
<pre><code class="language-sh">apt install php8.5-{fpm,cli,mysql,curl,gd,mbstring,xml,zip,imap,soap,gmp,bcmath} -y
</code></pre>
<p>安装 PHP 8.4.x：</p>
<pre><code class="language-sh">apt install php8.4-{fpm,cli,mysql,curl,gd,mbstring,xml,zip,imap,opcache,soap,gmp,bcmath} -y
</code></pre>
<p>安装 PHP 8.3.x：</p>
<pre><code class="language-sh">apt install php8.3-{fpm,cli,mysql,curl,gd,mbstring,xml,zip,imap,opcache,soap,gmp,bcmath} -y
</code></pre>
<p>安装 PHP 8.2.x：</p>
<pre><code class="language-sh">apt install php8.2-fpm php8.2-cli php8.2-mysql php8.2-curl php8.2-gd php8.2-mbstring php8.2-xml php8.2-zip php8.2-imap php8.2-opcache php8.2-soap php8.2-gmp php8.2-bcmath -y
</code></pre>
<p><strong>以下版本 PHP 已经 EOL，PHP 官方不再提供支持，请尽快更新您的程序兼容最新的 PHP，如果您的程序还未兼容，建议鞭策开发者</strong></p>
<p>安装 PHP 8.1.x：</p>
<pre><code class="language-sh">apt install php8.1-fpm php8.1-cli php8.1-mysql php8.1-curl php8.1-gd php8.1-mbstring php8.1-xml php8.1-zip php8.1-imap php8.1-opcache php8.1-soap php8.1-gmp php8.1-bcmath -y
</code></pre>
<p>安装 PHP 8.0.x：</p>
<pre><code class="language-sh">apt install php8.0-fpm php8.0-cli php8.0-mysql php8.0-curl php8.0-gd php8.0-mbstring php8.0-xml php8.0-zip php8.0-imap php8.0-opcache php8.0-soap php8.0-gmp php8.0-bcmath -y
</code></pre>
<p>安装 PHP 7.4.x：</p>
<pre><code class="language-sh">apt install php7.4-fpm php7.4-cli php7.4-mysql php7.4-curl php7.4-gd php7.4-mbstring php7.4-xml php7.4-xmlrpc php7.4-zip php7.4-json php7.4-imap php7.4-opcache php7.4-soap php7.4-gmp php7.4-bcmath -y
</code></pre>
<p>安装 PHP 7.3.x：</p>
<pre><code class="language-sh">apt install php7.3-fpm php7.3-mysql php7.3-curl php7.3-gd php7.3-mbstring php7.3-xml php7.3-xmlrpc php7.3-zip php7.3-opcache
</code></pre>
<p>安装 PHP 7.2.x (PHP 7.2 开始已经不支持 mcrypt 组件)：</p>
<pre><code class="language-sh">apt install php7.2-fpm php7.2-mysql php7.2-curl php7.2-gd php7.2-mbstring php7.2-xml php7.2-xmlrpc php7.2-zip php7.2-opcache
</code></pre>
<p>安装 PHP 7.1.x：</p>
<pre><code class="language-sh">apt install php7.1-fpm php7.1-mysql php7.1-curl php7.1-gd php7.1-mbstring php7.1-mcrypt php7.1-xml php7.1-xmlrpc php7.1-zip php7.1-opcache
</code></pre>
<p>安装 PHP 7.0.x：</p>
<pre><code class="language-sh">apt install php7.0-fpm php7.0-mysql php7.0-curl php7.0-gd php7.0-mbstring php7.0-mcrypt php7.0-xml php7.0-xmlrpc php7.0-zip php7.0-opcache
</code></pre>
<p>安装 PHP 5.6.x：</p>
<pre><code class="language-sh">apt install php5.6-fpm php5.6-mysql php5.6-curl php5.6-gd php5.6-mbstring php5.6-mcrypt php5.6-xml php5.6-xmlrpc php5.6-zip php5.6-opcache
</code></pre>
<p>如果希望安装其他组件，可以通过搜索看看有没有对应的包：</p>
<pre><code class="language-sh">apt-cache search php8.5* | grep php8.5
</code></pre>
<p>修改 <code>php.ini</code> 防止跨目录攻击，如果安装的 PHP 8.5.x 请修改 <code>/etc/php/8.5/fpm/php.ini</code> PHP 7.4.x 请对应修改 <code>/etc/php/7.4/fpm/php.ini</code>：</p>
<pre><code class="language-sh">sed -i &#39;s/;cgi.fix_pathinfo=1/cgi.fix_pathinfo=0/&#39; /etc/php/8.5/fpm/php.ini 
</code></pre>
<p>修改 <code>php.ini</code> 增加上传大小限制，比如我们设置 10MB：</p>
<pre><code class="language-sh">sed -i &#39;s/upload_max_filesize = 2M/upload_max_filesize = 10M/&#39; /etc/php/8.5/fpm/php.ini
sed -i &#39;s/post_max_size = 8M/post_max_size = 10M/&#39; /etc/php/8.5/fpm/php.ini
</code></pre>
<p>您也可以同时安装多个 PHP 版本，然后使用以下命令选择系统默认的 PHP 版本：</p>
<pre><code class="language-sh">update-alternatives --config php
</code></pre>
<h3>3.5 重启 PHP 和 Nginx</h3>
<pre><code class="language-sh">systemctl restart php8.5-fpm
</code></pre>
<p>对应 PHP 7.4.x 命令如下：</p>
<pre><code class="language-sh">systemctl restart php7.4-fpm
</code></pre>
<p>Nginx 参考配置文件如下，新建立个 <code>/etc/nginx/sites-available/example.com.conf</code>：</p>
<pre><code class="language-nginx">cat &gt;&gt; /etc/nginx/sites-available/example.com.conf &lt;&lt; EOF
server {
    listen 80;
    listen [::]:80;

# 指定网站目录，可根据自己情况更换，建议放在 /var/www 目录下
    root /var/www/example.com;
    index index.php index.html index.htm;

# 默认第一个域名，替换 example.com 为您的域名
    server_name example.com;

    location / {
        try_files \$uri \$uri/ =404;
    }

# 开启 PHP8.5-fpm 模式，如需要安装 PHP 7.4.x 请修改为 fastcgi_pass unix:/run/php/php7.4-fpm.sock;
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.5-fpm.sock;
    }
}
EOF
</code></pre>
<p>然后把这个配置文件软链接到 <code>/etc/nginx/sites-enabled</code> 目录使其生效：</p>
<pre><code class="language-sh">ln -s /etc/nginx/sites-available/example.com.conf /etc/nginx/sites-enabled/example.com.conf
</code></pre>
<p>到这里基本没有问题，可以直接重新加载 Nginx：</p>
<pre><code class="language-sh">nginx -t
nginx -s reload
</code></pre>
<p>或者暴力点直接重启 Nginx：</p>
<pre><code class="language-sh">systemctl restart nginx
</code></pre>
<p>我们的目录在 <code>/var/www/example.com</code>，我们先创建这个目录：</p>
<pre><code class="language-sh">mkdir -p /var/www/example.com
</code></pre>
<p>然后创建一个 <code>phpinfo.php</code> 并输入 <code>phpinfo()</code> 函数：</p>
<pre><code class="language-sh">cat &gt;&gt; /var/www/example.com/phpinfo.php &lt;&lt; EOF
&lt;?php phpinfo(); ?&gt;
EOF
</code></pre>
<p>好了，此时在浏览器输入 <code>http://example.com/phpinfo.php</code>，如果看到经典的 <code>phpinfo</code> 页面则说明安装成功，如果不成功，请仔细对比步骤查找哪里出错。</p>
<p>效果如下：</p>
<p><img src="https://s.bh.sb/images/debian-lemp-php-info.png" alt="Debian Install LEMP PHPINFO"></p>
<h2>4、安装 MariaDB</h2>
<p>自从 Debian 9.x Stretch 开始，Debian <a href="https://mariadb.com/resources/blog/mariadb-server-default-debian-9">已经默认使用</a> Mariadb，所以我们不做对于 MySQL 和 MariaDB 的争论，直接跟着开源社区走即可。</p>
<h3>4.1 首先，添加并导入 Mariadb 的官方源</h3>
<p>下载 GPG Key：</p>
<pre><code class="language-sh">curl -sSL https://supplychain.mariadb.com/MariaDB-Server-GPG-KEY | gpg --dearmor &gt; /usr/share/keyrings/mariadb.gpg
</code></pre>
<p>然后添加 MariaDB 的源：</p>
<div data-code-group>
<div data-code-title="Debian"><pre><code class="language-sh">echo &quot;deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/mariadb.gpg] https://dlm.mariadb.com/repo/mariadb-server/11.8/repo/debian $(lsb_release -sc) main&quot; &gt; /etc/apt/sources.list.d/mariadb.list
</code></pre>
</div>
<div data-code-title="Ubuntu"><pre><code class="language-sh">echo &quot;deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/mariadb.gpg] https://dlm.mariadb.com/repo/mariadb-server/11.8/repo/ubuntu $(lsb_release -sc) main&quot; &gt; /etc/apt/sources.list.d/mariadb.list
</code></pre>
</div>
</div><p>Debian 下也可以直接使用 <a href="https://u.sb/debian-extrepo/">extrepo</a>:</p>
<pre><code class="language-sh">sudo extrepo enable mariadb-11.8
</code></pre>
<p>国内可以用清华 TUNA 的源：</p>
<div data-code-group>
<div data-code-title="Debian"><pre><code class="language-sh">echo &quot;deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/mariadb.gpg] https://mirrors.tuna.tsinghua.edu.cn/mariadb/repo/11.8/debian $(lsb_release -sc) main&quot; &gt; /etc/apt/sources.list.d/mariadb.list
</code></pre>
</div>
<div data-code-title="Ubuntu"><pre><code class="language-sh">echo &quot;deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/mariadb.gpg] https://mirrors.tuna.tsinghua.edu.cn/mariadb/repo/11.8/ubuntu $(lsb_release -sc) main&quot; &gt; /etc/apt/sources.list.d/mariadb.list
</code></pre>
</div>
</div><p>您也可以<a href="https://mariadb.com/kb/en/mirror-sites-for-mariadb/">在这儿</a>找到更多的 MariaDB 源。</p>
<h3>4.2 接着更新一下系统</h3>
<pre><code class="language-sh">apt update
</code></pre>
<h3>4.3 然后直接安装最新稳定版 MariaDB</h3>
<pre><code class="language-sh">apt install mariadb-server mariadb-client
</code></pre>
<p>安装完毕后强烈推荐使用 <code>mariadb-secure-installation</code> 命令做一次安全设置。</p>
<h3>4.4 创建数据库并测试</h3>
<p><em>开启数据库之前，您可以使用 <code>pwgen</code> 这个小工具或者<a href="https://free.tools/tools/random-password-generator/">随机密码生成器</a>生成一个强大的随机密码，比如 32 位，然后随意挑选一个使用</em></p>
<pre><code class="language-sh">apt install pwgen
pwgen 32
</code></pre>
<p>使用 Mariadb root 用户登陆，因为默认使用 Unix domain socket 模式，所以本机不需要 MySQL root 密码即可登录：</p>
<pre><code class="language-sql">mariadb -u root
</code></pre>
<p>创建数据库 <code>example_database</code>：</p>
<pre><code class="language-sql">CREATE DATABASE example_database DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
</code></pre>
<p>创建用户名 <code>example_user</code> 并赋予权限：</p>
<pre><code class="language-sql">GRANT ALL ON example_database.* TO &#39;example_user&#39;@&#39;localhost&#39; IDENTIFIED BY &#39;这里改成您要设置的强大的没人能猜出来的随机的密码&#39;;
</code></pre>
<p>刷新 MySQL 权限：</p>
<pre><code class="language-sql">FLUSH PRIVILEGES;
</code></pre>
<p>没问题以后就可以退出了：</p>
<pre><code class="language-sql">EXIT;
</code></pre>
<p>新建立一个 <code>/var/www/example.com/mysql-test.php</code> 文件并测试：</p>
<pre><code class="language-sh">cat &gt;&gt; /var/www/example.com/mysql-test.php &lt;&lt; EOF
&lt;?php
\$dbname = &#39;example_database&#39;;    //MySQL 数据库名
\$dbuser = &#39;example_user&#39;;   //MySQL 用户名
\$dbpass = &#39;您的强大的没人可以猜出来的密码&#39;;
\$dbhost = &#39;localhost&#39;;  //安装在本地就用 localhost
\$link = mysqli_connect(\$dbhost, \$dbuser, \$dbpass) or die(&quot;Unable to Connect to &#39;\$dbhost&#39;&quot;);
mysqli_select_db(\$link, \$dbname) or die(&quot;Could not open the db &#39;\$dbname&#39;&quot;);
\$test_query = &quot;SHOW TABLES FROM \$dbname&quot;;
\$result = mysqli_query(\$link, \$test_query);
\$tblCnt = 0;
while(\$tbl = mysqli_fetch_array(\$result)) {
  \$tblCnt++;
  #echo \$tbl[0].&quot;&amp;lt;br /&amp;gt;\n&quot;;
}
if (!\$tblCnt) {
  echo &quot;MySQL is working fine. There are no tables.&quot;;
} else {
  echo &quot;MySQL is working fine. There are \$tblCnt tables.&quot;;
}
?&gt;
EOF
</code></pre>
<p>创建完毕后访问 <code>http://example.com/mysql-test.php</code> 如果出现 <code>MySQL is working fine. There are no tables.</code> 则说明 MariaDB 工作正常。</p>
<h2>5、安装 MySQL (可选)</h2>
<p>如果您必须使用某些 MySQL 才有的功能，那么可以按照 MySQL <a href="https://dev.mysql.com/doc/mysql-apt-repo-quick-guide/en/#apt-repo-fresh-install">官网的教程</a>安装 MySQL。</p>
<p><em>注意：除非您知道您在做什么，否则不要同时安装 MySQL 和 MariaDB。</em></p>
<h3>5.1 添加 apt 源</h3>
<pre><code class="language-sh">wget https://repo.mysql.com/mysql-apt-config_0.8.34-1_all.deb
dpkg -i mysql-apt-config_0.8.34-1_all.deb
</code></pre>
<p>国内的机器可以在添加完成后修改为清华 TUNA 源，您可以修改 <code>/etc/apt/sources.list.d/mysql-community.list</code> 文件，替换成如下内容：</p>
<pre><code class="language-sh">deb https://mirrors.tuna.tsinghua.edu.cn/mysql/apt/debian $(lsb_release -sc) mysql-8.0 mysql-8.4-lts mysql-apt-config mysql-tools
</code></pre>
<p>Debian 下也可以直接使用 <a href="https://u.sb/debian-extrepo/">extrepo</a>:</p>
<pre><code class="language-sh">sudo extrepo enable mysql-lts
</code></pre>
<h3>5.2 安装 MySQL</h3>
<pre><code class="language-sh">apt update
apt install mysql-server -y
</code></pre>
<p>默认 MySQL 会安装最新的 8.4 版本，如果您需要更低的版本，比如 8.0，可以使用如下命令：</p>
<pre><code class="language-sh">dpkg-reconfigure mysql-apt-config
</code></pre>
<p>您可能需要设置一个强大的 root 密码，接下来的步骤和 MariaDB 基本相同，把 <code>mariadb</code> 命令换成 <code>mysql</code> 命令即可，本文不再赘述。</p>
<p>好了，以上就是基本的 Debian 和 Ubuntu 安装最新版 LEMP 的教程，如有问题可以随时发评论留言讨论。</p>
]]></content>
        <author>
            <name>Showfom</name>
            <email>i@m.ac</email>
            <uri>https://u.sb</uri>
        </author>
        <published>2022-01-23T19:49:04.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[Docker 安装 Shlink 自建短网址]]></title>
        <id>https://u.sb/docker-shlink/</id>
        <link href="https://u.sb/docker-shlink/"/>
        <link href="https://s.bh.sb/images/docker-shlink.webp" rel="enclosure" type="image/webp"/>
        <updated>2026-01-18T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[本文将指导使用 Docker 安装 Shlink 搭建自建短网址服务。]]></summary>
        <content type="html"><![CDATA[<p><img src="https://s.bh.sb/images/docker-shlink.webp" alt="Docker 安装 Shlink 自建短网址" title="Docker 安装 Shlink 自建短网址" /></p>
<p>本文将指导使用 Docker 安装 Shlink 搭建自建短网址服务。</p>
<p><em>PS：本文同时适用于任何可安装 Docker 的 Linux 发行版。</em></p>
<h2>什么是短网址？</h2>
<p>短网址，即 URL Shortener (缩略网址服务)，一般我们使用 <code>HTTP 协议</code> 的 <code>301</code> 或 <code>302</code> 响应码，现在也有使用 <code>307</code> 或 <code>308</code> 来跳转一个长网址，简单的区别：</p>
<table>
<thead>
<tr>
<th align="center">状态码</th>
<th align="left">名称</th>
<th align="center">是否永久</th>
<th align="center">是否保留请求方法</th>
<th align="center">浏览器缓存</th>
</tr>
</thead>
<tbody><tr>
<td align="center">301</td>
<td align="left">Moved Permanently</td>
<td align="center">✅ 永久</td>
<td align="center">❌ 可能改为 GET</td>
<td align="center">会缓存</td>
</tr>
<tr>
<td align="center">302</td>
<td align="left">Found</td>
<td align="center">❌ 临时</td>
<td align="center">❌ 可能改为 GET</td>
<td align="center">不缓存</td>
</tr>
<tr>
<td align="center">307</td>
<td align="left">Temporary Redirect</td>
<td align="center">❌ 临时</td>
<td align="center">✅ 保留</td>
<td align="center">不缓存</td>
</tr>
<tr>
<td align="center">308</td>
<td align="left">Permanent Redirect</td>
<td align="center">✅ 永久</td>
<td align="center">✅ 保留</td>
<td align="center">会缓存</td>
</tr>
</tbody></table>
<p>MDN 上有对这几个状态码的详细介绍：</p>
<ul>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status/301">301 Moved Permanently</a></li>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status/302">302 Found</a></li>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status/307">307 Temporary Redirect</a></li>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status/308">308 Permanent Redirect</a></li>
</ul>
<p><code>301</code> 和 <code>302</code> 有一个最重要的区别，前者会在浏览器留下缓存，后者不会，导致如果你需要精确的统计访客，尤其是有一些使用一个浏览器的重复访客会不准确，但是影响不大，而使用 <code>302</code> 每次都会请求服务器造成服务器资源紧张，所以一般没有特殊需求的话，使用 <code>301</code> 就行。</p>
<p>举一个典型的 <code>301</code> 跳转的例子：</p>
<pre><code class="language-bash">root@debian ~ # curl -I http://u.sb/ -A Mozilla
HTTP/1.1 301 Moved Permanently
Date: Tue, 26 Apr 2022 18:28:14 GMT
Content-Type: text/html
Content-Length: 162
Location: https://u.sb/
</code></pre>
<p>我们可以看到，使用浏览器访问 <code>http://u.sb/</code> 的时候，会返回 <code>HTTP/1.1 301 Moved Permanently</code> 状态，对应跳转到 Location <code>https://u.sb/</code>。</p>
<h2>市面上开源和收费的短网址源码</h2>
<p>众所周知，本人的短域名贼多，对各种短网址程序都有所研究，市面上主要有这几款免费的短网址程序：</p>
<ul>
<li><a href="https://docs.blink.rest">Blink</a> - Easy-to-host，SSO-integrated，CDN-powered link shortener (+decoupled analytics) for teams。(<a href="https://github.com/JaneJeon/blink">Source Code</a>) <code>AGPL-3.0</code> <code>Nodejs</code></li>
<li><a href="https://kutt.it">Kutt</a> - A modern URL shortener with support for custom domains。(<a href="https://github.com/thedevs-network/kutt">Source Code</a>) <code>MIT</code> <code>Nodejs</code></li>
<li><a href="https://project.polr.me/">Polr</a> - Modern，minimalist，modular，and lightweight URL shortener。(<a href="https://github.com/Cydrobolt/polr">Source Code</a>) <code>GPL-2.0</code> <code>PHP</code></li>
<li><a href="https://shlink.io">Shlink</a> - URL shortener with REST API and command line interface。Includes official progressive web application and docker images。(<a href="https://github.com/shlinkio/shlink">Source Code</a>，<a href="https://shlink.io/apps">Clients</a>) <code>MIT</code> <code>PHP</code></li>
<li><a href="https://yourls.org/">YOURLS</a> - YOURLS is a set of PHP scripts that will allow you to run Your Own URL Shortener。Features include password protection，URL customization，bookmarklets，statistics，API，plugins，jsonp。(<a href="https://github.com/YOURLS/YOURLS">Source Code</a>) <code>MIT</code> <code>PHP</code></li>
</ul>
<p>我基本上都安装使用过，数据量大了以后性能基本惨不忍睹，对比以后还是使用 <a href="https://www.php.net/manual/en/intro.swoole.php">PHP Swoole</a> 写的 <a href="https://shlink.io">Shlink</a> 稍微占优，所以本文推荐安装 Shlink。</p>
<p><del>至于收费的？呵呵，没一个好用的，建议别去踩坑，我都帮你们踩过了。。。</del></p>
<p>广告：因为市面上没有好用的收费短网址，所以我们做了一个 <a href="https://s.ee/">S.EE</a> 欢迎购买使用~</p>
<h2>安装 Docker 和 Docker Compose</h2>
<p>Debian 和 Ubuntu 系统请参考<a href="https://u.sb/debian-install-docker/">本站教程</a>。</p>
<p>其他 Linux 系统可以使用 Docker 官方的脚本安装 Docker 和 Docker Compose：</p>
<pre><code class="language-bash">curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
</code></pre>
<h2>安装 Shlink Server 和 Web Client</h2>
<p>参考官网的<a href="https://shlink.io/documentation/install-docker-image/">安装教程</a>，我们可以把 Server 和 Web Client 装在一个地方方便管理。</p>
<p>首先我们新建 <code>/opt/shlink</code> 和 <code>/opt/shlink/data</code> 目录：</p>
<pre><code class="language-bash">mkdir -p /opt/shlink
mkdir -p /opt/shlink/data
</code></pre>
<p>然后我们新建一个 <code>compose.yaml</code> 文件，假设你的域名是 <code>example.com</code>，放在 <code>/opt/shlink/compose.yaml</code>：</p>
<pre><code class="language-bash">cat &gt; /opt/shlink/compose.yaml &lt;&lt; EOF
services:
    shlink:
      image: shlinkio/shlink:stable
      container_name: shlink
      ports:
        - 127.0.0.1:8080:8080
      environment:
        - DEFAULT_DOMAIN=example.com
        - IS_HTTPS_ENABLED=true
        - GEOLITE_LICENSE_KEY=
        - DB_DRIVER=maria
        - DB_NAME=shlink
        - DB_USER=shlink
        - DB_PASSWORD=随机密码1
        - DB_HOST=db
        - DB_PORT=3306
        - TIMEZONE=UTC
        - REDIRECT_STATUS_CODE=301
      depends_on:
        db:
          condition: service_healthy
      restart: always

    db:
      image: mariadb:lts
      container_name: db
      ports:
        - 127.0.0.1:3306:3306
      environment:
        - MYSQL_ROOT_PASSWORD=随机密码2
        - MYSQL_DATABASE=shlink
        - MYSQL_USER=shlink
        - MYSQL_PASSWORD=随机密码1
      volumes:
        - /opt/shlink/data:/var/lib/mysql
      healthcheck:
        test: [&quot;CMD&quot;, &quot;healthcheck.sh&quot;, &quot;--connect&quot;, &quot;--innodb_initialized&quot;]
        interval: 10s
        timeout: 5s
        retries: 3
        start_period: 30s
      restart: always

    shlink-web-client:
        image: shlinkio/shlink-web-client:stable
        container_name: shlink-web-client
        ports:
          - 127.0.0.1:8081:8080
        restart: always
EOF
</code></pre>
<p>如果希望有用户登录等功能，则可以用官方的下一代面板 <a href="https://github.com/shlinkio/shlink-dashboard">shlink-dashboard</a>，这里我们可以让 <code>shlink-dashboard</code> 容器和 <code>shlink</code> 容器共用一个 MariaDB 容器和用户，首先还是一样建立 docker compose 文件：</p>
<pre><code class="language-bash">cat &gt; /opt/shlink/compose.yaml &lt;&lt; EOF
services:
    shlink:
      image: shlinkio/shlink:stable
      container_name: shlink
      ports:
        - 127.0.0.1:8080:8080
      environment:
        - DEFAULT_DOMAIN=example.com
        - IS_HTTPS_ENABLED=true
        - GEOLITE_LICENSE_KEY=
        - DB_DRIVER=maria
        - DB_NAME=shlink
        - DB_USER=shlink
        - DB_PASSWORD=随机密码1
        - DB_HOST=db
        - DB_PORT=3306
        - TIMEZONE=UTC
        - REDIRECT_STATUS_CODE=301
      depends_on:
        db:
          condition: service_healthy
      restart: always

    db:
      image: mariadb:lts
      container_name: db
      ports:
        - 127.0.0.1:3306:3306
      environment:
        - MYSQL_ROOT_PASSWORD=随机密码2
        - MYSQL_DATABASE=shlink
        - MYSQL_USER=shlink
        - MYSQL_PASSWORD=随机密码1
      volumes:
        - /opt/shlink/data:/var/lib/mysql
        - /opt/shlink/mariadb-init:/docker-entrypoint-initdb.d
      healthcheck:
        test: [&quot;CMD&quot;, &quot;healthcheck.sh&quot;, &quot;--connect&quot;, &quot;--innodb_initialized&quot;]
        interval: 10s
        timeout: 5s
        retries: 3
        start_period: 30s
      restart: always

    shlink-dashboard:
      image: shlinkio/shlink-dashboard:stable
      container_name: shlink-dashboard
      ports:
        - &#39;127.0.0.1:8081:8080&#39;
      environment:
        - SHLINK_DASHBOARD_DB_DRIVER=mariadb
        - SHLINK_DASHBOARD_DB_HOST=db
        - SHLINK_DASHBOARD_DB_PORT=3306
        - SHLINK_DASHBOARD_DB_USER=shlink
        - SHLINK_DASHBOARD_DB_PASSWORD=随机密码1
        - SHLINK_DASHBOARD_DB_NAME=shlink-dashboard
        - SHLINK_DASHBOARD_SESSION_SECRETS=secret1,secret2  # 设置会话加密，需要设置多个高强度随机值，用半角逗号隔开
      depends_on:
        db:
          condition: service_healthy
      restart: always
EOF
</code></pre>
<p>然后创建一个 <code>/opt/shlink/mariadb-init/01-create-databases.sql</code> 文件</p>
<pre><code class="language-bash">mkdir -p /opt/shlink/mariadb-init

cat &gt; /opt/shlink/mariadb-init/01-create-databases.sql &lt;&lt; &#39;EOF&#39;
CREATE DATABASE IF NOT EXISTS `shlink-dashboard`;
GRANT ALL PRIVILEGES ON `shlink-dashboard`.* TO &#39;shlink&#39;@&#39;%&#39;;
FLUSH PRIVILEGES;
EOF
</code></pre>
<p>然后我们可以先启动 MariaDB 容器创建用户和数据库：</p>
<pre><code class="language-bash">cd /opt/shlink
docker compose up db -d
</code></pre>
<p>验证下是否成功：</p>
<pre><code class="language-bash">docker exec -it db mariadb -u root -p&#39;随机密码2&#39; -e &quot;SHOW DATABASES;&quot;
</code></pre>
<p>看到有 <code>shlink</code> 和 <code>shlink-dashboard</code> 两个数据库就成功了：</p>
<pre><code class="language-bash"># docker exec -it db mariadb -u root -p&#39;随机密码2&#39; -e &quot;SHOW DATABASES;&quot;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| shlink             |
| shlink-dashboard   |
| sys                |
+--------------------+
</code></pre>
<p>注意：</p>
<p><code>GEOLITE_LICENSE_KEY</code> 需要在 <a href="https://www.maxmind.com/">Maxmind</a> 注册帐号获取，可以参考《<a href="https://u.sb/docker-plausible/#%E5%AE%89%E8%A3%85-plausible-analytics-t2">使用 Docker 安装 Plausible Analytics 自建网站统计</a>》</p>
<p><code>MYSQL_ROOT_PASSWORD</code> 和 <code>MYSQL_PASSWORD</code> 记得设置两个随机的密码，同时 <code>MYSQL_PASSWORD</code> 需要和 <code>DB_PASSWORD</code> 一致。</p>
<p>更多的环境变量参数可以参考<a href="https://shlink.io/documentation/environment-variables/">这里</a>。</p>
<p>然后拉取所有的 Docker 镜像并运行：</p>
<pre><code class="language-bash">docker compose pull
docker compose up -d
</code></pre>
<p>然后获取一个 API Key：</p>
<pre><code class="language-bash">docker exec -it shlink shlink api-key:generate
</code></pre>
<p><img src="https://s.bh.sb/uploads/2022/04/27/F3ehKWInwVkqQdD.png" alt="image.png"></p>
<p>注意第一个 <code>shlink</code> 是 Docker 容器名字，第二个 <code>shlink</code> 是命令名称。</p>
<p>所有 API 命令如下：</p>
<pre><code class="language-bash">docker exec -it shlink shlink
</code></pre>
<p>记得保存你的 API Key，下面会要用到。</p>
<h2>安装配置 Nginx 反代</h2>
<p>我们的 Docker Compose 配置文件中，Shlink Server 服务监听在 <code>127.0.0.1:8080</code> 端口，Shlink Web Client 监听在 <code>127.0.0.1:8081</code> 端口，所以我们需要配置 Nginx 反代来访问，假设你短网址是 <code>https://example.com/</code> Web 客户端是 <code>https://app.example.com/</code></p>
<p><code>example.com</code> 段配置：</p>
<pre><code class="language-nginx">	location / {
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header Host $http_host;
		proxy_http_version 1.1;
		proxy_set_header Upgrade $http_upgrade;
		proxy_redirect off;
		proxy_set_header        X-Forwarded-Proto $scheme;
		proxy_connect_timeout       300;
		proxy_send_timeout          300;
		proxy_read_timeout          300;
		send_timeout                300;
		proxy_pass http://127.0.0.1:8080;
	}
</code></pre>
<p><code>app.example.com</code> 段配置：</p>
<pre><code class="language-nginx">	location / {
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header Host $http_host;
		proxy_http_version 1.1;
		proxy_set_header Upgrade $http_upgrade;
		proxy_redirect off;
		proxy_set_header        X-Forwarded-Proto $scheme;
		proxy_connect_timeout       300;
		proxy_send_timeout          300;
		proxy_read_timeout          300;
		send_timeout                300;
		proxy_pass http://127.0.0.1:8081;
	}
</code></pre>
<p>最后记得参考本站 <a href="https://u.sb/nginx-ssl/">Nginx SSL 配置教程</a>加上 SSL 证书后，即可访问 <code>https://app.example.com/</code>：</p>
<p>点击 <code>+ Add a server</code> 添加你的 Shlink 服务：</p>
<p><img src="https://s.bh.sb/uploads/2022/04/27/MqzdHDPh6F9AvWu.png" alt="image.png"></p>
<p>输入名称，URL 和 API Key 以后点击 <code>Create server</code> 即可使用：</p>
<p><img src="https://s.bh.sb/uploads/2022/04/27/4RmMsk3VCUHDjhf.png" alt="image.png"></p>
<p>如果你懒得搭建 Web Client，也可以使用官方现成的服务：</p>
<p><a href="https://app.shlink.io/">Shlink Web Client</a></p>
<p>数据都是储存在浏览器本地的，可放心使用。</p>
<p>如果搭建的 Shlink Dashboard，则默认账号密码都是 <code>admin</code>， 登录以后记得修改哦！</p>
<h2>升级 Shlink</h2>
<p>直接使用 Docker Compose 升级并删除旧的镜像文件：</p>
<pre><code class="language-bash">cd /opt/shlink
docker compose pull
docker compose up -d
docker system prune -f
</code></pre>
<p>切记不要跨多个版本升级，最好按照每个小版本的最新补丁顺序更新 Shlink。</p>
<p>比如，要从 <code>3.2.1</code> 版本升级到 <code>3.4.0</code>，先更新到 <code>3.3.2</code>，然后再升级到 <code>3.4.0</code>，可以自行替换 Docker 镜像里的 <code>stable</code> 标签为具体版本号来更新升级。</p>
<h2>迁移 Shlink</h2>
<p>可以参考《<a href="https://u.sb/docker-mailcow/#mailcow-%E7%9A%84%E8%BF%81%E7%A7%BB-t2">使用 Docker 安装 Mailcow 自建域名邮箱</a>》。</p>
<h2>备份 Shlink</h2>
<p>我们可以定期备份数据库，导出命令如下：</p>
<pre><code class="language-bash">docker exec db mariadb-dump -u root --password=&#39;随机密码2&#39; --databases shlink &gt; shlink-all-$(date +&quot;%Y_%m_%d_%I_%M_%p&quot;).sql
</code></pre>
<p>如果装了 <code>shlink-dashboard</code>：</p>
<pre><code class="language-bash">docker exec db mariadb-dump -u root --password=&#39;随机密码2&#39; --databases shlink shlink-dashboard &gt; shlink-all-$(date +&quot;%Y_%m_%d_%I_%M_%p&quot;).sql
</code></pre>
<p>请替换 <code>随机密码2</code> 为你的数据库 <code>root</code> 密码。</p>
]]></content>
        <author>
            <name>Showfom</name>
            <email>i@m.ac</email>
            <uri>https://u.sb</uri>
        </author>
        <published>2022-04-27T03:51:25.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[使用 acme.sh 配置 Let's Encrypt 签发的 IP 地址 SSL 证书]]></title>
        <id>https://u.sb/acme-sh-ip-ssl/</id>
        <link href="https://u.sb/acme-sh-ip-ssl/"/>
        <link href="https://s.bh.sb/images/acme-sh-ip-ssl.webp" rel="enclosure" type="image/webp"/>
        <updated>2025-12-17T01:00:00.000Z</updated>
        <summary type="html"><![CDATA[本文将介绍使用 acme.sh 配置 Let's Encrypt 为 IP 地址签发 SSL 证书。]]></summary>
        <content type="html"><![CDATA[<p><img src="https://s.bh.sb/images/acme-sh-ip-ssl.webp" alt="使用 acme.sh 配置 Let's Encrypt 签发的 IP 地址 SSL 证书" title="使用 acme.sh 配置 Let's Encrypt 签发的 IP 地址 SSL 证书" /></p>
<p>本文将介绍使用 acme.sh 配置 Let&#39;s Encrypt 为 IP 地址签发 SSL 证书。</p>
<p>之前写过一篇使用 <a href="https://github.com/acmesh-official/acme.sh">acme.sh</a> 签发证书的<a href="https://u.sb/acme-sh-ssl/">教程</a>，但在很长一段时间里，Let&#39;s Encrypt 只能给<strong>域名</strong>签发证书。  </p>
<p>经过几个月的<a href="https://letsencrypt.org/2025/07/01/issuing-our-first-ip-address-certificate">测试</a>之后，现在终于可以对 <strong>IP 地址</strong> 下手了。</p>
<h2>为什么要给 IP 签发证书</h2>
<p>在很多场景下，我们并不一定需要域名，但<strong>确实需要 HTTPS</strong>。比如：</p>
<ol>
<li>DNS over HTTPS（DoH）服务无需依赖域名解析</li>
</ol>
<p>直接通过 IP 提供 DoH 服务，避免「为了安全先做一次不安全的域名解析」这种哲学问题。</p>
<ol start="2">
<li>Web 服务默认站点隐藏真实域名</li>
</ol>
<p>默认站点只暴露 IP，不暴露真实域名，顺便还能挡掉一部分不太礼貌的爬虫。</p>
<ol start="3">
<li>临时服务或测试环境</li>
</ol>
<p>临时起个服务，只想加个锁，不想再去 DNS 那边折腾。</p>
<ol start="4">
<li>避免证书透明日志（Certificate Transparency Log）暴露域名，保护隐私</li>
</ol>
<p>有些域名不太想出现在公开日志里，低调一点总是好的。</p>
<h2>准备工作</h2>
<p>首先更新 acme.sh 到最新版本：</p>
<pre><code class="language-sh">acme.sh --upgrade
</code></pre>
<p>因为 IP 证书目前只能通过 <a href="https://letsencrypt.org/docs/challenge-types/#http-01-challenge">http-01</a> 和 <a href="https://letsencrypt.org/docs/challenge-types/#tls-alpn-01">tls-alpn-01</a> 方式进行验证，所以你需要检查服务器的防火墙，设置允许 TCP 80 和 TCP / UDP 443 端口在公网可以访问。</p>
<h2>配置 Nginx 80 端口的默认站点</h2>
<p>这里我只介绍在 Nginx 下的配置吧，我们可以直接写入 80 端口的默认配置：</p>
<p>如果你在 Debian 或 Ubuntu 下安装 Nginx，可以直接覆盖 <code>/etc/nginx/sites-available/default</code> 文件：</p>
<pre><code class="language-nginx">server {
    # Listen on port 80 for all IPv4 and IPv6 addresses
    listen 80 default_server;
    listen [::]:80 default_server;
   
    # Match all domain names
    server_name _;

    # Merge Let&#39;s Encrypt and SSL verification path configuration
    location ~ ^/.well-known/(acme-challenge|pki-validation)/ {
        add_header Content-Type text/plain;
        root /var/www/letsencrypt;
    }

    # Redirect all other HTTP requests to HTTPS using 301 permanent redirect
    location / {
        return 301 https://$host$request_uri;
    }
}
</code></pre>
<p>然后创建两个目录并重新加载 Nginx：</p>
<pre><code class="language-bash">mkdir -p /var/www/letsencrypt
mkdir -p /etc/nginx/ssl
nginx -t
nginx -s reload
</code></pre>
<h2>使用 acme.sh 签发 IP 证书</h2>
<p>假设你服务器的 IP 地址是 <code>192.0.2.2</code> 和 <code>2001:db8::2</code>：</p>
<pre><code class="language-bash">acme.sh --issue --server letsencrypt -d 192.0.2.2 -d 2001:db8::2 \
  -w /var/www/letsencrypt \
  --certificate-profile shortlived \
  --days 3
</code></pre>
<p>注意这里我们必须使用 <a href="https://letsencrypt.org/docs/profiles/#shortlived">shortlived</a> 这个 Profile，因为 Let&#39;s Encrypt 的 IP 证书有效期只有 6.66666 天（160 小时），同时 acme.sh 需要更短的时间来进行检查更新证书，所以可以设置 <code>--days 3</code> 参数，让它 3 天检查并更新一次，你也可以设置 4 或 5，但是不要设置 6，否则可能证书过期了都没更新哦。</p>
<p>执行命令以后会看到类似的申请成功返回：</p>
<pre><code>[Wed Dec 17 05:46:28 AM UTC 2025] Using CA: https://acme-v02.api.letsencrypt.org/directory
[Wed Dec 17 05:46:28 AM UTC 2025] Multi domain=&#39;IP:192.0.2.2,IP:2001:db8::2&#39;
[Wed Dec 17 05:46:30 AM UTC 2025] Getting webroot for domain=&#39;192.0.2.2&#39;
[Wed Dec 17 05:46:30 AM UTC 2025] Getting webroot for domain=&#39;2001:db8::2&#39;
[Wed Dec 17 05:46:30 AM UTC 2025] Verifying: 192.0.2.2
[Wed Dec 17 05:46:31 AM UTC 2025] Pending. The CA is processing your order, please wait. (1/30)
[Wed Dec 17 05:46:34 AM UTC 2025] Success
[Wed Dec 17 05:46:34 AM UTC 2025] Verifying: 2001:db8::2
[Wed Dec 17 05:46:35 AM UTC 2025] Pending. The CA is processing your order, please wait. (1/30)
[Wed Dec 17 05:46:38 AM UTC 2025] Success
[Wed Dec 17 05:46:38 AM UTC 2025] Verification finished, beginning signing.
[Wed Dec 17 05:46:38 AM UTC 2025] Let&#39;s finalize the order.
[Wed Dec 17 05:46:38 AM UTC 2025] Le_OrderFinalize=&#39;https://acme-v02.api.letsencrypt.org/acme/finalize/blablablablablablablabla/blablablablablablablabla&#39;
[Wed Dec 17 05:46:41 AM UTC 2025] Downloading cert.
[Wed Dec 17 05:46:41 AM UTC 2025] Le_LinkCert=&#39;https://acme-v02.api.letsencrypt.org/acme/cert/blablablablablablablabla&#39;
[Wed Dec 17 05:46:42 AM UTC 2025] Cert success.
-----BEGIN CERTIFICATE-----
blablablablablablablablablablablablablablablablablablabla
-----END CERTIFICATE-----
[Wed Dec 17 05:46:42 AM UTC 2025] Your cert is in: /root/.acme.sh/192.0.2.2_ecc/192.0.2.2.cer
[Wed Dec 17 05:46:42 AM UTC 2025] Your cert key is in: /root/.acme.sh/192.0.2.2_ecc/192.0.2.2.key
[Wed Dec 17 05:46:42 AM UTC 2025] The intermediate CA cert is in: /root/.acme.sh/192.0.2.2_ecc/ca.cer
[Wed Dec 17 05:46:42 AM UTC 2025] And the full-chain cert is in: /root/.acme.sh/192.0.2.2_ecc/fullchain.cer
</code></pre>
<p>然后我们可以把申请好的证书放在 <code>/etc/nginx/ssl</code> 目录：</p>
<pre><code class="language-bash">mkdir -p /etc/nginx/ssl

acme.sh --install-cert -d 192.0.2.2 \
  --key-file       /etc/nginx/ssl/ip.key  \
  --fullchain-file /etc/nginx/ssl/ip.crt \
  --ca-file        /etc/nginx/ssl/ip.ca.crt \
  --reloadcmd     &quot;systemctl restart nginx&quot;
</code></pre>
<h2>配置 Nginx 443 端口的默认站点</h2>
<p>安装完证书后我们即可配置 Nginx 默认的 443 端口了，你可以把这段配置一起放入 <code>/etc/nginx/sites-available/default</code> 文件：</p>
<pre><code class="language-nginx"># HTTPS Server block - Handle all HTTPS requests
server {
    # Standard TLS listening
    listen 443 ssl default_server;
    listen [::]:443 ssl default_server;

    # HTTP/2 protocol support
    http2 on;
   
    # HTTP/3 QUIC protocol support
    listen 443 quic reuseport;
    listen [::]:443 quic reuseport;
    add_header Alt-Svc &#39;h3=&quot;:443&quot;; ma=86400&#39; always;
    add_header X-Protocol $server_protocol always;
   
    # Match all domain names
    server_name _;
    return 403;

    # modern configuration
    ssl_protocols TLSv1.3;
    ssl_ecdh_curve X25519:prime256v1:secp384r1;
    ssl_prefer_server_ciphers off;

    ssl_certificate /etc/nginx/ssl/ip.crt;
    ssl_certificate_key /etc/nginx/ssl/ip.key;
}
</code></pre>
<p>然后检查并重新加载 Nginx：</p>
<pre><code class="language-bash">nginx -t
nginx -s reload
</code></pre>
<p>一切就绪以后就可以直接访问 <code>https://192.0.2.2/</code> 并返回 403 错误页面，我们可以看到证书里 <code>Subject Alt Names</code> 字段也显示 <code>IP Address</code> 了：</p>
<p><img src="https://s.bh.sb/2025/12/17/image_sK4sr.png" alt=""></p>
<p>读者们在使用过程中如果遇到问题，可以在 V2EX 交流讨论或在下方评论：</p>
<p><a href="https://be.st/pGx9">https://be.st/pGx9</a></p>
]]></content>
        <author>
            <name>Showfom</name>
            <email>i@m.ac</email>
            <uri>https://u.sb</uri>
        </author>
        <published>2025-12-17T16:13:37.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[Debian 双栈网络时开启 IPv4 优先]]></title>
        <id>https://u.sb/debian-prefer-ipv4/</id>
        <link href="https://u.sb/debian-prefer-ipv4/"/>
        <link href="https://s.bh.sb/images/debian-prefer-ipv4.webp" rel="enclosure" type="image/webp"/>
        <updated>2025-11-04T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[本文原理适用于大多数 Linux 系统，其他系统尚未测试，请读者自行验证。]]></summary>
        <content type="html"><![CDATA[<p><img src="https://s.bh.sb/images/debian-prefer-ipv4.webp" alt="Debian 双栈网络时开启 IPv4 优先" title="Debian 双栈网络时开启 IPv4 优先" /></p>
<p>本文原理适用于大多数 Linux 系统，其他系统尚未测试，请读者自行验证。</p>
<h2>背景介绍</h2>
<p>双协议栈技术就是指在一台设备上同时启用 IPv4 协议栈和 IPv6 协议栈，这样就可以同时使用 IPv4 和 IPv6 的网络。</p>
<p>现代操作系统和浏览器通常会优先使用 IPv6，只有当 IPv6 无法访问时，才会回退到 IPv4。但在某些特定的应用或场景中，我们可能更希望系统优先使用 IPv4，这时就需要通过配置文件进行调整。</p>
<h2>修改 /etc/gai.conf</h2>
<p>在 Debian 等 Linux 系统下，有一个 <code>/etc/gai.conf</code> 文件，用于系统的 <code>getaddrinfo</code> 调用，默认情况下，它会使用 IPv6 优先，如果您安装了 <code>curl</code> 并且本地支持 IPv6，那么可以使用 <code>curl ip.sb</code> 测试：</p>
<pre><code class="language-bash">root@debian ~ # curl ip.sb
2001:db8::2
</code></pre>
<p>结果与 <code>curl ip.sb -6</code> 等效。</p>
<p>从 Debian 13 开始，curl (8.14.1) 默认强制使用 IPv6。因此，如果希望测试本地出口公网 IP，可以改用 <code>wget</code>：</p>
<pre><code class="language-bash">root@debian ~ # wget -qO- http://ip.sb
2001:db8::2
</code></pre>
<p>效果等同于 <code>wget -qO- http://ip.sb -6</code></p>
<p>如果你不想使用 IPv6 优先，可以在这个文件中找到：</p>
<pre><code class="language-bash">#precedence ::ffff:0:0/96  100
</code></pre>
<p>取消注释，修改为：</p>
<pre><code class="language-bash">precedence ::ffff:0:0/96  100
</code></pre>
<p>一行命令修改：</p>
<pre><code class="language-bash">sed -i &#39;s/#precedence ::ffff:0:0\/96  100/precedence ::ffff:0:0\/96  100/&#39; /etc/gai.conf
</code></pre>
<p>此时再次执行 <code>curl ip.sb</code> 测试：</p>
<pre><code class="language-bash">root@debian ~ # curl ip.sb
192.0.2.2
</code></pre>
<p>效果等同于 <code>curl ip.sb -4</code></p>
<p>从 Debian 13 开始的 curl (8.14.1) 会强制 IPv6 优先，所以我们可以使用 <code>wget</code> 命令：</p>
<pre><code class="language-bash">root@debian ~ # wget -qO- http://ip.sb
192.0.2.2
</code></pre>
<p>效果等同于 <code>wget -qO- http://ip.sb -4</code></p>
<p>某些情况下，你可能又需要强制启用 IPv6 优先（是的，有些系统和用户的需求确实有点奇怪😅），因为目前 IANA 分配的公网 IPv6 还未进行到 <code>3000:0000::/4</code>，所以我们只要把这段之前的 IPv6 加到优先级列表即可，加入这两行 <code>label</code> 的优先级：</p>
<pre><code class="language-bash">label 2002::/16    1
label 2001:0::/32   1
</code></pre>
<h2>禁用 IPv6</h2>
<p>有一些极端情况下，我们可能需要禁止系统的 IPv6 功能，这时候就需要添加或修改 <code>/etc/sysctl.d/local.conf</code> 文件，首先找到你的网卡名称，这里以 <code>eth0</code> 为例，然后加入如下内容：</p>
<pre><code class="language-bash">net.ipv6.conf.all.autoconf = 0
net.ipv6.conf.default.autoconf = 0
net.ipv6.conf.all.accept_ra = 0
net.ipv6.conf.default.accept_ra = 0
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
net.ipv6.conf.eth0.disable_ipv6 = 1
</code></pre>
<p>若需对其他网卡禁用 IPv6，只需将 <code>eth0</code> 替换为对应网卡名称即可。</p>
<p>一句话命令</p>
<pre><code class="language-bash">cat &gt;&gt; /etc/sysctl.d/local.conf &lt;&lt; EOF
net.ipv6.conf.all.autoconf = 0
net.ipv6.conf.default.autoconf = 0
net.ipv6.conf.all.accept_ra = 0
net.ipv6.conf.default.accept_ra = 0
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
net.ipv6.conf.eth0.disable_ipv6 = 1
EOF
</code></pre>
<p>注意：<code>cat</code> 命令中的 <code>&gt;&gt;</code> 表示追加内容；若使用 <code>&gt;</code>，则会覆盖原有内容。</p>
<p>然后执行 <code>sysctl --system</code> 重新加载配置文件。此时查看 <code>ip a</code>，即可发现 IPv6 已被禁用。</p>
<p>下图为修改前后的对比示例：</p>
<p>使用前，我们可以看到无论是本地还是公网网卡都有 <code>inet6</code>，即都有 IPv6 地址：</p>
<p><img src="https://s.bh.sb/uploads/2022/02/10/oyKXC1YLbu2Mi6j.png" alt="image.png"></p>
<p>使用后，无论本地还是公网网卡均无 IPv6 地址：</p>
<p><img src="https://s.bh.sb/uploads/2022/02/10/qi2nwjWeLfQXJH4.png" alt="image.png"></p>
<h2>其他系统和软件</h2>
<p>Windows 系统可参考<a href="https://superuser.com/questions/436574/ipv4-vs-ipv6-priority-in-windows-7">这篇回答</a></p>
<p>Firefox 下打开 <code>about:config</code> 然后把 <code>network.dns.disableIPv6</code> 改成 <code>true</code> 即可禁止 Firefox 请求 IPv6</p>
]]></content>
        <author>
            <name>Showfom</name>
            <email>i@m.ac</email>
            <uri>https://u.sb</uri>
        </author>
        <published>2022-02-10T18:17:32.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[RDAP.SS - 基于 RDAP 协议的 Whois 查询网站]]></title>
        <id>https://u.sb/rdap-ss/</id>
        <link href="https://u.sb/rdap-ss/"/>
        <link href="https://s.bh.sb/images/rdap-ss.webp" rel="enclosure" type="image/webp"/>
        <updated>2025-11-03T08:00:00.000Z</updated>
        <summary type="html"><![CDATA[RDAP.SS 是一个基于 RDAP 协议的 Whois 查询网站，本文介绍 RDAP 协议以及如何使用。]]></summary>
        <content type="html"><![CDATA[<p><img src="https://s.bh.sb/images/rdap-ss.webp" alt="RDAP.SS - 基于 RDAP 协议的 Whois 查询网站" title="RDAP.SS - 基于 RDAP 协议的 Whois 查询网站" /></p>
<p>RDAP.SS 是一个基于 RDAP 协议的 Whois 查询网站，本文介绍 RDAP 协议以及如何使用。</p>
<h2>1、什么是 RDAP 协议？</h2>
<p><a href="https://www.icann.org/en/contracted-parties/registry-operators/resources/registration-data-access-protocol">RDAP</a>，全称 Registration Data Access Protocol，由 IETF（互联网工程任务组）制定，主要用于查询以下注册信息：</p>
<ul>
<li>域名（Domain）</li>
<li>IP 地址（IPv4 / IPv6）</li>
<li>ASN（自治系统号）</li>
</ul>
<p>它定义在 RFC <a href="https://www.rfc-editor.org/rfc/rfc7480.txt">7480</a>、<a href="https://www.rfc-editor.org/rfc/rfc7481.txt">7481</a>、<a href="https://www.rfc-editor.org/rfc/rfc7482.txt">7482</a>、<a href="https://www.rfc-editor.org/rfc/rfc7483.txt">7483</a>、<a href="https://www.rfc-editor.org/rfc/rfc7484.txt">7484</a> 等系列标准中。</p>
<h2>2、RDAP 和 Whois 协议的区别</h2>
<p>以下是对两者主要区别的总结：</p>
<table>
<thead>
<tr>
<th>比较项目</th>
<th>WHOIS</th>
<th>RDAP</th>
</tr>
</thead>
<tbody><tr>
<td>传输协议</td>
<td>基于 TCP 文本协议</td>
<td>基于 HTTPS RESTful API</td>
</tr>
<tr>
<td>数据格式</td>
<td>纯文本、非结构化</td>
<td>JSON 格式、结构化</td>
</tr>
<tr>
<td>国际化支持</td>
<td>差，编码不统一</td>
<td>完全支持 UTF-8</td>
</tr>
<tr>
<td>安全性</td>
<td>无加密、无认证</td>
<td>支持 HTTPS、OAuth</td>
</tr>
<tr>
<td>分布式查询</td>
<td>依靠人工跳转</td>
<td>内置 <code>bootstrap</code> 机制，可自动重定向至正确注册局</td>
</tr>
<tr>
<td>标准化程度</td>
<td>各注册局格式不同</td>
<td>格式统一，易于机器读取与解析</td>
</tr>
</tbody></table>
<p>在传统的 Whois 协议中，用户需要给 Whois 服务器的 43 端口发送查询，然后 Whois 服务器返回纯文本的结果。</p>
<p>这导致了传统的 Whois 协议有几个无法修补的劣势：</p>
<ol>
<li>非结构化的文本输出，没有标准化的接口</li>
</ol>
<p>传统的 Whois 没有统一的查询和返回 API，只有简单的文本命令，这导致每家注册局返回的信息格式不一致，例如有的字段写作 <code>Registrant Email</code>，有的写作 <code>Contact Email</code>。这给开发者带来了巨大的解析工作量，需要为不同注册局甚至注册商编写对应的匹配规则。</p>
<ol start="2">
<li>非加密传输</li>
</ol>
<p>TCP 43 端口使用明文传输，任何中间节点（包括运营商）都能看到查询内容。🤷‍♂️ 这在 2025 年已经难以被接受。</p>
<ol start="3">
<li>缺乏访问控制权限</li>
</ol>
<p>Whois 服务器只能识别查询的 IP 地址，无法针对单个用户分配权限或进行身份区分，安全设置也只能基于 IP 层面。</p>
<ol start="4">
<li>管理混乱</li>
</ol>
<p>Whois 客户端需要为每个 TLD 手动配置对应的 Whois 服务器，缺乏类似 RDAP 的自动 <code>bootstrap</code> 机制。</p>
<h2>3、RDAP 协议的优势</h2>
<p>而 RDAP 协议完美的弥补了这些劣势：</p>
<ol>
<li>标准化的 JSON 输出</li>
</ol>
<p>RDAP 返回的数据是结构化的 JSON，字段统一定义，例如：</p>
<pre><code class="language-json">{
  &quot;objectClassName&quot;: &quot;domain&quot;,
  &quot;ldhName&quot;: &quot;example.com&quot;,
  &quot;status&quot;: [&quot;active&quot;],
  &quot;entities&quot;: [...]
}
</code></pre>
<p>这让程序能直接解析字段，无需依赖正则或人工格式识别。</p>
<ol start="2">
<li>统一的 API</li>
</ol>
<p>RDAP 基于 HTTP/HTTPS 的 RESTful API，支持 GET 请求、分页、过滤等现代查询方式。</p>
<p>例如：</p>
<pre><code class="language-sh">GET https://rdap.verisign.com/com/v1/domain/example.com
</code></pre>
<p>即可从注册局调用 <code>example.com</code> 的信息</p>
<ol start="3">
<li>Bootstrap 自动重定向</li>
</ol>
<p>RDAP 客户端无需知道具体注册商，它会根据 IANA 的 bootstrap 数据自动跳转到正确的注册局或 RIR</p>
<p>IANA 的 Bootstrap 数据是公开的，可在以下地址访问：</p>
<p><a href="https://data.iana.org/rdap/">https://data.iana.org/rdap/</a></p>
<p>按照 RDAP.org 的<a href="https://deployment.rdap.org/">统计</a>，目前大约有 77% 的注册局已经接入了 RDAP 协议，除了少部分 TLD 需要手工添加 RDAP 服务器，大部分已经都接入了 IANA 的 bootstrap 数据。</p>
<ol start="4">
<li>安全和隐私</li>
</ol>
<p>RDAP 强制使用 HTTPS，保障查询与返回内容的完整性与保密性。防止中间人攻击与数据监听。</p>
<p>如有需要，注册局还可以通过 OAuth 2.0 或 Token 认证实现访问控制，根据用户身份（如公众、注册商或执法机构）返回不同级别的信息。这非常契合 GDPR 等隐私法规的要求。</p>
<ol start="5">
<li>可扩展性</li>
</ol>
<p>RDAP 支持使用 extensions（扩展字段），注册局可以在标准字段外添加自定义信息，而不会破坏兼容性。</p>
<p>例如：</p>
<pre><code class="language-json">&quot;rdapConformance&quot;: [&quot;rdap_level_0&quot;, &quot;icann_rdap_technical_implementation_guide_0&quot;]
</code></pre>
<h2>4、RDAP.SS 网站</h2>
<p>基于 RDAP 的优势，我使用 Claude Code 开发了一个基于 RDAP 协议的 Whois 查询网站： <a href="https://rdap.ss/">RDAP.SS</a></p>
<p>技术栈：</p>
<ul>
<li>Next.js</li>
<li>Tailwind CSS</li>
<li>Redis 缓存</li>
</ul>
<p>目前支持如下格式的 Whois 查询：</p>
<ul>
<li>域名 - <a href="https://rdap.ss/whois/google.com">https://rdap.ss/whois/google.com</a></li>
<li>IPv4 - <a href="https://rdap.ss/whois/8.8.8.8">https://rdap.ss/whois/8.8.8.8</a></li>
<li>IPv4 CIDR - <a href="https://rdap.ss/whois/8.8.8.0/24">https://rdap.ss/whois/8.8.8.0/24</a></li>
<li>IPv6 - <a href="https://rdap.ss/whois/2001:4860:4860::8888">https://rdap.ss/whois/2001:4860:4860::8888</a></li>
<li>IPv6 CIDR - <a href="https://rdap.ss/whois/2001:4860::/32">https://rdap.ss/whois/2001:4860::/32</a></li>
<li>ASN - <a href="https://rdap.ss/whois/AS15169">https://rdap.ss/whois/AS15169</a></li>
</ul>
<p>仅当对应的 TLD 注册局支持 RDAP 协议时，域名查询才可用；未支持的注册局会降级为传统 Whois 协议返回结果。</p>
<p>如果在使用过程中遇到问题，请随时在 GitHub 提交 <a href="https://github.com/rdapss/rdap.ss/issues">issue</a>。</p>
]]></content>
        <author>
            <name>Showfom</name>
            <email>i@m.ac</email>
            <uri>https://u.sb</uri>
        </author>
        <published>2025-11-03T17:01:40.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[Debian 使用 extrepo 配置第三方软件源]]></title>
        <id>https://u.sb/debian-extrepo/</id>
        <link href="https://u.sb/debian-extrepo/"/>
        <link href="https://s.bh.sb/images/debian-extrepo.webp" rel="enclosure" type="image/webp"/>
        <updated>2025-10-20T20:45:00.000Z</updated>
        <summary type="html"><![CDATA[本文将指导如何在 Debian 下使用 extrepo 配置第三方软件源。]]></summary>
        <content type="html"><![CDATA[<p><img src="https://s.bh.sb/images/debian-extrepo.webp" alt="Debian 使用 extrepo 配置第三方软件源" title="Debian 使用 extrepo 配置第三方软件源" /></p>
<p>本文将指导如何在 Debian 下使用 extrepo 配置第三方软件源。</p>
<h2>什么是 extrepo？</h2>
<p><a href="https://packages.debian.org/stable/extrepo">extrepo</a> 用于管理 Debian 中的外部软件源。</p>
<p>在没有 extrepo 之前，想要使用未被 Debian 官方打包的软件，用户通常需要手动编写 APT 配置文件、以 root 身份运行未经签名的脚本，或安装一个包含所有系统配置的未签名 <code>.deb</code> 包。</p>
<p>遗憾的是，这些方法都不是很安全。</p>
<p>打个比方，我们要添加 Docker 的软件源，有三种方式。</p>
<p>第一种是传统的 <code>One-Line Style</code>：</p>
<pre><code class="language-bash">curl -sSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor &gt; /usr/share/keyrings/docker-ce.gpg

echo &quot;deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-ce.gpg] https://download.docker.com/linux/debian $(lsb_release -sc) stable&quot; | sudo bash -c &#39;cat &gt; /etc/apt/sources.list.d/docker-ce.list&#39;

sudo apt update
sudo install docker-ce docker-ce-cli containerd.io docker-compose-plugin -y
</code></pre>
<p>第二种是新的 <code>DEB822</code> 格式：</p>
<pre><code class="language-bash">curl -sSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor &gt; /usr/share/keyrings/docker-ce.gpg

sudo bash -c &#39;cat &gt; /etc/apt/sources.list.d/docker-ce.sources &lt;&lt; EOF
Components: stable
Architectures: $(dpkg --print-architecture)
Suites: $(lsb_release -cs)
Types: deb
Uris: https://download.docker.com/linux/debian
Signed-By: /usr/share/keyrings/docker-ce.gpg
EOF&#39;

sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin -y
</code></pre>
<p>第三种方法则更为简单粗暴，直接运行脚本：</p>
<pre><code class="language-bash">curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
</code></pre>
<p>在以上这些方法中（包括第三种脚本方式），我们都需要手动下载并导入 GPG 密钥，创建必要的 APT 配置文件，更新软件列表，然后再安装软件。而我们推荐使用 extrepo 的话，只需要简单的三个命令即可完成整个过程。</p>
<h2>安装并使用 extrepo</h2>
<p>Debian Stable 下直接用如下命令安装 extrepo 即可：</p>
<pre><code class="language-bash">sudo apt update
sudo apt install extrepo -y
</code></pre>
<p>接着我们就可以启用比如 Docker CE 的仓库源：</p>
<pre><code class="language-bash">sudo extrepo enable docker-ce
</code></pre>
<p>然后更新系统并安装 Docker：</p>
<pre><code class="language-bash">sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin -y
</code></pre>
<p>此时我们会发现在 <code>/etc/apt/sources.list.d</code> 目录中多了一个 <code>extrepo_docker-ce.sources</code> 文件：</p>
<pre><code class="language-bash"># cat /etc/apt/sources.list.d/extrepo_docker-ce.sources 
Suites: trixie
Types: deb
Uris: https://download.docker.com/linux/debian
Components: stable
Architectures: amd64 arm64 armhf s390x ppc64el
Signed-By: /var/lib/extrepo/keys/docker-ce.asc
</code></pre>
<p>换句话说，extrepo 帮我们完成了以下工作：</p>
<ol>
<li>抓取经过验证的 GPG Key</li>
<li>使用 DEB822 格式写入 APT 配置</li>
</ol>
<p>对比之前的几种方法，你可能还需要满大街找配置命令，找脚本命令，现在只要敲几行命令就可以搞定，何乐而不为呢？</p>
<h2>External Repository Metadata</h2>
<p>看到这里读者可能有疑问，extrepo 的数据又是谁维护的呢？它是由 <a href="https://salsa.debian.org/extrepo-team">Debian External Repositories Team</a> 维护，成员主要为志愿者（包括本文作者），数据本身也有个仓库叫做 <a href="https://salsa.debian.org/extrepo-team/extrepo-data">extrepo-data</a>。</p>
<p>我个人维护了一些仓库，包括 <a href="https://salsa.debian.org/extrepo-team/extrepo-data/-/commits/master/repos/debian/redis.yaml?ref_type=heads">Redis</a>，<a href="https://salsa.debian.org/extrepo-team/extrepo-data/-/commits/master/repos/debian/mariadb.yaml?ref_type=heads">MariaDB</a>，<a href="https://salsa.debian.org/extrepo-team/extrepo-data/-/commits/master/repos/debian/n.wtf.yaml?ref_type=heads">N.WTF</a> 等，你可以从这里看到我的 <a href="https://salsa.debian.org/showfom/extrepo-data/-/commits/master?author=Xiufeng+Guo">commits</a>。</p>
<p>任何人都可以对这个 Git 仓库做出贡献，而且 YAML 语法也十分容易上手，你可以参考默认的<a href="https://salsa.debian.org/extrepo-team/extrepo-data/-/blob/master/template.yaml?ref_type=heads">模板文件</a>。</p>
<p>需要查看完整的第三方软件源列表的话，你可以在<a href="https://salsa.debian.org/extrepo-team/extrepo-data/-/tree/master/repos/debian?ref_type=heads">这里</a>浏览所有的第三方仓库的 <code>.yaml</code> 文件。</p>
<p>了解了 extrepo 的机制和优势后，我们再来总结一下。</p>
<h2>结论</h2>
<p>总之，使用 extrepo 是一种既安全又方便的添加第三方软件源的方法。</p>
<p>它的主要优点在于：</p>
<ol>
<li>可以让你快速启用经过测试和验证的软件源，而无需手动搜索和复制第三方软件源的配置信息。</li>
<li>当第三方仓库的 GPG 密钥过期或 URI 发生变化（这种情况相当常见）时，也不再需要手动更新。</li>
</ol>
<p>比如，使用 extrepo 时，只需运行 <code>extrepo enable docker-ce</code> 和 <code>extrepo update docker-ce</code>，它就会自动刷新 Docker CE 仓库的 GPG 密钥和 URI。</p>
<p>不过，extrepo 也有一定的缺点：</p>
<ol>
<li>目前只兼容 Debian 系统，Ubuntu 因为没有人维护 extrepo-data 所以基本没有可以用的第三方软件源。</li>
<li>默认的 <a href="https://extrepo-team.pages.debian.net/extrepo-data/">extrepo-data</a> 托管在 Debian 官方的 GitLab Pages，这对一些网络不通畅的服务器来说获取仓库信息就有点困难，而自建 extrepo-data 的过程相对繁琐，并不算方便。</li>
</ol>
<p><img src="https://s.bh.sb/2025/10/20/that-will-save-lots-of-time-alicia-rodriguez_BhSQa.gif" alt="That will save a lot of time"></p>
<p><a href="https://be.st/MIQr">English Version</a></p>
]]></content>
        <author>
            <name>Showfom</name>
            <email>i@m.ac</email>
            <uri>https://u.sb</uri>
        </author>
        <published>2025-10-21T05:45:49.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[Debian 安装 Nextcloud 服务端]]></title>
        <id>https://u.sb/debian-nextcloud/</id>
        <link href="https://u.sb/debian-nextcloud/"/>
        <link href="https://s.bh.sb/images/debian-nextcloud.webp" rel="enclosure" type="image/webp"/>
        <updated>2025-10-20T18:00:00.000Z</updated>
        <summary type="html"><![CDATA[本文将指导如何在 Debian 下安装并配置 Nextcloud 服务端。]]></summary>
        <content type="html"><![CDATA[<p><img src="https://s.bh.sb/images/debian-nextcloud.webp" alt="Debian 安装 Nextcloud 服务端" title="Debian 安装 Nextcloud 服务端" /></p>
<p>本文将指导如何在 Debian 下安装并配置 Nextcloud 服务端。</p>
<p>本文的教程同时适用于 <a href="https://www.debian.org/releases/stable/">Debian Stable</a> 以及 <a href="https://releases.ubuntu.com/">Ubuntu LTS</a>。</p>
<p>以下操作需要在 root 用户下完成，请使用 <code>sudo -i</code> 或 <code>su root</code> 切换到 root 用户进行操作。</p>
<h2>什么是 Nextcloud？</h2>
<p><a href="https://nextcloud.com/">Nextcloud</a> 是一套用于建立网络硬盘的客户端和服务器软件。其功能和 Dropbox 相近，但 Nextcloud 是开源的，任何人都可以在自己的服务器上安装并运行它。</p>
<p>虽然 Nextcloud 性能比较弱，但是实际测试下来几个人的小团队用用也足够了。</p>
<p>安装之前你可以先去官方的 <a href="https://try.nextcloud.com/access">Demo</a> 体验。</p>
<h2>准备环境</h2>
<p>由于 Nextcloud 消耗资源比较大，一般我们不建议在 4GB 内存以下的 VPS 安装，官方<a href="https://docs.nextcloud.com/server/stable/admin_manual/installation/system_requirements.html">推荐配置</a>为 512MB 内存，实际体验下来安装在 8GB 内存上跑 Nextcloud 会比较流畅。</p>
<h2>配置 LEMP 环境</h2>
<p>首先，可以参考本站<a href="https://u.sb/debian-install-nginx-php-mysql/">教程</a>配置好 LEMP 环境，在安装 PHP 的时候，请选择 PHP 8.3 以及以下模块：</p>
<pre><code class="language-bash">apt install php8.4-{common,fpm,mysql,curl,gd,mbstring,xml,xmlrpc,zip,bz2,intl,ldap,smbclient,bcmath,gmp,imap,opcache,imagick,redis} imagemagick redis-server -y
</code></pre>
<p>这里我们使用了 Redis 作为缓存，所以需要安装 <code>redis-server</code> 和 <code>php8.4-redis</code>，请不要直接安装 <code>php-redis</code>，否则系统会默认把所有的 PHP 版本都给你安装一遍哦。</p>
<p>如果想用最新的官方 Redis 的话可以添加官方源：</p>
<div data-code-group><div data-code-title="传统 One-Line-Style"><pre><code class="language-bash">curl -sSL https://packages.redis.io/gpg | gpg --dearmor &gt; /usr/share/keyrings/redis-archive-keyring.gpg

echo &quot;deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main&quot; &gt; /etc/apt/sources.list.d/redis.list

apt update
apt install redis-server -y
</code></pre>
</div><div data-code-title="DEB822"><pre><code class="language-bash">curl -sSL https://packages.redis.io/gpg | gpg --dearmor &gt; /usr/share/keyrings/redis-archive-keyring.gpg

cat &gt; /etc/apt/sources.list.d/redis.sources &lt;&lt; EOF
Components: main
Architectures: $(dpkg --print-architecture)
Suites: $(lsb_release -cs)
Types: deb
Uris: https://packages.redis.io/deb
Signed-By: /usr/share/keyrings/redis-archive-keyring.gpg
EOF

apt update
apt install redis-server -y
</code></pre>
</div><div data-code-title="extrepo (仅适用于 Debian)"><pre><code>apt install extrepo -y
extrepo enable redis
apt update
apt install redis-server -y
</code></pre>
</div></div><h2>优化 PHP-FPM 设置</h2>
<p>由于默认的 PHP-FPM 设置只适合小型应用，不适合 Nextcloud 这种消耗资源比较大的程序，所以我们可以修改如下参数，这里的例子是你想设置最大上传的文件为 10GB：</p>
<pre><code class="language-bash">sed -i &#39;s/;cgi.fix_pathinfo=1/cgi.fix_pathinfo=0/&#39; /etc/php/8.4/fpm/php.ini 
sed -i &#39;s/upload_max_filesize = 2M/upload_max_filesize = 10240M/&#39; /etc/php/8.4/fpm/php.ini
sed -i &#39;s/post_max_size = 8M/post_max_size = 10240M/&#39; /etc/php/8.4/fpm/php.ini
sed -i &#39;s/memory_limit = 128M/memory_limit = 512M/&#39; /etc/php/8.4/fpm/php.ini
sed -i &#39;s/;opcache.interned_strings_buffer=8/opcache.interned_strings_buffer=16/&#39; /etc/php/8.4/fpm/php.ini
sed -i &#39;s/;listen.mode = 0660/listen.mode = 0660/&#39; /etc/php/8.4/fpm/pool.d/www.conf
sed -i &#39;s/pm.max_children = 5/pm.max_children = 20/&#39; /etc/php/8.4/fpm/pool.d/www.conf
sed -i &#39;s/pm.start_servers = 2/pm.start_servers = 4/&#39; /etc/php/8.4/fpm/pool.d/www.conf
sed -i &#39;s/pm.min_spare_servers = 1/pm.min_spare_servers = 2/&#39; /etc/php/8.4/fpm/pool.d/www.conf
sed -i &#39;s/pm.max_spare_servers = 3/pm.max_spare_servers = 8/&#39; /etc/php/8.4/fpm/pool.d/www.conf
sed -i &#39;s/;clear_env = no/clear_env = no/&#39; /etc/php/8.4/fpm/pool.d/www.conf
</code></pre>
<p>具体配置可以参考<a href="https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html">官网教程</a>。</p>
<p>然后我们重启 PHP-FPM 生效：</p>
<pre><code class="language-bash">systemctl restart php8.4-fpm.service
</code></pre>
<h2>配置 Nginx</h2>
<p>我们假设你的 Nextcloud 需要安装在 <code>/var/www/nextcloud</code> 目录，配置的域名是 <code>cloud.example.com</code>，证书文件位于 <code>/etc/nginx/ssl/cloud.example.com.crt</code>，证书私钥位于 <code>/etc/nginx/ssl/cloud.example.com.key</code>，那么我们直接参考官网上的<a href="https://docs.nextcloud.com/server/stable/admin_manual/installation/nginx.html">第三方教程</a>配置 Nginx：</p>
<pre><code class="language-nginx">upstream php-handler {
    #server 127.0.0.1:9000;
    server unix:/var/run/php/php8.4-fpm.sock;
}

# Set the `immutable` cache control options only for assets with a cache busting `v` argument
map $arg_v $asset_immutable {
    &quot;&quot; &quot;&quot;;
    default &quot;immutable&quot;;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    listen 443 quic;
    listen [::]:443 quic;

    http2 on;

    server_name cloud.example.com;

    # Path to the root of your installation
    root /var/www/nextcloud;

    ssl_session_timeout 1d;
    ssl_session_cache shared:MozSSL:10m;  # about 40000 sessions
    ssl_session_tickets off;

    # curl https://ssl-config.mozilla.org/ffdhe2048.txt &gt; /etc/nginx/ssl/dhparam
    ssl_dhparam /etc/nginx/ssl/dhparam;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;

    ssl_certificate /etc/nginx/ssl/cloud.example.com.crt;
    ssl_certificate_key /etc/nginx/ssl/cloud.example.com.key;

    # HSTS settings
    # WARNING: Only add the preload option once you read about
    # the consequences in https://hstspreload.org/. This option
    # will add the domain to a hardcoded list that is shipped
    # in all major browsers and getting removed from this list
    # could take several months.
    add_header Strict-Transport-Security &quot;max-age=63072000; includeSubDomains; preload&quot; always; 
    add_header Alt-Svc &#39;h3=&quot;:443&quot;; ma=86400&#39; always;

    # set max upload size and increase upload timeout:
    client_max_body_size 10240M;
    client_body_timeout 300s;
    fastcgi_buffers 64 4K;

    # Enable gzip but do not remove ETag headers
    gzip on;
    gzip_vary on;
    gzip_comp_level 4;
    gzip_min_length 256;
    gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
    gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/wasm application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;

    # Pagespeed is not supported by Nextcloud, so if your server is built
    # with the `ngx_pagespeed` module, uncomment this line to disable it.
    #pagespeed off;

    # HTTP response headers borrowed from Nextcloud `.htaccess`
    add_header Referrer-Policy                      &quot;no-referrer&quot;   always;
    add_header X-Content-Type-Options               &quot;nosniff&quot;       always;
    add_header X-Download-Options                   &quot;noopen&quot;        always;
    add_header X-Frame-Options                      &quot;SAMEORIGIN&quot;    always;
    add_header X-Permitted-Cross-Domain-Policies    &quot;none&quot;          always;
    add_header X-Robots-Tag                         &quot;none&quot;          always;
    add_header X-XSS-Protection                     &quot;1; mode=block&quot; always;

    # Remove X-Powered-By, which is an information leak
    fastcgi_hide_header X-Powered-By;

    # Specify how to handle directories -- specifying `/index.php$request_uri`
    # here as the fallback means that Nginx always exhibits the desired behaviour
    # when a client requests a path that corresponds to a directory that exists
    # on the server. In particular, if that directory contains an index.php file,
    # that file is correctly served; if it doesn&#39;t, then the request is passed to
    # the front-end controller. This consistent behaviour means that we don&#39;t need
    # to specify custom rules for certain paths (e.g. images and other assets,
    # `/updater`, `/ocm-provider`, `/ocs-provider`), and thus
    # `try_files $uri $uri/ /index.php$request_uri`
    # always provides the desired behaviour.
    index index.php index.html /index.php$request_uri;

    # Rule borrowed from `.htaccess` to handle Microsoft DAV clients
    location = / {
        if ( $http_user_agent ~ ^DavClnt ) {
            return 302 /remote.php/webdav/$is_args$args;
        }
    }

    location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
    }

    # Make a regex exception for `/.well-known` so that clients can still
    # access it despite the existence of the regex rule
    # `location ~ /(\.|autotest|...)` which would otherwise handle requests
    # for `/.well-known`.
    location ^~ /.well-known {
        # The rules in this block are an adaptation of the rules
        # in `.htaccess` that concern `/.well-known`.

        location = /.well-known/carddav { return 301 /remote.php/dav/; }
        location = /.well-known/caldav  { return 301 /remote.php/dav/; }

        location /.well-known/acme-challenge    { try_files $uri $uri/ =404; }
        location /.well-known/pki-validation    { try_files $uri $uri/ =404; }

        # Let Nextcloud&#39;s API for `/.well-known` URIs handle all other
        # requests by passing them to the front-end controller.
        return 301 /index.php$request_uri;
    }

    # Rules borrowed from `.htaccess` to hide certain paths from clients
    location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/)  { return 404; }
    location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console)                { return 404; }

    # Ensure this block, which passes PHP files to the PHP process, is above the blocks
    # which handle static assets (as seen below). If this block is not declared first,
    # then Nginx will encounter an infinite rewriting loop when it prepends `/index.php`
    # to the URI, resulting in a HTTP 500 error response.
    location ~ \.php(?:$|/) {
        # Required for legacy support
        rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+|.+\/richdocumentscode\/proxy) /index.php$request_uri;

        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
        set $path_info $fastcgi_path_info;

        try_files $fastcgi_script_name =404;

        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $path_info;
        fastcgi_param HTTPS on;
        fastcgi_param HTTP_HOST $host;

        fastcgi_param modHeadersAvailable true;         # Avoid sending the security headers twice
        fastcgi_param front_controller_active true;     # Enable pretty urls
        fastcgi_pass php-handler;

        fastcgi_intercept_errors on;
        fastcgi_request_buffering off;

        fastcgi_max_temp_file_size 0;
    }

    location ~ \.(?:css|js|svg|gif|png|jpg|ico|wasm|tflite|map)$ {
        try_files $uri /index.php$request_uri;
        add_header Cache-Control &quot;public, max-age=15778463, $asset_immutable&quot;;
        access_log off;     # Optional: Don&#39;t log access to assets

        location ~ \.wasm$ {
            default_type application/wasm;
        }
    }

    location ~ \.woff2?$ {
        try_files $uri /index.php$request_uri;
        expires 7d;         # Cache-Control policy borrowed from `.htaccess`
        access_log off;     # Optional: Don&#39;t log access to assets
    }

    # Rule borrowed from `.htaccess`
    location /remote {
        return 301 /remote.php$request_uri;
    }

    location / {
        try_files $uri $uri/ /index.php$request_uri;
    }
}
</code></pre>
<p>关于 SSL 配置可以参考本站教程《<a href="https://u.sb/nginx-ssl/">Nginx 配置 SSL 证书</a>》和《<a href="https://u.sb/acme-sh-ssl/">使用 acme.sh 配置自动续签 SSL 证书</a>》。</p>
<p>如果要修改上传文件大小限制，请求改 Nginx 配置里的 <code>client_max_body_size 10240M;</code> 和 PHP 配置里的 <code>upload_max_filesize</code> 和 <code>post_max_size</code> 参数，本教程举例是上传文件最大限制 10GB。</p>
<p>检查无误后重启 Nginx 生效</p>
<pre><code class="language-bash">nginx -t
nginx -s reload
</code></pre>
<h2>安装 Nextcloud</h2>
<p>首先进入 <code>/var/www</code> 目录，下载并解压最新的 Nextcloud：</p>
<pre><code class="language-bash">cd /var/www
wget -O nextcloud.zip https://download.nextcloud.com/server/releases/latest.zip
unzip nextcloud.zip
</code></pre>
<p>然后我们设置解压出来的 <code>nextcloud</code> 文件夹权限和 PHP 以及 Nginx 对应，设置为 <code>www-data</code> 用户，因为 Debian 下默认 <code>www-data</code> 用户/用户组的 uid 和 gid 是 33，所以直接使用 <code>chown 33:33</code> 即可：</p>
<pre><code class="language-bash">chown 33:33 nextcloud -R
find nextcloud/ -type d -exec chmod 750 {} \;
find nextcloud/ -type f -exec chmod 640 {} \;
</code></pre>
<p>安装完成后，直接访问 <code>https://cloud.example.com</code> 填入你配置好的数据库信息以及管理员帐号密码即可登录你的 Nextcloud。</p>
<h2>配置 Redis 缓存</h2>
<p>Debian 默认安装的 <code>redis-server</code> 已经给你基本配置好了，只监听在本地 <code>127.0.0.1</code> 的 <code>6379</code> 端口，如果没有特殊需求不需要修改。</p>
<p>首先，我们把 <code>redis</code> 用户加入 <code>www-data</code> 用户组：</p>
<pre><code class="language-bash">usermod -a -G redis www-data
</code></pre>
<p>然后修改 <code>/var/www/nextcloud/config/config.php</code> 文件，在最后一行 <code>);</code> 字符前加入：</p>
<pre><code class="language-php">  &#39;memcache.locking&#39; =&gt; &#39;\\OC\\Memcache\\Redis&#39;,
  &#39;memcache.distributed&#39; =&gt; &#39;\\OC\\Memcache\\Redis&#39;,
  &#39;memcache.local&#39; =&gt; &#39;\\OC\\Memcache\\Redis&#39;,
  &#39;redis&#39; =&gt; 
  array (
    &#39;host&#39; =&gt; &#39;127.0.0.1&#39;,
    &#39;port&#39; =&gt; 6379,
  ),
</code></pre>
<p>重启 PHP-FPM 生效：</p>
<pre><code class="language-bash">systemctl restart php8.4-fpm
</code></pre>
<p>其他缓存方式可以参考<a href="https://docs.nextcloud.com/server/stable/admin_manual/configuration_server/caching_configuration.html">官方文档</a>。</p>
<p>如果没有问题，可以访问 <code>https://cloud.example.com/settings/admin/serverinfo</code> 查看服务器信息了。</p>
<h2>配置 Crontab</h2>
<p>我们需要使用 Linux 内置的 cron 来运行自动化任务，直接使用 www-data 用户修改定时任务：</p>
<pre><code class="language-bash">crontab -u www-data -e
</code></pre>
<p>选择一款你喜欢的编辑器然后加入：</p>
<pre><code class="language-bash">*/5  *  *  *  * /usr/bin/php -f /var/www/nextcloud/cron.php
</code></pre>
<p>这个命令的含义是每 5 分钟执行一次 Nextcloud 的定时任务，具体可以参考<a href="https://docs.nextcloud.com/server/stable/admin_manual/configuration_server/background_jobs_configuration.html">官网教程</a>。</p>
<p>保存后可以使用 <code>crontab -u www-data -l</code> 命令查看当前 <code>www-data</code> 用户下的定时任务。</p>
<h2>安装 Nextcloud 客户端</h2>
<p>这里就不再赘述了，直接从<a href="https://nextcloud.com/install/">官网</a>下载并安装对应操作系统的软件即可，登录的时候输入完整的网址 <code>https://cloud.example.com/</code> 即可登录你自己的 Nextcloud。</p>
<p><img src="https://s.bh.sb/2025/10/20/nextcloud_KAJxG.webp" alt="Nextcloud Demo"></p>
<h2>Nextcloud 更新</h2>
<p>如果你的用户和数据不多，直接用管理员访问 <code>https://cloud.example.com/updater/</code> 即可更新到最新稳定版本。</p>
<p>如果服务器的负载较高或自动下载网速较慢，可以使用命令行更新：</p>
<pre><code class="language-bash">cd /var/www/nextcloud
sudo -u www-data php ./updater/updater.phar --no-interaction
</code></pre>
<p>具体可以参考官网教程：<a href="https://docs.nextcloud.com/server/stable/admin_manual/maintenance/update.html">更新</a>、<a href="https://docs.nextcloud.com/server/stable/admin_manual/maintenance/upgrade.html">升级</a>和<a href="https://docs.nextcloud.com/server/stable/admin_manual/maintenance/manual_upgrade.html">手工升级</a>。</p>
<p><strong>切记更新之前先备份数据，避免丢失重要数据哦。</strong></p>
<h2>Nextcloud 备份</h2>
<p>Nextcloud 目前还是个典型的 PHP + MySQL 程序，所以理论上只要备份 <code>/var/www/nextcloud</code> 目录，你的文件储存目录 (默认在 <code>/var/www/nextcloud/data</code>) 以及 MySQL 数据库即可，这里不再赘述。</p>
]]></content>
        <author>
            <name>Showfom</name>
            <email>i@m.ac</email>
            <uri>https://u.sb</uri>
        </author>
        <published>2022-04-22T21:14:11.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[升级 Debian 后 GitLab PostgreSQL 无法启动的解决方法]]></title>
        <id>https://u.sb/fix-gitlab-postgresql-not-starting-debian-upgrade/</id>
        <link href="https://u.sb/fix-gitlab-postgresql-not-starting-debian-upgrade/"/>
        <link href="https://s.bh.sb/images/fix-gitlab-postgresql-not-starting-debian-upgrade.webp" rel="enclosure" type="image/webp"/>
        <updated>2025-09-16T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[本文将介绍在升级 Debian 系统后，GitLab 的 PostgreSQL 数据库无法正常启动的问题。]]></summary>
        <content type="html"><![CDATA[<p><img src="https://s.bh.sb/images/fix-gitlab-postgresql-not-starting-debian-upgrade.webp" alt="升级 Debian 后 GitLab PostgreSQL 无法启动的解决方法" title="升级 Debian 后 GitLab PostgreSQL 无法启动的解决方法" /></p>
<p>本文将介绍在升级 Debian 系统后，GitLab 的 PostgreSQL 数据库无法正常启动的问题。</p>
<h2>背景前提</h2>
<p><strong>本文的 GitLab 是基于官方源安装的，具体安装和升级方法限于篇幅，不再阐述。</strong></p>
<p>我们从 Debian 11 升级到 Debian 12 后，再更新 GitLab 会发现如下报错：</p>
<pre><code class="language-bash">gitlab [execute] WARNING: database &quot;postgres&quot; has a collation version mismatch DETAIL: The database was created using collation version 2.31, but the operating system provides version 2.36.
</code></pre>
<p>或者从 Debian 12 升级到 Debian 13 后更新 GitLab 也会有如下报错：</p>
<pre><code class="language-bash">WARNING:  database “gitlabhq_production” has a collation version mismatch
DETAIL:  The database was created using collation version 2.36, but the operating system provides version 2.41.
HINT:  Rebuild all objects in this database that use the default collation and run ALTER DATABASE gitlabhq_production REFRESH COLLATION VERSION, or build PostgreSQL with the right library version.
</code></pre>
<p>我们以 Debian 13 为例，可以看到系统自带的 glibc 版本为 2.41：</p>
<pre><code class="language-bash"># ldd --version
ldd (Debian GLIBC 2.41-12) 2.41
</code></pre>
<p>而 Debian 12 自带的 glibc 版本为 2.36：</p>
<pre><code class="language-bash"># ldd --version
ldd (Debian GLIBC 2.36-9+deb12u13) 2.36
</code></pre>
<p>Debian 11 的 glibc 版本为 2.31：</p>
<pre><code class="language-bash"># ldd --version
ldd (Debian GLIBC 2.31-13+deb11u13) 2.31
</code></pre>
<p>所以我们直接升级系统以后 GitLab 的 PostgreSQL 数据库仍使用旧版本的排序规则，导致版本不匹配，所以 GitLab 升级会失败。</p>
<h2>解决方法</h2>
<p>我们以 Debian 11 升级到 Debian 12 后的情况举例，首先备份一下 GitLab：</p>
<pre><code class="language-bash">sudo gitlab-backup create
</code></pre>
<p>然后我们进入 GitLab 的 PostgreSQL 控制台：</p>
<pre><code class="language-bash">sudo gitlab-psql
</code></pre>
<p>接着重新建立索引并修复 <code>gitlabhq_production</code> 数据库：</p>
<pre><code class="language-sql">SET statement_timeout = 0;
REINDEX DATABASE gitlabhq_production;
ALTER DATABASE gitlabhq_production REFRESH COLLATION VERSION;
</code></pre>
<p>重建完成后输入 <code>\q</code> 并按回车退出，然后我们修复 <code>template1</code> 和 <code>postgres</code> 数据库：</p>
<pre><code class="language-bash">sudo gitlab-psql -d template1 -c &quot;ALTER DATABASE template1 REFRESH COLLATION VERSION;&quot;
sudo gitlab-psql -d postgres -c &quot;ALTER DATABASE postgres REFRESH COLLATION VERSION;&quot;
</code></pre>
<p>修复完成后验证结果：</p>
<pre><code class="language-bash">sudo gitlab-psql -c &quot;
SELECT 
    datname,
    datcollversion,
    pg_collation_actual_version((SELECT oid FROM pg_collation WHERE collname = &#39;default&#39;)) as system_version,
    CASE 
        WHEN datcollversion = pg_collation_actual_version((SELECT oid FROM pg_collation WHERE collname = &#39;default&#39;)) 
        THEN &#39;✅ Good&#39; 
        ELSE &#39;❌ Bad&#39; 
    END as status
FROM pg_database 
WHERE datname IN (&#39;template1&#39;, &#39;postgres&#39;, &#39;gitlabhq_production&#39;)
ORDER BY datname;&quot;
</code></pre>
<p>如果出现如下结果，则修复完成：</p>
<pre><code class="language-bash">       datname       | datcollversion | system_version | status  
---------------------+----------------+----------------+---------
 gitlabhq_production | 2.36           | 2.36           | ✅ Good
 postgres            | 2.36           | 2.36           | ✅ Good
 template1           | 2.36           | 2.36           | ✅ Good
</code></pre>
<p>然后我们就可以重新配置 GitLab 并更新升级了：</p>
<pre><code class="language-bash">sudo gitlab-ctl reconfigure
</code></pre>
<p>完成以后重启 GitLab 即可恢复服务：</p>
<pre><code class="language-bash">sudo gitlab-ctl restart
</code></pre>
]]></content>
        <author>
            <name>Showfom</name>
            <email>i@m.ac</email>
            <uri>https://u.sb</uri>
        </author>
        <published>2025-09-16T06:09:09.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[Debian 12 Bookworm 升级 Debian 13 Trixie]]></title>
        <id>https://u.sb/debian-upgrade-13/</id>
        <link href="https://u.sb/debian-upgrade-13/"/>
        <link href="https://s.bh.sb/images/debian-upgrade-13.webp" rel="enclosure" type="image/webp"/>
        <updated>2025-08-05T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[本文将指导如何升级 Debian 12 Bookworm 到 Debian 13 Trixie。]]></summary>
        <content type="html"><![CDATA[<p><img src="https://s.bh.sb/images/debian-upgrade-13.webp" alt="Debian 12 Bookworm 升级 Debian 13 Trixie" title="Debian 12 Bookworm 升级 Debian 13 Trixie" /></p>
<p>本文将指导如何升级 Debian 12 Bookworm 到 Debian 13 Trixie。</p>
<p>相关教程：<a href="https://u.sb/debian-upgrade-12/">Debian 11 Bullseye 升级 Debian 12 Bookworm</a>。</p>
<h2>准备工作</h2>
<p>除非你是物理服务器，以及没有用过奇奇怪怪定制或修改的内核的 KVM 构架的 VPS 和云主机，否则升级大版本更新内核是有一定机率导致 Grub 加载失败的，切记备份重要数据！</p>
<p><em>OpenVZ 6 和 LXC 构架的 VPS 是无法升级的，因为他们没有自己独立的内核</em></p>
<p>再强调一遍，一定要备份重要数据！</p>
<p>以下操作需要在 root 用户下完成，请使用 <code>sudo -i</code> 或 <code>su root</code> 切换到 root 用户进行操作</p>
<h2>更新系统</h2>
<p>首先需要更新你当前的系统</p>
<pre><code class="language-bash">apt update
apt upgrade -y
apt full-upgrade -y
apt autoclean
apt autoremove -y
</code></pre>
<p>如果内核更新了，可以重启让最新的内核生效，也可以直接进行升级。</p>
<h2>升级系统</h2>
<p>首先更新 <code>apt</code> 源，替换 <code>bookworm</code> 为 <code>trixie</code>：</p>
<pre><code class="language-bash">sed -i &#39;s/bookworm/trixie/g&#39; /etc/apt/sources.list
sed -i &#39;s/bookworm/trixie/g&#39; /etc/apt/sources.list.d/*.list
sed -i &#39;s/bookworm/trixie/g&#39; /etc/apt/sources.list.d/*.sources
</code></pre>
<p>如果没有对应文件会提示诸如 <code>sed: can&#39;t read /etc/apt/sources.list.d/*.sources: No such file or directory</code> 的错误，忽略即可。</p>
<p>或者直接一行命令：</p>
<pre><code class="language-bash">sed -i &#39;s/bookworm/trixie/g&#39; /etc/apt/sources.list /etc/apt/sources.list.d/*.{list,sources} 2&gt;/dev/null
</code></pre>
<p>修改完成后你的 <code>/etc/apt/sources.list</code> 文件内容应该类似如下：</p>
<pre><code class="language-bash">deb https://deb.debian.org/debian trixie main contrib non-free non-free-firmware

deb https://security.debian.org/debian-security trixie-security main contrib non-free non-free-firmware

deb https://deb.debian.org/debian trixie-updates main contrib non-free non-free-firmware
</code></pre>
<p>大部分旧的安装的 Debian 的软件源配置文件使用传统的 One-Line-Style，路径为 <code>/etc/apt/sources.list</code>；但是从 Debian 12 的容器版本开始，以及 Debian 13 正式版后，其软件源配置文件变更为 <code>DEB822</code> 格式，路径为 <code>/etc/apt/sources.list.d/debian.sources</code>:（<a href="https://mirrors.help/debian/">参考</a>）</p>
<pre><code class="language-bash">Types: deb
URIs: https://deb.debian.org/debian
Suites: trixie trixie-updates trixie-backports
Components: main contrib non-free non-free-firmware
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg

Types: deb
URIs: https://security.debian.org/debian-security
Suites: trixie-security
Components: main contrib non-free non-free-firmware
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
</code></pre>
<p>如果使用 <code>DEB822</code> 格式，则你可以安全地删除 <code>/etc/apt/sources.list</code> 文件，只保留 <code>/etc/apt/sources.list.d/debian.sources</code> 即可。</p>
<p><em>国内服务器可以替换 <code>deb.debian.org</code> 和 <code>security.debian.org</code> 为 <code>mirrors.tuna.tsinghua.edu.cn</code></em></p>
<p>然后我们再次执行更新系统：</p>
<pre><code class="language-bash">apt update
apt upgrade -y
apt full-upgrade -y
</code></pre>
<p><em>更新过程中，Debian 13 早期版本可能会提示你是否自动更换为软件源配置文件为 <code>DEB822</code> 格式，如果需要的话可以使用 <code>apt modernize-sources</code> 命令来自动转换，或者自行重写 sources 文件。如果使用命令转换，请自行重写 backports 仓库，目前的转换会有 bug 漏掉这个仓库的 GPG Key。</em></p>
<p>推荐更换成更科学的 <code>DEB822</code> 格式哦~</p>
<p><em>如果 apt 命令最后没有带 <code>-y</code> 自动同意参数的话，更新过程中还会提示一些软件的更新列表，是否需要自动重启等，选 <code>Yes</code> 即可，以及一些软件的配置文件是否需要更新，按照自己的情况选择即可，默认回车即视为使用旧的配置文件，一般会出现在 <code>OpenSSH</code> 等软件的更新上。</em></p>
<p>在 <code>apt-listchanges: News</code> 界面可以按 <code>q</code> 退出：</p>
<p><img src="https://s.bh.sb/2025/08/05/image_S2xQK.png" alt="image.png"></p>
<p>提示是否自动重启服务：</p>
<p><img src="https://s.bh.sb/2025/08/05/image_CeXju.png" alt="image.png"></p>
<p>提示是否更新配置文件：</p>
<p><img src="https://s.bh.sb/2025/08/05/image_v7Yk2.png" alt="image.png"></p>
<p>注意某些软件更新后可能会更新 <code>systemd</code> 服务配置，此时我们可以执行 <code>systemctl daemon-reload</code> 重新加载配置。</p>
<p>更新后删除不必要的软件和依赖：</p>
<pre><code class="language-bash">apt autoclean
apt autoremove -y
</code></pre>
<p>然后我们使用 <code>reboot</code> 命令重启系统，耐心等待后，查看最新的系统版本：</p>
<pre><code class="language-bash">root@debian ~ # cat /etc/debian_version 
13.0
</code></pre>
<pre><code class="language-bash">root@debian ~ # lsb_release -a
No LSB modules are available.
Distributor ID:	Debian
Description:	Debian GNU/Linux 13 (trixie)
Release:	13
Codename:	trixie
</code></pre>
<pre><code class="language-bash">root@debian ~ # uname -a
Linux upload 6.12.38+deb13-cloud-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.38-1 (2025-07-16) x86_64 GNU/Linux
</code></pre>
<p>这时我们就已经更新到了最新的 Debian 13 Trixie 和内核了。</p>
]]></content>
        <author>
            <name>Showfom</name>
            <email>i@m.ac</email>
            <uri>https://u.sb</uri>
        </author>
        <published>2025-08-05T17:46:44.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[Ubuntu 22.04 Jammy 升级 Ubuntu 24.04 Noble]]></title>
        <id>https://u.sb/ubuntu-upgrade-2404/</id>
        <link href="https://u.sb/ubuntu-upgrade-2404/"/>
        <link href="https://s.bh.sb/images/ubuntu-upgrade-2404.webp" rel="enclosure" type="image/webp"/>
        <updated>2024-06-21T05:55:00.000Z</updated>
        <summary type="html"><![CDATA[本文将指导如何升级 Ubuntu 22.04 Jammy Jellyfish 到 Ubuntu 24.04 Noble Numbat。]]></summary>
        <content type="html"><![CDATA[<p><img src="https://s.bh.sb/images/ubuntu-upgrade-2404.webp" alt="Ubuntu 22.04 Jammy 升级 Ubuntu 24.04 Noble" title="Ubuntu 22.04 Jammy 升级 Ubuntu 24.04 Noble" /></p>
<p>本文将指导如何升级 Ubuntu 22.04 Jammy Jellyfish 到 Ubuntu 24.04 Noble Numbat。</p>
<p>相关教程：<a href="https://u.sb/ubuntu-upgrade/">Ubuntu 20.04 Focal Fossa 升级 Ubuntu 22.04 Jammy Jellyfish</a>。</p>
<h2>准备工作</h2>
<p>除非你是物理服务器，以及没有用过奇奇怪怪定制或修改的内核的 KVM 构架的 VPS 和云主机，否则升级大版本更新内核是有一定机率导致 Grub 加载失败的，切记备份重要数据！</p>
<p><em>OpenVZ 和 LXC 构架的 VPS 是无法升级的，因为他们没有自己独立的内核</em></p>
<p>再强调一遍，一定要备份重要数据！</p>
<p>以下操作需要在 root 用户下完成，请使用 <code>sudo -i</code> 或 <code>su root</code> 切换到 root 用户进行操作</p>
<h2>更新系统</h2>
<p>首先需要更新你当前的系统</p>
<pre><code class="language-bash">apt update
apt upgrade -y
apt dist-upgrade -y
apt autoclean
apt autoremove -y
</code></pre>
<p>如果内核更新了，可以重启让最新的内核生效，也可以直接进行升级。</p>
<h2>升级系统</h2>
<p>这里有两种升级系统的方法，第一种是使用 <code>do-release-upgrade</code> 命令，第二种是手动更新 <code>apt</code> 源文件。</p>
<h3>方法一：使用 <code>do-release-upgrade</code> 命令</h3>
<p>首先需要安装 <code>ubuntu-release-upgrader-core</code> 包：</p>
<pre><code class="language-bash">apt install ubuntu-release-upgrader-core
</code></pre>
<p>然后修改 <code>/etc/update-manager/release-upgrades</code> 文件，确保 <code>Prompt</code> 值为 <code>lts</code>：</p>
<pre><code class="language-bash">cat /etc/update-manager/release-upgrades | grep lts
</code></pre>
<p>显示如下内容即可：</p>
<pre><code class="language-bash">root@ubuntu ~ # cat /etc/update-manager/release-upgrades | grep lts
#  lts    - Check to see if a new LTS release is available.  The upgrader
Prompt=lts
</code></pre>
<p>最后执行以下命令升级系统：</p>
<pre><code class="language-bash">do-release-upgrade -d
</code></pre>
<h3>方法二：手动更新 <code>apt</code> 源文件</h3>
<p>首先更新 <code>apt</code> 源，替换 <code>jammy</code> 为 <code>noble</code>：</p>
<pre><code class="language-bash">sed -i &#39;s/jammy/noble/g&#39; /etc/apt/sources.list
sed -i &#39;s/jammy/noble/g&#39; /etc/apt/sources.list.d/*.list
</code></pre>
<p>系统 <code>apt</code> 源文件 <code>/etc/apt/sources.list</code> 应该是类似这样的：</p>
<pre><code class="language-bash">deb https://archive.ubuntu.com/ubuntu/ noble main restricted universe multiverse

deb https://archive.ubuntu.com/ubuntu/ noble-updates main restricted universe multiverse

deb https://archive.ubuntu.com/ubuntu/ noble-backports main restricted universe multiverse

deb http://security.ubuntu.com/ubuntu/ noble-security main restricted universe multiverse
</code></pre>
<p>由于在 Ubuntu 24.04 之前，Ubuntu 的软件源配置文件使用传统的 One-Line-Style，路径为 <code>/etc/apt/sources.list</code>；从 Ubuntu 24.04 开始，Ubuntu 的软件源配置文件变更为 <code>DEB822</code> 格式，路径为 <code>/etc/apt/sources.list.d/ubuntu.sources</code>（<a href="https://mirrors.help/ubuntu/">参考</a>），所以使用 <code>DEB822</code> 格式的源文件 <code>/etc/apt/sources.list.d/ubuntu.sources</code>：</p>
<pre><code class="language-bash">Types: deb
URIs: https://archive.ubuntu.com/ubuntu
Suites: noble noble-updates noble-backports
Components: main restricted universe multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg

Types: deb
URIs: http://security.ubuntu.com/ubuntu
Suites: noble-security
Components: main restricted universe multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
</code></pre>
<p><em>国内服务器可以替换 <code>archive.ubuntu.com</code> 和 <code>security.ubuntu.com</code> 为 <code>mirrors.tuna.tsinghua.edu.cn</code></em></p>
<p>然后我们再次执行更新系统：</p>
<pre><code class="language-bash">apt update
apt upgrade -y
apt full-upgrade -y
</code></pre>
<p><em>更新过程中会提示一些软件是否需要自动重启，选 Yes 即可，以及一些软件的配置文件是否需要更新，按照自己的情况选择即可，默认回车即视为使用旧的配置文件，一般会出现在 OpenSSH 等软件的更新上。</em></p>
<p>更新后删除不必要的软件和依赖：</p>
<pre><code class="language-bash">apt autoclean
apt autoremove -y
</code></pre>
<p>然后我们使用 <code>reboot</code> 命令重启系统，耐心等待后，查看最新的系统版本：</p>
<pre><code class="language-bash">root@ubuntu ~ # lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 24.04 LTS
Release:	24.04
Codename:	noble
</code></pre>
<pre><code class="language-bash">root@ubuntu ~ # uname -a
Linux ubuntu 6.8.0-35-generic #35-Ubuntu SMP PREEMPT_DYNAMIC Mon May 20 15:51:52 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
</code></pre>
<p>这时我们就已经更新到了最新的 Ubuntu 24.04 Noble 和内核了。</p>
]]></content>
        <author>
            <name>Showfom</name>
            <email>i@m.ac</email>
            <uri>https://u.sb</uri>
        </author>
        <published>2024-06-21T15:28:05.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[Docker 安装 FreshRSS 教程]]></title>
        <id>https://u.sb/docker-freshrss/</id>
        <link href="https://u.sb/docker-freshrss/"/>
        <link href="https://s.bh.sb/images/docker-freshrss.webp" rel="enclosure" type="image/webp"/>
        <updated>2024-06-21T05:29:00.000Z</updated>
        <summary type="html"><![CDATA[本文将指导如何在 Linux 下使用 Docker 和 Docker Compose 安装 FreshRSS 开源 RSS 聚合器服务。]]></summary>
        <content type="html"><![CDATA[<p><img src="https://s.bh.sb/images/docker-freshrss.webp" alt="Docker 安装 FreshRSS 教程" title="Docker 安装 FreshRSS 教程" /></p>
<p>本文将指导如何在 Linux 下使用 Docker 和 Docker Compose 安装 FreshRSS 开源 RSS 聚合器服务。</p>
<p><em>PS：本文同时适用于任何可安装 Docker 的 Linux 发行版。</em></p>
<h2>什么是 FreshRSS？</h2>
<p><a href="https://freshrss.org/">FreshRSS</a> 是一款免费且开源的 RSS 聚合器，设计用于帮助用户集中管理和阅读来自不同网站的新闻源。它具有高效、轻量的特点，并且支持多用户使用。</p>
<h2>安装 Docker 和 Docker Compose</h2>
<p>Debian 和 Ubuntu 系统请参考<a href="https://u.sb/debian-install-docker/">本站教程</a>。</p>
<p>其他 Linux 系统可以使用 Docker 官方的脚本安装 Docker 和 Docker Compose：</p>
<pre><code class="language-bash">curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
</code></pre>
<h2>安装 FreshRSS</h2>
<p>这里我们使用 PostgreSQL 数据库，首先创建一个目录用于存放 FreshRSS 的配置文件和数据库文件：</p>
<pre><code class="language-bash">mkdir -p /opt/freshrss
cd /opt/freshrss
</code></pre>
<p>然后创建一个 <code>docker-compose.yml</code> 文件：</p>
<pre><code class="language-yaml">services:

  freshrss:
    image: freshrss/freshrss:latest
    container_name: freshrss
    hostname: freshrss
    restart: unless-stopped
    logging:
      options:
        max-size: 10m
    volumes:
      - ./data:/var/www/FreshRSS/data
      - ./extensions:/var/www/FreshRSS/extensions
    environment:
      TZ: Etc/UTC
      CRON_MIN: &#39;3,33&#39;
      TRUSTED_PROXY: 172.16.0.1/12 192.168.0.1/16
      ADMIN_EMAIL: 你的邮箱
      BASE_URL: 你的 FreshRSS 访问地址
    ports:
      - 127.0.0.1:8080:80

  freshrss-db:
    image: postgres:16
    container_name: freshrss-db
    hostname: freshrss-db
    restart: unless-stopped
    logging:
      options:
        max-size: 10m
    volumes:
      - ./db:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: freshrss
      POSTGRES_USER: freshrss
      POSTGRES_PASSWORD: freshrss
    command:
      - -c
      - shared_buffers=1GB
      - -c
      - work_mem=32MB
</code></pre>
<p>请自行替换 <code>ADMIN_EMAIL</code> 和 <code>BASE_URL</code> 的值，<code>BASE_URL</code> 需要写全，比如 <code>https://freshrss.example.com</code>。</p>
<p>然后拉取 Docker 镜像并运行：</p>
<pre><code class="language-bash">cd /opt/freshrss
docker compose pull
docker compose up -d
</code></pre>
<h2>安装配置 Nginx 反向代理</h2>
<p>我们的 Docker Compose 配置文件中，FreshRSS 服务监听在 <code>127.0.0.1:8080</code> 端口，所以我们需要配置 Nginx 反代来访问，假设你 FreshRSS 的地址是 <code>https://freshrss.example.com</code>：</p>
<p><code>freshrss.example.com</code> 段配置：</p>
<pre><code class="language-nginx">	location / {
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header Host $http_host;
		proxy_http_version 1.1;
		proxy_set_header Upgrade $http_upgrade;
		proxy_redirect off;
		proxy_set_header        X-Forwarded-Proto $scheme;
		proxy_connect_timeout       300;
		proxy_send_timeout          300;
		proxy_read_timeout          300;
		send_timeout                300;
		proxy_pass http://127.0.0.1:8080;
	}
</code></pre>
<p>最后记得参考本站 <a href="https://u.sb/nginx-ssl/">Nginx SSL 配置教程</a>加上 SSL 证书后，即可访问 <code>https://freshrss.example.com/</code>：</p>
<h2>配置 FreshRSS</h2>
<p>访问 <code>https://freshrss.example.com/</code>，在安装向导中填写数据库信息：</p>
<ul>
<li>数据库类型：<code>PostgreSQL</code></li>
<li>数据库主机（Host）：<code>freshrss-db</code></li>
<li>数据库名称（Database）：<code>freshrss</code></li>
<li>数据库用户（User）：<code>freshrss</code></li>
<li>数据库密码（Password）：<code>freshrss</code></li>
</ul>
<p>然后点击 <code>Install FreshRSS</code> 即可完成安装。</p>
<p>如果需要安装插件，可以把插件上传到 <code>/opt/freshrss/extensions</code> 目录，然后在 FreshRSS 后台安装。</p>
<p>推荐在官方插件仓库里下载插件：<a href="https://github.com/FreshRSS/Extensions">FreshRSS Extensions</a></p>
<p>推荐安装 <a href="https://github.com/FreshRSS/Extensions/tree/master/xExtension-CustomCSS">CustomCSS</a> 插件，就可以使用自定义 CSS 样式了，个人比较喜欢<a href="https://github.com/catppuccin/freshrss">这个主题系列</a>。</p>
<p>如果需要第三方客户端，可以在这里查看支持的应用：<a href="https://github.com/FreshRSS/FreshRSS/blob/edge/README.md#apis--native-apps">APIs &amp; native apps</a></p>
<h2>迁移 FreshRSS</h2>
<p>可以参考《<a href="https://u.sb/docker-mailcow/#mailcow-%E7%9A%84%E8%BF%81%E7%A7%BB-t2">使用 Docker 安装 Mailcow 自建域名邮箱</a>》。</p>
<h2>备份 FreshRSS</h2>
<p>我们可以定期备份 FreshRSS 网站文件和数据库，压缩网站目录和导出数据库命令如下：</p>
<pre><code class="language-bash">backup_folder_name=$(date +&quot;%Y_%m_%d_%I_%M_%p&quot;)
# 备份 FreshRSS 网站文件
tar --exclude /opt/freshrss/data/cache -zcvf data-$backup_folder_name.tar.gz /opt/freshrss/data
tar -zcvf extensions-$backup_folder_name.tar.gz /opt/freshrss/extensions
# 备份 FreshRSS 数据库
docker exec -t freshrss-db pg_dumpall -c -U freshrss | gzip &gt; database-$backup_folder_name.gz
</code></pre>
]]></content>
        <author>
            <name>Showfom</name>
            <email>i@m.ac</email>
            <uri>https://u.sb</uri>
        </author>
        <published>2024-06-21T14:34:43.000Z</published>
    </entry>
</feed>