Cài đặt Docker trên VPS Ubuntu 24.04/22.04: cách chính thức
- 3 tháng 10, 2026
- 09:00
- Bởi Adrien Roche
- Cập nhật 7 tháng 10, 2026
- Hướng dẫn

Trước khi bắt đầu: một VPS KVM, một bản Ubuntu được hỗ trợ và một người dùng sudo
Docker Engine không đi kèm kernel riêng. Mọi container đều dùng chung kernel của máy chủ và dựa vào namespaces, cgroups và netfilter của Linux, vì vậy daemon cần một VPS mà bạn làm chủ kernel. Đó chính là điều VPS KVM (ảo hóa toàn phần) mang lại. Trên các gói dựa trên container như OpenVZ hoặc LXC, kernel thuộc về nhà cung cấp và Docker hoặc từ chối khởi động, hoặc chỉ chạy được nếu máy chủ đã bật nesting. Hãy kiểm tra trước khi cài bất cứ thứ gì:
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 phát hành gói cho Ubuntu 64-bit 24.04 LTS (noble) và 22.04 LTS (jammy), trên x86_64 và arm64. Bạn cũng cần một người dùng có quyền sudo; các phần về nhóm docker và chế độ rootless bên dưới giả định bạn dùng tài khoản thường thay vì root.
Cài Docker Engine từ kho apt chính thức
Có bốn cách phổ biến để cài Docker trên Ubuntu, và chỉ một cách là phương pháp được Docker ghi trong tài liệu và hỗ trợ cho máy chủ:
| Phương pháp | Gói | Đánh giá |
|---|---|---|
| Kho apt của Docker | docker-ce từ download.docker.com | Khuyên dùng: phiên bản mới nhất, cập nhật qua apt upgrade, có sẵn plugin Compose và Buildx. |
| Kho universe của Ubuntu | docker.io | Dùng được, nhưng bản build của Ubuntu chậm hơn upstream và thiếu các plugin. |
| Snap | snap install docker | Cài đặt bị giới hạn (confined) với đường dẫn riêng; thường gây ra lỗi "permission denied" bất ngờ. Tránh dùng trên máy chủ. |
| Script tiện lợi (convenience script) | get.docker.com | Cùng các gói đó, nhưng được đưa thẳng vào shell root mà không qua kiểm tra. Chỉ dùng cho máy thử nghiệm dùng xong bỏ. |
Bước 1: gỡ các gói xung đột
Nếu image VPS đã có sẵn gói docker.io của Ubuntu, một bản docker-compose cũ hoặc podman-docker, hãy gỡ chúng để hai bản build không tranh nhau /usr/bin/docker. Vòng lặp này lấy từ tài liệu của Docker và vô hại trên hệ thống sạch; nó không động đến /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
Bước 2: thêm khóa GPG và nguồn apt của Docker
Khóa được đặt trong /etc/apt/keyrings/ và được tham chiếu bằng signed-by=, giới hạn nó cho riêng kho này thay vì tin cậy toàn cục qua apt-key đã lỗi thời. Tên mã (codename) được đọc từ /etc/os-release, nên cùng một đoạn lệnh chạy được trên cả 24.04 và 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
Lỗi chữ ký ở lệnh apt-get update cuối cùng gần như luôn có nghĩa là không đọc được tệp khóa; hãy chạy lại dòng chmod.
Bước 3: cài engine và các plugin
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
docker-ce là daemon, docker-ce-cli là client, containerd.io là runtime bên dưới, còn hai plugin cung cấp docker buildx và docker compose. Trên Ubuntu, script hậu cài đặt của gói sẽ khởi động docker.service ngay lập tức và bật nó khởi động cùng hệ thống.
Bước 4: kiểm tra bằng hello-world
sudo docker run hello-world
docker --version
sudo docker info | head -n 20
hello-world là phép thử quyết định: client giao tiếp với daemon, daemon tải một image rất nhỏ từ Docker Hub, tạo container, chạy nó và truyền kết quả trở lại. Nếu bạn thấy "Hello from Docker!", mọi tầng đều hoạt động. Trên một bản cài 24.04 hoặc 22.04 bình thường, docker info hiển thị Storage Driver: overlay2 và Cgroup Version: 2.
Chạy Docker không cần sudo: nhóm docker
Daemon lắng nghe trên Unix socket /var/run/docker.sock, thuộc sở hữu của root:docker, nên thành viên nhóm docker có thể dùng CLI mà không cần sudo:
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
Hãy hiểu rõ quyền bạn vừa cấp. Bất kỳ ai truy cập được socket đó đều có thể chạy docker run -v /:/host --privileged ... và đọc hoặc ghi đè mọi tệp trên VPS, khiến việc là thành viên nhóm docker tương đương với root, không cần nhập mật khẩu và không để lại dòng log sudo nào. Hãy coi nó đúng như quyền sudo; hướng dẫn của chúng tôi về chuyển người dùng và leo thang đặc quyền trên Ubuntu giải thích vì sao sự khác biệt này quan trọng trên máy nhiều người dùng.
Plugin Docker Compose
Gói docker-compose-plugin cung cấp Compose v2 dưới dạng lệnh con docker compose (có dấu cách). Bản nhị phân Python docker-compose cũ đã ngừng hỗ trợ và không nên cài song song. Kiểm tra phiên bản, rồi thử với một tệp compose.yaml tối giản:
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
Có hai chi tiết được chủ ý: restart: unless-stopped đưa container chạy lại sau khi khởi động lại máy, và cổng được gắn vào 127.0.0.1 thay vì mọi giao diện mạng, vì những lý do được giải thích trong phần tường lửa.
Chế độ rootless: daemon không chạy bằng root
Chế độ rootless chạy daemon và các container bên trong một user namespace, nên nếu container bị thoát ra (escape), kẻ tấn công chỉ rơi vào một tài khoản không có đặc quyền thay vì root. Đây là lựa chọn đúng khi nhiều người dùng chung một VPS. Điều kiện cần là các dải UID/GID phụ (Ubuntu tạo sẵn cho người dùng thường trong /etc/subuid và /etc/subgid), bộ công cụ uidmap, một phiên D-Bus của người dùng và gói rootless extras của Docker:
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
Công cụ thiết lập sẽ tạo một unit systemd riêng cho người dùng và in ra hai biến mà shell của bạn cần. Thêm chúng vào ~/.bashrc, rồi cho daemon của người dùng khởi động cùng hệ thống mà không cần đăng nhập:
export PATH=/usr/bin:$PATH
export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock
systemctl --user enable docker
sudo loginctl enable-linger $(whoami)
Chế độ rootless có những giới hạn thực sự: không thể publish các cổng dưới 1024 trừ khi bạn chạy sudo setcap cap_net_bind_service=ep $(which rootlesskit) và khởi động lại daemon của người dùng, không có mạng overlay, và dữ liệu nằm trong ~/.local/share/docker. Trên Ubuntu 24.04, AppArmor hạn chế user namespace không đặc quyền; các gói deb đã kèm profile mà rootlesskit cần, nhưng nếu cài bằng bản nhị phân tĩnh thì bạn phải tự thêm profile như mô tả trong tài liệu rootless.
Khởi động cùng hệ thống, lưu trữ và xoay vòng log
Hai yếu tố riêng biệt quyết định container có chạy lại sau khi khởi động lại máy hay không: daemon phải được bật (enable), và mỗi container phải có chính sách khởi động lại (restart policy). Các gói của Ubuntu đã bật dịch vụ cho bạn; hãy xác nhận lại, và nếu trạng thái của unit trông lạ lẫm, tài liệu tham khảo của chúng tôi về liệt kê dịch vụ bằng systemctl giải thích từng cột:
systemctl is-enabled docker.service containerd.service
sudo systemctl enable docker.service containerd.service # only if the line above said disabled
Container được khởi chạy mà không có --restart sẽ không chạy lại sau khi khởi động lại máy; hãy dùng --restart unless-stopped trên dòng lệnh hoặc restart: unless-stopped trong Compose cho mọi thứ cần chạy lâu dài.
Mọi thứ Docker lưu trữ (các lớp image, hệ thống tệp của container, volume, log) đều nằm trong /var/lib/docker, và trên một VPS nhỏ, đây chính là thư mục làm đầy ổ đĩa. Theo dõi nó bằng docker system df, thu hồi các lớp thừa bằng docker system prune, và đọc kỹ lời nhắc trước khi thêm -a, vì tùy chọn này còn xóa mọi image không được container đang chạy nào sử dụng. Nếu VPS có ổ đĩa thứ hai, hãy trỏ data-root sang đó trong /etc/docker/daemon.json trước khi tải bất cứ thứ gì dung lượng lớn.
Log cần một lời cảnh báo riêng. Driver mặc định json-file ghi mọi dòng stdout của container vào /var/lib/docker/containers/<id>/<id>-json.log và không xoay vòng trừ khi bạn cấu hình. Một container ghi log nhiều có thể chiếm hết ổ đĩa trong một tuần. Hãy đặt giới hạn cho toàn bộ daemon ngay từ ngày đầu:
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
Tùy chọn log chỉ áp dụng cho các container được tạo sau khi khởi động lại daemon, nên hãy tạo lại một lần các container chạy lâu dài. live-restore cho phép daemon khởi động lại để nâng cấp mà không làm dừng các container đang chạy.
Docker và ufw: các cổng được publish vượt qua tường lửa của bạn
Đây là phần khiến hầu hết mọi người mắc bẫy trên một VPS công khai. Docker tự quản lý các quy tắc iptables của mình: khi bạn publish một cổng bằng -p 8080:80, daemon chèn một quy tắc DNAT vào chuỗi PREROUTING của bảng nat và một quy tắc chấp nhận vào FORWARD. Các quy tắc của ufw nằm trong INPUT, nơi lưu lượng được chuyển tiếp không bao giờ đi qua. Vì vậy ufw deny 8080 không có tác dụng gì, và một container cơ sở dữ liệu được publish trên 0.0.0.0:5432 vẫn truy cập được từ toàn bộ internet trong khi ufw báo cổng đó đã bị chặn. Hãy tìm 0.0.0.0: trong cột PORTS của docker ps, hoặc chạy ss -tlnp; hướng dẫn của chúng tôi về kiểm tra cổng đang mở trên Linux chỉ cách đọc kết quả đó và xác nhận mức độ lộ diện từ bên ngoài.
Hai cách khắc phục gọn gàng, theo thứ tự ưu tiên:
- Gắn các cổng publish vào loopback.
-p 127.0.0.1:8080:80giữ cho dịch vụ chỉ truy cập được từ chính VPS, thường nằm sau một reverse proxy như nginx hoặc Caddy mà bạn chủ động công khai. Các container chỉ giao tiếp với nhau thì hoàn toàn không cần-p. - Lọc trong chuỗi
DOCKER-USER. Docker xử lý chuỗi này trước các quy tắcFORWARDcủa chính nó và không bao giờ ghi đè lên nó. Vì DNAT đã diễn ra khi gói tin tới chuỗi này, hãy so khớp cổng gốc bằng conntrack:
sudo iptables -I DOCKER-USER -i eth0 ! -s 203.0.113.10 -p tcp \
-m conntrack --ctorigdstport 8080 --ctdir ORIGINAL -j DROP
Thay eth0 bằng giao diện mạng công khai của bạn và 203.0.113.10 bằng địa chỉ được phép truy cập. Các quy tắc này không tồn tại sau khi khởi động lại; hãy lưu chúng bằng gói iptables-persistent. Đặt "iptables": false trong daemon.json sẽ làm hỏng mạng container theo những cách rất khó gỡ lỗi; đừng động đến nó.
Gỡ cài đặt Docker hoàn toàn
Trước tiên hãy gỡ sạch (purge) các gói, rồi xóa các thư mục dữ liệu mà apt cố ý giữ lại:
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
Với thiết lập rootless, trước tiên hãy chạy dockerd-rootless-setuptool.sh uninstall bằng chính người dùng đó, sau đó xóa ~/.local/share/docker.
Tất cả những điều trên đều giả định một máy chủ mà bạn có quyền root, kiểm soát kernel và có thể ghi lại iptables mà không cần hỏi ai. Nếu bạn vẫn cần một máy như vậy, VPS Linux KVM với toàn quyền root của rdp.monster cung cấp sẵn Ubuntu 24.04 hoặc 22.04 với CPU và RAM riêng, băng thông không giới hạn (sử dụng hợp lý), không cần KYC, chấp nhận tiền mã hóa, giá từ $8.99/tháng, và được giao trong khoảng 10 giây sau khi thanh toán được xác nhận, nên bạn có thể chạy phép thử hello-world ở trên trên máy chủ của mình ngay sau khi đặt hàng.
Câu hỏi thường gặp
apt cài phiên bản Docker nào trên Ubuntu, và làm sao để cập nhật?
apt-get install docker-ce sẽ tải bản ổn định mới nhất dành cho tên mã Ubuntu của bạn, và mỗi lần chạy sudo apt-get update && sudo apt-get upgrade sau đó sẽ đưa bạn lên bản mới hơn. Chạy docker version để xem phiên bản của cả client lẫn server. Để cố định một phiên bản cụ thể, hãy liệt kê các phiên bản khả dụng bằng apt-cache madison docker-ce và cài docker-ce=<version> cùng với docker-ce-cli tương ứng, rồi apt-mark hold cả hai gói.Vì sao tôi gặp lỗi "permission denied while trying to connect to the Docker daemon socket"?
root:docker, nên người dùng thường sẽ nhận lỗi permission denied. Bạn có thể thêm sudo trước các lệnh, hoặc tự thêm mình vào nhóm bằng sudo usermod -aG docker $USER, rồi mở một phiên đăng nhập mới (hoặc chạy newgrp docker) vì thay đổi nhóm chỉ áp dụng cho các phiên mới. Nếu lỗi vẫn còn, hãy chạy groups để xác nhận tư cách thành viên, kiểm tra daemon có đang chạy bằng systemctl status docker, và đảm bảo DOCKER_HOST không trỏ tới một socket rootless không tồn tại.Nên cài Docker bằng snap hay apt trên Ubuntu Server?
daemon.json và dữ liệu, và nhịp cập nhật do snapd quyết định chứ không phải bạn. Những điều này biểu hiện thành các lỗi permission denied và lỗi bind-mount khó hiểu. Các gói apt tuân theo quy ước chuẩn của Ubuntu, tích hợp với systemd bình thường và nhận bản cập nhật upstream qua chính lệnh apt upgrade bạn vẫn chạy.VPS chạy Docker cần bao nhiêu RAM và ổ đĩa?
/var/lib/docker, và 20 GB đầy rất nhanh nếu không có docker system prune và xoay vòng log. Hãy ưu tiên CPU và RAM riêng thay vì tài nguyên chia sẻ kiểu burstable, vì quá trình build tăng tải đột ngột và container không thể swap một cách êm ái.Adrien Roche, Biên tập viên hạ tầng & hosting
Kỹ sư hệ thống với hơn 10 năm vận hành các hệ thống Windows Server và Linux. Adrien phụ trách tài liệu hạ tầng của rdp.monster và viết các hướng dẫn về RDP, hosting VPS, quản trị máy chủ, mạng và công cụ bảo vệ quyền riêng tư.




