WordPress в GitOps с Bedrock на VPS HMS
Это руководство объясняет, как разместить сайт WordPress на VPS HostMyServers и управлять им в стиле GitOps с помощью Bedrock — шаблона (boilerplate) WordPress от Roots. Ваш Git-репозиторий становится источником истины: ядро WordPress, плагины и темы декларируются в коде, версионируются, а затем автоматически развёртываются на вашем VPS при каждом git push.
VPS даёт вам root-доступ, SSH и полный контроль над стеком (Nginx, PHP-FPM, MariaDB), необходимый для такого способа развёртывания, — то, что не позволяет сделать shared-хостинг (виртуальный хостинг).
Обычный WordPress смешивает код (ядро, плагины, темы), конфигурацию (wp-config.php) и данные (загрузки) в одной папке и обновляется из панели администратора. Поэтому его сложно версионировать и воспроизводить. Bedrock даёт:
- Composer для управления ядром WordPress, плагинами и темами как зависимостями (версии фиксируются в
composer.lock) - Конфигурацию через переменные окружения (файл
.env) без секретов в Git - Файлы конфигурации для каждого окружения (разработка, staging, продакшн)
- Более безопасную структуру каталогов: сервер отдаёт наружу только папку
web/ - Отключение изменений из панели администратора в продакшне (
DISALLOW_FILE_MODS)
Заказать VPS HostMyServers
Это руководство рассчитано на VPS HostMyServers. Выберите тариф в зависимости от вашего трафика:
- VPS NVMe — отличное соотношение цены и качества, идеально для старта сайта-визитки или блога
- VPS Performance — рекомендуется для интернет-магазинов на WooCommerce и сайтов с высоким трафиком
| Использование | vCPU | RAM | Хранилище |
|---|---|---|---|
| Сайт-визитка / блог | 1-2 | 2 ГБ | 20 ГБ NVMe |
| Сайт со средним трафиком, несколько сайтов | 2-4 | 4 ГБ | 40 ГБ NVMe |
| WooCommerce / высокий трафик | 4+ | 8 ГБ+ | 80 ГБ NVMe+ |
При заказе выберите Ubuntu 24.04 LTS или Debian 12 в качестве операционной системы. После доставки VPS IP-адрес и root-данные для входа будут отправлены вам по электронной почте и доступны в вашем личном кабинете HostMyServers.
Для нескольких сайтов с высоким трафиком та же процедура применима к выделенным серверам Eco и Performance.
Принцип GitOps применительно к WordPress
| Элемент | Где живёт | Версионируется в Git? |
|---|---|---|
| Ядро WordPress, публичные плагины и темы | composer.json + composer.lock | Да (декларация + точные версии) |
| Тема и плагины, разработанные вами | web/app/themes/, web/app/plugins/ | Да (исходный код) |
| Конфигурация (константы, окружения) | config/ | Да |
| Секреты (пароли, ключи, соли) | .env на каждом сервере | Нет |
| Медиафайлы (загрузки) | web/app/uploads/ на сервере | Нет (нужно резервировать) |
| Контент (запи си, страницы, настройки) | База данных MySQL/MariaDB | Нет (нужно резервировать) |
Жизненный цикл выглядит так:
- Вы изменяете код или обновляете зависимость локально
- Коммитите и отправляете (push) изменения в ветку
main - CI-пайплайн устанавливает зависимости и разворачивает новый релиз на сервере
- В случае проблемы вы возвращаетесь к предыдущему релизу (
git revertили переключение символической ссылки)
База данных и медиафайлы — это данные, а не код. Git ими не управляет: настройте регулярное резервное копирование (wp db export, резервная копия папки shared/uploads, снапшоты VPS).
Предварительные требования
На вашей машине разработки:
- Git
- PHP ≥ 8.1 (рекомендуется 8.3) и Composer 2
- Аккаунт GitHub (или GitLab) для размещения репозитория
На вашем VPS HMS (Ubuntu 24.04 LTS / Debian 12):
- SSH-доступ как root или пользователь с правами sudo
- Защищённый VPS (пользователь без root, SSH, файрвол): см. Защита сервера
- Доменное имя, чья DNS-запись
Aуказывает на IP-адрес VPS
В этом руководстве composer install выполняется CI-пайплайном. VPS получает только уже готовые файлы: ему не нужны ни Composer, ни Git, ни доступ к репозиториям пакетов.
Шаг 1: создать проект Bedrock локально
-
Создайте проект:
composer create-project roots/bedrock mon-sitecd mon-site -
Изучите структуру каталогов:
mon-site/├── composer.json # Ядро WordPress, плагины и темы декларируются здесь├── composer.lock # Точные установленные версии├── .env.example # Шаблон конфигурации├── wp-cli.yml # Указывает WP-CLI, где находится WordPress (web/wp)├── config/│ ├── application.php # Основная конфигурация (заменяет wp-config.php)│ └── environments/│ ├── development.php│ └── staging.php├── vendor/ # Зависимости Composer (не версионируется)└── web/ # Корень веб-сервера (document root)├── app/ # Аналог wp-content│ ├── mu-plugins/│ ├── plugins/│ ├── themes/│ └── uploads/├── wp/ # Ядро WordPress (не версионируется)├── wp-config.php└── index.phpinfoВ Bedrock панель администратора находится по адресу
https://your-domain.com/wp/wp-admin, аwp-contentзаменён наweb/app. -
Создайте локальный файл
.envна основе шаблона:cp .env.example .env.envDB_NAME='wordpress_dev'DB_USER='wordpress_user'DB_PASSWORD='local_password'DB_HOST='localhost'DB_PREFIX='wp_'WP_ENV='development'WP_HOME='http://mon-site.test'WP_SITEURL="${WP_HOME}/wp"AUTH_KEY='generateme'SECURE_AUTH_KEY='generateme'LOGGED_IN_KEY='generateme'NONCE_KEY='generateme'AUTH_SALT='generateme'SECURE_AUTH_SALT='generateme'LOGGED_IN_SALT='generateme'NONCE_SALT='generateme' -
Сгенерируйте уникальные ключи и соли (нужно делать для каждого окружения):
for k in AUTH_KEY SECURE_AUTH_KEY LOGGED_IN_KEY NONCE_KEY AUTH_SALT SECURE_AUTH_SALT LOGGED_IN_SALT NONCE_SALT; doecho "$k='$(openssl rand -base64 48 | tr -d '\n')'"doneСкопируйте результат в ваш
.envвместо строкgenerateme. Также можно воспользоваться генератором от Roots.
Чтобы запустить сайт локально, используйте любой удобный инструмент (DDEV, Lando, Laravel Valet, Docker…), указав в качестве корня веб-сервера папку web/.
Шаг 2: управление плагинами и темами через Composer
Bedrock уже настроен для работы с WPackagist — Composer-зеркалом всех плагинов и тем из официального каталога WordPress.org.
-
Установите плагин или тему:
composer require wpackagist-plugin/wordpress-seocomposer require wpackagist-plugin/wp-mail-smtpcomposer require wpackagist-theme/twentytwentyfiveИмя пакета соответствует slug на WordPress.org (
https://wordpress.org/plugins/<slug>/). -
Обновите зависимости:
# Обновить всё согласно ограничениям composer.jsoncomposer update# Обновить только ядро WordPresscomposer update roots/wordpress --with-all-dependencies# Посмотреть доступные обновленияcomposer outdated -
Удалите плагин:
composer remove wpackagist-plugin/wp-mail-smtp
Именно файл composer.lock гарантирует, что на продакшне установятся точно те же версии, что были протестированы локально. Он всегда должен быть версионирован.
Платные или приватные плагины
Платные плагины отсутствуют на WPackagist. Есть два варианта:
-
Рекомендуется: использовать Composer-репозиторий, предоставляемый разработчиком плагина (ACF Pro, Gravity Forms, WPML… предлагают такой), с ключом аутентификации, хранящимся в
auth.json(не версионируется) и в секретах вашего CI. -
Альтернатива: версионировать плагин напрямую в репозитории, добавив исключение в
.gitignore:.gitignoreweb/app/plugins/*!web/app/plugins/.gitkeep!web/app/plugins/mon-plugin-premium
Ваша собственная тема (в web/app/themes/) версионируется по умолчанию.
Шаг 3: настройка конфигурации
Общая конфигурация находится в config/application.php. Пе реопределения для каждого окружения — в config/environments/<WP_ENV>.php. По умолчанию Bedrock:
- отключает редактор файлов и установку/обновление плагинов из панели администратора (
DISALLOW_FILE_MODS) в продакшне - разрешает их в
development - скрывает ошибки PHP в продакшне
Такое поведение — суть GitOps: в продакшне любое изменение кода проходит через Git. Если плагин показывает «доступно обновление», обновите его через Composer локально, протестируйте, а затем закоммитьте.
Пример добавления константы для всех окружений:
Config::define('WP_POST_REVISIONS', 10);
Config::define('WP_MEMORY_LIMIT', '256M');
Шаг 4: инициализировать Git-репозиторий
Bedrock поставляется с готовым .gitignore: vendor/, web/wp/, плагины, установленные через Composer, загрузки и файл .env игнорируются.
git init -b main
git add .
git commit -m "Initial Bedrock project"
git remote add origin git@github.com:your-account/mon-site.git
git push -u origin main
Убедитесь, что ни один секрет не версионируется:
git ls-files | grep -E '(^|/)\.env$|auth\.json' || echo "OK: секретов в репозито рии нет"
Шаг 5: подготовка VPS HMS
Подключиться к VPS
Подключитесь с помощью IP-адреса и учётных данных, полученных при доставке сервера:
ssh root@vps_ip_address
Установить Nginx, PHP-FPM и MariaDB
sudo apt update && sudo apt upgrade -y
sudo apt install -y nginx mariadb-server \
php-fpm php-mysql php-curl php-gd php-intl php-mbstring php-xml php-zip php-imagick
php -v
Ubuntu 24.04 устанавливает PHP 8.3 (Debian 12: PHP 8.2). В дальнейшем в руководстве заменяйте php8.3-fpm на версию, которую показывает php -v. Для защиты MariaDB следуйте руководству Установка и защита MariaDB.
Если файрвол UFW активен, откройте веб-порты:
sudo ufw allow 'Nginx Full'
Создать пользователя для развёртывания и структуру каталогов
Пайплайн будет подключаться под отдельным пользователем deploy. Код принадлежит deploy и доступен PHP (www-data) только для чтения; только папка загрузок доступна PHP для записи.
sudo adduser --disabled-password --gecos "" deploy
sudo usermod -aG www-data deploy
sudo mkdir -p /var/www/mon-site/{releases,shared/uploads}
sudo chown -R deploy:www-data /var/www/mon-site
sudo chown -R www-data:www-data /var/www/mon-site/shared/uploads
sudo chmod 2775 /var/www/mon-site/shared/uploads
Структура каталогов развёртывания будет следующей:
/var/www/mon-site/
├── current -> releases/<sha> # Символическая ссылка на активный релиз
├── releases/
│ ├── 3f2a9c1.../ # Один релиз на каждый развёрнутый коммит
│ └── 8be41d0.../
└── shared/
├── .env # Конфигурация продакшна (вне Git)
└── uploads/ # Медиафайлы, сохраняющиеся между релизами
Создать базу данных
sudo mysql -u root -p
CREATE DATABASE wordpress_prod DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'wordpress_user'@'localhost' IDENTIFIED BY 'secure_password';
GRANT ALL PRIVILEGES ON wordpress_prod.* TO 'wordpress_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Создать файл .env для продакшна
sudo -u deploy nano /var/www/mon-site/shared/.env
DB_NAME='wordpress_prod'
DB_USER='wordpress_user'
DB_PASSWORD='secure_password'
DB_HOST='localhost'
DB_PREFIX='wp_'
WP_ENV='production'
WP_HOME='https://your-domain.com'
WP_SITEURL="${WP_HOME}/wp"
# Ключи и соли, сгенерированные командой openssl из шага 1
AUTH_KEY='...'
SECURE_AUTH_KEY='...'
LOGGED_IN_KEY='...'
NONCE_KEY='...'
AUTH_SALT='...'
SECURE_AUTH_SALT='...'
LOGGED_IN_SALT='...'
NONCE_SALT='...'
sudo chown deploy:www-data /var/www/mon-site/shared/.env
sudo chmod 640 /var/www/mon-site/shared/.env
Настроить Nginx
Корень веб-сервера указывает на current/web: код, vendor/ и .env никогда не отдаются наружу.
server {
listen 80;
server_name your-domain.com www.your-domain.com;
root /var/www/mon-site/current/web;
index index.php;
client_max_body_size 64M;
location / {
try_files $uri $uri/ /index.php?$args;
}
# Запретить выполнение PHP в папке загрузок
location ~* ^/app/uploads/.*\.php$ {
deny all;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
# Разрешать символическую ссылку "current" при каждом запросе
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
}
location ~ /\. {
deny all;
}
}
sudo ln -s /etc/nginx/sites-available/mon-site /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
Использование $realpath_root (вместо $document_root) гарантирует, что PHP-FPM загружает файлы нового релиза сразу после переключения ссылки current, не отдавая закэшированные старые пути.
Затем включите HTTPS, например с помощью Certbot (см. также Установка SSL-сертификата):
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.com -d www.your-domain.com
Разрешить перезагрузку PHP-FPM
Пайплайн перезагружает PHP-FPM после каждого развёртывания, чтобы сбросить кэш OPcache. Разрешите для пользователя deploy только эту команду:
echo 'deploy ALL=(root) NOPASSWD: /usr/bin/systemctl reload php8.3-fpm' | sudo tee /etc/sudoers.d/deploy
sudo chmod 440 /etc/sudoers.d/deploy
sudo visudo -c
Создать SSH-ключ для развёртывания
На вашей машине сгенерируйте отдельную пару ключей для пайплайна:
ssh-keygen -t ed25519 -C "github-actions-deploy" -f ./deploy_key -N ""
Добавьте публичный ключ на VPS:
sudo mkdir -p /home/deploy/.ssh
sudo nano /home/deploy/.ssh/authorized_keys # вставьте содержимое deploy_key.pub
sudo chown -R deploy:deploy /home/deploy/.ssh
sudo chmod 700 /home/deploy/.ssh && sudo chmod 600 /home/deploy/.ssh/authorized_keys
Получите SSH-отпечаток VPS (чтобы избежать атаки типа man-in-the-middle):
ssh-keyscan -H vps_ip_address
Шаг 6: автоматизировать развёртывание с GitHub Actions
Объявить секреты
В вашем репозитории GitHub перейдите в Settings → Secrets and variables → Actions и создайте:
| Секрет | Значение |
|---|---|
SSH_HOST | IP-адрес вашего VPS HMS |
SSH_USER | deploy |
SSH_PRIVATE_KEY | Содержимое файла deploy_key (приватный ключ) |
SSH_KNOWN_HOSTS | Результат команды ssh-keyscan |
После этого удалите файлы deploy_key и deploy_key.pub со своей машины.
Создать workflow
name: Deploy
on:
push:
branches: [main]
workflow_dispatch:
concurrency:
group: production
cancel-in-progress: false
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
env:
BASE_DIR: /var/www/mon-site
TARGET: ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
tools: composer:v2
- name: Установить зависимости
run: composer install --no-dev --prefer-dist --no-interaction --optimize-autoloader
- name: Настроить SSH
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
echo "${{ secrets.SSH_KNOWN_HOSTS }}" > ~/.ssh/known_hosts
chmod 600 ~/.ssh/id_ed25519
- name: Отправить релиз
run: |
rsync -az --delete \
--exclude='.git' --exclude='.github' \
--exclude='.env' --exclude='web/app/uploads' \
./ "$TARGET:$BASE_DIR/releases/$GITHUB_SHA/"
- name: Активировать релиз
run: |
ssh "$TARGET" bash -s -- "$BASE_DIR" "$GITHUB_SHA" <<'EOF'
set -euo pipefail
BASE_DIR="$1"
RELEASE="$BASE_DIR/releases/$2"
# Связать общую конфигурацию и медиафайлы
ln -sfn "$BASE_DIR/shared/.env" "$RELEASE/.env"
rm -rf "$RELEASE/web/app/uploads"
ln -sfn "$BASE_DIR/shared/uploads" "$RELEASE/web/app/uploads"
# Атомарное переключение ссылки "current"
ln -sfn "$RELEASE" "$BASE_DIR/current.tmp"
mv -Tf "$BASE_DIR/current.tmp" "$BASE_DIR/current"
# Сбросить OPcache
sudo systemctl reload php8.3-fpm
# Сохранить 5 последних релизов
cd "$BASE_DIR/releases"
ls -1t | tail -n +6 | xargs -r rm -rf
EOF
Закоммитьте и отправьте этот файл: первое развёртывание запустится автоматически. Следите за его выполнением на вкладке Actions репозитория.
Тот же принцип применим с GitLab CI: образ composer:2 для composer install, затем rsync и ssh с защищёнными переменными CI/CD вместо секретов GitHub.
Первый запуск
После успешного первого развёртывания установите WordPress. Либо через браузер по адресу https://your-domain.com/wp/wp-admin/install.php, либо с помощью WP-CLI на сервере:
cd /var/www/mon-site/current
sudo -u www-data wp core install \
--url="https://your-domain.com" \
--title="Your site title" \
--admin_user="admin" \
--admin_password="StrongPassword123!" \
--admin_email="your@email.com"
См. руководство Установка WordPress с WP-CLI для установки WP-CLI на сервере.
XML-карта сайта и SEO
WordPress нативно генерирует карту сайта (sitemap) по адресу https://your-domain.com/wp-sitemap.xml. С Bedrock и приведённой выше конфигурацией Nginx (try_files ... /index.php?$args) она работает без дополнительных настроек. Проверьте это:
curl -sI https://your-domain.com/wp-sitemap.xml | head -n 1
Если вы устанавливаете SEO-плагин через Composer (например, composer require wpackagist-plugin/wordpress-seo), он заменяет нативную карту сайта своей собственной (/sitemap_index.xml для Yoast SEO). Карта сайта по-прежнему генерируется динамически из базы данных: версионировать в Git нечего.
После этого не забудьте:
- Указать URL карты сайта в Google Search Console и Bing Webmaster Tools
- Проверить, что в продакшне не отмечена галочка Настройки → Чтение → Видимость для поисковых систем
Bedrock включает mu-plugin bedrock-disallow-indexing: на окружении, где WP_ENV равен staging или development, сайт просит поисковые системы не индексировать его (DISALLOW_INDEXING). Таким образом, в результатах поиска появляется только продакшн.
Повседневная работа
Обновление WordPress и плагинов
composer update
# Протестировать сайт локально, затем:
git add composer.json composer.lock
git commit -m "chore: update WordPress and plugins"
git push
Развёртывание происходит автоматически. Чтобы получать уведомления о новых версиях, включите Dependabot (экосистема composer) или Renovate в репозитории: они будут открывать pull request'ы с обновлениями, которые останется только одобрить.
Использование ветки staging
Чтобы проверять изменения перед продакшном, создайте второе окружение (поддомен staging.your-domain.com, другая папка, другая база данных, WP_ENV='staging') и продублируйте workflow, запуская его для ветки staging. Файл config/environments/staging.php при этом применится автоматически.
Откат назад (rollback)
Метод GitOps (рекомендуется): отмените проблемный коммит, пайплайн заново развернёт предыдущее состояние.
git revert <commit_sha>
git push
Аварийный метод: вручную переключите ссылку current на предыдущий релиз прямо на сервере.
cd /var/www/mon-site
ls -1t releases/ # найти предыдущий релиз
ln -sfn "$PWD/releases/<previous_sha>" current.tmp && mv -Tf current.tmp current
sudo systemctl reload php8.3-fpm
Откат кода не восстанавливает базу данных. Если обновление изменило схему базы, восстановите также SQL-резервную копию, сделанную до развёртывания.
Перенос существующего сайта WordPress на Bedrock
-
Составьте список плагинов и тем, установленных на старом сайте:
wp plugin listиwp theme list -
Добавьте их в проект Bedrock с помощью
composer require wpackagist-plugin/<slug>(а собственные разработки версионируйте напрямую) -
Скопируйте старую папку
wp-content/uploads/в/var/www/mon-site/shared/uploads/ -
Импортируйте базу данных, затем исправьте пути к медиафайлам, которые меняются с
wp-contentнаapp:cd /var/www/mon-site/currentsudo -u www-data wp db import /path/to/backup.sqlsudo -u www-data wp search-replace '/wp-content/uploads' '/app/uploads' --all-tables -
Если префикс таблиц отличается от
wp_, скорректируйтеDB_PREFIXв файле.env
Перед любым wp search-replace сделайте пробный запуск с --dry-run, чтобы проверить количество замен.
Рекомендации
- Никогда не изменяйте файлы напрямую на сервере: всё проходит через Git
- Всегда коммитьте
composer.lockи фиксируйте критичные версии вcomposer.json - Держите секреты вне репозитория (
.env,auth.json) и используйте секреты CI - Защитите ветку
main(обязательное ревью pull request'ов) - Регулярно делайте резервные копии базы данных и папки
shared/uploads, дополняя их снапшотами/резервными копиями вашего VPS - Сначала развёртывайте на staging-окружении
При возникновении проблем
- Белая страница или ошибка 500: проверьте логи
sudo tail -f /var/log/nginx/error.logи/var/log/php8.3-fpm.log, а также наличие ссылки.envв активном релизе - Ошибка подключения к базе данных: проверьте учётные данные в файле
shared/.env - После развёртывания отображается старая версия: убедитесь, что PHP-FPM действительно был перезагружен и что Nginx использует
$realpath_root - Не удаётся загрузить медиафайлы: проверьте, что
shared/uploadsпринадлежитwww-dataи что ссылкаweb/app/uploadsдействительно указывает на неё - Пайплайн не может подключиться по SSH: проверьте секреты
SSH_PRIVATE_KEYиSSH_KNOWN_HOSTSи протестируйтеssh deploy@vps_ip_addressсо своей машины - Невозможно установить плагины из панели администратора: это нормально в продакшне (
DISALLOW_FILE_MODS), используйте Composer