很多用 Ubuntu 的朋友应该都有过这种经历:系统用得好好的,突然某天开机进不去桌面,或者升级内核之后显卡驱动挂了,折腾半天只能重装。重装本身不可怕,可怕的是装完之后要把之前装过的几十个软件一个个重新找回来。尤其是那些在官网慢慢下载的第三方安装包、为了适配某个环境反复测试过的依赖库,一旦丢了,再找一遍真的很浪费时间。
我在几年前经历过一次大版本升级翻车之后,就养成了一个习惯:每次系统稳定运行一段时间,就做一次安装包备份。这个操作看似不起眼,但关键时刻能省下大量时间和流量。这篇文章就把我一直在用的 Ubuntu 安装包备份方案完整写出来,包括 apt 缓存包的保存、已安装软件的重新打包、第三方 deb 包的归档,以及重装系统后如何用这些备份快速恢复环境。整个过程不需要额外安装复杂工具,大部分操作在终端里输入命令就能完成。
1. 备份思路先行:搞清楚 Ubuntu 软件包到底装在哪里
1.1 三类软件来源,备份策略完全不同
在动手备份之前,先要明白 Ubuntu 里的软件来源并不只有一种。最常见的当然是 apt 源里的软件,这类软件通过apt install安装,下载的 deb 包会缓存在本地目录中。其次是第三方提供的 deb 安装包,比如搜狗输入法、微信、Chrome、VSCode 这类软件,它们通常需要去官网手动下载,或者通过添加第三方源来安装。还有一种是通过编译源码、pip、conda 等方式安装的,这类不涉及 deb 包,严格来说不在安装包备份的范畴内,但可以作为环境备份的一部分来对待。
这三类软件的处理方式完全不同。apt 源里的软件可以用apt download或者缓存包直接备份,第三方 deb 包则需要单独归档,而源码编译安装的软件只能用dpkg-repack或者记录安装步骤的方式来做备份。搞清楚了这个分类,后面的操作才会有针对性。
1.2 备份前先看一眼系统和磁盘
在开始备份之前,我建议你先确认两件事:系统架构和磁盘剩余空间。系统架构直接决定了备份出来的 deb 包在别的机器上能不能用,64 位系统的包不能装到 32 位系统上,这个应该不难理解。查看架构用下面的命令:
dpkg --print-architecture大多数现代电脑输出的是amd64,如果你用的是树莓派或者其他 ARM 设备,输出的可能是arm64。这个信息很重要,后面恢复的时候要确认架构一致。
磁盘空间方面,主要看/var分区和当前用户 home 目录的空间。备份出来的 deb 包要存放在一个单独的位置,不要直接放在系统盘里,否则系统崩了备份也跟着没。建议准备一个移动硬盘、U 盘,或者局域网里的另一台机器。查看空间可以用:
df -h如果/var/cache/apt/archives目录下已经积累了很多缓存包,先在这个阶段清理一下,只备份当前系统正在使用的版本,而不是把历史残留版本都带上。
2. 核心操作:apt 缓存目录的备份与恢复
2.1 先备份 /var/cache/apt/archives 里的存量 deb 包
apt 在安装软件时,下载的 deb 文件默认缓存在/var/cache/apt/archives/目录。这个目录就是系统安装包备份最关键的一处资源。每次你用apt install安装软件,只要能装上,对应的 deb 包就一定会出现在这个目录里。因此,只要定期把这个目录里的文件拷贝出来,就相当于给系统里大部分 apt 安装的软件做了一次快照。
实际操作非常简单,一条命令就能把缓存目录完整复制到备份位置:
mkdir -p ~/backup/debs sudo cp -r /var/cache/apt/archives/*.deb ~/backup/debs/这里我建议先建一个专门的备份目录,比如在 home 目录下建backup/debs,把 deb 包都集中放到这里。注意用sudo是因为 apt 缓存目录的读取权限属于 root,普通用户进去只能看到文件列表但拷不出来。
还有一个细节值得注意,/var/cache/apt/archives/partial/目录里通常是下载了一半的临时文件,这些不需要备份,直接过滤掉就行。用*.deb匹配就可以自动避开。
拷贝完成后,可以统计一下备份的总大小和包数量:
ls ~/backup/debs | wc -l du -sh ~/backup/debs我自己的经验是,一套日常开发环境大概会有 500 到 1000 个缓存包,占用空间在 1GB 到 3GB 之间。这个大小放到 U 盘或者移动硬盘里完全没问题。
2.2 补齐依赖包:用 apt download 把关键软件及其依赖一网打尽
仅仅备份缓存目录有一个问题:系统运行一段时间后,apt 会自动清理旧的缓存文件,或者你手动执行过apt clean,那么缓存目录里可能只剩最近安装的软件包,很多早期安装的依赖已经不见了。为了保证备份的完整性,我通常会在备份缓存包的基础上,用apt download主动把当前系统里已安装软件及其依赖的 deb 包全部拉取一遍。
列出当前系统所有已安装的非自动安装软件包,可以这样操作:
apt list --installed | grep -v "^Listing" | cut -d/ -f1 > ~/backup/installed-packages.txt这个列表记录了你手动安装过哪些软件,排除掉自动安装的依赖后,恢复系统时只需要重新安装这些软件,依赖会自动补齐。
但要想把依赖也一起下载下来,单纯apt download就不够了,因为apt download只下载软件本身,不下载依赖。需要配合apt-cache depends递归获取所有依赖包,写一段脚本来批量下载。我常用的脚本如下:
#!/bin/bash mkdir -p ~/backup/debs-all cd ~/backup/debs-all while read pkg; do apt download "$pkg" 2>/dev/null deps=$(apt-cache depends "$pkg" | grep "Depends:" | awk '{print $2}' | tr -d '<>' | grep -v "^libc6$") for dep in $deps; do apt download "$dep" 2>/dev/null done done < ~/backup/installed-packages.txt这个脚本的原理是遍历已安装软件列表,对每个软件包先下载本体,再通过apt-cache depends查找它的直接依赖,然后把依赖也一并下载。这样得到的debs-all目录基本就是一套完整的离线安装源,重装系统之后可以用它脱离网络完成大部分软件的安装。
脚本需要一点耐心,因为要下载的包数量比较多,网速快的话十分钟内能完成。执行过程中如果apt download报错找不到某些包,多半是软件源里没有对应版本,这个可以忽略,后面用恢复时的在线源来解决。
2.3 离线恢复:dpkg -i 与 apt install 的取舍
备份的目的是为了有一天能够恢复。恢复的场景通常有两种:一是系统重装后需要把常用软件快速装回来,二是另一台同架构的机器需要复刻相同的环境。
先看单包安装的情况。如果你只需要恢复某个特定的软件,比如之前下载好的 VSCode 或者搜狗输入法,直接使用dpkg命令安装即可:
sudo dpkg -i /path/to/package.deb如果安装时报依赖缺失,再执行sudo apt -f install来修复依赖。但单独用dpkg -i有一个尴尬的地方:如果这个软件依赖的其他库不在系统里,dpkg不会自动解决依赖,必须自己一个一个装依赖。这也是为什么我在备份阶段就强调要把依赖包也一并下载下来。
再说批量恢复。如果你备份了debs-all目录,那么恢复效率会高很多。先把所有 deb 包装一遍:
sudo dpkg -i ~/backup/debs-all/*.deb这一步会尝试安装所有包,中途可能因为依赖顺序问题报错。这是正常的,第一次执行的目的实际上是让 dpkg 记录下所有包的期望安装状态。接着执行:
sudo apt -f installapt会自动检测并修复上一步遗留的依赖关系,把没装上的依赖从这个目录里找出来装上。这两条命令组合起来,就能完成 90% 以上的离线软件恢复。我自己实验过,在完全断网的虚拟机里,用这套方法可以把一套含开发工具、输入法、浏览器的环境完整还原出来,只有个别需要在线下载额外组件的软件会失败。
如果你恢复时有网络,更推荐的做法是把备份的debs-all目录加进本地源,然后用apt install安装。给 apt 添加一个本地源只需要在/etc/apt/sources.list.d/下新建一个配置文件,指向备份目录。这个操作稍微复杂一点,但好处是能自动处理依赖顺序,不用手动介入。
3. 进阶方案:dpkg-repack 把已装软件打包带走
3.1 dpkg-repack 的原理和适用场景
apt 缓存和apt download能覆盖大多数官方源里的软件,但有一个盲区:你通过源码编译安装,或者手工改过配置的软件,它们并没有对应的 deb 包存在缓存目录里。比如你手动编译安装了某个版本的 Node.js,或者定制了内核模块,这些在 apt 的世界里是不存在的。
这个场景需要用到dpkg-repack工具。它的工作原理很简单:分析当前系统里某个已安装软件的文件清单,把这些文件连同软件的元信息重新打包成一个 deb 文件。也就是说,你不需要知道软件当初是怎么装上的,只要它当前在系统里是完整的,就能打出一个可移植的安装包出来。
安装dpkg-repack同样走 apt:
sudo apt install dpkg-repack然后针对某个软件生成 deb 包:
dpkg-repack <package-name>在当前目录下会生成一个package-name_version_arch.deb文件。这个文件拿到别的机器上,用dpkg -i就能安装,效果和你在这台机器上手动配置好之后的软件状态几乎一样。
3.2 批量重打包脚本,一次搞定所有手动装过的软件
如果当前系统里有大量手动编译安装的软件,一条一条执行dpkg-repack显然太低效。我写了一个简单的循环脚本,把系统中标记为手动安装的软件全部重打包:
#!/bin/bash mkdir -p ~/backup/repacked cd ~/backup/repacked apt-mark showmanual | while read pkg; do if dpkg -s "$pkg" >/dev/null 2>&1; then dpkg-repack "$pkg" 2>/dev/null fi done这里用apt-mark showmanual列出所有手动安装的包,这个列表比apt list --installed更精确,因为自动安装的依赖不会被包含。脚本会跳过已经被移除的包,不会产生错误。
重打包生成的 deb 文件可能会非常大,尤其是像编译工具链、桌面环境这一类软件包,动辄几百兆。建议用 zip 或者 tar 压缩后再归档,可以至少节省 30% 的空间。
这里要特别提醒一点:dpkg-repack打出来的包只包含文件本身,不包含软件运行过程中依赖的数据文件。比如数据库软件,重打包后安装到新机器上,你原来的数据库数据还是在旧机器里,需要单独备份数据目录。所以不要把dpkg-repack当成全量系统迁移工具,它适合的是那些配置文件不敏感、主要靠二进制文件运行的软件。
4. 容易被忽略的第三方软件包与系统配置备份
4.1 搜狗输入法、VSCode 这类第三方 deb 包的归档
很多国内用户在 Ubuntu 上使用的软件并不来自官方源,比如搜狗输入法、微信、QQ、WPS 这些都需要从官网或论坛下载 deb 包手动安装。这类软件有一个特点:安装后不会在/var/cache/apt/archives里留下缓存,因为你是用dpkg -i或图形化安装器装的,没有经过 apt 的下载流程。
所以备份这类软件必须要有一个独立的归档目录。我习惯在~/backup/thirdparty下按软件名分类存放,每次从官网下载完安装包后,先复制到这个地方再安装。这样等哪天需要重装系统,直接到这个目录里看一遍就能想起来当初装过哪些第三方软件。
以搜狗输入法为例,下载到的文件通常是sogoupinyin_xxx_amd64.deb这样的命名。我一般会把它重命名成sogoupinyin-2024-11-amd64.deb这样带日期的格式,方便以后知道是哪个版本。同样的逻辑适用于 VSCode、Chrome、TeamViewer、微信等所有官网软件。
如果你之前没有保留这些安装包的习惯,可以用一个小技巧从系统里反向提取。比如微信已经安装在系统里,查看它的包名:
dpkg -l | grep wechat然后针对这个包名用dpkg-repack打包。这样即使原始安装包丢了,也能把已安装的版本备份出来。
4.2 软件源列表备份:sources.list 才是恢复环境的钥匙
有一件事经常被忽略,但实际非常关键,那就是软件源配置文件的备份。apt 能安装的软件范围、优先级、甚至是某些第三方软件能否正常更新,都取决于/etc/apt/sources.list和/etc/apt/sources.list.d/目录下的配置文件。
每次重装系统后,默认的源是国内还是国外的镜像、是否启用了 deb-src 源、有哪些第三方 PPA 源,全都丢失了。如果之前为了装某个软件手动添加过 PPA,重装系统后忘记添加这个源,那就没法直接用 apt 装回那个软件。
备份源配置很简单:
sudo cp -r /etc/apt/sources.list /etc/apt/sources.list.d ~/backup/恢复的时候把这两个文件原样拷回去,然后执行:
sudo apt update如果你的备份目录里包含了一个额外的第三方源,注意源的密钥也会需要同步备份。密钥文件通常在/etc/apt/keyrings/或者/etc/apt/trusted.gpg.d/,不同版本位置略有差别。我一般直接打包整个/etc/apt/目录:
sudo tar czf ~/backup/apt-config.tar.gz /etc/apt/这样源列表、密钥、preferences 策略文件全部都保存下来了,恢复时一条 tar 解压命令搞定。
5. 常见问题与排查实录
5.1 依赖报错 unmet dependencies 的处理
用备份恢复软件时最常遇到的问题就是依赖缺失。典型的场景是:你从备份目录里单独安装一个软件,比如sudo dpkg -i xxx.deb,结果提示依赖缺失,例如libssl1.1未被安装,而系统里只有libssl3。
遇到这种情况不要慌,先在备份目录里找找有没有对应的依赖包:
ls ~/backup/debs-all | grep libssl如果有就手动安装依赖,再安装目标软件。如果没有,就尝试让 apt 从在线源安装依赖:
sudo apt -f install如果网络可用,这个命令会自动从官方源下载缺失的依赖。如果网络不可用,就只能去网上找对应架构和系统版本的 deb 包手动下载。这也再次说明了备份阶段为什么要把依赖一并拉全,避免恢复时陷入依赖地狱。
5.2 架构不匹配和系统版本不一致的问题
把一台 Ubuntu 22.04 上备份的软件包装到 20.04 上,大概率会遇到架构和版本不匹配的报错。架构问题表现为wrong architecture 'amd64',这基本是因为你把 64 位的包往 32 位系统上装。版本问题则更隐蔽,软件依赖的某个库版本在旧系统里不存在,或者新系统里库版本更高但改动破坏了兼容性。
我的建议是:备份时记录系统版本和架构,归档目录里写一个简单的说明文件。等到恢复时先确认目标系统的版本和架构,不要盲目地把备份包全部灌进去。如果你要恢复的系统版本和你备份时的系统版本一致,成功率会非常高;如果不一致,建议只在目标系统上重新从官方源安装。
5.3 备份文件应该放哪里才能真的万无一失
最后聊一个很多人容易忽视的问题:备份文件放在哪里。把备份放在家目录下,然后系统某天完全无法启动,备份也跟着没法拿出来,这就失去了备份的意义。我自己经历过一次磁盘损坏导致所有数据丢失的惨痛教训,之后学乖了。
备份的存放原则是"异地",也就是尽量不要和系统放在同一块物理硬盘上。最省事的方案是准备一个专门的移动硬盘或者大容量 U 盘,备份完就拔下来放好。如果你不想用实体介质,可以备份到局域网内的 NAS 上,用scp或rsync把备份目录传到另一台机器:
rsync -av ~/backup/ user@nas-ip:/backup/ubuntu/另一个思路是定期把备份打包压缩,然后传到网盘或者是代码托管平台的私有仓库里。虽然 deb 包体积不小,但好在大多数开发环境的备份压缩后也就是 1GB 左右,分卷上传完全可行。
实际操作中,我个人的习惯是双备份:一份放在移动硬盘里,一份放在 NAS 上。移动硬盘负责应对单机故障,NAS 负责应对移动硬盘损坏这种小概率事件。定期备份的频率也不需要太高,每两个月做一次全量备份,日常安装了重要软件之后顺手更新一下备份目录,就足够应付绝大多数场景。
写在最后的一点经验
从开始做 Ubuntu 安装包备份到现在,这套方法帮我至少省下了两三次重装系统后的恢复时间。以前重装完系统可能要花大半天在找软件、下依赖、调配置上,现在只需要把备份目录拷贝回来,跑两三条命令,半小时内就能回到基本可用的状态。如果你也经常折腾系统,或者手上有几台 Ubuntu 设备需要保持软件环境一致,强烈建议从今天开始养成备份安装包的习惯。哪怕只是简单地把/var/cache/apt/archives复制一份出来,也比什么都不做强太多。