想做博客、企业官网或内容型独立站,WordPress 仍然是 VPS 上最常见、搜索需求也最稳定的选择之一。真正让站点长期稳定运行的,不是把安装向导打开,而是把数据库持久化、HTTPS、更新、备份、邮件和安全边界一次配置好。
本文以 Ubuntu 24.04 LTS、Docker Compose、WordPress 官方镜像、MariaDB 和 Caddy 为例,从零部署一个单站点生产环境。示例把 WordPress 和数据库放在 Docker 内网,应用只监听本机端口,再由 Caddy 负责公网 HTTPS。
WordPress 的配置取决于主题、插件、图片和并发量。不要只按“能装上”来买机器:
| 使用场景 | 建议配置 | 说明 |
|---|---|---|
| 个人博客、企业展示站 | 2 核 4GB、50GB SSD | 适合少量插件和中低流量 |
| WooCommerce 或内容站 | 4 核 8GB、100GB NVMe | 给 PHP、MariaDB 和缓存留余量 |
| 多站点或高流量 | 4-8 核、8-16GB 起 | 需要压测、对象存储和独立数据库规划 |
提前准备一个域名,例如 www.example.com,将 A / AAAA 记录指向 VPS。防火墙只开放 SSH、80 和 443,不要把 MariaDB 的 3306 端口暴露到公网。
按 Docker 官方仓库安装 Docker Engine 与 Compose v2,确认命令可用:
docker --version
docker compose version
创建独立目录,并限制配置文件权限:
sudo mkdir -p /opt/wordpress/backups
sudo chown -R "$USER":"$USER" /opt/wordpress
cd /opt/wordpress
生成数据库密码:
openssl rand -hex 32
openssl rand -hex 32
nano .env
chmod 600 .env
写入 /opt/wordpress/.env:
MYSQL_ROOT_PASSWORD=替换为第一组随机值
MYSQL_PASSWORD=替换为第二组随机值
WORDPRESS_DB_NAME=wordpress
WORDPRESS_DB_USER=wordpress
WORDPRESS_DB_HOST=db:3306
WORDPRESS_DEBUG=0
不要把 .env 提交到 Git,也不要在聊天、工单或截图中暴露密码。
创建 /opt/wordpress/compose.yaml:
services:
db:
image: mariadb:11.4
container_name: wordpress-db
restart: unless-stopped
env_file:
- .env
environment:
MARIADB_ROOT_PASSWORD: "${MYSQL_ROOT_PASSWORD:?set MYSQL_ROOT_PASSWORD in .env}"
MARIADB_DATABASE: "${WORDPRESS_DB_NAME:?set WORDPRESS_DB_NAME in .env}"
MARIADB_USER: "${WORDPRESS_DB_USER:?set WORDPRESS_DB_USER in .env}"
MARIADB_PASSWORD: "${MYSQL_PASSWORD:?set MYSQL_PASSWORD in .env}"
volumes:
- wordpress_db:/var/lib/mysql
healthcheck:
test: ["CMD-SHELL", "healthcheck.sh --connect --innodb_initialized"]
interval: 10s
timeout: 5s
retries: 20
start_period: 30s
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --max-allowed-packet=64M
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
wordpress:
image: wordpress:6-php8.3-apache
container_name: wordpress
restart: unless-stopped
depends_on:
db:
condition: service_healthy
ports:
- "127.0.0.1:8080:80"
env_file:
- .env
environment:
WORDPRESS_DB_HOST: "${WORDPRESS_DB_HOST:?set WORDPRESS_DB_HOST in .env}"
WORDPRESS_DB_USER: "${WORDPRESS_DB_USER:?set WORDPRESS_DB_USER in .env}"
WORDPRESS_DB_PASSWORD: "${MYSQL_PASSWORD:?set MYSQL_PASSWORD in .env}"
WORDPRESS_DB_NAME: "${WORDPRESS_DB_NAME:?set WORDPRESS_DB_NAME in .env}"
WORDPRESS_DEBUG: "${WORDPRESS_DEBUG:-0}"
WORDPRESS_CONFIG_EXTRA: |
if (isset($$_SERVER['HTTP_X_FORWARDED_PROTO']) && strpos($$_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') !== false) {
$$_SERVER['HTTPS'] = 'on';
}
define('FORCE_SSL_ADMIN', true);
volumes:
- wordpress_data:/var/www/html
healthcheck:
test: ["CMD-SHELL", "php -r '$$c=@fsockopen(\"127.0.0.1\",80,$$e,$$s,5); exit($$c ? 0 : 1);'"]
interval: 30s
timeout: 10s
retries: 10
start_period: 60s
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
volumes:
wordpress_db:
name: wordpress_db
wordpress_data:
name: wordpress_data
WordPress 只绑定到 127.0.0.1:8080,MariaDB 没有 ports 配置,因此数据库不会直接暴露公网。正式升级前固定镜像系列并查看 WordPress、PHP 和 MariaDB 的兼容性,不要长期使用 latest。
启动并检查:
docker compose config >/dev/null
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=100 wordpress db
curl -I http://127.0.0.1:8080/wp-login.php
两个服务正常后,再配置反向代理。首次初始化时不要执行 docker compose down -v,它会删除数据库卷。
编辑 /etc/caddy/Caddyfile:
www.example.com {
encode zstd gzip
reverse_proxy 127.0.0.1:8080
request_body {
max_size 64MB
}
}
检查并重载 Caddy:
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
curl -I https://www.example.com/wp-login.php
Caddy 会自动申请和续期证书,并传递 X-Forwarded-Proto。Compose 中的 WORDPRESS_CONFIG_EXTRA 会让 WordPress 正确识别外部 HTTPS。后台的“站点地址”和“WordPress 地址”仍应使用 https://www.example.com,否则会出现混合内容、登录循环或后台不断跳转。反向代理细节可参考 VPS 用 Caddy 反向代理完全指南。
浏览器打开 https://www.example.com,填写站点标题、管理员用户名、强密码和专用邮箱。不要把管理员用户名设置成 admin,也不要使用与 VPS、邮箱相同的密码。
安装完成后立即处理这些设置:
- 将“搜索引擎可见性”确认成符合上线计划的状态;
- 删除默认文章、页面、主题和未使用插件;
- 设置固定链接为“文章名”,再测试旧链接是否需要重定向;
- 设置正确的站点时区、语言和媒体缩略图尺寸;
- 在隐私设置中补充站点隐私政策页面;
- 管理员账号启用双因素认证(插件或托管服务支持时)。
不要一开始安装几十个“优化”插件。每增加一个插件,就增加 PHP 执行、数据库查询、更新和安全审计成本。
WordPress 默认 wp_mail() 依赖服务器邮件能力,很多 VPS 的 25 端口受限,导致密码重置、表单通知和订单邮件丢失。生产站应使用可靠 SMTP 服务,通过 SMTP 插件配置专用凭据,并测试:
- 管理员密码重置;
- 新用户邀请或注册通知;
- 联系表单和订单通知;
- SPF、DKIM、DMARC 和发件人域名一致性。
不要把个人邮箱主密码写入 WordPress,优先使用应用专用密码或独立 SMTP 账号。
数据库保存文章、用户、设置和订单;wordpress_data 卷保存主题、插件、上传图片和 wp-config.php。只备份数据库,恢复后媒体文件会丢;只备份文件,文章和用户数据又不完整。
备份数据库:
cd /opt/wordpress
mkdir -p backups
docker compose exec -T db sh -c \
'mariadb-dump --single-transaction --quick --routines --triggers \
-u root -p"$MARIADB_ROOT_PASSWORD" "$MARIADB_DATABASE"' \
| gzip > "backups/wordpress-db-$(date +%F-%H%M).sql.gz"
备份 WordPress 文件卷:
docker run --rm \
-v wordpress_data:/source:ro \
-v "$PWD/backups:/backup" \
alpine sh -c 'tar -C /source -czf /backup/wordpress-data.tar.gz .'
备份文件至少复制一份到另一台机器或对象存储,限制访问权限并定期校验。可以参考 VPS 备份恢复演练 建立加密、异地和恢复验收流程。
恢复时先停止 WordPress 写入,恢复文件卷和数据库,再启动应用。完整验收必须包括管理员登录、文章、图片、固定链接、媒体上传和邮件,而不是只看首页能打开。
WordPress 核心、主题、插件和数据库都可能发生结构变化。更新前先备份,并记录当前版本:
docker compose exec wordpress php -r \
'include "/var/www/html/wp-includes/version.php"; echo $wp_version, PHP_EOL;'
docker compose images
docker compose ps
容器镜像升级:
docker compose config >/dev/null
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=200 wordpress
更新后验证登录、文章发布、媒体上传、表单邮件和前台缓存。插件升级应一次少量进行,遇到白屏或 500 时先从最近变更回滚,而不是同时更新全部插件。
不要用 docker compose down -v 解决 WordPress 报错,也不要在没有备份时直接删除 wordpress_data 或 wordpress_db 卷。
最低限度应做到:
- SSH 使用密钥和普通 sudo 用户,VPS 防火墙只放行必要端口;
- WordPress 管理员使用强密码和双因素认证;
- 删除停用插件和主题,不安装来源不明的破解插件;
- 限制
/xmlrpc.php、登录接口和后台暴力尝试,必要时使用 WAF 或限速; - 定期更新 WordPress、PHP、主题和插件,更新前保留可恢复备份;
- 不将 MariaDB 端口映射到公网,不在文章、日志和工单中泄露数据库密码;
- 为 Docker 日志、磁盘空间和 HTTPS 证书设置监控。
如果站点已经变慢,再按 VPS 上 WordPress 很慢怎么办 排查 PHP-FPM、OPcache、Redis、缓存和慢查询。不要把缓存插件当成数据库或主机资源不足的万能解法。
docker compose ps
curl -I http://127.0.0.1:8080/wp-login.php
docker compose logs --tail=200 wordpress
sudo journalctl -u caddy --no-pager -n 100
确认 WordPress 容器仍在运行、端口绑定为 127.0.0.1:8080,并检查容器是否因内存不足被 OOM Kill。
Compose 网络内的数据库主机名是 db,不是 localhost。检查 .env 中的用户名、密码、数据库名和现有数据卷是否一致:
docker compose logs --tail=200 db
docker compose exec wordpress getent hosts db
docker compose config
修改 .env 不会自动改变已经初始化的 MariaDB 用户密码;这时应使用数据库管理命令同步修改,或从备份恢复,不要删除卷重来。
确认 Caddy 终止 HTTPS,WordPress 两个站点 URL 都是 HTTPS,并清理缓存插件和浏览器 Cookie。Cloudflare 等 CDN 不要同时配置互相冲突的 SSL 模式和重定向规则。
先检查 Caddy 的 request_body max_size,再检查 PHP 的 upload_max_filesize、post_max_size 和 WordPress 内存限制。调大限制前先确认磁盘和备份容量,避免一次上传耗尽 VPS 空间。
- WordPress 只监听
127.0.0.1:8080,MariaDB 未暴露公网 - 域名、Caddy HTTPS 和 WordPress 站点 URL 全部一致
- 管理员强密码、双因素认证和最小权限已配置
- SMTP 密码重置、表单或订单邮件测试成功
- 数据库与
wordpress_data都有异地备份 - 已完成一次恢复演练,并验证文章、图片和登录
- 更新前固定版本并备份,更新后检查 502、500 和磁盘空间
- 已删除默认内容、停用插件和不需要的主题
完成这些步骤后,这套 WordPress 才具备可维护的生产基础。Docker 负责隔离应用,Caddy 负责 HTTPS,MariaDB 负责数据持久化,而备份、邮件和安全更新决定了站点能否真正长期运行。
