如何修复 apt 源重复和 .list、.sources 冲突
apt update 报错“Target is configured multiple times”时,检查旧式 .list 与 deb822 .sources 文件,保留一个源配置,恢复干净的更新结果。
重复 apt 源错误的含义
重复的 apt 源表示同一个软件仓库在两个不同文件中被声明了两次,APT(高级软件包工具)检测到了这两个副本。在 Ubuntu 24.04 及更高版本中,这几乎总是因为第三方安装脚本写入了一个旧式单行 .list 文件,而同一软件仓库的 deb822 .sources 文件已经存在于磁盘上。系统没有损坏,也没有软件包面临风险。删除其中一个声明,消息就会消失。
人们通常会将下面这行内容粘贴到搜索框中:
W: Target Packages (stable/binary-amd64/Packages) is configured multiple times in /etc/apt/sources.list.d/docker.list:1 and /etc/apt/sources.list.d/docker.sources:1从末尾开始阅读。两个文件分别在指定行号声明了同一个内容。Target Packages 是 apt 下载的索引,用于了解软件仓库提供哪些软件包;stable/binary-amd64/Packages 指定该索引覆盖的组件(stable)和架构(amd64)。因此,apt 表示 stable 组件的 amd64 索引在 docker.list 的第 1 行配置了一次,又在 docker.sources 的第 1 行配置了一次。
在 apt 3.0 及更高版本中,也就是 Ubuntu 25.04 及更高版本和 Debian 13 中,同一条消息会以 Warning: 开头,而不是 W:。此前缀之后的文本相同。
这个警告属于影响较轻的情况。apt 会合并这两个声明,并且仍会执行更新,因为两者描述的是使用同一个密钥的同一个存档。严重的情况会中止全部操作:
E: Conflicting values set for option Signed-By regarding source https://download.docker.com/linux/ubuntu/ noble: /usr/share/keyrings/docker-archive-keyring.gpg != /etc/apt/keyrings/docker.asc
E: The list of sources could not be read.这里 apt 会拒绝继续,因为两个声明为同一个存档指定了不同的签名密钥。它会合并两个完全相同的声明,但不会在两个 Signed-By 值之间进行选择,因为选择错误的密钥,意味着使用存档所有者从未用于签名的密钥验证软件包签名。因此,apt 不会读取任何软件源。在您手动编辑这些文件之前,apt update 和 apt install 都会因同样的两行错误而失败。
重复项是如何产生的
这两种格式位于扩展名不同的独立文件中,因此文件系统不会阻止它们同时存在。apt 会在较晚阶段才发现重叠:它会将每个源文件展开为计划下载的索引目标列表。在此之前,docker.list 和 docker.sources 是两个互不相关的文件。
以下 4 个常见事件会产生这对文件:
- 供应商安装脚本,或从旧文章中复制的命令,会使用
tee行写入/etc/apt/sources.list.d/vendor.list。 - 供应商自己的软件包随后发布
/etc/apt/sources.list.d/vendor.sources,并自动为您安装它。 - Ubuntu 24.04 及更高版本中的
add-apt-repository会写入 deb822 格式的.sources文件,因此您之前手动添加为.list的 PPA(个人软件包存档)会以.sources的形式重新出现。 - 发行版升级会将发行版自身的软件源改写为 deb822 格式,但会将您手动创建的
.list文件原样保留在旁边。
每条路径单独看都合理。当其中两条路径在同一台服务器上先后发生时,就会产生重复项,而且两次事件之间往往相隔数月。
两种格式并列对比
旧格式每个软件仓库占一行,各部分都按位置解析。
deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu noble stable顺序是固定的:类型(deb 表示二进制软件包,deb-src 表示源软件包),然后是方括号中的选项,再后面是归档的 URI(统一资源标识符)、套件,最后是一个或多个组件。由于含义由位置决定,空格放错位置就会改变 apt 的解析结果。
deb822 将相同内容表示为由命名字段组成的段落。这个名称来自 RFC 822,即 Debian 已用于软件包控制文件的邮件头格式。
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: noble
Components: stable
Architectures: amd64
Signed-By: /etc/apt/keyrings/docker.asc同一个软件仓库、同一个密钥,没有添加任何内容。字段之间可以直接对应:deb 变为 Types,归档地址变为 URIs,套件变为 Suites,组件变为 Components;每个方括号选项都变为独立字段,因此 signed-by= 变为 Signed-By:,arch= 变为 Architectures:。
每个字段名都使用复数形式,因为每个字段都接受以空格分隔的列表。一个段落中的 Suites: noble noble-updates noble-backports 会替代 3 个独立的 deb 行。空行表示一个段落结束,因此单个 .sources 文件可以包含多个软件仓库。deb822 还支持单行格式难以处理的设置:使用 Enabled: no 停用软件仓库,使用 Trusted、Check-Valid-Until,以及将内联密钥直接粘贴到 Signed-By 中;密钥的每一行都要缩进 1 个空格,空行则写成单独的句点。
各文件的位置
/etc/apt/sources.list:原始的单文件配置。在 Ubuntu 24.04 及更高版本中,该文件通常为空,或只包含一条指向新位置的注释。/etc/apt/sources.list.d/*.list:单行条目,通常每个软件源使用一个文件。/etc/apt/sources.list.d/*.sources:deb822 条目。Ubuntu 24.04 及更高版本将发行版自带的软件源保存在此处的ubuntu.sources中。/etc/apt/keyrings/:存放您添加的密钥。/usr/share/keyrings/存放软件包提供的密钥。
apt 只读取以 .list 或 .sources 结尾的文件,文件名可以包含字母、数字、下划线、连字符和句点。其他扩展名的文件会被跳过,并显示提示;这对下面的修复过程很重要。
查找重复项
从目录列表开始:
ls -l /etc/apt/sources.list.d/-rw-r--r-- 1 root root 195 Aug 3 09:12 docker.list
-rw-r--r-- 1 root root 254 Aug 9 14:40 docker.sources
-rw-r--r-- 1 root root 2683 Jun 11 08:02 ubuntu.sources两个文件的主文件名相同、扩展名不同,通常就是重复的一对,但不要只信文件名。请读取文件内容,因为重复项可能隐藏在任意名称的文件中:
grep -rn -E '^(deb |deb-src |Types:|URIs:|Suites:|Signed-By:)' /etc/apt/sources.list /etc/apt/sources.list.d//etc/apt/sources.list.d/docker.list:1:deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu noble stable
/etc/apt/sources.list.d/docker.sources:1:Types: deb
/etc/apt/sources.list.d/docker.sources:2:URIs: https://download.docker.com/linux/ubuntu
/etc/apt/sources.list.d/docker.sources:3:Suites: noble
/etc/apt/sources.list.d/docker.sources:6:Signed-By: /etc/apt/keyrings/docker.asc这两个条目包含相同的主机和相同的软件套件,因此是同一软件源的重复配置。两者都指向 https://download.docker.com/linux/ubuntu 和软件套件 noble,所以它们重复定义了同一个软件源。它们的 Signed-By 路径也不一致,因此产生了前面显示的 Conflicting values 错误。
此步骤请使用 grep,不要使用 apt 命令。apt 已因冲突停止时,也无法列出软件源,因此 apt-cache policy 只会输出相同的错误,而不是您需要的结果。
修复方法:保留 deb822 文件,移除旧文件
保留 .sources 文件。apt 工具现在会写入这种格式,Debian 和 Ubuntu 也都在向这种格式迁移。删除任何文件前,先检查磁盘上是否存在以下两个关键路径:
ls -l /etc/apt/keyrings/ /usr/share/keyrings/ | grep -i docker-rw-r--r-- 1 root root 4813 Aug 9 14:40 docker.asc磁盘上只有 /etc/apt/keyrings/docker.asc,因此 deb822 文件中的配置是有效的,而 .list 文件指向的密钥已被删除。如果要保留的文件恰好指定了缺失的密钥,请先将可用路径复制到该文件中,再删除另一个文件。
将旧文件移出该目录,而不是直接删除:
sudo mkdir -p /root/apt-sources-backup
sudo mv /etc/apt/sources.list.d/docker.list /root/apt-sources-backup/
sudo apt update也可以将文件重命名为 docker.list.bak 并保留在原位置,因为 apt 会忽略未知扩展名。但这样每次运行 apt 都会显示以下内容:
N: Ignoring file 'docker.list.bak' in directory '/etc/apt/sources.list.d/' as it has an invalid filename extension将文件移到其他位置可以避免屏幕显示该提示,同时保留备份。完成后,正常的 apt update 应类似下面这样,其中没有任何一行同时指定两个文件:
Hit:1 http://archive.ubuntu.com/ubuntu noble InRelease
Get:2 https://download.docker.com/linux/ubuntu noble InRelease [48.8 kB]
Get:3 http://security.ubuntu.com/ubuntu noble-security InRelease [126 kB]
Fetched 175 kB in 1s (146 kB/s)
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
All packages are up to date.现在确认编辑后仓库仍然可用:
apt-cache policy | grep download.docker.com 500 https://download.docker.com/linux/ubuntu noble/stable amd64 Packages
origin download.docker.com如果供应商的文档仍假设使用单行文件,也可以保留该文件,改为删除 .sources 文件。无论采用哪种方式,都必须遵循一条规则:同一个 archive 和 suite 只能由一个文件声明。
为什么一个损坏的第三方源会导致 apt update 失败
相邻的故障看起来不同,但根本原因相同:apt 无法使用某个第三方源。第一种情况是缺少密钥:
Err:5 https://download.docker.com/linux/ubuntu noble InRelease
The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 7EA0A9C3F273FCD8
E: The repository 'https://download.docker.com/linux/ubuntu noble InRelease' is not signed.
N: Updating from such a repository can't be done securely, and is therefore disabled by default.Signed-By 字段缺失,或指向的文件不是可用的密钥,因此 apt 无法验证存档的 InRelease 文件中的签名。随后,apt 会丢弃整个软件仓库,而不是信任无法验证的软件包列表。检查密钥文件本身:
ls -l /etc/apt/keyrings/docker.asc
gpg --show-keys /etc/apt/keyrings/docker.asc正常的密钥会输出一行 pub,其中包含密钥 ID,还会输出一行 uid,其中包含供应商名称。gpg: no valid OpenPGP data found. 表示该文件根本不是密钥。通常,这是因为密钥 URL 已变更,下载内容实际保存成了错误页面。重新获取密钥,检查文件,然后运行 apt update。
第二种情况发生在发行版升级之后:
Err:6 https://ppa.launchpadcontent.net/ondrej/php/ubuntu plucky InRelease
404 Not Found [IP: 10.0.0.80 443]
E: The repository 'https://ppa.launchpadcontent.net/ondrej/php/ubuntu plucky Release' does not have a Release file.该 PPA 没有为此套件发布任何内容,因此服务器上不存在对应路径,请求会返回 404。其他软件仓库仍可正常更新,系统中已经安装的软件包也不会受到影响。但本次运行会以非零状态退出,因此任何检查 apt update 退出状态的脚本,之后每次运行都会报告失败。这就是在配置了无人值守安全升级的服务器上,应清理失效源的原因:每日产生的噪声会掩盖真正的故障。
禁用一个软件源,同时不影响其他源
对于 deb822 文件,在该段中添加一个字段,然后保存:
Types: deb
URIs: https://ppa.launchpadcontent.net/ondrej/php/ubuntu
Suites: plucky
Components: main
Signed-By: /etc/apt/keyrings/ondrej-php.asc
Enabled: noapt 手册建议使用这种方法,而不是注释掉该段中的每一行,并且这种方法更容易撤销。对于单行文件,在行首添加 #。对于这两种格式,也可以将文件移出 /etc/apt/sources.list.d/;如果该软件源已永久弃用,应选择这种方式。
再次运行 sudo apt update。该软件源对应的 Err: 块会消失,退出状态也会恢复为 0。您可以在下一行使用 echo $? 检查这一点。
不要使用 sudo rm /etc/apt/sources.list.d/* 修复损坏的软件源。在 Ubuntu 24.04 及更高版本中,该命令会删除 ubuntu.sources;该目录包含发行版自带的软件源。这样 apt 将完全没有软件包列表,并会对明显存在的软件报告 E: Unable to locate package curl。如果您已经运行了该命令,请将文件写回:
Types: deb
URIs: http://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将其保存为 /etc/apt/sources.list.d/ubuntu.sources,并将 noble 替换为您在 lsb_release -cs 中获取的发行版名称,然后运行 sudo apt update。
将旧版 .list 文件转换为 deb822
截至 2026 年 8 月,apt 3.0 及更高版本已提供转换工具。Debian 13 包含该工具,Ubuntu 25.04 及之后的每个版本也都包含该工具,包括 26.04。先检查版本,然后运行:
apt --version
sudo apt modernize-sources该工具会将 /etc/apt/sources.list.d/ 下的单行文件重写为 deb822 .sources 文件。阅读命令输出的内容,然后自行列出该目录,并在确认结果前运行 apt update。Ubuntu 24.04 附带的 apt 版本较旧,不包含此子命令,因此该命令会返回 E: Invalid operation modernize-sources。在该版本中,请按照上面的字段映射手动转换。
目前转换不是必需的,因为 apt 仍会读取这两种格式。对于计划长期运行的服务器,建议完成转换,因为现在所有会写入软件源的工具都使用 deb822;只有 .sources 文件的服务器不会再产生这类重复配置。
保持服务器上的第三方软件源整洁
第三方软件源是服务器上最容易老化的部分。每个软件源都意味着其维护者承诺继续为您的 Ubuntu 版本发布软件包,而升级发行版时,系统会在同一时间检验这些承诺是否仍然有效。
- 只有在发行版软件包无法满足需求时,才添加第三方软件源。普通的 Ubuntu 24.04 上的 LAMP 堆栈不需要第三方源:Ubuntu 软件仓库提供了它使用的所有软件包,并在该版本的支持周期内提供安全更新。
- 将密钥保存在
/etc/apt/keyrings/中,每个供应商使用一个文件,权限设为 644。无特权的_apt用户负责下载,并且必须能够读取密钥;如果密钥文件只有 root 可读,那么每次从该软件源获取软件包时都会出现权限错误。 - 在每个源条目中,让
Signed-By指向该确切文件。保存在/etc/apt/trusted.gpg或/etc/apt/trusted.gpg.d/中的密钥会对整台服务器上的所有软件源生效,因此多年前添加的供应商密钥可能验证来自任意来源的软件包。 - 升级发行版前,检查软件源配置,并确认每个供应商已经为您要迁移到的套件发布软件包。
旧的全局密钥环中的密钥会在每次更新时触发提示:
W: https://download.docker.com/linux/ubuntu/dists/noble/InRelease: Key is stored in legacy trusted.gpg keyring (/etc/apt/trusted.gpg), see the DEPRECATION section in apt-key(8) for details.将该单个密钥导出到独立文件中,然后让源条目指向该文件:
gpg --no-default-keyring --keyring /etc/apt/trusted.gpg --export 7EA0A9C3F273FCD8 | sudo tee /etc/apt/keyrings/docker.gpg > /dev/null
sudo chmod 644 /etc/apt/keyrings/docker.gpg将 Signed-By: /etc/apt/keyrings/docker.gpg 添加到软件源条目中,然后运行 sudo apt update。当没有软件源再依赖旧密钥环后,警告就会停止;随后可以使用 sudo gpg --no-default-keyring --keyring /etc/apt/trusted.gpg --delete-key 7EA0A9C3F273FCD8 删除该条目。
还有一个习惯可以避免最多的问题。do-release-upgrade 会在升级期间禁用第三方软件源,并在升级完成后继续保持禁用状态。之后手动逐个重新启用软件源,正是产生重复声明的常见原因。开始前请阅读 Ubuntu 24.04 升级到 26.04 的指南,并记录仍然需要哪些软件源。对于刚刚完成安装的服务器,配置软件源的最佳时机是 新 VPS 上的前十分钟,因为此时服务器中只有 Ubuntu 随系统提供的软件源。
FAQ
为什么 apt 会提示某个目标被重复配置?
因为 /etc/apt/sources.list.d/ 下的两个文件声明了相同的仓库、套件和组件。消息会列出这两个文件及其行号,例如 docker.list:1 和 docker.sources:1。apt 会合并这些配置并继续运行,因此更新操作本身仍然有效。不过,仍应清理重复配置:一旦两个文件指定了不同的签名密钥,apt 就会因 E: Conflicting values set for option Signed-By 停止运行,并拒绝读取任何源,这也会阻塞 apt install。
应该保留 .list 文件还是 .sources 文件?
保留 .sources 文件。Ubuntu 24.04 及更高版本中的 add-apt-repository 会写入 deb822 格式。该格式为每个设置使用一个命名字段,而不是在方括号中使用按位置解析的文本,也是各发行版的发展方向。删除 .list 文件前,使用 ls -l /etc/apt/keyrings/ 确认 .sources 文件中的 Signed-By 路径指向一个实际存在的密钥。将旧文件移出 /etc/apt/sources.list.d/,不要在该目录内重命名,因为残留的 .bak 文件名会使 apt 每次运行都打印忽略文件提示。
如何停用一个 apt 仓库而不删除它?
在 deb822 .sources 文件中,将 Enabled: no 添加到该段配置中。在单行格式的 .list 文件中,将 # 放在该行开头。无论采用哪种方式,之后都运行 sudo apt update,该仓库对应的 Err: 块就会消失。当第三方仓库尚未提供适用于当前 Ubuntu 版本的软件包,且其 404 错误导致 apt update 以非零状态退出时,应采用这种方式。
单行 sources.list 格式会被弃用吗?
它已被弃用,但尚未移除。apt 仍会读取 .list 文件,并且很长时间内都会继续支持,因此服务器不会在第二天突然出现故障。新工具会写入 deb822 格式:Ubuntu 24.04 及更高版本会将发行版仓库保存在 /etc/apt/sources.list.d/ubuntu.sources 中,而 add-apt-repository 会写入 .sources 文件。在 apt 3.0 及更高版本中,sudo apt modernize-sources 可以转换现有文件。