在 Ubuntu 24.04/22.04 VPS 上按官方方式安装 Docker
- 2026年10月3日
- 09:00
- 作者: Adrien Roche
- 教程

开始之前:一台 KVM VPS、受支持的 Ubuntu 和一个 sudo 用户
Docker Engine 不自带内核。每个容器都共享宿主机内核,并依赖 Linux 命名空间、cgroups 和 netfilter,因此守护进程需要一台内核由你掌控的 VPS。这正是 KVM(完全虚拟化)VPS 所提供的。在基于 OpenVZ 或 LXC 的容器型套餐上,内核属于服务商,Docker 要么拒绝启动,要么只有在宿主机启用嵌套时才能工作。安装任何东西之前先检查:
systemd-detect-virt # expect: kvm
. /etc/os-release && echo "$VERSION_CODENAME" # noble (24.04) or jammy (22.04)
uname -m # x86_64 or aarch64
Docker 为 64 位的 Ubuntu 24.04 LTS(noble)和 22.04 LTS(jammy)提供软件包,支持 x86_64 与 arm64。你还需要一个拥有 sudo 权限的用户;下文的 docker 组和 rootless 部分都假定你使用普通账号而非 root。
从官方 apt 仓库安装 Docker Engine
在 Ubuntu 上安装 Docker 常见有四种方式,但只有一种是 Docker 为服务器场景撰写文档并提供支持的方法:
| 方式 | 软件包 | 结论 |
|---|---|---|
| Docker 的 apt 仓库 | 来自 download.docker.com 的 docker-ce | 推荐:版本最新,通过 apt upgrade 更新,并附带 Compose 与 Buildx 插件。 |
| Ubuntu universe | docker.io | 可以用,但 Ubuntu 自建的版本落后于上游,且缺少插件。 |
| Snap | snap install docker | 受限安装,使用自己的路径;经常引发莫名其妙的“permission denied”。服务器上请避免。 |
| 一键脚本 | get.docker.com | 软件包相同,但未经审阅就管道进 root shell。仅适用于用完即弃的机器。 |
第 1 步:移除冲突的软件包
如果 VPS 镜像里已经带有 Ubuntu 的 docker.io、旧版 docker-compose 或 podman-docker,请先卸载,以免两套构建争抢 /usr/bin/docker。下面这段循环来自 Docker 官方文档,在干净系统上执行无害,并且不会动 /var/lib/docker:
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do
sudo apt-get remove $pkg
done
第 2 步:添加 Docker 的 GPG 密钥和 apt 源
密钥放在 /etc/apt/keyrings/,并通过 signed-by= 引用,这样它只对这一个仓库生效,而不是像已废弃的 apt-key 那样全局信任。代号从 /etc/os-release 读取,因此同一段命令在 24.04 和 22.04 上都适用:
sudo apt-get update
sudo apt-get install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
如果最后那次 apt-get update 报签名错误,几乎总是因为密钥文件不可读;重新执行 chmod 那一行即可。
第 3 步:安装引擎及其插件
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
docker-ce 是守护进程,docker-ce-cli 是客户端,containerd.io 是底层运行时,两个插件分别提供 docker buildx 和 docker compose。在 Ubuntu 上,软件包的安装后脚本会立即启动 docker.service 并设置开机自启。
第 4 步:用 hello-world 验证
sudo docker run hello-world
docker --version
sudo docker info | head -n 20
hello-world 是最权威的测试:客户端与守护进程通信,守护进程从 Docker Hub 拉取一个很小的镜像,创建容器、运行它,并把输出回传。如果你看到“Hello from Docker!”,说明每一层都正常。在健康的 24.04 或 22.04 安装上,docker info 会显示 Storage Driver: overlay2 和 Cgroup Version: 2。
不用 sudo 运行 Docker:docker 组
守护进程监听 Unix 套接字 /var/run/docker.sock,属主为 root:docker,因此 docker 组的成员无需 sudo 即可使用 CLI:
sudo groupadd docker # usually exists already; harmless if it does
sudo usermod -aG docker $USER
newgrp docker # or log out and back in
docker run hello-world
请理解你刚刚授予了什么。任何能访问该套接字的人都可以运行 docker run -v /:/host --privileged ...,读取或改写 VPS 上的任意文件,这使得 docker 组成员身份等同于 root,而且没有密码提示、也不会留下 sudo 日志。请把它完全当作 sudo 权限对待;我们关于 在 Ubuntu 上切换用户与权限提升 的指南解释了这一区别在多用户机器上为何重要。
Docker Compose 插件
docker-compose-plugin 包以 docker compose 子命令(中间有空格)的形式提供 Compose v2。旧的 Python 版 docker-compose 二进制文件已停止维护,不应与之并存安装。先查看版本,再用一个最小的 compose.yaml 测试:
docker compose version
mkdir -p ~/web && cd ~/web
cat > compose.yaml <<'EOF'
services:
web:
image: nginx:alpine
ports:
- "127.0.0.1:8080:80"
restart: unless-stopped
EOF
docker compose up -d
docker compose ps
curl -I http://127.0.0.1:8080
docker compose down
其中两个细节是刻意为之:restart: unless-stopped 让容器在重启后自动恢复,而端口绑定到 127.0.0.1 而非所有网卡,原因见后面的防火墙部分。
Rootless 模式:不以 root 身份运行的守护进程
Rootless 模式让守护进程和容器都运行在用户命名空间内,因此容器逃逸只会落到一个非特权账号,而不是 root。当多人共用一台 VPS 时,这是正确的选择。前提条件包括:从属 UID/GID 范围(Ubuntu 会为普通用户在 /etc/subuid 和 /etc/subgid 中创建)、uidmap 工具、用户级 D-Bus 会话,以及 Docker 的 rootless 扩展包:
sudo apt-get install -y uidmap dbus-user-session docker-ce-rootless-extras
grep "^$USER:" /etc/subuid /etc/subgid # each should show a range of 65536 IDs
# optional: stop the system-wide daemon if you only want rootless
sudo systemctl disable --now docker.service docker.socket
dockerd-rootless-setuptool.sh install
安装工具会创建一个按用户的 systemd 单元,并打印出你的 shell 需要的两个变量。把它们加入 ~/.bashrc,然后让用户级守护进程在无需登录的情况下开机启动:
export PATH=/usr/bin:$PATH
export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock
systemctl --user enable docker
sudo loginctl enable-linger $(whoami)
Rootless 模式有实际限制:除非运行 sudo setcap cap_net_bind_service=ep $(which rootlesskit) 并重启用户守护进程,否则无法发布 1024 以下的端口;overlay 网络不可用;数据存放在 ~/.local/share/docker。在 Ubuntu 24.04 上,AppArmor 会限制非特权用户命名空间;deb 包已附带 rootlesskit 所需的配置文件,但若采用静态二进制安装,则需按 rootless 文档手动添加。
开机行为、存储与日志轮转
决定容器在重启后是否运行的是两件独立的事:守护进程必须已启用,且每个容器必须带有重启策略。Ubuntu 的软件包会替你启用该服务;确认一下,如果单元状态看起来陌生,我们关于 用 systemctl 列出服务 的参考文章解释了每一列的含义:
systemctl is-enabled docker.service containerd.service
sudo systemctl enable docker.service containerd.service # only if the line above said disabled
没有加 --restart 启动的容器在重启后不会恢复;对任何需要长期运行的容器,请在命令行使用 --restart unless-stopped,或在 Compose 中使用 restart: unless-stopped。
Docker 存储的一切(镜像层、容器文件系统、数据卷、日志)都位于 /var/lib/docker,在小型 VPS 上,正是这个目录把磁盘填满。用 docker system df 观察它,用 docker system prune 回收悬空层;在加上 -a 之前请先读提示,因为它还会删除所有未被运行中容器使用的镜像。如果 VPS 有第二块磁盘,请在拉取大体积内容之前,在 /etc/docker/daemon.json 中把 data-root 指向它。
日志值得特别提醒。默认的 json-file 驱动会把容器 stdout 的每一行写入 /var/lib/docker/containers/<id>/<id>-json.log,并且在你配置之前不做任何轮转。一个话痨容器可以在一周内吃光整块磁盘。第一天就设置守护进程级别的限制:
sudo tee /etc/docker/daemon.json > /dev/null <<'EOF'
{
"log-driver": "json-file",
"log-opts": { "max-size": "10m", "max-file": "3" },
"live-restore": true
}
EOF
sudo systemctl restart docker
日志选项只对重启之后创建的容器生效,因此需要把长期运行的容器重建一次。live-restore 让守护进程可以为升级而重启,同时不杀死运行中的容器。
Docker 与 ufw:发布的端口会绕过你的防火墙
这是在公网 VPS 上最容易让人栽跟头的一点。Docker 管理自己的 iptables 规则:当你用 -p 8080:80 发布端口时,守护进程会在 nat 表的 PREROUTING 链中插入一条 DNAT 规则,并在 FORWARD 中插入一条 accept 规则。而 ufw 的规则位于 INPUT,转发流量根本不经过那里。于是 ufw deny 8080 毫无作用,一个发布在 0.0.0.0:5432 上的数据库容器对整个互联网可达,而 ufw 却报告该端口已被阻止。请在 docker ps 的 PORTS 列中查找 0.0.0.0:,或运行 ss -tlnp;我们关于 在 Linux 上检查开放端口 的指南说明了如何读懂这些输出并从外部确认暴露情况。
两种干净的解决办法,按优先级排列:
- 把发布的端口绑定到回环地址。
-p 127.0.0.1:8080:80让服务只能从 VPS 本机访问,通常再由你有意对外暴露的 nginx 或 Caddy 之类反向代理转发。只在容器之间通信的服务根本不需要-p。 - 在
DOCKER-USER链中过滤。 Docker 会在自己的FORWARD规则之前评估这条链,并且从不改写它。由于数据包到达时 DNAT 已经完成,请用 conntrack 匹配原始端口:
sudo iptables -I DOCKER-USER -i eth0 ! -s 203.0.113.10 -p tcp \
-m conntrack --ctorigdstport 8080 --ctdir ORIGINAL -j DROP
把 eth0 换成你的公网网卡,把 203.0.113.10 换成允许放行的地址。这些规则不会持久化;请用 iptables-persistent 包保存。在 daemon.json 中设置 "iptables": false 会以极难排查的方式破坏容器网络;别去碰它。
彻底卸载 Docker
先清除软件包,再删除数据目录(apt 会刻意保留这些目录):
sudo apt-get purge -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras
sudo rm -rf /var/lib/docker /var/lib/containerd
sudo rm -f /etc/apt/sources.list.d/docker.list /etc/apt/keyrings/docker.asc
sudo apt-get autoremove -y
对于 rootless 安装,请先以该用户身份运行 dockerd-rootless-setuptool.sh uninstall,随后删除 ~/.local/share/docker。
以上一切都假定你拥有一台由自己掌握 root 权限、控制内核、并且无需请示就能改写 iptables 的服务器。如果你正需要这样一台机器,rdp.monster 的 具备完整 root 权限的 KVM Linux VPS 预装 Ubuntu 24.04 或 22.04,提供独享 CPU 与内存、无限流量(合理使用)、免 KYC、支持加密货币付款,每月 $8.99 起,并在付款确认后约 10 秒内交付,让你结账后立刻就能在自己的服务器上运行上文的 hello-world 检查。
常见问题
在 Ubuntu 上 apt 会安装哪个 Docker 版本,我该如何更新?
apt-get install docker-ce 会拉取适用于你的 Ubuntu 代号的最新稳定版,之后每次 sudo apt-get update && sudo apt-get upgrade 都会把版本向前推进。运行 docker version 可同时查看客户端和服务端版本。若要锁定某个特定版本,用 apt-cache madison docker-ce 列出候选,安装 docker-ce=<version> 以及与之匹配的 docker-ce-cli,然后对两个包执行 apt-mark hold。为什么会出现“permission denied while trying to connect to the Docker daemon socket”?
root:docker,因此普通用户会收到 permission denied。要么在命令前加 sudo,要么用 sudo usermod -aG docker $USER 把自己加进去,然后重新登录一个新会话(或运行 newgrp docker),因为组变更只对新会话生效。如果错误依旧,用 groups 确认成员身份,用 systemctl status docker 确认守护进程在运行,并确保 DOCKER_HOST 没有指向一个并不存在的 rootless 套接字。在 Ubuntu Server 上应该用 snap 还是 apt 安装 Docker?
daemon.json 和数据目录也使用不同路径,更新节奏由 snapd 而非你决定。这些怪问题会表现为莫名其妙的 permission denied 和绑定挂载失败。apt 包遵循标准的 Ubuntu 约定,与 systemd 正常集成,并通过你本来就会执行的 apt upgrade 获得上游更新。运行 Docker 的 VPS 需要多少内存和磁盘?
/var/lib/docker,若没有 docker system prune 和日志轮转,20 GB 很快就会写满。相比可突发的共享份额,更建议选择独享 CPU 与内存,因为构建时负载会剧烈飙升,而容器无法优雅地使用交换分区。Adrien Roche, 基础设施与主机托管编辑
系统工程师,拥有 10 年以上运维 Windows Server 和 Linux 集群的经验。Adrien 负责 rdp.monster 的基础设施文档,并撰写关于 RDP、VPS 托管、服务器管理、网络和隐私工具的指南。




