Fedora 45 的 dnf 仓库配置:三层结构与升级后遗症

升级到 Fedora 45 之后,我做了一件很平常的事:把 fedora 和 updates 两个仓库切到阿里云镜像。改完文件、makecache 也跑通了,看起来一切正常。

直到第二天发现系统还在连 mirror.nju.edu.cn。

这篇记录了这次踩坑的全过程,以及 Fedora 45 在仓库配置上真正的结构变化——它比”换个 baseurl”要复杂一层,而且旧的经验在这里会直接失效。

一、现象:一半的 repo 文件消失了

升级完成后,/etc/yum.repos.d/ 长这个样子:

1
2
3
4
5
6
7
8
9
$ ls /etc/yum.repos.d/
_copr:copr.fedorainfracloud.org:ruojiner:fedora-copa.repo
google-chrome.repo
vscode.repo
fedora.repo.rpmsave
fedora-updates.repo.rpmsave
rpmfusion-free.repo.rpmsave
rpmfusion-nonfree-updates.repo.rpmsave
...

一共 19 个文件,其中 10 个变成了 .rpmsave,真正生效的只剩 8 个(6 个 copr 加 chrome、vscode)。

但 dnf repolist 却显示一切正常:fedora、updates、rpmfusion 全都在。

这说明这些仓库的配置来自别处。.rpmsave 是 rpm 升级时的行为——当它发现某个配置文件被用户改过,不会覆盖,而是把你改过的那份留成 .rpmsave,然后装上新的。问题在于:新的那份去哪了?

二、新结构:三个目录,三层优先级

翻 fedora-repos 包的文件清单就能看到答案:

1
2
3
4
5
$ rpm -ql fedora-repos | grep '\.repo'
/usr/share/dnf5/repos.d/fedora-cisco-openh264.repo
/usr/share/dnf5/repos.d/fedora-updates-testing.repo
/usr/share/dnf5/repos.d/fedora-updates.repo
/usr/share/dnf5/repos.d/fedora.repo

Fedora 45 把发行版自带的仓库配置从 /etc/yum.repos.d/ 挪到了 /usr/share/dnf5/repos.d/。

这个改动背后是 dnf5 的配置分层设计。现在完整的优先级从高到低是:

层级 路径 定位
最高 /etc/dnf/repos.override.d/*.repo 覆盖层,config-manager 写入
中 /etc/yum.repos.d/*.repo 用户与第三方自定义
基础 /usr/share/dnf5/repos.d/*.repo 发行版默认,随 rpm 包更新

这个设计本身是合理的:发行版默认配置归包管理,用户改动不再和升级抢同一个文件,也就不用再产生 .rpmsave。

但它带来一个后果:以前”改 /etc/yum.repos.d/fedora.repo“这个动作,现在改到的是一个不被读取的旧文件。你改得再对,也没有任何效果。

我第一轮修改就栽在这里。

三、被 override 悄悄接管

事情更有意思的地方在于,/etc/yum.repos.d/ 里甚至没有 fedora.repo——它已经被 rpm 改名成 .rpmsave 了。所以我当时改的,其实是升级前留下的那份快照。

而真正生效的配置在另一个完全没注意过的位置:

1
2
3
4
5
6
7
8
9
10
11
$ cat /etc/dnf/repos.override.d/99-config_manager.repo
# Generated by libdnf5.
# Do not modify this file manually, use dnf5 config-manager instead.
[fedora]
baseurl=https://mirror.nju.edu.cn/fedora/releases/$releasever/Everything/$basearch/os/ \
https://cmcc.mirrors.ustc.edu.cn/fedora/releases/... \
https://mirror.nyist.edu.cn/fedora/releases/...
metalink=
[updates]
baseurl=https://mirror.nju.edu.cn/fedora/updates/...
metalink=

这里有两个关键信息:

  1. 文件头明确写着 Generated by libdnf5. Do not modify this file manually
  2. 它显式写了 metalink=(空值),用来禁用官方 metalink

也就是说,如果只改 /etc/yum.repos.d/ 里的 metalink 和 baseurl,override 层会把你的改动完全压住。这就是”改完还是连 nju”的原因。

四、正确的改法

dnf5 提供了 config-manager setopt 来操作这一层:

1
2
3
4
5
6
7
sudo dnf config-manager setopt \
'fedora.baseurl=https://mirrors.aliyun.com/fedora/releases/$releasever/Everything/$basearch/os/'
sudo dnf config-manager setopt 'fedora.metalink='

sudo dnf config-manager setopt \
'updates.baseurl=https://mirrors.aliyun.com/fedora/updates/$releasever/Everything/$basearch/'
sudo dnf config-manager setopt 'updates.metalink='

执行后 override 文件会被重写:

1
2
3
4
5
6
7
[fedora]
baseurl=https://mirrors.aliyun.com/fedora/releases/$releasever/Everything/$basearch/os/
metalink=

[updates]
baseurl=https://mirrors.aliyun.com/fedora/updates/$releasever/Everything/$basearch/
metalink=

三个细节值得单独说:

第一,$releasever 必须用单引号包住。 双引号下 shell 会尝试展开这个变量,结果变成空字符串,URL 直接坏掉。单引号能让 dnf 自己去做变量替换。

第二,metalink= 不能省略。 基础层的 fedora.repo 里 metalink 是启用的,override 只改 baseurl 的话,两者会同时存在,dnf 优先走 metalink,你的 baseurl 就成了摆设。显式置空才能真正接管。

第三,别手动编辑这个文件。 它由 libdnf5 生成,格式和顺序都由工具决定,手改之后下次 config-manager 操作会覆盖掉。文件头的警告不是客套话。

五、怎么确认真的生效了

这个问题最麻烦的地方是:dnf 不告诉你它到底连了哪个地址。

试过这些手段,都不行:

1
2
3
4
5
$ dnf -v makecache --refresh --repo=fedora | grep -oE 'https?://[^ "]+'
(无输出)

$ dnf repoinfo fedora | grep -i baseurl
(无输出)

dnf5 的 verbose 模式不打印 URL,repoinfo 的输出格式也变了。

可靠的验证方法是看速度:先删掉对应仓库的元数据缓存,再重新拉取。

1
2
sudo rm -rf /var/cache/libdnf5/fedora-* /var/cache/libdnf5/updates-*
sudo dnf makecache --repo=fedora --repo=updates

结果:

1
2
Fedora 45 - x86_64 - Updates   100% | 5.9 MiB/s | 12.3 MiB | 00m02s
Fedora 45 - x86_64 100% | 8.7 MiB/s | 36.6 MiB | 00m04s

5~9 MiB/s 是国内镜像的水平。官方 metalink 在国内通常只有几百 KB/s,差距足够明显,一眼就能分辨。

注意不要用”只刷新了一点点”的结果来判断。我第一次验证时只下了几 KB 的变更文件,速度显示 3.3 KiB/s——那是因为文件太小,测不出真实带宽。必须删缓存让它重新拉完整的 repomd.xml 和 primary 元数据,数据才有意义。

六、连带清理

升级过程中还留下一些东西,顺手一起处理了:

旧内核。 系统里同时存在 7.2.14-200.fc44、7.2.15-200.fc44 和 7.2.15-300.fc45。前两个是 f44 时代的,在 f45 用户空间下基本无法启动,但会一直占着 /boot 和在 GRUB 菜单里占位。

1
sudo dnf remove --oldinstallonly --setopt=installonly_limit=2

.rpmsave 与 .rpmnew 的区别。 这两个后缀容易被混为一谈:

  • .rpmsave —— 你改过的旧配置被留存,新配置已生效
  • .rpmnew —— 你改过的配置仍在用,新版本的默认配置被存成 .rpmnew

也就是说 .rpmnew 里是”新版本推荐的配置”,删掉它等于放弃参考。我这次清理了 23 个(含 5 个 /etc/pam.d/*、sddm.conf),删之前确认系统能正常登录——说明现有配置和 f45 兼容。如果日后认证或显示管理器出问题,可以用 dnf reinstall pam sddm 重新生成对照。

升级缓存。 /var/cache/PackageKit 84M、/var/cache/libdnf5 85M、还有 /var/lib/dnf/system-upgrade/ 里躺着一个 2024 年 10 月的状态文件。清理命令:

1
2
3
sudo dnf clean all
sudo rm -rf /var/cache/PackageKit/*
sudo rm -rf /var/lib/dnf/system-upgrade/*

七、小结

Fedora 45 这次改动可以总结成一句话:

发行版仓库配置从 /etc/yum.repos.d/ 迁到 /usr/share/dnf5/repos.d/,用户层面的覆盖统一走 /etc/dnf/repos.override.d/,用 dnf config-manager setopt 操作。

几条可以直接拿走的经验:

  • 改源前先看 /etc/dnf/repos.override.d/ 有没有文件,有的话你改 yum.repos.d 基本无效
  • baseurl 和 metalink= 要同时处理,只改一个可能不生效
  • 验证源是否生效,在 dnf5 下只能靠”删缓存 + 看下载速度”
  • 升级后 /etc/yum.repos.d/ 里的 .rpmsave 是残留,不是配置

最后补一句:这种”配置位置迁移”的改动,比参数调整更容易误导人。表面上看文件都在、repolist 也正常,实际上你修改的对象根本没有被读取。遇到”改了没效果”的情况,先确认文件是否真的在被读,再怀疑配置内容本身。