WordPress zero-downtime deployment là quy trình cập nhật WordPress production sao cho người dùng gần như không thấy gián đoạn, đội vận hành có thể rollback nhanh, còn nội dung/database không bị mất hoặc lệch phiên bản. Với website có traffic thật, cách “upload đè file bằng FTP rồi cầu nguyện” là một rủi ro lớn: chỉ cần plugin lỗi, cache sai, migration database fail hoặc theme thiếu file là site có thể trắng trang.
Bài này đi theo hướng thực chiến cho sysadmin/DevOps quản trị WordPress: có staging, backup, Git deploy, WP-CLI, kiểm thử, bật/tắt maintenance đúng lúc và checklist nghiệm thu. Mục tiêu không phải làm pipeline phức tạp cho đẹp, mà là biến mỗi lần cập nhật plugin/theme/core thành một thao tác có thể đoán trước và có đường lùi.
1. Zero-downtime trong WordPress nghĩa là gì?
Zero-downtime không có nghĩa là không bao giờ có request lỗi. Trong thực tế, mục tiêu là:
- Không để người dùng thấy trang bảo trì kéo dài.
- Không có giai đoạn file PHP bị thiếu một nửa vì upload chưa xong.
- Không làm mất upload, order, comment hoặc bài viết mới.
- Có thể rollback trong vài giây đến vài phút.
- Có log để biết bản nào được deploy, ai deploy và deploy lúc nào.
Điểm khó của WordPress nằm ở chỗ code, database và nội dung media thường sống chung. Vì vậy quy trình deploy phải tách rõ phần nào được quản lý bằng Git, phần nào là dữ liệu runtime.
2. Kiến trúc thư mục khuyến nghị
Một mẫu đơn giản nhưng hiệu quả là dùng thư mục release và symlink:
/var/www/example.com/
├── current -> releases/20260610-1900
├── releases/
│ ├── 20260608-1900/
│ └── 20260610-1900/
├── shared/
│ ├── wp-content/uploads/
│ ├── wp-config.php
│ └── cache/
└── logs/Mỗi lần deploy, bạn build một release mới trong releases/, kiểm tra xong mới đổi symlink current. Thư mục upload và cấu hình nhạy cảm nằm trong shared để không bị ghi đè.
3. Những thứ nên và không nên đưa vào Git
Nên đưa vào Git:
- Theme tự phát triển.
- Plugin custom.
- File cấu hình mẫu, script deploy, composer.json nếu dùng Composer.
- Mu-plugin bắt buộc cho production.
Không nên đưa vào Git:
wp-content/uploadsvì đây là dữ liệu người dùng.wp-config.phpchứa credential.- Cache, log, backup dump.
- Plugin premium nếu license không cho phép lưu trong repo chung.
4. Backup trước deploy: không có backup thì không gọi là quy trình
Trước khi chạm vào production, tối thiểu cần backup database và ghi lại version hiện tại.
cd /var/www/example.com/current
wp core version
wp plugin list --format=table
wp theme list --format=table
wp db export /var/backups/wp/example-$(date +%F-%H%M).sql --single-transactionGiải thích:
wp core versiongiúp biết WordPress đang ở bản nào.wp plugin listvàwp theme listlà snapshot nhanh để rollback logic nếu cần.--single-transactionphù hợp với InnoDB để giảm lock trong lúc export.
Output mẫu:
Success: Exported to '/var/backups/wp/example-2026-06-10-1855.sql'.
5. Staging phải giống production ở những điểm quan trọng
Staging không cần cùng kích thước server, nhưng nên giống production ở PHP version, web server, extension PHP, object cache, database engine và plugin chính. Nếu production chạy Redis object cache mà staging không có Redis, nhiều lỗi cache sẽ không xuất hiện trước khi deploy.
php -v
php -m | egrep 'redis|imagick|mysqli|curl|mbstring|zip|intl'
wp --info
wp option get home
wp option get siteurlKhi clone database production về staging, hãy thay URL và chặn email thật:
wp search-replace 'https://example.com' 'https://staging.example.com' --skip-columns=guid
wp option update blog_public 0
6. Quy trình deploy theo release
Ví dụ script deploy tối giản:
#!/usr/bin/env bash
set -euo pipefail
APP=/var/www/example.com
REL=$(date +%Y%m%d-%H%M%S)
RELEASE=$APP/releases/$REL
git clone --depth=1 git@github.com:company/wp-site.git "$RELEASE"
ln -sfn $APP/shared/wp-config.php $RELEASE/wp-config.php
rm -rf $RELEASE/wp-content/uploads
ln -sfn $APP/shared/wp-content/uploads $RELEASE/wp-content/uploads
cd "$RELEASE"
composer install --no-dev --optimize-autoloader || true
wp core verify-checksums --allow-root || true
wp plugin list --allow-root
ln -sfn "$RELEASE" $APP/current
systemctl reload php8.3-fpm
Điểm quan trọng là symlink chỉ đổi sau khi release mới đã chuẩn bị xong. Người dùng không gặp trạng thái thư mục WordPress bị upload dở.
7. Database migration: phần nguy hiểm nhất
Nhiều plugin chạy migration database khi được cập nhật. Đây là điểm khiến rollback không còn đơn giản vì code có thể lùi nhưng database đã đổi. Với website quan trọng, hãy kiểm tra plugin changelog trước khi update và test migration trên staging.
wp plugin update --dry-run --all
wp plugin update woocommerce --dry-run
wp db check
wp db optimizeNếu update liên quan WooCommerce, membership, LMS hoặc plugin custom có bảng riêng, cần lên lịch maintenance window ngắn và backup ngay trước thời điểm deploy.
8. Dùng maintenance mode đúng cách
Với deploy chỉ đổi file theme/plugin không migration database, có thể không cần maintenance mode. Nhưng nếu có update core/plugin lớn, nên bật maintenance trong thời gian rất ngắn:
wp maintenance-mode activate
# chạy migration/update đã test
wp plugin update plugin-name
wp core update-db
wp maintenance-mode deactivateKhông nên bật maintenance rồi để đó trong toàn bộ quá trình build. Hãy build release trước, chỉ bật maintenance khi chạy bước ảnh hưởng database.
9. Cache: nguyên nhân phổ biến khiến deploy “đúng mà sai”
Sau khi đổi release, cần làm sạch các lớp cache liên quan:
wp cache flush
wp transient delete --all
systemctl reload nginx
systemctl reload php8.3-fpmNếu dùng Cloudflare hoặc CDN, purge theo URL quan trọng thay vì xóa toàn bộ cache nếu traffic lớn. Nếu dùng Redis object cache, xác nhận drop-in vẫn hoạt động:
wp redis status || true
wp cache type
10. Smoke test sau deploy
Không nên chỉ mở homepage bằng mắt. Hãy kiểm tra các URL đại diện:
curl -I https://example.com/
curl -I https://example.com/wp-login.php
curl -I https://example.com/category/news/
curl -s https://example.com/ | grep -i '<title>'Nếu là WooCommerce hoặc site có form, cần kiểm tra thêm cart, checkout sandbox, contact form, cron và REST API:
curl -s https://example.com/wp-json/ | jq '.name'
wp cron event list --fields=hook,next_run_relative --format=table
11. Rollback trong vài giây bằng symlink
Nếu release mới lỗi ở tầng code, rollback rất nhanh:
APP=/var/www/example.com
PREV=$(ls -1dt $APP/releases/* | sed -n '2p')
ln -sfn "$PREV" $APP/current
systemctl reload php8.3-fpm
wp --path=$APP/current cache flushLưu ý: rollback code không tự rollback database. Nếu migration đã thay đổi schema hoặc dữ liệu, cần kế hoạch riêng: restore backup, migration ngược, hoặc maintenance window có thông báo rõ.
12. Lỗi thường gặp và cách xử lý
Trang trắng sau deploy
tail -100 /var/log/nginx/error.log
tail -100 /var/log/php8.3-fpm.log
wp --path=/var/www/example.com/current plugin listThường do fatal error PHP, thiếu extension hoặc plugin không tương thích PHP version.
Upload ảnh lỗi sau deploy
ls -ld /var/www/example.com/shared/wp-content/uploads
sudo -u www-data test -w /var/www/example.com/shared/wp-content/uploads && echo OKKiểm tra symlink uploads và quyền ghi của user web server.
CSS/JS cũ vẫn hiển thị
Xóa cache plugin, object cache, CDN và kiểm tra version asset trong theme. Nếu dùng build tool, hãy đảm bảo file manifest mới được deploy cùng release.
13. Checklist nghiệm thu production
- Đã backup database và lưu được file backup.
- Release mới nằm trong thư mục riêng, không upload đè trực tiếp production.
wp-config.phpvà uploads dùng shared path đúng.- Staging đã test plugin/theme/core update.
- Maintenance mode chỉ bật trong bước cần thiết.
- Đã flush cache PHP/WordPress/CDN phù hợp.
- Homepage, login, REST API, category, form quan trọng trả HTTP 200.
- Có lệnh rollback đã kiểm tra trước.
- Log deploy ghi release ID, người thực hiện và thời gian.
14. Bài tập lab
- Tạo một WordPress lab bằng Docker hoặc VPS nhỏ.
- Chuyển cấu trúc sang
current/releases/shared. - Deploy một thay đổi nhỏ trong theme bằng symlink release.
- Cố tình tạo lỗi PHP trong release mới, sau đó rollback về release cũ.
- Viết runbook một trang gồm: backup, deploy, smoke test, rollback.
Kết luận: WordPress zero-downtime deployment không cần bắt đầu bằng hệ thống CI/CD đồ sộ. Chỉ cần tách dữ liệu runtime khỏi code, build release riêng, đổi symlink đúng thời điểm, kiểm thử bằng WP-CLI/curl và luôn có rollback. Khi quy trình này ổn định, bạn có thể nâng cấp dần lên GitHub Actions, GitLab CI hoặc deployment runner nội bộ.
