Một buổi sáng deploy lên server mới, mình mới phát hiện ra hosting đã cấp cho VPS một địa chỉ IPv6 xịn — nhưng toàn bộ container Docker vẫn đang chạy thuần IPv4. Lãng phí. Sau khoảng 2 tiếng ngồi đọc docs và thử lỗi, mình mới hiểu tại sao Docker không tự động dùng IPv6 dù host đã có sẵn.
Nếu VPS của bạn đã có IPv6 nhưng container vẫn chỉ nói chuyện qua IPv4, bài này giải quyết đúng vấn đề đó — từ bước bật daemon đến setup dual-stack hoàn chỉnh trong Docker Compose.
Quick Start: Bật IPv6 cho Docker trong 5 phút
Nếu bạn chỉ cần làm cho nhanh, đây là 3 bước tối giản:
Bước 1: Chỉnh file daemon.json
Mở (hoặc tạo) file /etc/docker/daemon.json:
sudo nano /etc/docker/daemon.json
Dán vào file (hoặc merge nếu đã có nội dung sẵn):
{
"ipv6": true,
"fixed-cidr-v6": "fd00::/80"
}
Bước 2: Restart Docker
sudo systemctl restart docker
Bước 3: Kiểm tra
docker network inspect bridge | grep -A 5 "EnableIPv6"
Thấy "EnableIPv6": true là Docker đã nhận cấu hình. Container mới tạo trong default bridge network từ đây sẽ được cấp địa chỉ IPv6 tự động.
Hiểu rõ hơn: Docker xử lý IPv6 như thế nào?
Lần đầu dùng Docker Compose cho dự án thực tế, mình mắc khá nhiều lỗi ngớ ngẩn. Trong đó có cái kinh điển nhất: enable IPv6 xong rồi ngồi thắc mắc tại sao container vẫn không kết nối được. Lý do là Docker có nhiều loại network, và mỗi loại phải cấu hình riêng.
Các loại network trong Docker
- bridge (mặc định): Container trên cùng host giao tiếp qua NAT
- host: Container dùng network của host, IPv6 hoạt động nếu host có
- overlay: Dùng trong Docker Swarm, cần cấu hình riêng
- macvlan: Container có địa chỉ MAC/IP riêng trên mạng vật lý
Bật ipv6: true trong daemon.json chỉ ảnh hưởng đến default bridge network. Mọi custom network bạn tạo trong Docker Compose — điểm này nhiều người bỏ qua — đều phải khai báo IPv6 riêng.
Dải địa chỉ IPv6 nên dùng
Với môi trường nội bộ (không cần route ra ngoài), dùng dải ULA (Unique Local Address) — tương tự 192.168.x.x trong IPv4:
fd00::/8 ← Private, dùng nội bộ là ổn
Ví dụ phổ biến:
fd00::/80— cho bridge mặc địnhfd00:db8:1::/64,fd00:db8:2::/64— cho các custom network
/64 là chuẩn subnet IPv6 theo RFC. /80 vẫn chạy được, nhưng một số tool network và router xử lý sai prefix không phải /64. Dùng production thì gắn luôn /64 cho chắc ăn.
Dual-stack: Chạy cả IPv4 và IPv6 cùng lúc
Dual-stack đơn giản là container có cả hai: IPv4 để tương thích với hệ thống cũ, IPv6 để sẵn sàng cho tương lai. Không cần chọn một trong hai — và đây cũng là setup bạn nên dùng mặc định.
Tạo custom network dual-stack bằng CLI
docker network create \
--driver bridge \
--subnet 172.20.0.0/24 \
--gateway 172.20.0.1 \
--ipv6 \
--subnet fd00:db8::/64 \
--gateway fd00:db8::1 \
myapp-network
Kiểm tra kết quả:
docker network inspect myapp-network | grep -A 20 '"IPAM"'
Output sẽ có cả 2 block config IPv4 và IPv6:
"IPAM": {
"Driver": "default",
"Config": [
{
"Subnet": "172.20.0.0/24",
"Gateway": "172.20.0.1"
},
{
"Subnet": "fd00:db8::/64",
"Gateway": "fd00:db8::1"
}
]
}
Xem địa chỉ IPv6 của container
docker run -d --name web --network myapp-network nginx:alpine
# Cách 1: inspect
docker inspect web | grep -A 5 "GlobalIPv6"
# Cách 2: vào trong container xem
docker exec web ip -6 addr show
Cấu hình IPv6 trong Docker Compose
Thực tế thì mình hiếm khi dùng lệnh docker network create tay. Docker Compose quản lý network sẽ gọn hơn nhiều:
services:
web:
image: nginx:alpine
networks:
- frontend
api:
image: node:18-alpine
networks:
- frontend
- backend
db:
image: postgres:15
networks:
- backend
networks:
frontend:
driver: bridge
enable_ipv6: true
ipam:
driver: default
config:
- subnet: 172.20.0.0/24
gateway: 172.20.0.1
- subnet: fd00:db8:1::/64
gateway: fd00:db8:1::1
backend:
driver: bridge
enable_ipv6: true
ipam:
driver: default
config:
- subnet: 172.21.0.0/24
gateway: 172.21.0.1
- subnet: fd00:db8:2::/64
gateway: fd00:db8:2::1
Khởi động stack và kiểm tra ping giữa các service:
docker compose up -d
docker compose exec web ping6 api
Nâng cao: Expose container qua IPv6 ra ngoài
Muốn client bên ngoài kết nối vào container qua IPv6? Có 2 hướng tùy theo nhu cầu.
Cách 1: Port mapping (đơn giản nhất)
Trong docker-compose.yml:
services:
web:
image: nginx:alpine
ports:
- "80:80" # IPv4
- "[::]:80:80" # IPv6 explicit
Nếu host đã bật IPv6 forwarding và daemon.json đã có ipv6: true, thì 80:80 thường tự map cả IPv4 lẫn IPv6 rồi — không cần khai báo riêng dòng thứ hai.
Cách 2: NDP Proxy (cho VPS có IPv6 public thật)
Nếu VPS có dải IPv6 public từ hosting (ví dụ 2001:db8::/48), bạn có thể dùng NDP proxy để container nhận IPv6 public — không qua NAT.
Bật NDP proxy trên host:
sudo sysctl net.ipv6.conf.all.proxy_ndp=1
Thêm proxy entry cho container:
sudo ip -6 neigh add proxy 2001:db8::2 dev eth0
Cách này phức tạp hơn, nhưng có lợi rõ khi container cần IPv6 public thật sự. Không qua NAT, không cần port mapping — traffic đi thẳng vào container. Phù hợp với bare metal hoặc dedicated server có dải IPv6 public.
Tips thực tế
1. Kiểm tra IPv6 connectivity từ trong container
docker exec -it mycontainer ping6 google.com
docker exec -it mycontainer curl -6 https://ipv6.google.com
2. Tránh trùng subnet
Mỗi network phải có subnet riêng — cả IPv4 lẫn IPv6. Docker báo lỗi khi trùng, nhưng message đôi khi khó đọc. Xem nhanh tất cả subnet đang dùng:
docker network ls -q | xargs docker network inspect | grep '"Subnet"'
3. Persist IPv6 forwarding qua reboot
Thêm vào /etc/sysctl.conf để không mất sau khi reboot:
net.ipv6.conf.all.forwarding=1
Áp dụng ngay mà không cần reboot:
sudo sysctl -p
4. Nginx trong container cần listen IPv6 riêng
Nếu container chạy Nginx, thêm dòng này vào server block:
server {
listen 80;
listen [::]:80; # thêm dòng này cho IPv6
server_name example.com;
}
5. Debug traffic với tcpdump
sudo tcpdump -i docker0 ip6
Chạy lệnh này khi nghi ngờ traffic IPv6 không đi đúng hướng — sẽ thấy ngay có gói tin IPv6 chạy qua interface hay không.
Tóm lại
Ba tầng cần nhớ khi cấu hình IPv6 cho Docker: daemon, network, và container. Mỗi tầng độc lập — bật ở daemon không có nghĩa là custom network tự có IPv6. Phần lớn project chỉ cần ipv6: true trong daemon.json và enable_ipv6: true trong Compose file là xong.
Mình thường setup dual-stack từ đầu cho mọi project — thêm vài dòng config nhưng đỡ đau đầu về sau khi hosting thay đổi chính sách IPv4, hoặc khi cần debug network giữa các container.

