Site icon ZingServer

Cài WordPress Docker Compose siêu tốc kết hợp Nginx Proxy Manager (2026)

Hướng dẫn cài WordPress Docker Compose kết hợp Nginx Proxy Manager tối ưu hạ tầng VPS Linux 2026.

Triển khai WordPress theo chuẩn công nghệ Containerization hiện đại: Tốc độ, độc lập và bảo mật tuyệt đối.

Đã bao giờ bạn toát mồ hôi hột lúc 2 giờ sáng chỉ vì lỡ tay gõ lệnh update phiên bản PHP trên server bare-metal, làm sập toàn bộ hàng chục website khách hàng đang chạy chung? Hay chán nản tột độ với cảnh phải lặn lội dọn dẹp hàng tá file rác, gỡ rối database conflict tung mù sau khi uninstall một cái script cài đặt cũ rích? Tệ hơn nữa là cảnh ổ cứng VPS báo đỏ chót No space left on device do file log phình to vô tội vạ khiến máy chủ ngừng hoạt động.

Thực tế, việc maintain các hạ tầng server bằng phương pháp cài cắm trực tiếp (LAMP/LEMP stack truyền thống) hay thông qua các tài liệu hướng dẫn thiết lập WordPress trên VPS bằng các phần mềm và tập lệnh đang trở thành một di sản cồng kềnh, là cơn ác mộng thực sự đối với mọi developer và web agency khi cần scale dự án. Thay vì tự làm khổ mình với mớ bòng bong dependency (sự phụ thuộc phần mềm), tại sao bạn không đóng gói mọi thứ lại một cách thông minh, độc lập và an toàn hơn?

Bài viết này sẽ hướng dẫn bạn chi tiết cách cài WordPress Docker Compose, tiêu chuẩn triển khai hạ tầng website mượt mà và an toàn nhất tính đến thời điểm hiện tại. Sự kết hợp giữa kiến trúc Container hiện đại và Nginx Proxy Manager (NPM) không chỉ giúp bạn tránh những bẫy thực chiến chí mạng mà còn biến hệ thống của bạn thành một cỗ máy chịu tải tuyệt vời. Bạn đã sẵn sàng để dọn dẹp lại mớ hỗn độn trên VPS của mình và bước sang một kỷ nguyên quản trị nhàn nhã hơn chưa?

Quên kịch bản cài tay đi: Kiến trúc Container giải quyết được gì?

Trước đây, khi setup một web server, bạn sẽ phải cài tuần tự hệ điều hành Linux, Web Server (Nginx hoặc Apache), Database (MySQL hoặc MariaDB), và PHP. Cách làm tất cả chung một giỏ này tồn tại một điểm yếu chí mạng: Mọi website trên server đều phải dùng chung một môi trường hệ thống. Nếu Website A cần PHP 7.4 để chạy một plugin cũ, nhưng Website B lại yêu cầu PHP 8.3 mới nhất, bạn sẽ lập tức rơi vào dependency hell (địa ngục xung đột phần mềm).

Khi dịch chuyển sang sử dụng Docker, bạn đang áp dụng triết lý Infrastructure as Code (Quản lý hạ tầng bằng mã code). Mỗi một website giờ đây là một cụm (stack) hoàn toàn độc lập, sở hữu Web Server, PHP và Database riêng biệt.

Và mảnh ghép hoàn hảo đi kèm với Docker chính là Nginx Proxy Manager (NPM). Thay vì phải ssh vào server ngồi gõ từng dòng lệnh cấu hình block Nginx chay cực nhọc để trỏ domain, proxy pass, và cấu hình certbot để lấy chứng chỉ Let’s Encrypt, NPM cung cấp cho bạn một giao diện Web UI trực quan. Bạn có thể route traffic cho hàng trăm domain, bật HTTP/2, kích hoạt cache tĩnh chỉ bằng vài cú click chuột.

Kiến trúc Container hóa giúp WordPress hoạt động độc lập, đồng thời cô lập hoàn toàn MariaDB và Redis khỏi các cuộc rà soát từ Internet.

Chuẩn bị VPS và cài đặt Docker Engine (2026)

Để chạy mượt mà hệ sinh thái container này và đảm bảo khả năng chịu tải tốt, bạn cần chuẩn bị một hạ tầng server đáp ứng đủ tiêu chuẩn hiện đại.

Cấu hình VPS tối thiểu cho Production

Nhiều người lầm tưởng Docker nặng, nhưng thực tế nó tiêu tốn cực ít overhead (tài nguyên lãng phí) so với máy ảo (VM). Tuy nhiên, vì chúng ta sẽ chạy MariaDB, PHP-FPM/Apache và Redis Cache trong bộ nhớ, bạn cần chú ý:

Lệnh cài đặt Docker & Docker Compose chuẩn xác nhất

Truy cập SSH vào VPS của bạn bằng quyền root (hoặc user có quyền sudo). Hãy dọn dẹp các bản cũ và cài đặt môi trường mới nhất bằng loạt lệnh thực chiến sau:

Cập nhật cache của package manager và nâng cấp hệ thống:

sudo apt update && sudo apt upgrade -y

Gỡ bỏ sạch sẽ các phiên bản Docker cũ, lỗi thời (nếu có):

sudo apt remove docker docker-engine docker.io containerd runc -y

Cài đặt Docker Engine chính thức qua script tự động của Docker:

curl -fsSL https://get.docker.com | sudo bash

Cài đặt plugin Docker Compose (phiên bản v2, không dùng bản v1 có dấu gạch ngang):

sudo apt install docker-compose-plugin -y

Thêm user hiện tại vào group docker để không cần gõ lệnh quyền quản trị mỗi lần chạy lệnh:

sudo usermod -aG docker $USER
newgrp docker

(Mẹo: Việc gán user vào group docker giúp bạn thao tác nhanh hơn rất nhiều, tránh lỗi permission denied khi quên gõ sudo).

Triển khai Nginx Proxy Manager (NPM)

NPM sẽ đóng vai trò là chiếc cổng kết nối trung tâm (Reverse Proxy) duy nhất mở ra Internet. Chúng ta sẽ tạo một thư mục gốc ở thư mục home chuyên để quản lý các dịch vụ.

mkdir -p ~/docker/npm
cd ~/docker/npm

Tại đây, bạn tạo file docker-compose.yml cho NPM.

Cảnh báo bẫy thực chiến: Rất nhiều developer mới làm quen với Docker thường bỏ qua việc quản lý Log. Mặc định, Docker lưu log dưới dạng JSON vô hạn. Khi website bị DDoS hoặc dính lỗi liên tục, file log này có thể phình to lên hàng chục GB, nuốt trọn ổ cứng VPS. Do đó, chúng ta bắt buộc phải cấu hình Log Rotation (xoay vòng log) ngay tại đây.

version: '3.8'
services:
  app:
    image: 'jc21/nginx-proxy-manager:latest'
    container_name: nginx-proxy-manager
    restart: unless-stopped
    ports:
      - '80:80'      # Lắng nghe traffic HTTP 
      - '443:443'    # Lắng nghe traffic HTTPS 
      - '81:81'      # Cổng truy cập Web UI Admin của NPM
    environment:
      TZ: "Asia/Ho_Chi_Minh"
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt
    networks:
      - proxy_network
    logging: # BẮT BUỘC: Cấu hình giới hạn dung lượng log
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

networks:
  proxy_network:
    name: proxy_network
    driver: bridge

Sau này, khi hệ thống phình to, bạn có thể tham khảo cách triển khai giải pháp quản lý log VPS bằng Grafana Loki để theo dõi log tập trung theo thời gian thực.

Khởi chạy NPM bằng lệnh:

docker compose up -d

Sau vài chục giây để container pull image và khởi động, bạn hãy mở trình duyệt và truy cập http://<IP_CỦA_VPS>:81.

Sử dụng tài khoản mặc định (admin@example.com / changeme) để đăng nhập và ngay lập tức đổi sang một mật khẩu đủ mạnh. Web UI của NPM đã sẵn sàng!

Đừng quên cấu hình firewall chặn port 81 từ public IP sau khi đã setup xong để tăng cường bảo mật. Nếu chưa quen thao tác, hãy xem ngay các hướng dẫn bảo mật VPS Linux với UFW và Fail2Ban.

Thực chiến cài WordPress Docker Compose (có MariaDB & Redis)

Bây giờ là lúc chứng minh sức mạnh thực sự của Container. Chúng tối sẽ setup một stack WordPress hoàn chỉnh, an toàn cho domain giả định là myagency.com.

mkdir -p ~/docker/sites/myagency
cd ~/docker/sites/myagency

Bẫy ký tự đặc biệt trong file .env

Tuyệt đối không để mật khẩu dạng plain-text thẳng trong file docker-compose.yml. Hãy tạo file .env (file ẩn) để quản lý cấu hình.

Kinh nghiệm xương máu: Trong cú pháp của Docker Compose, dấu $ được dùng để nội suy biến (variable interpolation). Nếu bạn đặt mật khẩu database là RootPassVeryStrong2026$$, Docker sẽ dịch nhầm cặp $$ thành một dấu $. Kết quả là container WordPress sẽ không thể kết nối được với Database do sai mật khẩu. Để an toàn tuyệt đối, hãy bọc mật khẩu trong dấu nháy đơn '...'.

# File: .env
DB_NAME=wp_agency_db
DB_USER=wp_agency_user
DB_PASS='SuperStrongPass2026!@#'
DB_ROOT_PASS='RootPassVeryStrong2026$$'
WP_TABLE_PREFIX=agc_

Xử lý triệt để giới hạn dung lượng tải lên (uploads.ini)

Image chính thức của WordPress giới hạn file upload mặc định chỉ vỏn vẹn 2MB. Nếu bạn cố upload một cái theme nặng hoặc video, hệ thống báo lỗi ngay. Thay vì vào trong container sửa thủ công, hãy mount một file cấu hình ra ngoài host:

nano uploads.ini

Nhập cấu hình chuẩn sau:

file_uploads = On
memory_limit = 512M
upload_max_filesize = 128M
post_max_size = 128M
max_execution_time = 300
max_input_time = 300

Viết file docker-compose.yml (fix lỗi Race Condition)

Lại một cái bẫy kinh điển khác: Ở lần chạy docker compose up -d đầu tiên, MariaDB cần từ 15-30 giây để khởi tạo cấu trúc bảng dữ liệu (InnoDB). Nếu container WordPress khởi động nhanh hơn, nó sẽ lao vào kết nối với DB chưa sẵn sàng và sinh ra lỗi Error establishing a database connection.

Để giải quyết, chúng ta sử dụng healthcheck trên MariaDB và khai báo condition: service_healthy tại WordPress để ép nó phải ngoan ngoãn chờ đợi.

# File: docker-compose.yml
version: '3.8'

services:
  wordpress:
    image: wordpress:6.7-php8.3-apache
    container_name: agency_wordpress
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy # Chờ DB thực sự sẵn sàng
      redis:
        condition: service_started
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_NAME: ${DB_NAME}
      WORDPRESS_DB_USER: ${DB_USER}
      WORDPRESS_DB_PASSWORD: ${DB_PASS}
      WORDPRESS_TABLE_PREFIX: ${WP_TABLE_PREFIX}
    volumes:
      - ./wp_data:/var/www/html
      - ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini:ro
    networks:
      - proxy_network  
      - internal_wp    
    logging:
      driver: "json-file"
      options: { max-size: "10m", max-file: "3" }

  db:
    image: mariadb:11.4
    container_name: agency_db
    restart: unless-stopped
    environment:
      MARIADB_DATABASE: ${DB_NAME}
      MARIADB_USER: ${DB_USER}
      MARIADB_PASSWORD: ${DB_PASS}
      MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASS}
    volumes:
      - ./db_data:/var/lib/mysql
    networks:
      - internal_wp
    healthcheck: # Khám sức khỏe cho Database
      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
      interval: 10s
      timeout: 5s
      retries: 5
    logging:
      driver: "json-file"
      options: { max-size: "10m", max-file: "3" }

  redis:
    image: redis:7-alpine
    container_name: agency_redis
    restart: unless-stopped
    command: redis-server --maxmemory 128mb --maxmemory-policy allkeys-lru
    networks:
      - internal_wp
    logging:
      driver: "json-file"
      options: { max-size: "10m", max-file: "3" }

networks:
  proxy_network:
    external: true     # Bắt buộc khớp với network của NPM
  internal_wp:
    driver: bridge
    internal: true     # Network cô lập, chặn đứng rà soát hệ thống từ bên ngoài

Mẹo nâng cao: Nếu website của bạn có lượng truy cập lớn, hãy kết hợp việc chia resource Docker với các kỹ thuật tối ưu database MySQL/MariaDB trên VPS Linux để xử lý hàng vạn query/giây mà không lo tràn RAM.

Xử lý lỗi Permission Denied (điểm nghẽn chí mạng)

Hãy khởi chạy cụm website:

docker compose up -d

Khi bạn dùng tính năng bind mount (./wp_data:/var/www/html) từ host Linux vào trong container, thư mục trên host thường được mặc định gán quyền cho user root. Tuy nhiên, tiến trình WordPress bên trong container lại đang chạy dưới quyền user www-data. Nếu bạn bỏ qua bước này, WordPress sẽ liên tục báo lỗi Permission Denied hoặc đòi thông tin FTP khi bạn cài plugin/theme.

Khắc phục triệt để bằng lệnh phân quyền lại thư mục ngay trên host (33 là UID mặc định của user www-data trên các base image Debian/Apache):

sudo chown -R 33:33 ./wp_data 
Cơ chế Bind Mount giúp mã nguồn và Database được lưu trữ an toàn ngay trên ổ cứng của VPS. Phân quyền (chown) đúng UID là chìa khóa để WordPress không bị lỗi Permission Denied.

Trỏ Domain và cấp phát SSL Let’s Encrypt qua NPM

Hãy chắc chắn bạn đã cấu hình bản ghi A của tên miền trỏ về đúng IP của VPS.

  1. Truy cập vào Web UI của NPM (Port 81).
  2. Chuyển sang tab Proxy Hosts -> Nhấn Add Proxy Host.
  3. Tab Details: Nhập tên miền (VD: myagency.com), Scheme chọn http.
  4. Forward Hostname / IP (bẫy 502 Bad Gateway): Bắt buộc nhập tên của container web, trong trường hợp này là agency_wordpress. Tuyệt đối không nhập localhost hay 127.0.0.1. Tại sao? Vì bản thân NPM đang nằm trong một container của riêng nó. Nếu bạn trỏ về localhost, NPM sẽ tự tìm web server bên trong chính container của nó và dĩ nhiên nó sẽ trả về lỗi 502.
  5. Forward Port: Nhập 80 (Do image WordPress bản Apache lắng nghe port 80). Bật Websockets SupportBlock Common Exploits.
  6. Tab SSL: Chọn Request a new SSL Certificate, bật Force SSL (bắt buộc HTTPS) và HTTP/2 Support (chuẩn giao thức mới giúp load tài nguyên cực nhanh). Điền Email và nhấn Save.

Đợi vài giây để NPM gọi API của Let’s Encrypt, chứng chỉ sẽ được cấp phát và website của bạn chính thức online với ổ khóa xanh an toàn.

Lưu ý chí mạng: Bản thân Nginx Proxy Manager là một container độc lập, nếu trỏ Forward Hostname về localhost sẽ lập tức gây ra lỗi 502 Bad Gateway.

Tối ưu hiệu suất & tự động hóa bảo trì

Hệ thống đã chạy, nhưng để nó trở thành một cỗ máy chịu tải chuyên nghiệp, bạn phải áp dụng hai kỹ thuật cuối cùng này.

Kích hoạt Redis Object Cache bằng WP-CLI (chuẩn bảo mật)

Khi có 100 người dùng truy cập cùng lúc, thay vì ép MariaDB phải thực thi hàng ngàn query liên tục gây nghẽn cổ chai, Redis sẽ cache lại dữ liệu Database trên RAM. Thời gian phản hồi (TTFB) sẽ giảm từ 1 giây xuống chỉ còn vài chục mili-giây.

Thay vì dùng giao diện mất thời gian, chúng ta sẽ gọi thẳng công cụ dòng lệnh WP-CLI. Theo Best Practice về bảo mật của Docker, bạn KHÔNG NÊN chạy lệnh dưới quyền root (cờ --allow-root) mà hãy ép Docker thực thi lệnh dưới quyền user www-data (UID 33):

Khai báo thông vị máy chủ Redis cho hệ thống:

docker exec -u 33 agency_wordpress wp config set WP_REDIS_HOST redis
docker exec -u 33 agency_wordpress wp config set WP_REDIS_PORT 6379

Tải, cài đặt và kích hoạt plugin Redis Object Cache:

docker exec -u 33 agency_wordpress wp plugin install redis-cache --activate

Kích hoạt tính năng Object Cache:

docker exec -u 33 agency_wordpress wp redis enable

Tự động hóa backup cực gọn nhẹ tại Host

Bởi vì toàn bộ mã nguồn của bạn đã được map thẳng ra ngoài thư mục của máy host (chính là ./wp_data), bạn không cần phải tạo các luồng container tạm thời lằng nhằng để nén dữ liệu. Tận dụng sức mạnh của lệnh tar trên Linux sẽ đem lại tốc độ tối đa.

Tạo một bash script tại ~/docker/scripts/backup_wp.sh:

#!/bin/bash
SITE="agency"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/backups/$SITE"
mkdir -p "$BACKUP_DIR"

# Lấy cấu hình password động từ file .env
source ~/docker/sites/myagency/.env

# Dump Database từ container ra ngoài và nén bằng gzip
docker exec ${SITE}_db mysqldump -u root -p"${DB_ROOT_PASS}" ${DB_NAME} | gzip > "$BACKUP_DIR/db_$DATE.sql.gz"

# Nén trực tiếp thư mục mã nguồn ./wp_data đang nằm trên máy host
tar -czf "$BACKUP_DIR/files_$DATE.tar.gz" ~/docker/sites/myagency/wp_data

# Dọn dẹp file backup cũ hơn 14 ngày để chống tràn ổ cứng
find "$BACKUP_DIR" -type f -mtime +14 -delete

Đừng quên cấp quyền thực thi cho file bằng lệnh chmod +x ~/docker/scripts/backup_wp.sh. Cuối cùng, gõ crontab -e và thêm dòng dưới đây để hệ thống tự backup vào lúc 3h sáng mỗi ngày:

0 3 * * * /bin/bash /root/docker/scripts/backup_wp.sh >> /var/log/wp_backup.log 2>&1

Câu hỏi thường gặp (FAQ)

1. Tôi có thể chạy bao nhiêu website WordPress trên một VPS bằng cách này?

Phụ thuộc vào RAM của VPS. Một VPS 2GB RAM hoạt động ổn định 1-2 website (có Redis). Nếu nâng lên 4GB RAM, bạn có thể vận hành mượt mà 3-5 website độc lập. Chỉ cần copy thư mục cấu hình và đổi port/domain.

2. Tại sao tôi cài xong nhưng truy cập domain lại báo lỗi 502 Bad Gateway?

Do cấu hình sai trong Nginx Proxy Manager. Ở ô Forward Hostname, bạn phải điền tên của container WordPress (VD: agency_wordpress). Tuyệt đối không điền localhost hay 127.0.0.1.

3. Tại sao WordPress của tôi không cho upload ảnh hoặc đòi tài khoản FTP khi cài Plugin?

Do lỗi phân quyền volume (Permission Denied). Thoát ra ngoài terminal của VPS và chạy ngay lệnh sudo chown -R 33:33 ./wp_data để cấp lại quyền ghi cho user www-data của WordPress.

4. Dùng Docker thì backup website có phức tạp không?

Dễ và nhanh hơn cài tay rất nhiều. Bạn chỉ cần chạy lệnh dump database ra thành file .sql và dùng lệnh tar để nén luôn thư mục wp_data nằm trên ổ cứng VPS là xong.

5. Tôi có cần mua chứng chỉ SSL bên ngoài để cài vào hệ thống này không?

Hoàn toàn KHÔNG. Nginx Proxy Manager đã tích hợp sẵn API của Let’s Encrypt. Hệ thống sẽ tự động cấp phát và tự động gia hạn SSL cho mọi tên miền của bạn hoàn toàn miễn phí.

6. MariaDB và Redis của tôi có nguy cơ bị hacker rà quét port không?

Không thể. Cả Database và Redis đều được cấu hình chạy trong mạng internal: true. Chúng bị cô lập hoàn toàn khỏi Internet, không mở bất kỳ port nào ra ngoài. Chỉ có WordPress mới nhìn thấy chúng.

Kết luận

Trong kỷ nguyên hạ tầng mạng dịch chuyển liên tục, việc rũ bỏ thói quen quản trị server thủ công để tiến tới tư duy Containerization là bước đi mang tính sống còn. Xuyên suốt bài viết, thông qua việc cài WordPress Docker Compose chuyên nghiệp, bạn không chỉ triển khai thành công một cụm website hiệu suất cao mà còn xử lý triệt để hàng loạt bẫy thực chiến – từ lỗi phân quyền permission, rác log phình to, sự cố Race Condition khó chịu của DB, cho đến lỗi 502 kinh điển khi trỏ sai host.

Sự linh hoạt của kiến trúc này là vũ khí tuyệt vời: Ngày mai nếu bạn chốt deal được thêm 5 khách hàng mới, bạn chỉ cần bỏ ra đúng 2 phút. Copy thư mục myagency, đổi các biến số trong file .env, sửa lại tên container và gõ docker compose up -d. Tất cả vận hành hoàn hảo, an toàn và kiểm soát 100% rủi ro đứt gãy.

Tài liệu tham khảo

Exit mobile version