Snap 与 Deb:Ubuntu 下到底该选哪个软件安装方式?

问题是怎么来的

最近我频繁地在 Ubuntu 上装软件,每次都会遇到同一个选择:

1
2
sudo apt install xxx        # deb
sudo snap install xxx # snap

有些软件两个渠道都有。比如 Firefox,apt 里有一个版本,snap 里也有一个版本。这时候我就懵了:到底该装哪个?两个都装会不会冲突?如果我想同时开两个 Firefox 怎么办?

这些问题不是”查一下命令”就能解决的。它们背后藏着 Linux 包管理的整个演进历史。这篇文章就是我当时想找但没找到的那篇——把 deb 和 snap 的来龙去脉、使用场景、以及你真正关心的那些问题,一次讲清楚。


一、先理解一个概念:Linux 软件安装的三层困境

在讲 deb 和 snap 之前,你得先理解为什么 Linux 会有这么多种安装方式。这不是因为开发者闲得慌,而是因为传统 Linux 软件分发有一个从娘胎里带来的问题。

问题 1:依赖地狱

你在 Windows 上装软件,一般是下载一个 .exe,双击,下一步下一步就装好了。每个软件自带它需要的所有库文件(DLL),互不干扰。

Linux 不一样。Linux 的设计哲学是”共享库”:你系统里只有一份 libssl.so,所有需要 SSL 的软件都共用它。这样做的好处是节省磁盘空间、统一安全更新,但代价是:

软件 A 要求 libssl.so.1.1,软件 B 要求 libssl.so.3,它们不能同时存在。

这就是著名的依赖地狱。deb 的设计初衷就是为了解决这个问题。

问题 2:发行版分裂

Ubuntu 22.04 里 Python 是 3.10,Ubuntu 24.04 里是 3.12。一个软件如果在 22.04 上编译好,拿到 24.04 上很可能跑不起来,因为底层系统库版本变了。

对软件开发者来说,这意味着:要想支持 5 个发行版,就要打 5 个不同的包

问题 3:安全边界

传统 deb 安装的软件和系统是”深度绑定”的:软件能访问你的 /home/etc/tmp,几乎不受限制。如果一个聊天软件有漏洞,攻击者理论上可以通过它访问你整个系统的文件。

这三个问题,催生了三种解决方案:deb 解决了问题 1,snap 试图同时解决三个问题。


二、deb:系统包管理的基石

它是什么

deb 是 Debian/Ubuntu 体系的软件包格式,本质上是一个 压缩归档文件,里面包含了:

  • 编译好的二进制文件
  • 配置文件的默认版本
  • 元信息(软件名、版本、依赖关系、描述)
  • 安装/卸载时执行的脚本(preinst、postinst、prerm、postrm)

当你执行 sudo apt install nginx 时,背后发生的事是:

  1. apt 从软件源下载 nginx_1.24.0_amd64.deb
  2. 解析这个包的依赖列表(比如要求 libpcre3 >= 2:8.35
  3. 检查系统里是否已安装这些依赖,没有就自动下载安装
  4. 调用 dpkg 把文件解压到系统标准路径(/usr/bin/nginx/etc/nginx/ 等)
  5. 执行安装后脚本,完成初始化

整个过程叫做依赖解析 + 文件部署

deb 的设计哲学

deb 的核心思想是:”软件是操作系统的一部分”。它假设:

  1. 依赖应该尽量共享,避免重复
  2. 软件版本由发行版仓库统一管理,保证兼容性
  3. 用户通过统一的更新机制(apt upgrade)管理所有软件

这个思路在服务器场景下非常完美。你的服务器装的都是”系统级”软件——nginx、MySQL、Docker、编译工具链。它们需要和系统紧密配合,共享同一个 openssl、同一个 glibc

deb 真正的缺点

不是”依赖处理不好”——apt 的依赖解析已经非常成熟了。真正的痛点是:

版本滞后。Ubuntu 的仓库遵循”稳定优先”策略。你装 python3,拿到的是仓库里的稳定版本,不是最新版。这是因为新版本可能引入不兼容的变更,Ubuntu 需要时间测试和集成。

跨发行版困难。你给 Ubuntu 打的 deb 包,拿到 Fedora 上完全不能用,因为 Fedora 用的是 RPM 体系。

多版本共存麻烦。如果你需要两个版本的 Python(比如 3.8 和 3.12),deb 的方案是给它们不同包名(python3.8python3.12),但这需要包维护者手动处理,不是所有软件都支持。


三、snap:从”系统的一部分”到”独立的应用容器”

它是什么

snap 是 Canonical 推出的通用 Linux 软件包格式。它的设计思路和 deb 完全相反:

不要把软件当作系统的一部分,而是当作一个独立的、自包含的”应用容器”。

一个 snap 包(.snap 文件)是一个压缩的 SquashFS 文件系统镜像。它里面包含了:

  • 软件本身
  • 所有依赖库(自包含)
  • 运行时环境
  • 沙箱配置文件(定义了这个应用能访问系统的哪些部分)

安装时,这个镜像被挂载到一个只读的文件系统上,应用在其中运行。它看不到宿主系统的 /usr/lib,用的是自己打包的那份依赖。

挂载机制

snap install 后,你会看到这样的目录结构:

1
2
3
4
/snap/firefox/
├── current -> 4178 # 符号链接指向当前版本
├── 4178/ # 版本 4178 的完整文件系统
├── 3624/ # 上一个版本(可用于回滚)

每次更新,新版本作为一个新目录被创建,current 符号链接切换到新目录。旧版本保留,随时可以回滚。

沙箱怎么做

snap 使用 AppArmor 和 seccomp 来限制应用的权限:

  • 默认情况下,snap 应用不能访问 /home(除非显式授权)
  • 不能访问系统配置文件
  • 网络访问、摄像头、麦克风都是需要申请权限的

你可以用 snap connections 查看一个应用被授予了哪些权限:

1
2
3
4
5
$ snap connections firefox
Interface Plug Slot
network firefox:network :network
network-bind firefox:network-bind :network-bind
home firefox:home :home

这就是为什么 snap 说它”更安全”——不是因为代码更安全,而是因为即使代码有漏洞,攻击面也被沙箱限制住了


四、deb vs snap:深层次对比

表面上你看到的是”安装方式不同”,但底层是两套完全不同的软件哲学。理解了这一点,以后遇到任何新的包格式都能快速判断。

维度 deb snap
核心思想 软件是系统的一部分 软件是独立的应用容器
依赖策略 依赖共享,由系统管理 依赖自包含,打包在应用内
隔离性 无隔离,软件可以访问系统 沙箱隔离,受 AppArmor 限制
更新方式 手动或系统统一更新 自动后台更新,无需用户操作
安装位置 /usr/bin/etc/usr/lib /snap/<name>/<version>
启动速度 原生速度,直接执行 ELF 通过 snapd 启动,有轻微 Cold Start 开销
磁盘占用 小,共享系统库 大,每个应用自带依赖
系统集成 深度集成,可以改系统配置 隔离运行,默认不触碰系统文件
跨发行版 限于 Debian 系 任何支持 snapd 的发行版

启动速度差异的真相

很多人吐槽 snap 的启动速度慢。这主要是因为首次冷启动时:

  1. snapd 需要挂载 squashfs 镜像
  2. 沙箱初始化(AppArmor profile 加载)
  3. 应用自身的初始化

以 Firefox 为例,snap 版首次启动可能比 deb 版慢 2-5 秒。但这个差异在热启动(应用刚关闭再打开)时基本消失,因为 squashfs 已被缓存。

如果你主要用桌面应用,snap 的启动延迟通常可以接受。如果是高频调用的命令行工具(比如 gitcurl),每次多等 1-2 秒就很烦人了——这种情况优先 deb。


五、回答你最关心的问题:多开和冲突

问题 1:同一个软件装了 deb 又装 snap,会冲突吗?

不会直接冲突,但因为它们的配置文件路径和可执行文件路径不同,你可能遇到一些困惑。

以 Firefox 为例:

deb 版 snap 版
可执行文件 /usr/bin/firefox /snap/bin/firefox
配置目录 ~/.mozilla/ ~/snap/firefox/current/.mozilla/
命令 直接 firefox 直接 firefox(snap 的 PATH 优先级更高)

两个版本可以同时存在,它们的安装目录、配置目录都是完全独立的,不会互相覆盖。

但有个坑:如果在终端输入 firefox,Ubuntu 会调用 PATH 中先找到的那个。通常在 Ubuntu 24.04 上,snap 的 /snap/bin 在 PATH 中的优先级更高,所以输入 firefox 启动的是 snap 版。如果想启动 deb 版,需要写完整路径 /usr/bin/firefox

问题 2:能不能多开(同时运行两个实例)?

理解这个问题需要先分清楚”多开”是什么意思:

情况 A:同一个应用,开两个窗口或多标签页

比如你同时打开两个 Firefox 窗口。这跟包的格式没有任何关系,是应用本身的功能。Firefox 支持多窗口,无论你装的是 deb 还是 snap,都能开多个窗口。需要多开标签的话,直接 Ctrl+T 就行。

情况 B:同时运行两个不同版本的应用

比如你装了 snap 版的 Firefox 120 和 deb 版的 Firefox ESR 115。它们可以同时运行。因为它们是完全独立的两个安装,有不同的配置目录、不同的进程。

1
2
3
# 启动两个不同的 Firefox
firefox # snap 版(最新版)
/usr/bin/firefox # deb 版(ESR 版)

两个窗口会同时出现在桌面,互不影响。而且它们可以登录同一个 Firefox 账号,各自同步。

情况 C:同一个包,运行两个完全隔离的实例(不同配置/不同账号)

这才是真正有挑战的场景。比如你想用两个不同的 Firefox 配置文件登录两个不同的网站账号。

deb 的方案

1
2
firefox -P "profile1" --no-remote &
firefox -P "profile2" --no-remote &

Firefox 自带的 Profile Manager 可以创建多个独立的配置文件。两个实例可以是同一个二进制文件,但读写不同的 ~/.mozilla/firefox/ 子目录。这和包格式无关——deb 能做到的事,snap 版 Firefox 也能做到,因为 Firefox 的 Profile 机制是应用自身的功能

snap 的方案

Snap 还有一个独特优势——如果某个应用需要用同一个 snap 包的独立实例,而且这个应用本身不支持多 Profile,你可以用 snap 的 --parallel-instances 功能:

1
2
3
snap set system experimental.parallel-instances=true
snap install firefox_fresh # 实例 1
snap install firefox_work # 实例 2

两个 snap 实例有完全独立的配置目录、独立的沙箱、独立的更新通道。但这个功能目前还是实验性的,不是所有应用都能正常使用。

总结一下”多开”这件事

需求 方案
同一个应用,多窗口/多标签 应用自身功能,和包格式无关
两个不同版本的同一个应用同时运行 deb + snap 各装一个,或 snap 两个不同版本
同一个应用,两个完全隔离的配置环境 应用自身的 Profile 机制(如 Firefox)或 snap 的 parallel-instances
需要完全独立的环境(不同用户/网络/文件系统) 不是包管理的问题,需要容器(Docker/LXC)

六、什么时候该用 deb?

场景 1:服务器软件

服务器上没有桌面环境,不需要沙箱。你装 nginx、MySQL、Redis、Docker,它们本来就应该和系统深度集成。

1
sudo apt install nginx

你不需要”隔离每个服务”,因为服务之间的隔离应该由 systemd 的 service 单元、用户权限、cgroup 来做,而不是包管理来做。deb + systemd + Docker 才是服务器上的正统方案。

场景 2:命令行的核心工具

gitcurlvimhtopssh——这些是你天天用的东西。它们需要:

  • 零延迟启动
  • 和系统无缝集成
  • 通过 shell 直接调用

这些工具用 deb 没有任何悬念。snap 版本的反而是”能用但没必要”。

场景 3:库和开发工具链

python3-devbuild-essentiallibssl-dev——这些是编程环境的基础,必须用 deb。它们提供的是头文件和静态库,snap 不适合干这个。

场景 4:需要定制和深度系统集成

如果你的 nginx 需要加载自定义模块、修改配置文件、读写系统证书,deb 是唯一合理的选择。snap 的沙箱会阻止这些操作。


七、什么时候该用 snap?

场景 1:桌面应用

这是 snap 的主场。Firefox、VS Code、Slack、Spotify、Discord——这些桌面应用:

  • 需要频繁更新
  • 不需要深度系统集成
  • 用户更关心”能用就行”而不是”怎么装的”
  • 能从沙箱中受益(聊天软件不会访问你的 ssh 密钥)
1
2
sudo snap install code --classic
sudo snap install spotify

--classic 标志是给那些需要更多系统访问权限的应用(比如 IDE 需要访问终端、调试器等),禁用了严格的沙箱限制。一般来说,除非你明确知道需要 --classic,否则用默认的沙箱模式

场景 2:追求最新版本

有些软件在 Ubuntu 官方仓库里更新很慢。比如 neovim

  • Ubuntu 24.04 仓库:v0.9.5
  • snap:v0.10.x(基本上跟上游最新版同步)

如果你需要新版本的新功能,snap 省去了自己编译或加 PPA 的麻烦。

场景 3:跨发行版统一部署

如果你的团队用 Ubuntu、Fedora、Arch Linux,想要所有人用同一个版本的同一个软件(比如一个内部工具),打 deb 包需要在每个发行版上测试和维护。snap 一次打包,到处都能用。


八、snap 的真正劣势:你必须知道的坑

我不是来给 snap 说好话的。有一些问题你必须知道:

1. 磁盘占用

每个 snap 自带依赖,导致磁盘占用显著增大:

1
2
$ du -sh /var/lib/snapd/snaps/
1.8G

如果你有 10 个 snap 应用,可能吃掉几个 GB。这在服务器上不可接受,在桌面 256GB+ 的机器上通常还行。

2. 启动速度

冷启动延迟不可忽视。以 Firefox 为例,snap 版冷启动可能在 3-6 秒,deb 版通常 1-2 秒。如果你每天打开浏览器一次,这不是问题。如果你经常开关,累计起来会有点烦。

3. 主题不兼容

snap 应用默认看不到你系统安装的 GTK/Qt 主题。你可能装了漂亮的主题,但 snap 应用还用着默认的灰白界面。虽然 Canonical 在不断改善这个问题(通过 gtk-common-themes snap),但目前仍然不是无缝的。

4. 只能安装在默认路径

snap 应用不能选择安装位置。所有 snap 都装在 /snap/ 下,无法更改。如果你习惯把软件装到 /opt/ 或自定义分区,snap 不给你这个自由。

5. 后台服务

snap 依赖 snapd 守护进程来管理安装、更新和沙箱。snapd 是一个常驻后台的服务,大约占用几十 MB 内存。如果你追求极致轻量,这算一个额外开销。


九、Flatpak 和 AppImage:顺便提一下

既然讲到通用包格式,不能不提另外两个玩家:

Flatpak

Flatpak 和 snap 的思路非常相似(自包含、沙箱、跨发行版),主要区别是:

区别 Flatpak snap
主导方 GNOME/Freedesktop.org 社区 Canonical(Ubuntu 母公司)
服务中心 Flathub Snap Store(由 Canonical 运营)
服务器端 完全开源,可自行搭建 服务端闭源(这是争议点)
发行版支持 Fedora 原生支持,多数发行版可用 Ubuntu 原生支持

如果你在意开源和去中心化,Flatpak 更符合价值观。不过在日常使用中,哪个有你要的软件就用哪个。

AppImage

AppImage 更像 Windows 的 .exe:下载一个文件,给执行权限,双击运行。没有沙箱、没有自动更新、没有依赖管理。优点是”零安装”,缺点是功能最弱。

对比总结:

格式 一句话 适用场景
deb 系统原生,稳定可靠 服务器、系统工具
snap 自带依赖,沙箱隔离,自动更新 桌面应用
Flatpak 开源的 snap 替代品 桌面应用,注重开源
AppImage 便携式,无需安装 临时使用、测试新版

十、未来的趋势

我可以明确地说:deb 不会消失,snap 也不会替代 deb。它们解决的是不同层面的问题。

未来大概率是这样:

  1. 系统层(内核、init 系统、系统库、核心工具)——继续由 deb/RPM 等传统格式管理。这些组件需要和发行版深度耦合,不能也不应该被通用格式取代。

  2. 服务层(数据库、Web 服务器、消息队列)——deb 负责安装基础软件,Docker/containerd 负责运行时隔离。snap 不适合这个场景,太重且不适合编排。

  3. 桌面应用层(浏览器、IDE、通讯工具、多媒体)——snap/Flatpak 会越来越主流。对桌面用户来说,”一个命令安装、自动更新、不会搞坏系统”比”轻量 20MB”重要得多。

  4. 开发工具层(语言运行时、编译器、linter)——可能走向混合方案。rustupnvmpyenv 这些版本管理器已经是事实标准,包管理器(无论是 apt 还是 snap)都难以满足开发者对”同时管理多个版本”的需求。

所以不要把”deb vs snap”当成零和博弈。真正重要的是:你当前要装的这个软件,属于哪一层?然后选对应的工具


十一、决策指南:一句话判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
flowchart TD
Start["🤔 我要装个软件,用 deb 还是 snap?"]

Start --> Q1{"是系统核心工具吗?<br/>(git/curl/ssh/nginx/mysql)"}
Q1 -->|是| A1["用 deb 👍"]
Q1 -->|否| Q2{"是桌面应用吗?<br/>(浏览器/IDE/聊天/音乐)"}
Q2 -->|是| Q3{"仓库里有最新版吗?"}
Q3 -->|有| A2["deb 就够了 👍"]
Q3 -->|没有| Q4{"需要长期稳定吗?"}
Q4 -->|是| A3["加 PPA 或手动装 deb 👍"]
Q4 -->|否| Q5{"在意磁盘空间吗?"}
Q5 -->|在意| A4["手动装 deb 或 Flatpak"]
Q5 -->|不在意| A5["用 snap 👍 省心"]
Q2 -->|否| Q6{"是开发工具/库吗?"}
Q6 -->|是| A6["用版本管理器 <br/>(nvm/pyenv/rustup) 或用 deb"]
Q6 -->|否| Q7{"需要完全隔离环境?"}
Q7 -->|是| A7["用 Docker/LXC 🐳"]
Q7 -->|否| A8["直接 deb 👍"]

style A1 fill:#A8E6CF,color:#333
style A5 fill:#A8E6CF,color:#333
style A8 fill:#A8E6CF,color:#333
style A7 fill:#FFD3B6,color:#333

十二、总结

这篇文章想传达的核心观点就这几条:

  1. deb 和 snap 不是竞争对手,它们是 Linux 包管理演化的不同阶段和不同层次的方案。deb 负责”系统基础”,snap 负责”桌面应用”。

  2. 多开和冲突不是包格式的问题。同一个软件的不同版本可以共存,但真正的”多开”靠的是应用自身的 Profile 机制或者容器,而不是包格式。

  3. 在服务器上只用 deb。服务器不需要沙箱,需要的是轻量、可控、和系统集成。

  4. 在桌面上,哪个方便用哪个。如果 deb 仓库里版本够新,用 deb。如果仓库里的版本太旧,snap 是最省心的选择。

  5. 未来不会只有一种包格式。Linux 生态的多样性是它的优点。理解每种方案的边界,比争论”哪个更好”有用得多。

下次装软件的时候,不用再纠结了。打开终端前,想想你要装的是什么类型的软件,答案就很明显了。