Оновлення WordPress мають бути нудними. Натиснув «Оновити», почекав кілька секунд і далі по справах.
Але іноді продакшн вирішує нагадати, що він працює на файлових системах, правах доступу та тих приємних рішеннях, які ви приймали о 2-й ночі шість місяців тому.
Якщо ви бачите «Update failed», «Could not create directory», «Permission denied», «PCLZIP_ERR», «Disk full» або сайт застряг у режимі обслуговування,
це практичний шлях вирішення. Не шлях «chmod 777, поки не запрацює». Правильний шлях.
Швидкий план діагностики
Коли оновлення ламаються, не починайте з того, щоб змінювати права. Почніть із визначення вузького місця.
WordPress каже вам «update failed», але система каже чому. Ваше завдання — слухати систему.
По-перше: визначте тип помилки
- Чи записувана файловa система для веб/PHP-користувача? Якщо ні — отримаєте «Permission denied» або «Could not create directory».
- Чи вичерпано місце на диску або іноди? Оновлення записують тимчасові файли, розпаковують архіви та роблять атомарні перейменування. Потрібен запас.
- Чи доступна та достатня тимчасова директорія? PHP використовує
/tmp(часто tmpfs) або налаштований тимчасовий шлях. - Чи WordPress застряг у режимі обслуговування? Може бути, що оновлення відпрацювало наполовину й залишило
.maintenance. - Чи процес оновлення не вичерпав час? Великі плагіни/теми можуть перевищувати таймаути та ліміти пам’яті PHP.
По-друге: перевіряйте помилку на правильному рівні
- Інтерфейс WordPress / логи: Повідомлення в адмінці часто загальні. Краще ніж нічого, але недостатньо.
- Логи вебсерверу / PHP-FPM: Часто містять реальний шлях з «Permission denied» або «No space left on device».
- Системний рівень:
df,mount,ls -l,namei,getfacl. Тут живе істина.
По-третє: робіть найменш ризиковане виправлення першими
- Диск/іноди: Безпечно перевірити і часто миттєво пояснює «таємничі» збої.
- Невідповідності власності: Виправте невідповідність, щоб потрібний користувач міг записувати. Уникайте глобальної доступності для всіх.
- Біти прав/ACL: Виправляйте до мінімально необхідного. Тримайте
wp-config.phpпід суворим контролем. - Таймаут/пам’ять: Якщо падає під час розпаковки або завантаження, підніміть ліміти PHP або використайте WP-CLI.
Одне керівне правило: якщо ваше виправлення — «зробити все 777», ви нічого не виправили. Ви просто розширили площу ураження для безпеки.
Як насправді працюють оновлення WordPress (і чому вони падають)
Оновлення WordPress — це операції з файлами, сховані під кнопкою. WordPress (через PHP) завантажує zip, пише його у тимчасову папку,
розпаковує, а потім замінює файли. Плагіни та теми роблять те саме, просто в інших директоріях.
Типові шляхи оновлення
- Оновлення ядра: запис у корінь WordPress та
wp-includes/wp-admin. Також створюється.maintenance. - Оновлення плагінів: запис у
wp-content/pluginsта іноді повна заміна директорії плагіна. - Оновлення тем: запис у
wp-content/themes. - Пакети мов: запис у
wp-content/languages.
Чому відбувається збій
Оновлення падають, коли PHP не може записати файли туди, де потрібно, або не може створити тимчасові файли, чи не може виконати перейменування директорій,
або вичерпує ресурси посеред процесу. Повідомлення про помилку залежить від того, яка функція зазнала невдачі: ініціалізація файлової системи, розпаковка, копіювання, перейменування або очищення.
Найбільша концептуальна помилка: думати, що «WordPress» володіє цими файлами. Ні. Власник — ОС. Власник — користувач вебсерверу. Ваш процес деплою відповідає.
WordPress — лише PHP-процес, який просить ОС дозволити операції з файлами.
Цікаві факти та історія (коротко, корисно, трохи для гіків)
- Автоматичні фонові оновлення в ядрі WordPress стали поширеними близько 3.7, змінивши режими помилок з «людина забула» на «машина спробувала і вдарилася об права».
- WordPress використовує абстракцію файлової системи, яка може обирати між «direct» записами та методами FTP/SSH залежно від виявленого середовища.
- Багато проблем з правами WordPress фактично пов’язані з обходом батьківських директорій; один відсутній біт виконання (
x) на директорії може заблокувати все нижче. - Іноди важливі не менше за байти; у вас може бути гігабайти вільного місця і при цьому відмовити створення файлу, якщо закінчилися іноди (поширене на файлових системах з багатьма дрібними файлами).
- Атомарні перейменування — класична трюк для деплою, але вони вимагають прав запису на батьківську директорію, а не лише на цільову.
- tmpfs для /tmp зазвичай використовується в сучасному Linux; він швидкий, але розмір береться з ОЗП і може заповнюватися під час розпаковки.
- Розпаковка zip у PHP історично залежала від зовнішніх інструментів або бібліотек; відсутність підтримки ZipArchive все ще викликає помилки розпаковки в деяких складаннях.
- «WordPress застряг у режимі обслуговування» зазвичай — це залишковий файл з назвою
.maintenance, створений під час оновлення і видаляється після успіху.
Практичні завдання: команди, виводи, рішення (12+)
Це перевірки, які я запускаю в продакшені приблизно в порядку їх корисності. Кожне завдання включає:
команду, типовий вивід і рішення, яке потрібно прийняти.
Завдання 1: Підтвердіть місце на диску на відповідному монтуванні
cr0x@server:~$ df -hT /var/www/html
Filesystem Type Size Used Avail Use% Mounted on
/dev/vda1 ext4 40G 39G 520M 99% /
Значення: 99% зайнято. Оновлення потребують простору для завантажень, екстракції і іноді дублювання файлів.
Рішення: Виправте місце на диску перед тим, як чіпати права. Почистіть логи, старі бекапи, кеші або розширте файлову систему. Якщо ви зараз «chmod» — ви просто поліруєте тону щогли.
Завдання 2: Перевірте вичерпання інодів (так, таке буває)
cr0x@server:~$ df -i /var/www/html
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/vda1 2621440 2621400 40 100% /
Значення: У вас закінчилися іноди. Створення ще одного файлу завершиться помилкою «No space left on device», навіть якщо байтів ще достатньо.
Рішення: Знайдіть директорії з величезною кількістю файлів (папки кешу, сесій) і очистіть їх. Не перезавантажуйтеся в надії, що це видалить файли.
Завдання 3: Визначте користувача виконання PHP (той, хто фактично записує)
cr0x@server:~$ ps -eo user,comm,args | egrep 'php-fpm|apache2|httpd' | head
www-data php-fpm8.2 php-fpm: pool www
www-data php-fpm8.2 php-fpm: pool www
root nginx nginx: master process /usr/sbin/nginx -g daemon on;
Значення: Робітники PHP-FPM працюють як www-data. Nginx master — root, але робітники проксірують до PHP-FPM.
Рішення: Права запису мають працювати для www-data, а не для вашого SSH-користувача. Якщо ви «виправите» власність для себе — оновлення все одно впадуть.
Завдання 4: Швидко перевірте власність кореня WordPress та wp-content
cr0x@server:~$ ls -ld /var/www/html /var/www/html/wp-content
drwxr-xr-x 5 root root 4096 Dec 27 08:10 /var/www/html
drwxr-xr-x 10 root root 4096 Dec 27 08:10 /var/www/html/wp-content
Значення: Root володіє всім; група не має права запису; веб-користувач не може писати. Оновлення плагінів/тем зазнають невдачі.
Рішення: Визначте модель власності (див. далі). Для більшості односерверних установок: зробіть www-data власником писаних шляхів або встановіть групову власність і використовуйте спільну групу.
Завдання 5: Підтвердіть точний шлях збою в логах
cr0x@server:~$ sudo tail -n 50 /var/log/php8.2-fpm.log
[27-Dec-2025 08:12:19] WARNING: [pool www] child 22190 said into stderr: "PHP Warning: copy(/var/www/html/wp-content/plugins/akismet/.htaccess): failed to open stream: Permission denied in /var/www/html/wp-admin/includes/class-wp-filesystem-direct.php on line 309"
Значення: Це не розмите «update failed». Це конкретний шлях і функція. ОС відмовила у записі.
Рішення: Виправте права/власність для цього піддерева директорій. Якщо шлях під wp-content, це ваша зона для запису.
Завдання 6: Перевірте права на обхід директорій за допомогою namei
cr0x@server:~$ namei -l /var/www/html/wp-content/plugins
f: /var/www/html/wp-content/plugins
drwxr-xr-x root root /
drwxr-xr-x root root var
drwxr-xr-x root root www
drwxr-xr-x root root html
drwxr-xr-x root root wp-content
drwxr-xr-x root root plugins
Значення: Навіть якщо plugins виглядає нормально, ви повинні мати біт виконання (x) на кожній директорії в шляху, щоб пройти її.
Рішення: Якщо будь-яка батьківська директорія не має x для веб-користувача (або групи), виправте це. «Але wp-content записуваний» не допоможе, якщо /var/www блокує обхід.
Завдання 7: Перевірте ACL, якщо права виглядають «правильними», але все одно падає
cr0x@server:~$ getfacl -p /var/www/html/wp-content | sed -n '1,20p'
# file: /var/www/html/wp-content
# owner: root
# group: root
user::rwx
group::r-x
other::r-x
Значення: Тут немає записів ACL; це класичні бітові права. Якщо ви бачите ACL, що забороняє доступ — це може перекрити ваші очікування.
Рішення: Якщо ACL існують і неправильні, виправте їх свідомо. Не видаляйте ACL навмання, якщо не розумієте, чому вони були додані.
Завдання 8: Переконайтеся, що монтування не встановлене в режимі лише для читання
cr0x@server:~$ mount | grep ' / '
/dev/vda1 on / type ext4 (rw,relatime,errors=remount-ro)
Значення: Змонтувано для читання-запису зараз, але errors=remount-ro означає, що помилка файлової системи може переключити його в режим лише для читання.
Рішення: Якщо ви бачите (ro,...), зупиніться. Виправте основну проблему диска/файлової системи спочатку (dmesg, fsck, стан сховища). Оновлення не можуть продовжуватися.
Завдання 9: Перевірте тимчасову директорію PHP і вільне місце в ній
cr0x@server:~$ php -i | egrep 'upload_tmp_dir|sys_temp_dir|temporary' | head
upload_tmp_dir => no value => no value
sys_temp_dir => no value => no value
cr0x@server:~$ df -hT /tmp
Filesystem Type Size Used Avail Use% Mounted on
tmpfs tmpfs 1.0G 980M 44M 96% /tmp
Значення: PHP за замовчуванням використовує системний тимчасовий каталог (часто /tmp). Якщо /tmp маленький або заповнений — розпаковка/завантаження впаде.
Рішення: Звільніть місце в /tmp або вкажіть окрему тимчасову директорію на диску з правильними правами для PHP-FPM.
Завдання 10: Підтвердіть наявність ZipArchive
cr0x@server:~$ php -m | grep -i zip
zip
Значення: Підтримка zip завантажена. Якщо відсутня — WordPress може перейти на повільніші методи або впасти в залежності від середовища.
Рішення: Якщо немає, встановіть/увімкніть розширення PHP zip і перезапустіть PHP-FPM. Помилки розпаковки часто пов’язані з відсутніми залежностями, а не з тим, що WordPress “зламаний”.
Завдання 11: Перевірте файл режиму обслуговування
cr0x@server:~$ ls -la /var/www/html/.maintenance
-rw-r--r-- 1 root root 58 Dec 27 08:12 /var/www/html/.maintenance
Значення: WordPress вважає, що оновлення триває. Якщо оновлення зазнало краху, це може залишатися і блокувати сайт.
Рішення: Видаліть його лише після підтвердження, що жоден процес оновлення не працює. Потім повторіть оновлення.
Завдання 12: Використайте WP-CLI, щоб відтворити помилку з чіткішим виводом
cr0x@server:~$ cd /var/www/html
cr0x@server:~$ sudo -u www-data wp plugin update --all
Downloading update from https://downloads.wordpress.org/plugin/example.1.2.3.zip...
Unpacking the update...
Error: Could not create directory '/var/www/html/wp-content/upgrade'.
Значення: Помилка відтворюється і вказує на робочу директорію upgrade.
Рішення: Переконайтеся, що wp-content записуваний і дозволено створювати wp-content/upgrade.
Завдання 13: Перевірте власність і права на конкретній директорії, що падає
cr0x@server:~$ ls -ld /var/www/html/wp-content /var/www/html/wp-content/upgrade
drwxr-xr-x 10 root root 4096 Dec 27 08:10 /var/www/html/wp-content
ls: cannot access '/var/www/html/wp-content/upgrade': No such file or directory
Значення: Неможливо створити upgrade, бо wp-content не записувана PHP.
Рішення: Виправте власність/групові права для wp-content (не обов’язково для всього дерева WordPress).
Завдання 14: Помилка «permission denied» через атрибут immutable
cr0x@server:~$ lsattr -d /var/www/html/wp-content
-------------e---- /var/www/html/wp-content
Значення: Тут немає прапорця immutable. Якщо ви бачите i, файл/директорія немодифіковані і їх неможливо змінити навіть root.
Рішення: Якщо immutable встановлено, зніміть його цілеспрямовано для постраждалих шляхів. Зазвичай це було зроблено як «закріплення» або помилковий скрипт бекапу.
Виправлення прав і власності файлів без шкоди безпеці
Потрібна модель. «Просто зробити записуваним» — це не модель. Модель визначає, хто має право записувати які директорії і чому.
Три розумні моделі власності
Модель A: Власник записуваного контенту — веб-користувач (поширено, просто, але ризиковіше)
У цій моделі www-data (або той користувач, під яким виконується PHP) володіє wp-content і може записувати завантаження та оновлення плагінів/тем.
Файли ядра можуть залишатися у власності root, щоб зменшити ризик підміни коду ядра.
Коли використовувати: один сервер, невелика команда, очікуються оновлення через адмінку, ви готові прийняти компроміс безпеки.
Коли уникати: вимоги відповідності, хостинг з багатьма сайтами, або якщо ви хочете незмінність коду.
Модель B: Спільна групова власність + setgid для директорій (мій стандарт для команд)
Створіть групу (наприклад wp), додайте до неї і користувача деплою, і веб-користувача, встановіть групову власність на зони запису.
Потім використайте setgid на директоріях, щоб нові файли успадковували групу.
Коли використовувати: ви деплоїте через SSH/CI і хочете, щоб WordPress все ще міг писати в wp-content.
Модель C: Веб-користувач нічого не записує; деплой і оновлення тільки через CI (найбезпечніша, найвимогливіша)
Оновлення WordPress виконуються через CI-пайплайни або в вікна обслуговування. Веб-користувач не може записувати до коду.
Завантаження (медіа) можуть жити на окремому записуваному монті або у сховищі об’єктів.
Коли використовувати: регульовані середовища, великий трафік, кілька інстансів або якщо вас вже «обпікало».
Виберіть одну. Їхнє випадкове змішування — шлях до файлової системи, яка працює лише коли ви залогінені як «правильна» людина у вівторок.
Практичні, безпечні цілі для прав
- Директорії: зазвичай
755(або775, якщо потрібен груповий запис). - Файли: зазвичай
644(або664з груповим записом). - wp-config.php: зазвичай
640або600, залежно від групової стратегії.
WordPress має писати в wp-content (плагіни/теми/мови/завантаження) для оновлень через адмінку.
Він не має потреби писати скрізь інші директорії, і ви не повинні дозволяти це без причини.
Реалізація Моделі B (спільна група) без драм
cr0x@server:~$ sudo groupadd -f wp
cr0x@server:~$ sudo usermod -aG wp www-data
cr0x@server:~$ sudo usermod -aG wp deploy
cr0x@server:~$ sudo chgrp -R wp /var/www/html/wp-content
cr0x@server:~$ sudo find /var/www/html/wp-content -type d -exec chmod 2775 {} \;
cr0x@server:~$ sudo find /var/www/html/wp-content -type f -exec chmod 664 {} \;
Значення: Група — wp. Директорії мають setgid (2 в 2775), тому нові файли зберігають групову власність.
Рішення: Якщо ви побачите нові файли з неправильною групою, ваш umask або конфігурація сервісу зцекомує з вами. Виправте це наступним кроком.
Встановіть передбачуваний umask для PHP-FPM (опційно, але часто необхідно)
Якщо PHP-FPM створює файли з надто суворими правами, оновлення можуть частково успішно пройти, але впасти потім, коли інший процес спробує змінити файли.
Виправлення — забезпечити консистентні права при створенні файлів.
cr0x@server:~$ sudo grep -R "umask" -n /etc/php/8.2/fpm/pool.d
/etc/php/8.2/fpm/pool.d/www.conf:404:;php_admin_value[error_log] = /var/log/php8.2-fpm.log
Значення: Немає явного umask у конфігу пулу (поширено). Режими створення залежать від значень за замовчуванням.
Рішення: Якщо потрібні артефакти з груповим записом, встановіть umask на рівні сервісу або забезпечте це ACL (нижче).
Використовуйте стандартні ACL для збереження групового запису (потужно, але легко забути)
cr0x@server:~$ sudo setfacl -R -m g:wp:rwx /var/www/html/wp-content
cr0x@server:~$ sudo setfacl -R -d -m g:wp:rwx /var/www/html/wp-content
cr0x@server:~$ getfacl -p /var/www/html/wp-content | sed -n '1,25p'
# file: /var/www/html/wp-content
# owner: root
# group: wp
user::rwx
group::r-x
group:wp:rwx
mask::rwx
other::r-x
default:user::rwx
default:group::r-x
default:group:wp:rwx
default:mask::rwx
default:other::r-x
Значення: Будь-які нові файли/директорії під wp-content отримують групу wp з rwx (у межах mask).
Рішення: Якщо ваше середовище дружнє до ACL, це запобігає проблемі «створено як 640, тепер оновлення не може перезаписати». Якщо ваші бекапи або інструменти синхронізації не зберігають ACL — задокументуйте це.
Жарт №1: Якщо ваш план інциденту — «chmod -R 777», вітаю — ви винайшли розподілений генератор витоків безпеки.
Вирішення проблем дискового простору та inode (тихі вбивці)
Оновлення WordPress підступно вимогливі до місця. Процес часто потребує:
місця для завантаження zip, місця для розпакованих файлів, плюс кінцеві встановлені файли.
На практиці бажано мати принаймні кілька сотень мегабайт вільних для дрібних оновлень і більше для великих плагінів/тем.
Швидко знайдіть, що займає місце
cr0x@server:~$ sudo du -xhd1 /var | sort -h
8.0M /var/cache
120M /var/log
4.2G /var/lib
12G /var/www
Значення: /var/www велике. Це можуть бути завантаження, бекапи або кеші.
Рішення: Розгорніть найбільшу директорію. Не видаляйте наосліп. Якщо це завантаження — думайте про переміщення медіа на окремий диск або очищення старих бекапів.
Знайдіть inode-важкі директорії (проблема «багато дрібних файлів»)
cr0x@server:~$ sudo find /var/www/html/wp-content -xdev -type f | wc -l
412356
Значення: Сотні тисяч файлів. Плагіни кешу можуть це робити. Кожен файл споживає інод.
Рішення: Визначте кеш-директорії і налаштуйте кеш на меншу кількість файлів або інше сховище. Якщо це кеш — його можна очистити.
Знайдіть типові «пожирачі місця» WordPress
cr0x@server:~$ sudo du -sh /var/www/html/wp-content/* | sort -h | tail -n 10
120M /var/www/html/wp-content/languages
1.3G /var/www/html/wp-content/plugins
3.8G /var/www/html/wp-content/uploads
9.1G /var/www/html/wp-content/cache
Значення: Кеш величезний. Його зазвичай безпечно очищати під час інциденту (з наслідками по продуктивності).
Рішення: Очистіть кеш, щоб звільнити місце, а потім виправте корінь проблеми (конфіг кешу, ротація логів, відвантаження завантажень, розмір диска).
Безпечно очистити директорію кешу (приклад)
cr0x@server:~$ sudo systemctl stop php8.2-fpm
cr0x@server:~$ sudo rm -rf /var/www/html/wp-content/cache/*
cr0x@server:~$ sudo systemctl start php8.2-fpm
Значення: Ви уникаєте конкурентних записів під час очищення. Не завжди потрібно, але зменшує крайові випадки.
Рішення: Якщо зупинка PHP-FPM надто руйнівна — видаляйте обережніше або очистіть через інтерфейс плагіна. Але в екстреному випадку місце важливіше за кеш.
Тимчасові каталоги, помилки розпаковки та ліміти PHP
Помилки розпаковки часто звинувачують WordPress, бо це те, що ви бачите. Але причина часто в тому, що:
тимчасове сховище заповнене, права неправильні, ZipArchive відсутній або PHP закінчує пам’ять під час розпаковки.
Підтвердіть, куди PHP пише тимчасові файли
cr0x@server:~$ php -r 'echo sys_get_temp_dir().PHP_EOL;'
/tmp
Значення: PHP використовує /tmp.
Рішення: Переконайтеся, що /tmp записуваний для користувача PHP-FPM і має місце. Якщо /tmp — tmpfs і часто заповнюється, перемістіть тимчасове в інше місце.
Встановіть окрему тимчасову директорію для оновлень WordPress
Практичний підхід: створіть /var/tmp/wp-tmp, зробіть її власністю PHP-користувача і вкажіть WordPress на неї через WP_TEMP_DIR.
cr0x@server:~$ sudo install -d -o www-data -g www-data -m 1770 /var/tmp/wp-tmp
cr0x@server:~$ sudo grep -n "WP_TEMP_DIR" /var/www/html/wp-config.php || true
cr0x@server:~$ sudo sh -c "printf '\ndefine(\"WP_TEMP_DIR\", \"/var/tmp/wp-tmp\");\n' >> /var/www/html/wp-config.php"
cr0x@server:~$ sudo tail -n 3 /var/www/html/wp-config.php
define("WP_TEMP_DIR", "/var/tmp/wp-tmp");
Значення: Тепер WordPress використовує контрольовану тимчасову директорію. 1770 дає «sticky-ish» поведінку через розділення груп; налаштуйте під вашу модель.
Рішення: Якщо декілька пулів/користувачів використовують ту саму директорію, розділіть їх. Спільна тимчасова папка швидко стає болотом прав.
Перевірте ліміт пам’яті PHP і максимальний час виконання (поширено для великих оновлень)
cr0x@server:~$ php -i | egrep 'memory_limit|max_execution_time' | head -n 2
memory_limit => 128M => 128M
max_execution_time => 30 => 30
Значення: 128MB і 30s підходять для малих сайтів, але можуть впасти на великих оновленнях, повільних дисках або під навантаженням.
Рішення: Якщо оновлення таймаутять, підвищте ці параметри для адмін/оновлень або виконуйте оновлення за допомогою WP-CLI в контрольованому шеллі (все одно використовує PHP, але менше залежить від браузерних таймаутів).
Apache, Nginx, PHP-FPM: моделі користувачів, що змінюють усе
Права — це не «річ WordPress». Це питання «який Unix-користувач записує файли». Різні стекові рішення обирають різних користувачів.
Apache з mod_php
PHP виконується всередині воркерів Apache. Користувач Apache (часто www-data або apache) є тим, хто записує файли.
Якщо Apache записує як www-data, а ваші деплои пишуть як deploy з umask 077, ви отримаєте змішану власність і майбутні збої.
Nginx + PHP-FPM
Nginx віддає статичні файли і переадресовує PHP до PHP-FPM. Писати файли буде користувач пулу PHP-FPM, а не Nginx.
Це добре: PHP виконується під більш контрольованою ідентичністю. Також це означає, що «Nginx має доступ» — не має значення, якщо PHP-FPM не має.
Кілька сайтів на одному хості
Якщо ви запускаєте кілька WordPress сайтів під одним www-data, ви створюєте спільну долю ризику.
Компрометація в одному сайті може записати в директорії іншого, якщо права дозволяють.
Якщо ви серйозно ставитесь до безпеки — використовуйте окремі користувачі/пули і ізолюйте на рівні файлової системи.
Одна операційна цитата (перефразована ідея)
«Надія — не стратегія.» — перефразована ідея, яку часто приписують лідерам інженерії та операцій.
Три корпоративні міні-історії з реальних шаблонів
Міні-історія 1: Інцидент через неправильне припущення
Середня компанія вела WordPress для маркетингу, але система жила на «серйозній» продакшн VM з іншими сервісами.
Їхнє внутрішнє припущення було простим: «якщо сайт працює, оновлення можна робити будь-коли».
Це припущення і привело їх на сторінку інциденту.
Маркетингова команда клікнула оновлення плагіна в робочий час. Оновлення впало, залишивши .maintenance.
Сайт почав повертати відповіді у режимі обслуговування. Продажі підняли тривогу, бо цільова сторінка кампанії була недоступна.
Перший респондент зробив те, що багато хто робить під тиском: перезапустив сервіси. Це не допомогло. Просто ускладнило кореляцію логів.
Справжня причина була нудною: тиск на диск. Не настільки великий, щоб спрацював мониторинг, але достатній, щоб екстракція zip в /tmp провалилася.
/tmp був tmpfs і забитий через несуміжні процеси.
WordPress увів сайт у maintenance, спробував записати файли оновлення, впав і не видалив сліди.
Виправлення було буденним: звільнити місце, перемістити тимчасове в диск, видалити .maintenance, повторити оновлення через WP-CLI.
Урок не в тому, щоб «не оновлювати в робочий час» (хоча, можливо), а в тому, що оновлення — це операції зі сховищем; ставтеся до них як до деплоїв.
Міні-історія 2: Оптимізація, що вдарила по вам
Інша організація захотіла «швидше». Хтось вирішив примонтувати весь каталог WordPress на мережеву файлову систему,
щоб кілька аплікаційних серверів могли ділити код та завантаження. Ідея звучала добре.
Файлова система не була на зустрічі.
Оновлення почали падати з дивними помилками розпаковки та неповними директоріями плагінів.
Іноді адмінка казала успіх, але наступний запит впадав з відсутніми PHP-файлами.
Інженери ганялися за правами, версіями PHP, навіть за теоріями «можливо WordPress пошкоджений».
Справжня проблема — семантика та затримки: поведінка мережевої файлової системи щодо блокувань та атомарних перейменувань не відповідала очікуванням процесу оновлення.
Процес оновлення покладається на швидкі та надійні файлові операції, особливо при заміні директорій.
Під навантаженням операції відставали, таймаути спрацьовували або вузли бачили різні списки директорій.
Вирішили проблему розділивши обов’язки: код деплоїться як незмінні артефакти на кожен вузол, завантаження зберігаються окремо в сховищі, яке підходить під свої можливості.
Оновлення перенесли в CI, а не адмінку. Продуктивність покращилась, а процес оновлення перестав грати в рулетку.
Міні-історія 3: Нудна, але правильна практика, що врятувала день
Велике підприємство дотримувалося старомодної практики: кожне оновлення WordPress виконувалося через WP-CLI у вікно обслуговування,
перед цим робився знімок файлової системи і після — швидка перевірка цілісності.
Ніхто не хвалився цим. Це був просто runbook.
Одного дня оновлення плагіна внесло фатальну помилку через відсутній PHP-залежність.
Адмінка була недоступна з фатальною помилкою, але сайт ще віддавав кешовані сторінки — поки кеш не скінчився.
Інцидент міг перерости в повний простій.
Оскільки оновлення виконувались через WP-CLI з логами, вони знали, який плагін змінився і коли.
Оскільки була знімок файлової системи, відкат зайняв секунди.
Вони відкотили директорію плагіна, відновили сервіс і потім протестували оновлення у стейджингу з потрібним PHP-модулем.
Нічого героїчного не сталось. Ось в чому суть. Нудні практики — це те, що рятує в інциденті.
Поширені помилки: симптом → корінь проблеми → виправлення
Це шаблони, які я бачу повторно. Симптом — що ви помітите. Корінь — що насправді зламалося. Виправлення — конкретне.
1) «Could not create directory» під час оновлення плагіна/теми
- Симптом: Оновлення падає; WP-CLI показує, що не може створити
wp-content/upgradeабо директорію плагіна. - Корінь:
wp-contentне записувана PHP-користувачем; або батьківські директорії не мають біта виконання. - Виправлення: Налаштуйте власність/групу та права для
wp-contentлише; перевірте зnamei -lі повторіть оновлення.
2) «Update failed» без деталей; логи показують «No space left on device»
- Симптом: Інтерфейс загальний; логи PHP показують ENOSPC при копіюванні/розпаковці.
- Корінь: Диск заповнений або іноди вичерпані; інколи
/tmpзаповнений на tmpfs. - Виправлення: Перевірте
df -hT,df -iтаdf -h /tmp. Звільніть місце або перемістіть тимчасове. Потім повторіть.
3) Сайт застряг у режимі обслуговування після невдалого оновлення
- Симптом: Фронтенд постійно показує повідомлення про обслуговування.
- Корінь:
.maintenanceзалишився через краш/таймаут/помилку прав. - Виправлення: Переконайтесь, що жоден процес оновлення не працює; видаліть
.maintenance; виправте первинну помилку; повторіть оновлення через WP-CLI.
4) Оновлення працюють як root через SSH, але падають в адмінці
- Симптом: Ручні зміни файлів успішні; UI-оновлення падають.
- Корінь: Неправильне припущення про те, хто записує файли; PHP працює як непривілейований користувач, не як root.
- Виправлення: Узгодьте права файлової системи з PHP-користувачем (або оберіть модель спільної групи). Перестаньте «виправляти» як root і вважати проблему вирішеною.
5) Деякі файли належать deploy, інші — www-data; оновлення потім випадково падають
- Симптом: Сьогодні оновлення пройшло; через тиждень впало. Власність несумісна в дереві.
- Корінь: Змішані шляхи оновлення: іноді через адмінку (www-data), іноді через SSH/CI (deploy) з різним umask.
- Виправлення: Виберіть модель. Використайте спільну групу + setgid + (опційно) дефолтні ACL. Забезпечте однаковий umask в процесі деплою.
6) «PCLZIP_ERR» або помилка розпаковки на великих плагінах/темах
- Симптом: Помилки розпаковки, неповні директорії або таймаути.
- Корінь: Нестача тимчасового місця, відсутній ZipArchive, ліміти пам’яті/часу або повільне сховище.
- Виправлення: Переконайтесь, що розширення zip встановлене, забезпечте тимчасовий простір і налаштуйте ліміти PHP. Віддавайте перевагу WP-CLI для контрольованих оновлень.
7) Оновлення падають лише на одному вузлі в кластері
- Симптом: Один вузол повідомляє про відсутні файли плагіна або різні версії.
- Корінь: Локальні диски розійшлися; семантика спільного сховища не підходить; деплои не координуються.
- Виправлення: Припиніть оновлення через адмінку в кластері. Використовуйте CI-деплой і тримайте код незмінним на всіх вузлах.
Жарт №2: Процес оновлення схожий на кота — якщо ви не контролюєте, куди він пише, він вибере найменш зручне місце.
Чеклісти / покроковий план
Чекліст інциденту: повернути сайт в роботу і завершити оновлення
- Перевірте диск і іноди:
df -hT,df -iіdf -h /tmp. Спочатку звільніть місце. - Перевірте режим обслуговування: видаліть
.maintenanceлише після підтвердження, що оновлення не виконується. - Читайте логи: логи PHP-FPM і вебсерверу для точного шляху збою.
- Підтвердіть PHP-користувача: визначте, чи пише
www-data,apacheабо пуловий користувач. - Виправте записуваність wp-content: власність/група/ACL відповідно до обраної моделі. Перевірте з
namei -l. - Повторіть через WP-CLI: він дає чіткіший вивід, скриптується та менше залежить від таймаутів браузера.
- Перевірте: завантаження головної сторінки, адмінки та кількох сторінок, що використовують плагіни (форми, оформлення замовлення тощо).
Чекліст жорсткості: запобігти повторним інцидентам
- Виберіть модель власності: веб-володіння, спільна група або CI-only. Документуйте і застосуйте.
- Відокремте області для запису: тримайте
wp-content/uploadsзаписуваним; розгляньте можливість робити корінь ядра тільки для читання. - Встановіть передбачувані права: setgid для директорій і/або дефолтні ACL для групового запису.
- Розмір тимчасового сховища: виділіть окрему тимчасову директорію, якщо
/tmpзанадто малий або спільний. - Моніторьте правильні речі: байти диска, іноди і використання
/tmp. Алерти мають спрацьовувати до 99%. - Віддавайте перевагу WP-CLI для продакшн-оновлень: захоплюйте логи; проводьте у вікні обслуговування; автоматизуйте кроки відкату.
Покроково: безпечний runbook «виправити права» (модель спільної групи)
Це план, який зазвичай працює в організаціях, де і люди, і автоматика торкаються файлової системи.
- Створіть групу
wp; додайте іwww-data, і вашого deploy-користувача. - Встановіть групову власність на
wp-contentі встановіть setgid на її директоріях. - Встановіть режими файлів і директорій, що дозволяють груповий запис, але не світовий.
- Опційно встановіть дефолтні ACL, щоб зберегти консистентність навіть при різних umask.
- Перевірте, створивши тестовий файл як
www-dataі як deploy-користувач, підтвердіть групу і права.
cr0x@server:~$ sudo -u www-data bash -lc 'touch /var/www/html/wp-content/.permtest && ls -l /var/www/html/wp-content/.permtest'
-rw-rw-r-- 1 www-data wp 0 Dec 27 08:40 /var/www/html/wp-content/.permtest
cr0x@server:~$ sudo -u deploy bash -lc 'echo ok >> /var/www/html/wp-content/.permtest && tail -n 1 /var/www/html/wp-content/.permtest'
ok
Значення: Обидві ідентичності можуть писати. Власність і група узгоджені.
Рішення: Якщо будь-який з користувачів не може писати — виправте членство в групі, права директорії або маску ACL перед повторним оновленням.
FAQ
1) Чи колись можна використовувати chmod -R 777 для виправлення оновлень WordPress?
Ні. Воно «працює», дозволяючи кожному записувати будь-що, але це провал безпеки, замаскований під прогрес.
Виправляйте власність і групові права замість цього, обмежуючися wp-content, якщо потрібно.
2) Чи потрібно зробити весь корінь WordPress записуваним?
Зазвичай ні. Для оновлень плагінів/тем через UI достатньо, щоб був записуваний wp-content.
Оновлення ядра можуть вимагати запису в корінь і каталоги ядра; багато команд уникають цього, виконуючи оновлення через CI/WP-CLI з контрольованими правами.
3) Чому WordPress запитує FTP-дані під час оновлення?
WordPress робить це, коли вважає, що не може писати напряму в файлову систему. Він намагається альтернативні методи.
На сучасних Linux-серверах зазвичай бажано дозволити прямі записи в wp-content (якщо ви дозволяєте оновлення через UI) і налаштувати права, а не використовувати FTP.
4) Оновлення падає з «No such file or directory» під час перейменування. Що сталося?
Часто це часткове оновлення: розпаковка пройшла, а потім перейменування або очищення впали через права або відсутній запис на батьківській директорії.
Перевірте шлях збою в логах, підтвердіть права на директорію, де відбувається перейменування, і повторіть через WP-CLI після очищення.
5) Який найнадійніший спосіб оновлень у мультивузловому середовищі?
Не оновлюйте через адмінку. Використовуйте пайплайн деплою, щоб кожен вузол отримував однаковий версійований артефакт.
Тримайте код незмінним; зберігайте завантаження у спеціальному спільному сховищі, якщо потрібно.
6) Якщо wp-content записувана, чому оновлення все одно падають?
Бо шлях до неї може бути непропрохідним (відсутній біт x), /tmp може бути заповнений, іноди закінчилися, ACL можуть забороняти доступ,
або PHP працює під іншим користувачем, ніж ви думаєте. Використайте namei -l, df -i і логи.
7) Чи нормально, що www-data володіє директоріями плагінів?
Це розповсюджено і працює. Це також компроміс безпеки: якщо WordPress або плагін скомпрометовано, записувані директорії плагінів — зручний механізм для збереження доступу.
За можливості віддавайте перевагу спільній груповій моделі або оновленням через CI для продакшну.
8) Як безпечно виправити «застряг у режимі обслуговування»?
Підтвердіть, що жоден процес оновлення не працює (перевірте процеси і останні логи), потім видаліть .maintenance у корені WordPress.
Якщо первинна причина — права або місце на диску, виправте її спочатку, інакше це повториться при наступній спробі.
9) Чому оновлення падають після відновлення з бекапу?
Відновлення часто змінюють власність, права або втрачають ACL. Раптом PHP не може писати до wp-content.
Після будь-якого відновлення виконайте перевірку прав/власності і виправте її під вашу модель.
10) Який мінімальний моніторинг, щоб уникнути несподіваних проблем при оновленнях?
Повідомляйте про використання диска і інодів на монтуванні з WordPress, і окремо по /tmp, якщо він tmpfs.
Також відстежуйте помилки PHP-FPM з «Permission denied» і «No space left on device», щоб бачити патерни до наступного вікна оновлень.
Наступні кроки, які ви можете зробити вже сьогодні
Якщо оновлення WordPress впало, не сприймайте це як містичну проблему WordPress. Майже завжди це одна з трьох речей:
процес не може записати, диск заповнений (або закінчилися іноди), або тимчасові/розпаковувальні ресурси обмежені.
- Запустіть швидку діагностику: диск/іноди,
/tmp, PHP-користувач, логи. - Виправте мінімальну поверхню: зробіть записуваним
wp-contentдля правильної ідентичності; уникайте робити весь дерево записуваним. - Стандартизуйтесь: веб-володіння, спільна група або CI-only. Змішана власність — майбутні аварії в маскараді.
- Операціоналізуйте оновлення: WP-CLI у вікнах обслуговування, захоплення логів і план відкату без паніки.