
很多用 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同样走 aptsudo 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 21; 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/ usernas-ip:/backup/ubuntu/另一个思路是定期把备份打包压缩然后传到网盘或者是代码托管平台的私有仓库里。虽然 deb 包体积不小但好在大多数开发环境的备份压缩后也就是 1GB 左右分卷上传完全可行。实际操作中我个人的习惯是双备份一份放在移动硬盘里一份放在 NAS 上。移动硬盘负责应对单机故障NAS 负责应对移动硬盘损坏这种小概率事件。定期备份的频率也不需要太高每两个月做一次全量备份日常安装了重要软件之后顺手更新一下备份目录就足够应付绝大多数场景。写在最后的一点经验从开始做 Ubuntu 安装包备份到现在这套方法帮我至少省下了两三次重装系统后的恢复时间。以前重装完系统可能要花大半天在找软件、下依赖、调配置上现在只需要把备份目录拷贝回来跑两三条命令半小时内就能回到基本可用的状态。如果你也经常折腾系统或者手上有几台 Ubuntu 设备需要保持软件环境一致强烈建议从今天开始养成备份安装包的习惯。哪怕只是简单地把/var/cache/apt/archives复制一份出来也比什么都不做强太多。