SSD Nodes Learn 🎉 VPS từ $5.50/tháng
Hướng dẫn Matt ConnorBởi Matt Connor · Cập nhật ngày 2026-08-21

Cài system container Incus trên VPS

Hướng dẫn cài system container Incus trên VPS: kiểm tra virtualisation, tạo storage pool, cấu hình networking và xử lý các lỗi dễ gặp khi chạy init, user, service.

Một system container của Incus là gì

System container của Incus trên VPS cung cấp cho bạn một máy hoàn chỉnh với init system và user account riêng, không phải một process đơn lẻ gắn với filesystem. Container khởi động, chạy init dưới dạng PID 1 và phản hồi systemctl. Container dùng chung kernel của host, nên không phải là virtual machine. Mọi thành phần phía trên kernel đều hoạt động như trên một máy riêng.

Incus là community fork của LXD, được duy trì trong dự án Linux Containers. Lệnh client là incus. Incus cũng chạy virtual machine thực thông qua QEMU khi bạn truyền --vm, nhưng system container mới là lý do hầu hết mọi người cài đặt Incus, và đó là nội dung của phần còn lại trong hướng dẫn này.

Vì sao so sánh Docker dễ gây hiểu lầm

Docker đóng gói một process. Incus đóng gói một hệ điều hành. Tài liệu Incus nêu rõ sự khác biệt này: "Application containers (as provided by, for example, Docker) package a single process or application. System containers, on the other hand, simulate a full operating system similar to what you would be running on a host or in a virtual machine."

Sự khác biệt này thay đổi cách bạn sử dụng container hằng ngày.

  • Docker image không có init, nên systemctl bên trong image sẽ fail. Container Incus chạy một init system, vì vậy service và timer hoạt động như trên server.
  • Docker container được thiết kế để bị xóa và build lại từ Dockerfile. Container Incus được thiết kế để giữ lại, patch và tạo snapshot.
  • Docker image là build artefact được push lên registry. Instance Incus là state được lưu trên disk trong storage pool, và bạn di chuyển nó bằng incus export.
  • Docker cô lập một workload. Incus cô lập một máy, nên một container có thể chứa nhiều workload và nhiều user account.

Bạn có thể chạy Docker bên trong system container của Incus. Bạn không nên chạy Incus bên trong application container của Docker. Nếu thứ bạn thực sự cần là một process cho mỗi container cùng một bước build image, hãy đọc Podman và Docker trên VPS trước để so sánh. Nếu bạn muốn mỗi workload có một kernel riêng thay vì dùng chung kernel, Firecracker microVM trên VPS là hướng ngược lại.

Incus có chạy được bên trong VPS không?

Điều này phụ thuộc vào loại virtualisation của VPS và kernel của VPS, vì vậy hãy kiểm tra cả hai trước khi cài bất cứ thứ gì. Không nên chỉ dựa vào trang marketing của nhà cung cấp. Hãy chạy 4 lệnh sau trên máy.

systemd-detect-virt
uname -r
stat -fc %T /sys/fs/cgroup
cat /sys/fs/cgroup/cgroup.controllers

systemd-detect-virt in ra kvm hoặc qemu nghĩa là VPS của bạn là một virtual machine có kernel riêng. Đây là trường hợp dễ xử lý, vì Incus khi đó hoạt động như trên phần cứng thật. lxc, lxc-libvirt hoặc openvz nghĩa là bản thân VPS là một container dùng chung kernel của nhà cung cấp. Các container Incus bên trong đó là nested container, và nesting chỉ hoạt động nếu nhà cung cấp đã bật tính năng này cho container của bạn. Bạn không thể bật từ bên trong, vì thiết lập này nằm trên host mà bạn không kiểm soát.

stat -fc %T /sys/fs/cgroup phải in ra cgroup2fs. Nếu in ra giá trị khác, máy đang dùng layout cgroup (control group) v1 hoặc hybrid, không phải layout mà Incus hiện hỗ trợ.

cat /sys/fs/cgroup/cgroup.controllers liệt kê các control-group controller được ủy quyền cho bạn. Tài liệu Incus xác định blkio, cpuset, devices, freezer, memorypids là bắt buộc. Trên một nested VPS, danh sách này thường ngắn hơn KVM, vì nhà cung cấp quyết định những controller nào được cấp xuống. Controller không có trong file đó là controller mà Incus không thể sử dụng, nên instance limit phụ thuộc vào controller đó sẽ không khả dụng với bạn.

Phiên bản kernel quan trọng hơn trước đây. Tính đến tháng 8 năm 2026, tài liệu Incus nêu 2 mức tối thiểu khác nhau cho 2 branch được upstream duy trì. Branch 6.0 LTS (long term support) ghi rằng: “The minimum supported kernel version is 5.4.” Branch stable hiện tại ghi rằng: “The minimum supported kernel version is 6.12.” Ubuntu 24.04 cung cấp series 6.0 LTS trong repository riêng và đi kèm kernel 6.8; đây là tổ hợp được hỗ trợ. Nếu cài bản stable hiện tại từ upstream repository trên cùng kernel 6.8 đó, bạn sẽ thấp hơn mức tối thiểu được tài liệu công bố, vì vậy hãy đọc uname -r trước khi chọn repository.

Nếu mục tiêu của bạn là chạy full virtual machine thay vì container, ràng buộc sẽ khác và khó hơn. Xem nested virtualisation trên VPS để biết VPS của bạn có thể expose /dev/kvm hay không, và xem Proxmox trên VPS thuê nếu bạn sở hữu phần cứng.

Cài đặt Incus trên Ubuntu hoặc Debian

Debian 13 và Ubuntu 24.04 trở lên đã có Incus trong repository riêng.

sudo apt update
sudo apt install -y incus

Trên Debian, incus-base cài phần hỗ trợ container mà không cài các thành phần máy ảo. Trên Ubuntu, thêm qemu-system nếu bạn cũng muốn tạo các instance --vm.

Nếu cần bản mới hơn bản mà distribution đang cung cấp, các package upstream nằm tại pkgs.zabbly.com. Các lệnh này lấy từ README của repository do dự án quản lý.

sudo apt update && sudo apt install -y curl
sudo mkdir -p /etc/apt/keyrings/
sudo curl -fsSL https://pkgs.zabbly.com/key.asc -o /etc/apt/keyrings/zabbly.asc
sudo sh -c 'cat <<EOF > /etc/apt/sources.list.d/zabbly-incus-stable.sources
Enabled: yes
Types: deb
URIs: https://pkgs.zabbly.com/incus/stable
Suites: $(. /etc/os-release && echo ${VERSION_CODENAME})
Components: main
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/zabbly.asc
EOF'
sudo apt-get update
sudo apt-get install -y incus

Sau đó, cấp quyền cho user của bạn truy cập socket của daemon.

sudo usermod -aG incus-admin "$USER"
newgrp incus-admin
incus info

incus info in cấu hình của server nghĩa là socket hoạt động. Lỗi permission nghĩa là thay đổi group chưa có hiệu lực trong shell hiện tại; newgrp incus-admin áp dụng thay đổi cho shell hiện tại, còn đăng nhập lại sẽ áp dụng đúng cách. Hãy xem membership của incus-admin tương đương với quyền root trên host, vì truy cập socket đó đồng nghĩa với toàn quyền điều khiển daemon chạy dưới quyền root. Một số distribution cũng tạo group incus thông thường để cấp quyền truy cập hạn chế cho user.

Bây giờ hãy khởi tạo daemon.

sudo incus admin init

Hãy trả lời các câu hỏi thay vì dùng incus admin init --minimal. Cách thiết lập tối thiểu chọn storage driver dir, và phần tiếp theo giải thích vì sao lựa chọn này tiếp tục ảnh hưởng đến hệ thống.

Khởi chạy một instance và xác nhận nó hoạt động.

incus launch images:debian/13 web
incus list
incus exec web -- bash

incus list phải hiển thị webRUNNING với địa chỉ IPv4 trên subnet incusbr0. Không có địa chỉ nghĩa là DHCP (dynamic host configuration protocol) chưa hoàn tất; phần networking sẽ giải thích vấn đề này. Container không khởi động được sẽ in nguyên nhân trong incus info web --show-log, còn lỗi ở cấp daemon sẽ xuất hiện trong sudo journalctl -u incus -n 50. Trên VPS mà systemd-detect-virt trả về lxc hoặc openvz, lần khởi chạy này là phép kiểm tra thực tế để xác định bạn có được phép dùng nesting hay không.

Vì sao storage backend mặc định quan trọng

Storage backend quyết định snapshot diễn ra tức thì hay phải sao chép toàn bộ disk của container. Đây là lựa chọn duy nhất lúc cài đặt mà sau này bạn không thể thay đổi với chi phí thấp.

Incus hỗ trợ dir, btrfs, lvm, zfs, Ceph và một số remote driver. Trên VPS chỉ có một disk, lựa chọn thực tế là giữa dirbtrfs.

Driver dir lưu mỗi container dưới dạng các file và directory thông thường trong /var/lib/incus. Incus ghi rõ driver này “chậm hơn nhiều so với tất cả driver khác”, vì nó phải giải nén từng image và tạo bản sao thực thay vì tham chiếu đến các block dùng chung. Snapshot của container 4 GiB sẽ ghi 4 GiB và mất thời gian tương đương với cp -a. Disk quota chỉ hoạt động trên ext4 hoặc XFS khi đã bật project quota ở cấp filesystem. Thiết lập này thường không được bật mặc định trên hầu hết VPS image, nên giới hạn disk trên pool dir thường không có tác dụng.

btrfszfs sử dụng copy-on-write, nên snapshot chỉ ghi lại các block thay đổi sau đó. Incus chỉ định đây là hai backend được khuyến nghị. Snapshot gần như được tạo tức thì. Disk quota hoạt động thông qua cơ chế quota riêng của filesystem.

Hầu hết gói VPS chỉ cung cấp một disk và không có partition trống, nên hãy đặt pool trên một loop file. Incus sẽ tự làm việc đó nếu bạn không cung cấp source=.

sudo apt install -y btrfs-progs
incus storage create fast btrfs size=30GiB
incus profile device set default root pool=fast
incus launch images:debian/13 web2 -s fast

Nếu không có size=, loop-backed pool sẽ sử dụng 20% dung lượng disk còn trống, với mức tối thiểu 5 GiB và tối đa 30 GiB. Hãy đặt giá trị này một cách chủ động. Loop file là một file trên root filesystem, nên pool và host dùng chung dung lượng trống. Vì vậy, khi pool đầy, disk của host cũng sẽ đầy.

ZFS trên Debian và Ubuntu là một DKMS module thay vì module có sẵn trong kernel tree. Do đó, nó được build lại sau mỗi lần nâng cấp kernel và có thể build thất bại sau một lần nâng cấp. Trên server mà bạn không theo dõi hằng ngày, btrfs là lựa chọn ít cần bảo trì hơn trong hai loại này.

Ba chế độ mạng và những gì mỗi chế độ cho phép truy cập

incus admin init tạo một bridge được quản lý tên là incusbr0 và đưa mọi instance mới vào đó. Đây là một trong ba cách gắn container vào mạng. Hai cách còn lại tồn tại vì cách đầu tiên đặt container phía sau NAT (network address translation).

Managed bridge. incusbr0 nhận một subnet private. Host giữ địa chỉ đầu tiên trong subnet đó và hoạt động như gateway. Incus chạy DHCP và DNS (domain name system) trên subnet này. Traffic outbound đi qua public address của host với source NAT. Không có kết nối nào từ bên ngoài đến được container cho đến khi bạn cho phép. Để forward một port, hãy dùng proxy device.

incus config device add web http proxy listen=tcp:0.0.0.0:8080 connect=tcp:127.0.0.1:80 nat=true

nat=true forward bằng các rule của netfilter thay vì proxy qua một kết nối userspace riêng. Vì vậy, địa chỉ thật của client vẫn xuất hiện trong log của container. Incus chỉ hỗ trợ chế độ này khi host là gateway của instance, đúng với trường hợp incusbr0.

macvlan. Container nhận một địa chỉ MAC (media access control) riêng trên mạng vật lý của host. Trên hầu hết nền tảng VPS, cách này không hoạt động vì port trên virtual switch chỉ gắn với MAC của VM và sẽ loại bỏ frame từ mọi MAC khác. Ngay cả khi hoạt động, còn một giới hạn thứ hai thường gây nhầm lẫn. Tài liệu Incus ghi rõ: "macvlan devices, while able to communicate between themselves and to the outside, cannot talk to their parent device. This means that you can't use macvlan if you ever need your instances to talk to the host itself." (macvlan có thể giao tiếp với nhau và với bên ngoài, nhưng không thể giao tiếp với thiết bị parent. Điều đó có nghĩa là bạn không thể dùng macvlan nếu có lúc cần instance giao tiếp với chính host.)

Routed. Đây thường là chế độ hoạt động trên VPS có thêm address. Tài liệu Incus mô tả device này là device "creates a virtual device pair to connect the host to the instance and sets up static routes and proxy ARP/NDP entries to allow the instance to join the network of a designated parent interface" (tạo một cặp virtual device để kết nối host với instance, đồng thời thiết lập static route và entry proxy ARP/NDP để instance tham gia mạng của một parent interface được chỉ định). ARP là address resolution protocol. Container giữ một public address. Host trả lời ARP cho address đó, vì vậy provider vẫn chỉ thấy MAC address của host.

incus config device add web eth0 nic nictype=routed parent=enp1s0 ipv4.address=203.0.113.20

Lấy tên parent interface từ ip route show default. Các image hiện tại thường dùng tên như enp1s0 hoặc ens3, hiếm khi dùng eth0. Đặt tên device là eth0 sẽ ghi đè device mà profile default cung cấp. Vì vậy, container sẽ dùng routed interface thay vì bridge. Kiểm tra kết quả từ bên trong container bằng ip aip route.

Vì sao container kết nối được đến service trên host

Một container trên incusbr0 có network namespace riêng. Container không có firewall boundary với host. Host nằm trên bridge đó tại địa chỉ gateway, nên từ bên trong container, host là một neighbour có thể truy cập trực tiếp và mọi service trên host bind vào 0.0.0.0 đều phản hồi tại đó.

Tự kiểm tra trên host. Liệt kê các service đang listening.

sudo ss -tlnp

Sau đó, từ bên trong một container, kết nối đến gateway mà ip route báo cáo.

ip route show default
nc -zv 10.0.0.1 6379

Nếu database, metrics endpoint hoặc admin panel trên host bind vào 0.0.0.0, phép kiểm tra đó sẽ thành công. Network firewall của provider không nhìn thấy packet này vì packet không rời khỏi máy. Đây là nguyên nhân khiến nhiều câu hỏi kiểu “làm sao nó kết nối được đến đó” gây bất ngờ: container được NAT cô lập khỏi Internet, nhưng không có gì cô lập nó khỏi host.

Bind các service trên host vào 127.0.0.1 khi có thể. Sau đó filter bridge trên host. Trên máy dùng ufw, policy deny mặc định đã chặn traffic từ container đến host. Điều này làm hỏng DNS và DHCP của Incus. Cách khắc phục trong tài liệu Incus là sudo ufw allow in on incusbr0. Lệnh đó mở lại mọi port trên host cho mọi container. Thay vào đó, chỉ allow những gì container thực sự cần.

sudo ufw allow in on incusbr0 to any port 53 proto udp
sudo ufw allow in on incusbr0 to any port 53 proto tcp
sudo ufw allow in on incusbr0 to any port 67 proto udp
sudo ufw route allow in on incusbr0
sudo ufw route allow out on incusbr0

Hai rule ufw route cho phép traffic của instance đi qua host để đến Internet. Nếu thiếu chúng, routed policy của ufw sẽ drop các packet được forward, nên container nhận được địa chỉ nhưng không kết nối được đến đâu.

Snapshot và profile

Snapshot là bản sao instance tại một thời điểm cụ thể trong storage pool của instance.

incus snapshot create web pre-upgrade
incus info web
incus snapshot restore web pre-upgrade
incus snapshot delete web pre-upgrade

incus info web liệt kê các snapshot mà instance đang có. Lên lịch snapshot riêng cho từng instance.

incus config set web snapshots.schedule=@daily
incus config set web snapshots.expiry=4w

Snapshot nằm trong cùng pool, trên cùng disk và cùng server. Nó bảo vệ bạn trước một lần upgrade lỗi. Nó không bảo vệ bạn khi disk hỏng hoặc instance bị xóa. Bản backup là incus export, và file backup phải được đưa ra khỏi máy.

incus export web /root/web-backup.tar.gz
incus import /root/web-backup.tar.gz

Profile là một tập hợp có tên gồm các config key và device được áp dụng cho instance. Mọi instance đều nhận profile default, trừ khi bạn chỉ định khác. Profile này cung cấp root disk và network interface cho instance. Chỉnh sửa default sẽ thay đổi mọi instance đang dùng profile đó. Điều này hữu ích, nhưng cũng có thể khiến network bị tách khỏi 20 container cùng lúc.

incus profile create small
incus profile set small limits.memory=512MiB
incus profile set small limits.cpu=1
incus launch images:debian/13 api -p default -p small

Các profile được áp dụng theo thứ tự, nên giá trị của key trong profile cuối cùng được liệt kê sẽ được ưu tiên. Dùng incus config show api --expanded để xem cấu hình thực tế mà instance đã nhận.

Chạy Docker bên trong container Incus

Docker bên trong system container của Incus cần bật nesting, vì Docker tự tạo namespace và mount. Theo mặc định, container không được phép tự tạo các đối tượng này.

incus config set web security.nesting=true
incus restart web

Incus mô tả security.nesting là “Cho phép nesting bên trong instance”, và giá trị mặc định của tùy chọn này là false đối với container. Có thêm 2 điểm được nêu trực tiếp trong FAQ của Incus. Container không thể load kernel module, vì vậy phải load module mà Docker cần trên host và liệt kê module đó trong incus config set web linux.kernel_modules overlay,br_netfilter. Ngoài ra, việc tạo file /.dockerenv bên trong container khiến Docker bỏ qua một số kiểm tra bị fail trong môi trường lồng nhau.

Trên host Ubuntu 24.04, các giới hạn user namespace không đặc quyền của AppArmor có thể chặn pivot_root do runc thực hiện. Docker bên trong container sẽ in:

failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: error jailing process inside rootfs: pivot_root .: permission denied

dmesg trên host sẽ hiển thị một dòng có chứa apparmor="DENIED" operation="pivotroot" class="mount". Tùy chọn mà người dùng thường thử là kernel.apparmor_restrict_unprivileged_userns. Tắt tùy chọn này không phải là cách sửa đáng tin cậy: báo cáo lỗi upstream của Incus cho đúng denial này ghi nhận rằng đặt tùy chọn thành 0 cũng không giải quyết được vấn đề. Trước tiên, hãy đọc dmesg để xem denial, nhằm xác định AppArmor có thực sự là nguyên nhân hay không trước khi thay đổi một thiết lập bảo mật mặc định.

Nếu muốn chạy container trực tiếp trên VPS và bỏ qua một lớp, hãy xem chạy Docker trên VPS để tham khảo riêng cách thiết lập đó.

Các lỗi thường gặp và chuỗi bạn sẽ thấy

Các instance mất toàn bộ mạng sau khi bạn cài Docker trên host. Tài liệu Incus nêu rõ nguyên nhân: "Docker đặt policy FORWARD toàn cục thành drop, khiến Incus không thể forward traffic và làm các instance mất kết nối mạng." Các instance vẫn giữ địa chỉ nhưng không thể kết nối đến đâu. Đặt ip-forward-no-drop thành true trong /etc/docker/daemon.json, sau đó bật tính năng forward một cách persistent và cho phép bridge đi qua chain riêng của Docker.

echo "net.ipv4.conf.all.forwarding=1" | sudo tee /etc/sysctl.d/99-forwarding.conf
sudo systemctl restart systemd-sysctl
sudo iptables -I DOCKER-USER -i incusbr0 -j ACCEPT
sudo iptables -I DOCKER-USER -o incusbr0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

Các rule iptables không tự tồn tại sau khi reboot. Hãy persist chúng.

Container không khởi động được và báo lỗi cgroup. FAQ của Incus có ghi nhận lỗi này. Thông báo chứa Failed to mount "/sys/fs/cgroup" thường có nghĩa là VPN client trên host đã mount controller cgroup v1 net_cls đè lên cgroup v2, là cgroup mà Incus sử dụng. sudo umount /sys/fs/cgroup/net_cls sẽ xóa lỗi này.

Instance không nhận được địa chỉ IPv4. incus list cho thấy instance đang chạy nhưng cột địa chỉ bị trống. DHCP reply từ host đang bị drop, thường do host firewall không biết về bridge. Trên ufw, sudo ufw allow in on incusbr0 to any port 67 proto udp sẽ khôi phục kết nối. Dùng sudo tcpdump -ni incusbr0 port 67 để theo dõi các request đi vào.

Instance không khởi động được trên nested VPS. Trước tiên hãy đọc incus info <name> --show-log, sau đó đọc sudo journalctl -u incus -n 50. Nếu systemd-detect-virt trả về lxc hoặc openvz, phần còn thiếu nằm ở phía provider và không có setting nào bên trong VPS của bạn có thể thay đổi được.

Snapshot chạy chậm và disk liên tục đầy. Bạn đang dùng pool dir. incus storage list in ra driver của từng pool. Chuyển sang pool copy-on-write cần tạo pool mới, sao chép các instance vào đó bằng incus copy web web-new -s fast, rồi xóa các instance gốc sau khi đã kiểm tra các bản sao khởi động được.

FAQ

Container Incus có giống container Docker không?

Không. Docker đóng gói một process hoặc application duy nhất. System container của Incus mô phỏng một operating system đầy đủ, với init, user, service và package manager riêng. Bạn giữ lại container Incus và cập nhật bản vá cho nó như một server. Với Docker, bạn xóa container rồi dựng lại từ image. Bạn có thể chạy Docker bên trong container Incus bằng cách đặt security.nesting=true trên container. Chiều ngược lại không hoạt động.

Tôi có thể chạy Incus trên VPS không?

Có, nếu đó là KVM VPS. systemd-detect-virt hiển thị kvm hoặc qemu thì bạn có kernel riêng và Incus hoạt động như trên phần cứng thật. Nếu hiển thị lxc, lxc-libvirt hoặc openvz thì bản thân VPS của bạn là một container. Khi đó, các container Incus bên trong là nested và chỉ hoạt động nếu nhà cung cấp đã bật nesting cho container của bạn. Hãy kiểm tra cả uname -r, vì tính đến August 2026, nhánh stable hiện tại của Incus ghi rõ kernel tối thiểu là 6.12, còn nhánh 6.0 LTS yêu cầu 5.4.

Nên chọn storage backend nào cho Incus trên VPS?

Dùng btrfs trên một loop file, trừ khi bạn có sẵn một block device để cấp cho nó. Driver dir được ghi nhận là chậm hơn nhiều so với các driver khác vì nó copy file thay vì dùng copy-on-write. Do đó, mỗi snapshot lại ghi toàn bộ container một lần nữa. incus admin init --minimal chọn dir, nên bạn chỉ mất khoảng hai phút để trả lời các câu hỏi tương tác. Tạo pool bằng incus storage create fast btrfs size=30GiB.

Tại sao container Incus của tôi có thể truy cập service đang chạy trên host?

Vì bridge incusbr0 mặc định đặt host trên cùng subnet với container, tại địa chỉ gateway, và không có bộ lọc nào giữa hai bên. Mọi service trên host bind vào 0.0.0.0 đều trả lời tại đó. Firewall của nhà cung cấp không thấy các packet này vì chúng không rời khỏi máy. Hãy bind service trên host vào 127.0.0.1. Trên host dùng ufw, chỉ cho phép DNS và DHCP đi vào incusbr0 thay vì cho phép toàn bộ bằng sudo ufw allow in on incusbr0.

Làm thế nào để backup container Incus?

incus export web /root/web-backup.tar.gz ghi instance và các snapshot của nó vào một file. incus import khôi phục file đó trên cùng server hoặc một server khác. Snapshot tạo bằng incus snapshot create không phải là backup. Chúng nằm trong cùng storage pool trên cùng disk, nên có thể phục hồi sau một lần nâng cấp lỗi nhưng không giúp ích khi server hỏng hoàn toàn. Hãy lập lịch bằng incus config set web snapshots.schedule=@daily và copy các bản export ra ngoài máy.

#incus#lxd#system-containers#virtualization#vps