Docker Compose là công cụ giúp bạn dễ dàng triển khai và quản lý nhiều container cùng lúc, điển hình như WordPress, cơ sở dữ liệu và Nginx. Giải pháp này mang lại lợi ích lớn về tính linh hoạt, khả năng tái sử dụng cấu hình và tiết kiệm thời gian thiết lập môi trường. Trong bài viết này, mình sẽ hướng dẫn chi tiết cách cài đặt WordPress bằng Docker Compose để bạn nhanh chóng sở hữu một website hoàn chỉnh và ổn định.
Những điểm chính
- Giới thiệu tổng quan về WordPress: Hiểu rõ WordPress là gì, vai trò của Docker Compose trong việc triển khai và vì sao nên chọn Nginx làm web server.
- Chuẩn bị để cài đặt WordPress bằng Docker Compose: Biết được các công cụ, môi trường và file cấu hình cần thiết trước khi bắt đầu.
- Các bước cài đặt WordPress bằng Docker Compose: Nắm được toàn bộ quy trình từ thiết lập web server, khai báo biến môi trường, định nghĩa service, tích hợp SSL, cập nhật cấu hình cho đến hoàn tất cài đặt và gia hạn chứng chỉ.
- Biết thêm Vietnix – Nhà cung cấp dịch vụ VPS, hosting tốc độ cao.
- Câu hỏi thường gặp: Tổng hợp các thắc mắc phổ biến khi triển khai WordPress bằng Docker Compose và cách giải quyết.

Giới thiệu tổng quan về WordPress
WordPress là một hệ quản trị nội dung (CMS) mã nguồn mở miễn phí, viết bằng PHP và sử dụng cơ sở dữ liệu MySQL. Nhờ hệ thống template và cấu trúc plugin dễ mở rộng, hầu hết tác vụ quản lí của WordPress có thể thực hiện bằng giao diện web. Vì lí do trên, WordPress trở nên nổi tiếng trong lĩnh vực thiết kế các loại website như blog, trang sản phẩm hay các trang thương mại điện tử.

Để sử dụng WordPress, các bạn cần cấu hình LAMP stack (Linux, Apache, MySQL và PHP) hoặc LEMP stack (Linux, Nginx, MySQL và PHP). Do thực hiện thủ công khá tốn thời gian, Vietnix sẽ hướng dẫn các bạn sử dụng Docker và Docker Compose để hỗ trợ cài đặt. Quy trình bao gồm việc thiết lập các thư viện, file cấu hình và biến môi trường bằng Docker image. Những image này sau đó được thực thi độc lập trong các container. Cuối cùng, chúng ta sẽ sử dụng Compose để cấu hình cho các container có khả năng giao tiếp (cơ sở dữ liệu và WordPress).
Các thông tin trong bài viết này sẽ giúp bạn biết cách cài đặt WordPress bằng multi-container (nhiều container): cơ sở dữ liệu MySQL, web server Nginx cũng như bản thân WordPress. Bạn sẽ cần yêu cầu Let’s Encrypt cấp các chứng chỉ TLS/SSL để bảo mật cho tên miền website. Ngoài ra, mình sẽ hướng dẫn các bạn cấu hình cho chứng chỉ có thể tự động gia hạn, đảm bảo trạng thái bảo mật của website luôn được duy trì.
Sau khi đã nắm được cách cài đặt WordPress bằng Docker Compose, bạn sẽ thấy rõ sức mạnh của việc chủ động triển khai và quản lý hệ thống. Tuy nhiên, không phải lúc nào bạn cũng có đủ thời gian và nguồn lực để duy trì server riêng. Đây là lúc các giải pháp WordPress Hosting trở nên cần thiết, giúp bạn vận hành website ổn định, bảo mật và tối ưu hiệu suất mà không phải lo lắng về hạ tầng. Nếu bạn muốn mở rộng quy mô hoặc quản lý nhiều website cùng lúc, các gói web hosting chuyên nghiệp từ Vietnix sẽ là lựa chọn đáng cân nhắc, mang lại sự tiện lợi và hiệu quả lâu dài. Liên hệ ngay!

TĂNG TỐC TỐI ĐA Với Hosting WORDPRESS
Nâng tầm website WordPress của bạn: Nhanh, ổn định và an toàn hơn
Chuẩn bị để cài đặt WordPress bằng Docker Compose
Trước khi cài đặt, bạn cần đáp ứng các yêu cầu sau:
- Đăng nhập server Ubuntu 20.04 bằng tài khoản non-root với đặc quyền
sudo
và kích hoạt tường lửa. - Cài đặt Docker.
- Cài đặt Docker Compose.
- Đăng ký một tên miền. Trong bài hướng dẫn này, mình sẽ ví dụ tên miền là
your_domain
. - Cấu hình hai DNS record:
- Một A record với tên miền
your_domain
trỏ tới địa chỉ IP của server. - Một A record với tên miền
www.your_domain
trỏ tới địa chỉ IP của server.
- Một A record với tên miền
Sau khi chuẩn bị xong, bạn hãy đến với các bước cài đặt chi tiết dưới đây:
Bước 1: Thiết lập cấu hình Web Server
Để bắt đầu, các bạn cần cấu hình cho Nginx bằng cách thêm một số location block đặc trưng của WordPress vào file cấu hình. Một location block khác cũng được thêm để hỗ trợ tự động gia hạn chứng chỉ cho website. Block này có nhiệm vụ điều hướng các request xác minh của Let’s Encrypt đến Certbot. Đầu tiên, bạn tạo một thư mục dự án để cài đặt WordPress. Bạn có thể tùy ý đặt tên thư mục. Ở đây, thư mục sẽ được đặt tên là wordpress
.
$ mkdir wordpress
Bạn mở thư mục vừa tạo bằng lệnh:
$ cd wordpress
Tiếp theo, bạn tạo thư mục chứa file cấu hình:
$ mkdir nginx-conf
Sau khi tạo xong, bạn sử dụng nano
hoặc trình editor khác tùy thích để mở file:
$ nano nginx-conf/nginx.conf
Tiếp theo, bạn thêm một server block vào file để thiết lập server_name
cũng như thư mục mã nguồn root
. Trong đó, một số location block được bổ sung để điều hướng các request của Certbot yêu cầu chứng chỉ, tài nguyên tĩnh và mã nguồn PHP. Bạn dán đoạn code dưới đây vào file, đừng quên thay your_domain
bằng tên miền của đã chuẩn bị:
server {
listen 80;
listen [::]:80;
server_name your_domain www.your_domain;
index index.php index.html index.htm;
root /var/www/html;
location ~ /.well-known/acme-challenge {
allow all;
root /var/www/html;
}
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
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;
}
location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
expires max;
log_not_found off;
}
}
Các thông tin trong server block bao gồm:
Các chỉ thị (directive):
listen
: Mở cổng80
để plugin webroot của Certbot có thể xử lí các request yêu cầu chứng chỉ. Tạm thời, các bạn chưa bao gồm mở cổng443
. Ở những bước tiếp theo, sau khi tên miền đã được Certbot xác thực, các bạn sẽ bổ sung SSL và cập nhật lại cấu hình cho Nginx.server_name
: Chọn server name và server block cho các request. Đừng quên thayyour_domain
bằng tên miền của các bạn.index
: Liệt kê các trang chỉ mục của request. Các bạn có thể thay đổi thứ tự trả về của file tại đây (đặt fileindex.php
trướcindex.html
để Nginx ưu tiên gọiindex.php
khi request không yêu cầu file cụ thể).root
: Chọn thư mục chứa mã nguồn website. Ngoài ra, theo mặc định trong WordPress Dockerfile,/var/www/html
còn là một mount point (thư mục chứa các file được mount) khi build image WordPress.
Location block:
location ~ /.well-known/acme-challenge
: Cấu hình cách xử lí các request đến thư mục.well-known
. Certbot sẽ tạo một file tạm trong thư mục này nhằm đối chiếu DNS tên miền với server của bạn, từ đó sử dụng webroot plugin để cấp chứng chỉ cho tên miền.location /
: Chứa chỉ thịtry_files
giúp xác định các tài nguyên được yêu cầu bởi URI request. Trường hợp không tìm thấy, request và các đối số sẽ được điều hướng đến fileindex.php
của WordPress, thay vì trả về trạng thái mặc định 404 Not Found.location ~ \.php$
: Xử lí và ủy quyền các PHP request đếnwordpress
container. Do Nginx không có trình xử lí PHP, chúng ta sử dụng trìnhphp-fpm
được imagephp:fpm
tích hợp sẵn. Vì phụ thuộc imagephp:fpm
, các bạn cần cấu hình bổ sung một số tùy chọn của giao thức FastCGI. Các tùy chọn trên có nhiệm vụ ủy quyền những request PHP chowordpress
, đồng thời hỗ trợ phân tích cú pháp và trả về tài nguyên được các request URI yêu cầu.location ~/\.th
: Chứa chỉ thịdeny_all
, đảm bảo Nginx không phục vụ cho người dùng các file.htaccess
.location = /favicon.ico
,location = /robots.txt
: Không lưu lịch sử các request yêu cầu file/favicon.ico
và/robots.txt
.location ~* \.(css|gif|ico|jpeg|jpg|js|png)$
: Không lưu lịch sử các request tài nguyên tĩnh. Vì khá tốn kém để phục vụ, ta cần đảm bảo những tài nguyên này dễ lưu vào bộ nhớ đệm cache.
Các bạn đóng file và lưu lại các tùy chọn vừa thiết lập. Như vậy, mình đã hướng dẫn xong cách để cấu hình Nginx web server. Ở bước tiếp theo sẽ giúp bạn biết cách tạo các biến môi trường mà container WordPress và MySQL sử dụng trong quá trình thực thi.
Bước 2: Định nghĩa biến môi trường (Environment Variables)
Khi thực thi, các container WordPress và MySQL sử dụng biến môi trường nhằm đảm bảo khả năng truy cập ứng dụng và tính nhất quán dữ liệu. Những biến môi trường này bao gồm mật khẩu root của MySQL, người dùng và mật khẩu truy cập (thông tin nhạy cảm) cũng như tên và host (thông tin không nhạy cảm) của cơ sở dữ liệu.
Thay vì thiết lập toàn bộ trong file Docker Compose (file quy định hành động của container), mình sẽ lưu và giới hạn truy cập cho các thông tin nhạy cảm vào file .env
. Bằng cách này, bạn có thể ngăn thông tin bị sao chép sang các repository khác và tránh nguy cơ bị lộ dữ liệu. Các bạn đến thư mục dự án và mở file .env
bằng lệnh sau:
$ nano .env
Mình sẽ lưu vào .env
mật khẩu root của MySQL cũng như người dùng và mật khẩu mà WordPress sử dụng để truy cập cơ sở dữ liệu:
MYSQL_ROOT_PASSWORD=your_root_password
MYSQL_USER=your_wordpress_database_user
MYSQL_PASSWORD=your_wordpress_database_password
Đóng và lưu file để cập nhật các thay đổi. Vì lưu thông tin nhạy cảm, bạn cần thêm .env
vào các file .gitignore
và .dockerignore
. Bằng cách này, .env
sẽ không được Git và Docker sao chép đến các Git repository cũng như các Docker image. Nếu quản lý version control bằng Git, bạn mở thư mục dự án và gọi lệnh git init
để tạo một repository:
$ git init
Bạn tạo và mở file .gitignore
:
$ nano .gitignore
Thêm .env
vào file vừa tạo:
.env
Đóng và lưu file để cập nhật thay đổi. Để an toàn hơn, bạn nên thêm .env
vào .dockerignore
, tránh trường hợp .env
được sao chép vào các container khi thư mục dự án được sử dụng làm build context.
Sau đó, bạn mở .dockerignore
bằng lệnh:
$ nano .dockerignore
Thêm .env
vào file:
.env
Bạn có thể tiếp tục thêm vào những dòng tiếp theo một số file và thư mục tùy chọn trong quá trình cài đặt:
.env
.git
docker-compose.yml
.dockerignore
Đóng và lưu file để lưu lại các thay đổi. Sau khi cấu hình xong cho các thông tin quan trọng, bạn tiến hành định nghĩa các service trong file docker-compose.yml
.
Bước 3: Định nghĩa service với Docker Compose
docker-compose.yml
là file định nghĩa các service cho quá trình cài đặt. Mỗi service sẽ tương ứng và quy định cách hoạt động của một container.
Compose cho phép các service kết nối qua mạng chia sẻ (shared network) và những không gian lưu trữ liên tục (volume). Vì vậy, bạn có thể định nghĩa nhiều service nếu muốn chạy một ứng dụng multi-container (nhiều container). Trong bài viết này, để hỗ trợ cài đặt WordPress, mình sẽ tạo một số container bao gồm: Cơ sở dữ liệu MySQL, ứng dụng WordPress, web server Nginx và một container Certbot có nhiệm vụ xác thực tên miền.
Đầu tiên, bạn tạo và mở file docker-compose.yml
:
$ nano docker-compose.yml
Thêm đoạn code sau để khai báo phiên bản Compose và định nghĩa service db
cho cơ sở dữ liệu:
version: '3'
services:
db:
image: mysql:8.0
container_name: db
restart: unless-stopped
env_file: .env
environment:
- MYSQL_DATABASE=wordpress
volumes:
- dbdata:/var/lib/mysql
command: '--default-authentication-plugin=mysql_native_password'
networks:
- app-network
Cấu hình của service db
bao gồm:
image
: Quy định image được dùng để tạo container. Do imagemysql:latest
luôn thay đổi, để tránh xung đột, ở đây mình sẽ sử dụng cố định phiên bản imagemysql:8.0
.container_name
: Đặt tên cho container.restart
: Chọn cách container tái khởi động. Thông thường, giá trị mặc định củarestart
làno
. Tuy nhiên, ở đây sẽ cho phép container tự khởi động lại trừ khi service được dừng bởi người quản trị.env_file
: Cho phép bổ sung biến môi trường từ file.env
trong build context (thư mục hiện hành).environment
: Bổ sung biến môi trường không được định nghĩa trong.env
. Các bạn gánwordpress
cho biếnMYSQL_DATABASE
để đặt tên cơ sở dữ liệu. Bạn có thể trực tiếp thêm giá trị này vào filedocker-compose.yml
vì tên cơ sở dữ liệu không phải là thông tin nhạy cảm.volumes
: Mount (sao chép) volume có têndbdata
(named volume) vào thư mục/var/lib/mysql
trên container. Đây là thư mục dữ liệu chuẩn của MySQL ở hầu hết các phiên bản.command
: Ghi đè các lệnh CMD (CMD instruction) mặc định của image. Ở đây, bạn sẽ thêm một lệnh mới vào tập lệnh tiêu chuẩn trongmysql:8.0
để khởi động server MySQL trên container. Vì image WordPress và PHP không hỗ trợ các phương thức xác thực gần đây của MySQL, bạn cần bổ sung tùy chọn--default-authentication-plugin=mysql_native_password
để quy định cơ chế xác minh người dùng (mysql_native_password
) cho cơ sở dữ liệu.networks
: Thêm service vào mạngapp-network
được định nghĩa cuối file.
Tiếp theo, bạn tiến hành cấu hình service wordpress
:
...
wordpress:
depends_on:
- db
image: wordpress:5.1.1-fpm-alpine
container_name: wordpress
restart: unless-stopped
env_file: .env
environment:
- WORDPRESS_DB_HOST=db:3306
- WORDPRESS_DB_USER=$MYSQL_USER
- WORDPRESS_DB_PASSWORD=$MYSQL_PASSWORD
- WORDPRESS_DB_NAME=wordpress
volumes:
- wordpress:/var/www/html
networks:
- app-network
Trong service này, ngoài đặt tên và thiết lập quy luật tái khởi động như db
, bạn cần bổ sung một số tùy chọn đặc trưng dưới đây của wordpress
, bao gồm:
depends_on
: Đảm bảo các container khởi động theo đúng thứ tự phụ thuộc. Vì WordPress sử dụng cơ sở dữ liệu và thông tin người dùng, để ứng dụng hoạt động ổn định, bạn cần cấu hình cho servicewordpress
khởi động sau servicedb
.image
: Tạo container bằng image WordPress5.1.1-fpm-alpine
– một imagealpine
có nguồn gốc từ dự án Alpine Linux.5.1.1-fpm-alpine
có nhiệm vụ giảm kích thước image tổng thể, đồng thời cung cấp trìnhphp-fpm
cần thiết để hỗ trợ Nginx xử lí các request PHP (đề cập ở trên).env_file
: Tương tựdb
, bạn tiếp tục sử dụng file.env
để lấy người dùng và mật khẩu truy cập cơ sở dữ liệu.environment
: Gán người dùng và mật khẩu từ.env
cho 2 biếnWORDPRESS_DB_USER
vàWORDPRESS_DB_PASSWORD
của image WordPress.WORDPRESS_DB_HOST
sẽ truy cập server MySQL trên cổng mặc định3306
của servicedb
. Tên cơ sở dữ liệuWORDPRESS_DB_NAME
được gán bằng tên mà các bạn đặt trong thân service của MySQL trước đó (MYSQL_DATABASE
:wordpress
).volumes
: Mount volume có tênwordpress
vào thư mục/var/www/html
được tạo bởi image WordPress. Bằng cách này, mã nguồn ứng dụng của các bạn có thể được chia sẻ giữa các container.networks
: thêm containerwordpress
vào mạngapp-network
.
Tiếp theo, bạn sẽ tiến hành định nghĩa service Nginx webserver
:
...
webserver:
depends_on:
- wordpress
image: nginx:1.15.12-alpine
container_name: webserver
restart: unless-stopped
ports:
- "80:80"
volumes:
- wordpress:/var/www/html
- ./nginx-conf:/etc/nginx/conf.d
- certbot-etc:/etc/letsencrypt
networks:
- app-network
Bạn đặt tên và cấu hình cho service webserver
khởi động sau wordpress
, đồng thời tạo container bằng image Nginx 1.15.12-alpine
. Các tùy chọn bổ sung của webserver
bao gồm:
ports
: Mở cổng80
, tạo điều kiện để cấu hình trong filenginx.conf
ở bước 1 hoạt động.volumes
: Định nghĩa nhóm các volume có tên và các bind mount (sao chép liên kết):wordpress:/var/www/html
: mount mã nguồn WordPress vào thư mục/var/www/html
– thư mụcroot
trong server block Nginx.
./nginx-conf:/etc/nginx/conf.d
: Bind mount thư mục cấu hình Nginx từ host vào thư mục tương ứng trong container, đảm bảo tất cả thay đổi trên host đều được cập nhật ở container.certbot-etc:/etc/letsencrypt
: Mount các khóa và chứng nhận tên miền Let’s Encrypt liên quan vào thư mục thích hợp trongwebserver
.
Bạn cũng thêm container này vào mạng app-network
. Sau service webserver
, bạn tiến hành định nghĩa cho service certbot
, đừng quên thay đổi tên miền và địa chỉ email sử dụng.
certbot:
depends_on:
- webserver
image: certbot/certbot
container_name: certbot
volumes:
- certbot-etc:/etc/letsencrypt
- wordpress:/var/www/html
command: certonly --webroot --webroot-path=/var/www/html --email sammy@your_domain --agree-tos --no-eff-email --staging -d your_domain -d www.your_domain
Với service này, bạn sẽ nhờ Compose kéo image certbot/certbot
từ Docker Hub để tạo container. Bạn sử dụng những volume có tên để chia sẻ tài nguyên với container Nginx, bao gồm các khóa, chứng nhận tên miền trong thư mục certbot-etc
cũng như mã nguồn ứng dụng trong wordpress
container.
Bạn tiếp tục cấu hình depends_on
để đảm bảo certbot
khởi động sau service webserver
. Cuối cùng, bạn thêm một tùy chọn command
để bổ sung các subcommand (lệnh con) vào các lệnh mặc định của certbot
. Các lệnh con certonly
này có nhiệm vụ cấp một chứng chỉ với các tùy chọn:
--webroot
: Chọn webroot làm thư mục chứa các file xác thực của webroot plugin. Plugin sẽ gọi một HTTP request để chứng minh Certbot có thể lấy tài nguyên định sẵn khi server phản hồi lại tên miền được cung cấp (phương pháp xác thực HTTP-01).--webroot-path
: Cho biết đường dẫn đến thư mục webroot.--email
: Email tùy chọn cho việc đăng ký và phục hồi.--agree-tos
: Chấp thuận các điều khoản trong ACME’s Subcriber Agreement (Thỏa thuận Đăng ký Người dùng ACME).--no-eff-email
: Ngăn Certbot chia sẻ email cho EFF (Electronic Frontier Foundation). Tùy chọn này có thể bỏ qua.--staging
: Sử dụng môi trường staging của Let’s Encrypt để thử nghiệm yêu cầu chứng chỉ. Tùy chọn này giúp bạn kiểm tra lại thiết lập cấu hình, đồng thời tránh khả năng bị giới hạn request của tên miền.-d
: Liệt kê danh sách tên miền sẽ phản hồi các request. Đừng quên sử dụng đúng tên miền các bạn đã chuẩn bị.
Sau khi certbot
được cấu hình, bạn tiếp tục định nghĩa các volume và mạng.
...
volumes:
certbot-etc:
wordpress:
dbdata:
networks:
app-network:
driver: bridge
volumes
là khóa top-level dùng để định nghĩa các volume certbot-etc
, dbdata
và wordpress
. Khi được tạo, các volume sẽ được lưu trong thư mục /var/lib/docker/volumes/
– quản lý bởi Docker trên filesystem (hệ thống file) của host. Khi có nhu cầu sử dụng, nội dung của các volume sẽ được mount đến các container để chia sẻ dữ liệu và mã nguồn.
Định nghĩa bridge network (cầu nối mạng) app-network
cho phép các container trên cùng một Docker daemon host có thể giao tiếp. Nhờ mở toàn bộ cổng nội bộ nhưng không để lộ các cổng nào ra ngoài, bridge network có khả năng điều phối lưu lượng mạng và hỗ trợ giao tiếp giữa các container, giúp db
, wordpress
và webserver
có thể giao tiếp với nhau trên cùng một host. Để front-end truy cập ứng dụng, bạn chỉ cần mở cổng 80
.
Cuối cùng, bạn có thể xem lại toàn bộ file docker-compose.yml
vừa thiết lập như sau:
version: '3'
services:
db:
image: mysql:8.0
container_name: db
restart: unless-stopped
env_file: .env
environment:
- MYSQL_DATABASE=wordpress
volumes:
- dbdata:/var/lib/mysql
command: '--default-authentication-plugin=mysql_native_password'
networks:
- app-network
wordpress:
depends_on:
- db
image: wordpress:5.1.1-fpm-alpine
container_name: wordpress
restart: unless-stopped
env_file: .env
environment:
- WORDPRESS_DB_HOST=db:3306
- WORDPRESS_DB_USER=$MYSQL_USER
- WORDPRESS_DB_PASSWORD=$MYSQL_PASSWORD
- WORDPRESS_DB_NAME=wordpress
volumes:
- wordpress:/var/www/html
networks:
- app-network
webserver:
depends_on:
- wordpress
image: nginx:1.15.12-alpine
container_name: webserver
restart: unless-stopped
ports:
- "80:80"
volumes:
- wordpress:/var/www/html
- ./nginx-conf:/etc/nginx/conf.d
- certbot-etc:/etc/letsencrypt
networks:
- app-network
certbot:
depends_on:
- webserver
image: certbot/certbot
container_name: certbot
volumes:
- certbot-etc:/etc/letsencrypt
- wordpress:/var/www/html
command: certonly --webroot --webroot-path=/var/www/html --email sammy@your_domain --agree-tos --no-eff-email --staging -d your_domain -d www.your_domain
volumes:
certbot-etc:
wordpress:
dbdata:
networks:
app-network:
driver: bridge
Sau đó, bạn đóng file và lưu lại các cấu hình. Như vậy là đã hoàn tất việc định nghĩa các service. Giờ đây, các bạn đã sẵn sàng cho việc khởi động các container và gọi thử các request yêu cầu chứng chỉ cho tên miền.
Bước 4: Yêu cầu chứng chỉ và chứng nhận SSL
Bạn sử dụng lệnh docker-compose up
để khởi động các container theo thứ tự, đồng thời bổ sung cờ -d
để cho phép db
, wordpress
và webserver
chạy nền bằng lệnh:
$ docker-compose up -d
Các service được tạo sẽ hiển thị trong Output dưới đây:
Output
Creating db ... done
Creating wordpress ... done
Creating webserver ... done
Creating certbot ... done
Bạn kiểm tra trạng thái của service bằng lệnh docker-compose ps
:
$ docker-compose ps
Khi hoàn tất khởi động, các service db
, wordpress
và webserver
sẽ có trạng thái Up
, riêng certbot
sẽ trả về thông điệp Exit 0
:
Output
Name Command State Ports
-------------------------------------------------------------------------
certbot certbot certonly --webroot ... Exit 0
db docker-entrypoint.sh --def ... Up 3306/tcp, 33060/tcp
webserver nginx -g daemon off; Up 0.0.0.0:80->80/tcp
wordpress docker-entrypoint.sh php-fpm Up 9000/tcp
Trường hợp cột State
không hiển thị như trên, các bạn có thể kiểm tra lại service logs bằng lệnh docker-compose logs
:
$ docker-compose logs service_name
Nếu mọi việc suôn sẻ, bạn gọi lệnh docker-compose exec
để kiểm tra các chứng chỉ được mount đến container webserver
:
$ docker-compose exec webserver ls -la /etc/letsencrypt/live
Các chứng chỉ được cấp thành công sẽ được hiển thị trong Output:
Output
total 16
drwx------ 3 root root 4096 May 10 15:45 .
drwxr-xr-x 9 root root 4096 May 10 15:45 ..
-rw-r--r-- 1 root root 740 May 10 15:45 README
drwxr-xr-x 2 root root 4096 May 10 15:45 your_domain
Sau khi đảm bảo các request xác thực đã hoạt động, các bạn có thể cập nhật lại định nghĩa service certbot
và bỏ đi cờ --staging
. Bạn mở lại file docker-compose.yml
.
$ nano docker-compose.yml
Dưới tùy chọn command
của service, các bạn thay cờ --staging
bằng cờ --force-renewal
. Thiết lập này cho phép chúng ta yêu cầu chứng chỉ mới cho một tên miền đã xác thực. Các bạn có thể kiểm tra lại cấu hình certbot
sau khi thay đổi.
...
certbot:
depends_on:
- webserver
image: certbot/certbot
container_name: certbot
volumes:
- certbot-etc:/etc/letsencrypt
- certbot-var:/var/lib/letsencrypt
- wordpress:/var/www/html
command: certonly --webroot --webroot-path=/var/www/html --email sammy@your_domain --agree-tos --no-eff-email --force-renewal -d your_domain -d www.your_domain
...
Các bạn gọi lệnh docker-compose up
để khởi tạo lại container certbot
. Ngoài ra, vì webserver
đã hoạt động, chúng ta sẽ bổ sung cờ --no-deps
để ngăn Certbot khởi động service này:
$ docker-compose up --force-recreate --no-deps certbot
Output dưới đây cho biết các bạn đã yêu cầu chứng chỉ thành công:
Output
Recreating certbot ... done
Attaching to certbot
certbot | Saving debug log to /var/log/letsencrypt/letsencrypt.log
certbot | Plugins selected: Authenticator webroot, Installer None
certbot | Renewing an existing certificate
certbot | Performing the following challenges:
certbot | http-01 challenge for your_domain
certbot | http-01 challenge for www.your_domain
certbot | Using the webroot path /var/www/html for all unmatched domains.
certbot | Waiting for verification...
certbot | Cleaning up challenges
certbot | IMPORTANT NOTES:
certbot | - Congratulations! Your certificate and chain have been saved at:
certbot | /etc/letsencrypt/live/your_domain/fullchain.pem
certbot | Your key file has been saved at:
certbot | /etc/letsencrypt/live/your_domain/privkey.pem
certbot | Your cert will expire on 2019-08-08. To obtain a new or tweaked
certbot | version of this certificate in the future, simply run certbot
certbot | again. To non-interactively renew *all* of your certificates, run
certbot | "certbot renew"
certbot | - Your account credentials have been saved in your Certbot
certbot | configuration directory at /etc/letsencrypt. You should make a
certbot | secure backup of this folder now. This configuration directory will
certbot | also contain certificates and private keys obtained by Certbot so
certbot | making regular backups of this folder is ideal.
certbot | - If you like Certbot, please consider supporting our work by:
certbot |
certbot | Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
certbot | Donating to EFF: https://eff.org/donate-le
certbot |
certbot exited with code 0
Sau khi được cấp chứng chỉ, bạn sẽ tiến hành bổ sung SSL và cập nhật lại cấu hình cho Nginx.
Bước 5: Cập nhật cấu hình Service và Web Server
Để cấu hình SSL cho Nginx, các bạn cần bổ sung một số thiết lập, bao gồm: Điều hướng HTTP đến HTTPS, chỉ định thư mục chứa các khóa và chứng chỉ SSL, thêm một vài tham số và header (tiêu đề) cho request. Vì webserver
sẽ được khởi tạo lại trong chốc lát, bạn tạm thời dừng service bằng lệnh sau:
$ docker-compose stop webserver
Trước khi cập nhật cấu hình, bạn sử dụng curl
để lấy danh sách tham số bảo mật khuyên dùng cho Nginx:
$ curl -sSLo nginx-conf/options-ssl-nginx.conf https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf
Dòng lệnh trên sẽ lưu các tham số cung cấp bởi Certbot vào file options-ssl-nginx.conf
trong thư mục nginx-conf
. Tiếp theo, bạn xóa file cấu hình Nginx ban đầu.
$ rm nginx-conf/nginx.conf
Bạn tạo lại một phiên bản khác của file:
$ nano nginx-conf/nginx.conf
Các bạn thêm đoạn code sau để điều hướng HTTP đến HTTPS, đồng thời bổ sung các header bảo mật, các giao thức cũng như các chứng chỉ SSL. Đừng quên thay your_domain
bằng tên miền đã chuẩn bị:
server {
listen 80;
listen [::]:80;
server_name your_domain www.your_domain;
location ~ /.well-known/acme-challenge {
allow all;
root /var/www/html;
}
location / {
rewrite ^ https://$host$request_uri? permanent;
}
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name your_domain www.your_domain;
index index.php index.html index.htm;
root /var/www/html;
server_tokens off;
ssl_certificate /etc/letsencrypt/live/your_domain/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your_domain/privkey.pem;
include /etc/nginx/conf.d/options-ssl-nginx.conf;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'" always;
# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# enable strict transport security only if you understand the implications
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
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;
}
location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
expires max;
log_not_found off;
}
}
Server block HTTP sẽ quy định thư mục .well-known/acme-challenge
làm webroot cho các renewal request (yêu cầu gia hạn) từ Certbot, đồng thời hỗ trợ điều hướng HTTP đến thư mục root của HTTPS bằng cách bổ sung một chỉ thị rewrite. Server block HTTPS cho phép lắng nghe ssl
và http2
trên cổng 443
. Block cũng bao gồm đường dẫn đến các khóa, chứng chỉ SSL và file nginx-conf/options-ssl-nginx.conf
– nơi lưu các tham số bảo mật được khuyên dùng của Certbot.
Ngoài ra, block còn bổ sung một số header bảo mật như: X-Frame-Options
, X-Content-Type-Options
, Referrer Policy
, Content-Security-Policy
và X-XSS-Protection
. Những header này có thể giúp các bạn đạt A rating trên các trang thử nghiệm server như SSL Labs hoặc Security Headers. Mặc định, header HTTP Strict Transport Security
(HSTS) sẽ được comment. Bạn chỉ nên sử dụng header này một khi đã xem xét và nắm rõ được chức năng “preload”.
Block HTTPS cũng bao gồm các chỉ thị root
và index
. Những location block còn lại chúng ta đã được tìm hiểu trong bước 1 của bài. Bạn đóng và lưu file để cập nhật lại cấu hình. Trước khi tái khởi động container, bạn cần bổ sung mapping (ánh xạ) cho cổng 443
vào service webserver
. Các bạn mở file docker-compose.yml
:
$ nano docker-compose.yml
Trong service webserver
, bạn thêm mapping của cổng 443
:
...
webserver:
depends_on:
- wordpress
image: nginx:1.15.12-alpine
container_name: webserver
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- wordpress:/var/www/html
- ./nginx-conf:/etc/nginx/conf.d
- certbot-etc:/etc/letsencrypt
networks:
- app-network
Các bạn kiểm tra lại file docker-compose.yml
sau khi chỉnh sửa:
version: '3'
services:
db:
image: mysql:8.0
container_name: db
restart: unless-stopped
env_file: .env
environment:
- MYSQL_DATABASE=wordpress
volumes:
- dbdata:/var/lib/mysql
command: '--default-authentication-plugin=mysql_native_password'
networks:
- app-network
wordpress:
depends_on:
- db
image: wordpress:5.1.1-fpm-alpine
container_name: wordpress
restart: unless-stopped
env_file: .env
environment:
- WORDPRESS_DB_HOST=db:3306
- WORDPRESS_DB_USER=$MYSQL_USER
- WORDPRESS_DB_PASSWORD=$MYSQL_PASSWORD
- WORDPRESS_DB_NAME=wordpress
volumes:
- wordpress:/var/www/html
networks:
- app-network
webserver:
depends_on:
- wordpress
image: nginx:1.15.12-alpine
container_name: webserver
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- wordpress:/var/www/html
- ./nginx-conf:/etc/nginx/conf.d
- certbot-etc:/etc/letsencrypt
networks:
- app-network
certbot:
depends_on:
- webserver
image: certbot/certbot
container_name: certbot
volumes:
- certbot-etc:/etc/letsencrypt
- wordpress:/var/www/html
command: certonly --webroot --webroot-path=/var/www/html --email sammy@your_domain --agree-tos --no-eff-email --force-renewal -d your_domain -d www.your_domain
volumes:
certbot-etc:
wordpress:
dbdata:
networks:
app-network:
driver: bridge
Đóng và lưu file để cập nhật cấu hình. Bạn tiến hành khởi tạo lại service webserver
:
$ docker-compose up -d --force-recreate --no-deps webserver
Một lần nữa, bạn gọi lệnh docker-compose ps
để kiểm tra các service:
$ docker-compose ps
Nếu mọi việc suôn sẻ, bạn sẽ nhận được output dưới đây.
Output
Name Command State Ports
----------------------------------------------------------------------------------------------
certbot certbot certonly --webroot ... Exit 0
db docker-entrypoint.sh --def ... Up 3306/tcp, 33060/tcp
webserver nginx -g daemon off; Up 0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp
wordpress docker-entrypoint.sh php-fpm Up 9000/tcp
Giờ đây, khi các container đã hoạt động ổn định, bạn sẽ sử dụng giao diện web để hoàn tất thiết lập cho WordPress.
Bước 6: Hoàn tất cài đặt bằng giao diện web
Các bạn truy cập vào tên miền của server. Đừng quên thay đổi your_domain
bằng tên miền đã chuẩn bị.
https://your_domain
Chọn ngôn ngữ hiển thị:

Click chọn Continue, các bạn sẽ được chuyển đến trang thiết lập chính của WordPress. Bạn tiến hành đặt tên cho website và chọn tên đăng nhập. Thay vì “admin”, bạn nên chọn một tên đăng nhập dễ nhớ khác đi kèm một mật khẩu mạnh. Nếu không muốn dùng mật khẩu riêng, các bạn có thể sử dụng mật khẩu tự tạo của WordPress. Cuối cùng, bạn cung cấp địa chỉ email và cho biết bạn có muốn search engine đánh chỉ mục (index) cho website của bạn hay không.

Click chọn Install WordPress ở cuối trang, một biểu mẫu sẽ được hiển thị để bạn đăng nhập:

Sau khi đăng nhập, bạn sẽ được truy cập đến trang dashboard quản lý của WordPress. Đến đây, bạn đã thành công trong việc cài đặt và thiết lập một ứng dụng WordPress cho riêng mình:

Sau khi hoàn tất cài đặt WordPress qua Docker Compose, việc duy trì và tối ưu hóa website là rất quan trọng để đảm bảo tốc độ nhanh, bảo mật cao và khả năng mở rộng. Với dịch vụ WordPress Hosting của Vietnix sẽ giúp bạn quản lý website dễ dàng với WordPress Toolkit và tối ưu tốc độ với LiteSpeed Web Server. Đồng thời, dịch vụ hosting này còn cung cấp các công cụ bảo mật như File Locker và Imunify360 sẽ bảo vệ website khỏi mối đe dọa.
Bước 7: Gia hạn chứng chỉ
Các chứng chỉ của Let’s Encrypt có thời hạn là 90 ngày. Để website luôn được bảo mật, các bạn nên cấu hình cho các chứng chỉ được làm mới một cách tự động. Một trong những cách thực hiện là sử dụng job của trình lập lich cron
. Trong phần tiếp theo, mình sẽ hướng dẫn cách tạo một cron
job để chạy script hỗ trợ các bạn gia hạn chứng chỉ và cập nhật cấu hình Nginx theo định kỳ. Các bạn mở file script ssl_renew.sh
:
$ nano ssl_renew.sh
Bạn thêm đoạn code sau vào file để script có khả năng làm mới chứng chỉ và cập nhật cấu hình Nginx. Các bạn thay username bằng username non-root của bạn:
#!/bin/bash
COMPOSE="/usr/local/bin/docker-compose --no-ansi"
DOCKER="/usr/bin/docker"
cd /home/sammy/wordpress/
$COMPOSE run certbot renew --dry-run && $COMPOSE kill -s SIGHUP webserver
$DOCKER system prune -af
Trong đoạn code trên, bạn để script gán binary docker-compose
cho biến COMPOSE
với cờ --no-ansi
nhằm chạy các lệnh docker-compose
mà không cần sử dụng ANSI control characters (ký tự điều khiển ANSI). Điều tương tự được thực hiện với binary của docker
. Tiếp theo, bạn sẽ cho script mở thư mục dự án ~/wordpress
và chạy các lệnh docker-compose
:
docker-compose run
: Chạy một containercertbot
và ghi đè tùy chọncommand
đã định nghĩa trong service. Thay chocertonly
, subcommandrenew
được sử dụng để gia hạn các chứng chỉ hết hiệu lực. Ngoài ra, bạn cũng bổ sung khả năng kiểm tra script với tùy chọn--dry-run
.docker-compose kill
: Gửi tín hiệuSIGHUP
đến containerwebserver
để cập nhật lại cấu hình service.
Cuối cùng, script sẽ gọi lệnh docker system prune
để xóa toàn bộ các container và image không được sử dụng. Sau khi đóng và lưu lại các thay đổi của file, bạn cấp quyền thực thi cho script bằng lệnh:
$ chmod +x ssl_renew.sh
Bạn tiếp tục mở file root crontab
để cấu hình cho script chạy định kỳ:
$ sudo crontab -e
Nếu đây là lần đầu chỉnh sửa, bạn sẽ được yêu cầu chọn một trình biên dịch để sử dụng:
Output
no crontab for root - using an empty one
Select an editor. To change later, run 'select-editor'.
1. /bin/nano <---- easiest
2. /usr/bin/vim.basic
3. /usr/bin/vim.tiny
4. /bin/ed
Choose 1-4 [1]:
...
Bạn thêm dòng dưới đây vào cuối file root.
...
*/5 * * * * /home/sammy/wordpress/ssl_renew.sh >> /var/log/cron.log 2>&1
Hiện tại, để kiểm tra request gia hạn chứng chỉ có hoạt động hay không, bạn sẽ cấu hình cho job chạy script mỗi 5 phút. File cron.log
được tạo để ghi lại các output liên quan của job.
Sau 5 phút, bạn mở file cron.log
và kiểm tra request gia hạn của mình:
$ tail -f /var/log/cron.log
Các bạn đã thành công nếu Output dưới đây được hiển thị:
Output
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
** DRY RUN: simulating 'certbot renew' close to cert expiry
** (The test certificates below have not been saved.)
Congratulations, all renewals succeeded. The following certs have been renewed:
/etc/letsencrypt/live/your_domain/fullchain.pem (success)
** DRY RUN: simulating 'certbot renew' close to cert expiry
** (The test certificates above have not been saved.)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Bạn sử dụng tổ hợp phím Ctrl+C
để thoát. Đến đây, bạn có thể cấu hình lại file crontab
để job chạy script hằng ngày. Giả sử bạn cần chạy script vào mỗi buổi trưa, sửa lại dòng cuối cùng trong file như sau:
...
0 12 * * * /home/sammy/wordpress/ssl_renew.sh >> /var/log/cron.log 2>&1
Bạn nhớ xóa tùy chọn --dry-run
trong script ssl_renew.sh
:
#!/bin/bash
COMPOSE="/usr/local/bin/docker-compose --no-ansi"
DOCKER="/usr/bin/docker"
cd /home/sammy/wordpress/
$COMPOSE run certbot renew && $COMPOSE kill -s SIGHUP webserver
$DOCKER system prune -af
Job cron
sẽ gia hạn các chứng chỉ Let’s Encrypt khi vẫn còn hiệu lực để đảm bảo các chứng chỉ không bị quá hạn. Bạn cũng có thể thiết lập Logrotate để luân phiên và nén các file log trong trường hợp cần thiết. Ngoài ra, bạn cũng có thể tham khảo thêm cách sử dụng Docker để cài đặt và triển khai Gitea trên Ubuntu một cách đơn giản và hiệu quả, một công cụ mã nguồn mở quản lý mã nguồn, trên Ubuntu bằng Docker, giúp tăng tính linh hoạt và độc lập cho dự án phát triển phần mềm của bạn.
Vietnix – Nhà cung cấp dịch vụ hosting, VPS tốc độ cao
Vietnix – Nhà cung cấp dịch vụ Hosting, VPS tốc độ cao được xây dựng trên nền tảng hạ tầng hiện đại, mang đến khả năng vận hành ổn định và tốc độ truy cập vượt trội cho website, ứng dụng. Ngoài ra, Vietnix còn tích hợp giải pháp backup tự động hàng tuần, tăng cường bảo mật và đảm bảo dữ liệu luôn được an toàn. Với sự đồng hành của đội ngũ kỹ thuật chuyên sâu hỗ trợ 24/7, bạn có thể yên tâm phát triển dự án mà không lo gián đoạn. Liên hệ ngay với Vietnix để nhận tư vấn và lựa chọn giải pháp tối ưu cho nhu cầu của bạn!
Thông tin liên hệ:
- Website: https://vietnix.vn/
- Hotline: 1800 1093
- Email: sales@vietnix.com.vn
- Địa chỉ: 265 Hồng Lạc, Phường Bảy Hiền, Thành Phố Hồ Chí Minh
Câu hỏi thường gặp
Làm thế nào để triển khai Docker WordPress Nginx với hiệu suất tối ưu và dễ dàng mở rộng khi lượng truy cập tăng cao?
Để triển khai Docker WordPress với Nginx hiệu quả và dễ mở rộng:
– Kiến trúc tách biệt service: Nginx (reverse proxy), PHP-FPM (WordPress), Database, Redis/Memcached (cache).
– Tối ưu hiệu suất: Bật FastCGI cache, dùng Redis/Memcached, kích hoạt OPcache, kết hợp CDN để giảm tải.
– Bảo mật & ổn định: Cài SSL (Let’s Encrypt), firewall, backup định kỳ.
– Mở rộng linh hoạt: Với traffic cao, dùng Docker Swarm hoặc Kubernetes để scale WordPress và Nginx, tách database ra hệ thống riêng, bổ sung load balancer và autoscaling.
Cài đặt WordPress Nginx có ưu điểm gì so với WordPress chạy trên Apache?
So với Apache, cài đặt WordPress trên Nginx mang lại một số ưu điểm nổi bật:
– Hiệu suất và tốc độ xử lý cao hơn: Nginx được thiết kế để xử lý hàng nghìn kết nối đồng thời với mức tiêu thụ tài nguyên thấp, giúp WordPress hoạt động nhanh và mượt hơn, đặc biệt khi có nhiều người truy cập cùng lúc.
– Tối ưu cho tĩnh và động: Nginx phục vụ file tĩnh (ảnh, CSS, JS) cực nhanh, giảm tải cho PHP và database, trong khi WordPress trên Apache dễ gặp tình trạng “nghẽn” khi lưu lượng tăng.
– Tiết kiệm tài nguyên: Với cùng cấu hình phần cứng, Nginx thường tiêu thụ CPU và RAM ít hơn, rất phù hợp cho VPS hoặc server có dung lượng hạn chế.
– Khả năng mở rộng tốt: Nginx dễ dàng kết hợp với các hệ thống cache như Redis, Memcached hoặc FastCGI Cache, giúp tăng tốc độ website đáng kể.
– Ổn định hơn khi tải lớn: Trong môi trường cần phục vụ lượng traffic cao (website tin tức, thương mại điện tử, forum), Nginx giúp WordPress giữ được sự ổn định mà Apache khó đáp ứng.
Cảm ơn các bạn đã theo dõi bài viết, hy vọng qua bài hướng dẫn này, các bạn đã biết cách cài đặt WordPress bằng Docker Compose cho một web server Nginx, cũng như cách để bảo mật tên miền và gia hạn chứng chỉ trước khi hết hiệu lực. Nếu có bất kỳ thắc mắc hay phản hồi nào, các bạn hãy bình luận bên dưới, mình sẽ giải đáp nhanh nhất!