Hướng Dẫn Triển Khai WordPress Bằng Docker Compose Trên VPS
WordPress là hệ quản trị nội dung (CMS) phổ biến, được sử dụng để xây dựng website doanh nghiệp, blog cá nhân, website tin tức và cửa hàng trực tuyến. Thông thường, để cài đặt WordPress trên VPS, người dùng cần thiết lập web server, PHP, cơ sở dữ liệu MySQL hoặc MariaDB và cấu hình các thành phần liên quan.
Tuy nhiên, với Docker Compose, quá trình triển khai WordPress trở nên đơn giản và thuận tiện hơn. Docker Compose cho phép quản lý nhiều container thông qua một file cấu hình duy nhất, giúp việc cài đặt, khởi chạy, cập nhật và sao lưu website dễ dàng hơn.
Trong bài viết này, chúng tôi sẽ hướng dẫn Quý khách triển khai WordPress bằng Docker Compose trên VPS Ubuntu, sử dụng Docker để chạy WordPress và MySQL để lưu trữ dữ liệu website.
Sau khi hoàn thành, các bạn có thể truy cập website WordPress thông qua trình duyệt và bắt đầu thiết lập nội dung.
1. Chuẩn bị trước khi triển khai
- VPS: Máy chủ sử dụng Ubuntu 22.04 hoặc Ubuntu 24.04. Có quyền truy cập SSH và sử dụng tài khoản root hoặc người dùng có quyền sudo.
- Cài đặt Docker và Docker Compose.
- Tên miền đã trỏ bạn ghi về VPS
2. Cài đặt Docker và Docker Compose trên VPS Ubuntu
Các bạn có thể tham khảo tài liệu hướng dẫn dưới đây
Hướng Dẫn Cài Đặt Docker và Docker Compose Trên Ubuntu 24.04
3. Tạo thư mục dự án
Tiếp theo, tạo riêng để lưu trữ các file cấu hình của WordPress:
vNode Tutorial
mkdir -p ~/wordpress

Trong thư mục này, chúng ra sẽ tạo 2 file quan trọng:
- .env: Lưu các biến môi trường, bao gồm thông tin đăng nhập cơ sở dữ liệu.
- docker-compose.yml: Khai báo các dịch vụ WordPress và MySQL, cùng các thiết lập mạng và lưu trữ.
- nginx-conf/nginx.conf: Chứa cấu hình chính của Nginx, đảm nhiệm việc tiếp nhận các yêu cầu HTTP/HTTPS và chuyển tiếp đến dịch vụ WordPress theo cấu hình đã thiết lập.
Việc tập trung cấu hình trong một thư mục giúp bạn dễ dàng quản lý, sao lưu và triển khai dự án khi cần.
4. Khai báo biến môi trong file .env
Bước 1: Tạo file .env
Chạy lệnh:
vNode Tutorial
nano .env
Thêm nội dung sau:
vNode Tutorial
MYSQL_DATABASE=wordpress MYSQL_USER=wpuser MYSQL_PASSWORD=THAY_BANG_MAT_KHAU_MẠNH MYSQL_ROOT_PASSWORD=THAY_BANG_MAT_KHAU_ROOT_MẠNH

Bạn cần thay hai giá trị mật khẩu bằng mật khẩu mạnh, riêng biệt và không sử dụng lại mật khẩu của tài khoản quản trị VPS.
Lưu file bằng tổ hợp phím Ctrl + O, nhấn Enter, sau đó Ctrl + X để thoát.
Giải thích các biến môi trường:
- MYSQL_DATABASE: Tên cơ sở dữ liệu được tạo cho WordPress.
- MYSQL_USER: Tài khoản MYSQL mà WordPress sử dụng để kết nối đến cơ sở dữ liệu.
- MYSQL_PASSWORD: Mật khẩu của tài khoản MYSQL dành cho WorPress.
- MYSQL_ROOT_PASSWORD: Mật khẩu của tài khoản quản trị MySQL.
Biến môi trường cho phép bạn tách thông tin cấu hình khỏi file triển khai chính. Docker Compose có thể đọc các giá trị trong file .env và sử dụng chúng khi tạo container.
Bạn không nên đưa file .env lên GitHub công khai vì file này chứa thông tin xác thực nhảy cảm
Lưu ý quan trọng: Các biến khởi tạo của image MySQL chỉ được sử dụng để thiết lập cơ sở dữ liệu khi thư mục dữ liệu chưa được khỏi tạo. Nếu thay đổi mật khẩu trong .env sau khi MySQL có dữ liệu, việc khởi động lại container không tự động thay đổi mật khẩu đã lưu trong cơ sở dữ liệu.
5. Tạo file cấu hình Nginx
Chạy lệnh tạo file:
vNode Tutorial
mkdir -p nginx-conf
Chạy lệnh:
vNode Tutorial
nano nginx-conf/default.conf
Thêm nội dung sau:
vNode Tutorial
server {
listen 80;
listen [::]:80;
server_name your_domain www.your_domain;
index index.php index.html index.htm;
location /.well-known/acme-challenge/ {
root /var/www/html;
allow all;
}
location / {
proxy_pass http://wordpress:80;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location ~ /\.ht {
deny all;
}
location = /favicon.ico {
log_not_found off;
access_log off;
}
location = /robots.txt {
log_not_found off;
access_log off;
allow all;
}
}Giải thích cấu hình Nginx:
- server: Khai báo cấu hình website trên Nginx.
- listen 80 và listen [::]:80: Cho phép Nginx tiếp nhận kết nối HTTP qua cổng 80 trên IPv4 và IPv6.
- server_name your_domain www.your_domain: Khai báo tên miền website. Thay your_domain bằng tên miền thực tế, ví dụ tai-lieu.io.vn.
- index index.php index.html index.htm: Khai báo danh sách file trang mặc định.
- location /.well-known/acme-challenge/: Cho phép truy cập đường dẫn xác thực tên miền khi cấp chứng chỉ SSL. Thư mục chứa file xác thực cần được cấu hình đúng.
- location /: Chuyển tiếp các yêu cầu truy cập website đến container WordPress thông qua proxy_pass http://wordpress:80.
- proxy_http_version 1.1: Sử dụng HTTP/1.1 khi Nginx giao tiếp với WordPress.
- proxy_set_header Host $host: Chuyển tên miền người dùng truy cập đến WordPress.
- proxy_set_header X-Real-IP $remote_addr: Chuyển địa chỉ IP của kết nối đến Nginx.
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for: Chuyển tiếp thông tin địa chỉ IP qua các máy chủ trung gian.
- proxy_set_header X-Forwarded-Proto $scheme: Chuyển giao thức truy cập (http hoặc https) đến WordPress.
- location ~ /\.ht: Chặn truy cập các file như .htaccess.
- location = /favicon.ico: Tắt ghi log truy cập và log lỗi không tìm thấy favicon.
- location = /robots.txt: Cho phép truy cập robots.txt và tắt ghi log cho đường dẫn này.
6. Tạo file cấu hình Docker Compose
Bước 1: Tạo file docker-compose.yml
Trong thư mục ~/wordpress, chạy lệnh:
vNode Tutorial
nano docker-compose.yml
Bước 2: Khai báo dịch vụ MySQL
Thêm đoạn code sau vào file docker-compose.yml để khai báo dịch vụ db, sử dụng MySQL làm hệ cơ sở dữ liệu cho WordPress.
vNode Tutorial
services:
db:
image: mysql:8.4
restart: unless-stopped
environment:
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
volumes:
- db_data:/var/lib/mysql
networks:
- wordpress_network
healthcheck:
test:
- CMD-SHELL
- 'mysqladmin ping -h 127.0.0.1 -uroot -p"$${MYSQL_ROOT_PASSWORD}" --silent'
interval: 10s
timeout: 5s
retries: 10
start_period: 30sCấu hình của service db bao gồm:
- image: Chỉ định image MySQL phiên bản 8.4 được sử dụng để tạo container. Việc chỉ định phiên bản cụ thể giúp kiểm soát môi trường triển khai thay vì sử dụng thẻ latest có thể thay đổi theo thời gian.
- restart: Thiết lập chính sách tự khởi động lại container. Giá trị unless-stopped cho phép Docker tự khởi động lại dịch vụ, trừ khi người quản trị chủ động dừng container.
- environment: Khai báo các biến môi trường phục vụ quá trình khởi tạo MySQL. Những giá trị ${MYSQL_DATABASE}, ${MYSQL_USER}, ${MYSQL_PASSWORD} và ${MYSQL_ROOT_PASSWORD} được lấy từ file .env.
- volumes: Gắn volume db_data vào thư mục /var/lib/mysql bên trong container. Đây là nơi MySQL lưu trữ dữ liệu, giúp dữ liệu vẫn tồn tại khi container được tạo lại, miễn là volume không bị xóa.
- networks: Kết nối container MySQL vào mạng nội bộ wordpress_network, cho phép WordPress giao tiếp với cơ sở dữ liệu thông qua tên dịch vụ.
- healthcheck: Kiểm tra MySQL có phản hồi hay chưa. Các tùy chọn interval, timeout, retries và start_period lần lượt quy định chu kỳ kiểm tra, thời gian chờ, số lần thử lại và khoảng thời gian khởi động ban đầu.
Trong cấu hình này, MySQL không cần công khai cổng 3306 ra Internet vì WordPress có thể kết nối trực tiếp thông qua mạng nội bộ Docker.
Bước 2: Khai báo dịch vụ WordPress
Tiếp theo, Thêm cấu hình dịch vụ WordPress để chạy ứng dụng WordPress cùng Apache và PHP.
vNode Tutorial
wordpress:
image: wordpress:php8.3-apache
restart: unless-stopped
depends_on:
db:
condition: service_healthy
ports:
- "127.0.0.1:8080:80"
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_NAME: ${MYSQL_DATABASE}
WORDPRESS_DB_USER: ${MYSQL_USER}
WORDPRESS_DB_PASSWORD: ${MYSQL_PASSWORD}
WORDPRESS_CONFIG_EXTRA: |
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strpos($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') !== false) {
$_SERVER['HTTPS'] = 'on';
}
volumes:
- wordpress_data:/var/www/html
networks:
- wordpress_networkCấu hình của serveice wordpress bao gồm:
- image: Sử dụng image wordpress:php8.3-apache, cung cấp WordPress cùng PHP 8.3 và Apache. Nhờ đó, bạn không cần cài đặt riêng PHP và Apache trực tiếp trên VPS cho ứng dụng này.
- restart: Cho phép container tự khởi động lại khi cần, trừ trường hợp đã được người quản trị chủ động dừng.
- depends_on: Khai báo WordPress phụ thuộc vào dịch vụ db. Điều kiện service_healthy yêu cầu MySQL đạt trạng thái khỏe mạnh trước khi WordPress được khởi động.
- ports: Ánh xạ cổng 8080 trên địa chỉ 127.0.0.1 của VPS vào cổng 80 bên trong container. Nginx trên VPS sẽ sử dụng địa chỉ 127.0.0.1:8080 để chuyển tiếp yêu cầu đến WordPress.
- environment: Khai báo thông tin kết nối cơ sở dữ liệu cho WordPress. Biến WORDPRESS_DB_HOST sử dụng tên dịch vụ db và cổng MySQL 3306; các biến còn lại lấy thông tin tương ứng từ file .env.
- WORDPRESS_CONFIG_EXTRA: Bổ sung cấu hình PHP để WordPress nhận biết giao thức HTTPS ban đầu khi Nginx chuyển tiếp yêu cầu qua HTTP nội bộ. Cấu hình này hỗ trợ hạn chế lỗi nhận diện giao thức và chuyển hướng khi triển khai SSL.
- volumes: Gắn volume wordpress_data vào /var/www/html, nơi chứa mã nguồn WordPress, plugin, giao diện và các file tải lên.
- networks: Kết nối WordPress vào mạng wordpress_network để giao tiếp với MySQL.
Khác với ví dụ sử dụng image WordPress FPM, cấu hình trên sử dụng image wordpress:php8.3-apache, do đó Apache đã có sẵn trong container. Bạn không cần tạo thêm container PHP-FPM riêng.
Bước 3: Khai báo dịch vụ Nginx
vNode Tutorial
nginx:
image: nginx:alpine
restart: unless-stopped
depends_on:
- wordpress
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx-conf/default.conf:/etc/nginx/conf.d/default.conf:ro
- ./certbot/www:/var/www/certbot:ro
- ./certbot/conf:/etc/letsencrypt:ro
networks:
- wordpress_networkGiải thích:
- image: nginx:alpine: Sử dụng image Nginx Alpine có dung lượng gọn nhẹ.
- depends_on: Khai báo Nginx phụ thuộc vào dịch vụ WordPress.
- ports: Mở cổng 80 cho HTTP và cổng 443 cho HTTPS.
- ./nginx-conf/default.conf: Mount file cấu hình Nginx từ VPS vào container.
- ./certbot/www: Chia sẻ thư mục chứa file xác thực tên miền để cấp SSL.
- ./certbot/conf: Mount thư mục chứa chứng chỉ SSL và khóa riêng tư.
- :ro: Chỉ cho phép đọc các file được mount vào Nginx.
- networks: Cho phép Nginx giao tiếp với WordPress thông qua tên service wordpress.
Bước 4: Khai báo cấp chứng chỉ SSL/TLS cho website WordPress
vNode Tutorial
certbot:
image: certbot/certbot
container_name: certbot
volumes:
- ./certbot/conf:/etc/letsencrypt
- ./certbot/www:/var/www/certbotGiải thích:
- image: certbot/certbot: Sử dụng image Certbot để cấp và quản lý chứng chỉ SSL miễn phí từ Let’s Encrypt.
- container_name: certbot: Đặt tên container là certbot để dễ nhận diện và quản lý.
- ./certbot/conf:/etc/letsencrypt: Lưu chứng chỉ SSL và các tệp cấu hình trên VPS để Nginx sử dụng HTTPS.
- ./certbot/www:/var/www/certbot: Lưu các tệp xác minh tên miền để Let’s Encrypt kiểm tra quyền quản lý tên miền khi cấp chứng chỉ.
Lưu ý: Cấu hình này chỉ khai báo service Certbot và nơi lưu dữ liệu, chưa tự động cấp chứng chỉ hoặc gia hạn SSL.
Bước 5: Khai báo volume và mạng Docker
Sau khi khai báo 2 dịch vụ, bổ sung đoạn code ở cưới file
vNode Tutorial
volumes: db_data: wordpress_data: networks: wordpress_network:
Giải thích cấu hình:
volumes: Khai báo hai volume có tên là db_data và wordpress_data. Docker Compose sẽ tạo các volume nếu chúng chưa tồn tại.
- db_data: Lưu trữ dữ liệu MySQL.
- wordpress_data: Lưu trữ file WordPress.
networks: Khai báo mạng wordpress_network để các container trong dự án có thể giao tiếp với nhau.
Cuối cùng, kiểm tra lại toàn bộ file docker-compose.yml trước khi triển khai.
vNode Tutorial
services:
db:
image: mysql:8.4
restart: unless-stopped
environment:
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
volumes:
- db_data:/var/lib/mysql
networks:
- wordpress_network
healthcheck:
test:
- CMD-SHELL
- 'mysqladmin ping -h 127.0.0.1 -uroot -p"$${MYSQL_ROOT_PASSWORD}" --silent'
interval: 10s
timeout: 5s
retries: 10
start_period: 30s
wordpress:
image: wordpress:php8.3-apache
restart: unless-stopped
depends_on:
db:
condition: service_healthy
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_NAME: ${MYSQL_DATABASE}
WORDPRESS_DB_USER: ${MYSQL_USER}
WORDPRESS_DB_PASSWORD: ${MYSQL_PASSWORD}
WORDPRESS_CONFIG_EXTRA: |
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strpos($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') !== false) {
$_SERVER['HTTPS'] = 'on';
}
volumes:
- wordpress_data:/var/www/html
networks:
- wordpress_network
nginx:
image: nginx:alpine
restart: unless-stopped
depends_on:
- wordpress
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx-conf/default.conf:/etc/nginx/conf.d/default.conf:ro
- ./certbot/www:/var/www/certbot:ro
- ./certbot/conf:/etc/letsencrypt:ro
networks:
- wordpress_network
certbot:
image: certbot/certbot
volumes:
- ./certbot/conf:/etc/letsencrypt
- ./certbot/www:/var/www/certbot
volumes:
db_data:
wordpress_data:
networks:
wordpress_network:
driver: bridgeBước 6: Khởi chạy và kiểm tra các container
Lệnh khởi tạo container:
vNode Tutorial
docker compose up -d

Lệnh kiểm tra trạng thái các container:
vNode Tutorial
docker compose ps

Bước 7: Kiểm tra DNS và truy cập HTTP
Đảm bảo tai-lieu.io.vn và www.tai-lieu.io.vn đã trỏ về VPS. Sau đó kiểm tra:
vNode Tutorial
curl -I http://tai-lieu.io.vn

Bước 8: Cấp chứng chỉ SSL bằng Certbot
Thay [email protected] bằng email thật của bạn:
vNode Tutorial
docker compose run --rm certbot certonly \ --webroot \ --webroot-path=/var/www/certbot \ --email [email protected] \ --agree-tos \ --no-eff-email \ -d tai-lieu.io.vn \ -d www.tai-lieu.io.vn
Lưu ý: Trong file Nginx default.conf, đường dẫn xác thực phải là /var/www/certbot, khớp với thư mục được mount trong Docker Compose. Nếu tên miền www chưa được cấu hình DNS, hãy bỏ -d www.tai-lieu.io.vn.

7. Chỉnh sửa lại cấu hình Nginx
Mở file cấu hình:
vNode Tutorial
nano /root/nginx-conf/default.conf
Thay toàn bộ nội dung hiện tại bằng cấu hình sau:
vNode Tutorial
server {
listen 80;
listen [::]:80;
server_name tai-lieu.io.vn www.tai-lieu.io.vn;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
allow all;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name tai-lieu.io.vn www.tai-lieu.io.vn;
ssl_certificate /etc/letsencrypt/live/tai-lieu.io.vn/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/tai-lieu.io.vn/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
location / {
proxy_pass http://wordpress:80;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location ~ /\.ht {
deny all;
}
location = /favicon.ico {
log_not_found off;
access_log off;
}
location = /robots.txt {
log_not_found off;
access_log off;
allow all;
}
}
Những phần đã thay đổi:
-
Khối server cổng 80: giữ đường dẫn xác thực Certbot, chuyển các yêu cầu còn lại sang HTTPS.
-
Thêm khối server cổng 443 ssl: khai báo đường dẫn chứng chỉ SSL và chuyển tiếp yêu cầu đến WordPress.
-
Các cấu hình favicon.io, robots.txt và chặn truy cập file .ht được giữ lại trong khối HTTPS.
Lưu file bằng Ctrl + O → Enter → Ctrl + X.
Kiểm tra cấu hình Nginx bên trong container Docker
vNode Tutorial
docker compose exec nginx nginx -t

Nếu thấy syntax is ok và test is successful, tải lại Nginx:
vNode Tutorial
docker compose exec nginx nginx -s reload
8. Hoàn tất cài đặt WordPress
Bước 1: Mở website
Truy cập vào https://tenmiencuaban.com. Nếu nó xuất hiện trang cài đặt WordPress, bạn có thể bắt đầu.
Chọn ngôn ngữ cần đặt bấm Continue

Bước 2: Nhập thông tin website
Đặt tiêu đề website, tên tài khoản quản trị, mật khẩu mạnh và email quản trị.

Bước 3: Đăng nhập vào trang quản trị
Sau khi cài đặt hoàn tất, truy cập https://tenmiencuaban.com/wp-admin để quản lý website.

9. Thiết lập gia hạn SSL tự động
Bước 1: Kiểm tra gia hạn thử nghiệm
vNode Tutorial
cd /root docker compose run --rm certbot renew --dry-run
Kết quả này xác nhận Certbot gia hạn thử nghiệm thành công cho cả tai-lieu.io.vn và www.tai-lieu.io.vn. Cấu hình xác thực để gia hạn chứng chỉ hiện tại đang hoạt động tốt.
Bước 2: Tạo script gia hạn SSL
Chạy lệnh:
vNode Tutorial
cat > /usr/local/bin/renew-wordpress-ssl.sh <<'EOF' #!/bin/bash exec >> /var/log/certbot-renew.log 2>&1 set -e cd /root /usr/bin/docker compose run --rm certbot renew --quiet /usr/bin/docker compose exec -T nginx nginx -s reload EOF chmod +x /usr/local/bin/renew-wordpress-ssl.sh
Script sẽ chạy Certbot để gia hạn khi cần, sau đó yêu cầu Nginx tải lại cấu hình và chứng chỉ
Bước 3: Thêm lịch Cron
Mở Cron của tài khoản root:
vNode Tutorial
crontab -e
Thêm dòng sau vào cuối file:
vNode Tutorial
17 3 * * * /usr/local/bin/renew-wordpress-ssl.sh >> /var/log/certbot-renew.log 2>&1

Lưu lại bằng Ctrl + O → Enter → Ctrl + X nếu sử dụng Nano. Lịch này chạy hằng ngày lúc 03:17 theo giờ máy chủ.
Bước 4: Xác nhận lịch đã được lưu
vNode Tutorial
crontab -l

Bạn cần thấy dòng Cron vừa thêm.
Bước 5: Kiểm tra script
Chạy thử:
vNode Tutorial
/usr/local/bin/renew-wordpress-ssl.sh echo $?
Nếu kết quả là 0, script đã chạy thành công. Sau đó kiểm tra nhật ký:
vNode Tutorial
tail -n 50 /var/log/certbot-renew.log

Kết Luận
Qua bài hướng dẫn, bạn đã nắm được các bước triển khai WordPress bằng Docker Compose trên VPS, từ việc cài đặt Docker, cấu hình các dịch vụ WordPress, MySQL và Nginx đến thiết lập tên miền và chứng chỉ SSL HTTPS. Đồng thời, việc cấu hình Certbot kết hợp Cron giúp tự động kiểm tra và gia hạn chứng chỉ SSL khi cần thiết.
Triển khai WordPress bằng Docker Compose giúp quản lý các dịch vụ một cách độc lập, thuận tiện cho việc vận hành, bảo trì và nâng cấp website. Hy vọng hướng dẫn này sẽ giúp bạn tự triển khai và quản lý website WordPress trên VPS hiệu quả, an toàn và dễ dàng hơn.
