Install
This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.
Install straight from GitHub with WP-CLI:
wp plugin install https://github.com/sirdadfar/wp-maintenance-toolkit/archive/refs/heads/main.zipAn open-source WordPress plugin for the monthly-maintenance service a freelancer sells to small businesses: database backups, uptime monitoring with email alerts, and a monthly health report.
- Version:
1.1.0 - Tested up to WordPress 6.7 (requires 5.8+)
- Requires PHP 7.4+
- License: GPL-2.0-or-later
- No
shell_exec, no systemmysqldump, no CDN, no build step
This plugin deliberately does a few things well instead of everything badly. Destructive operations stay in human hands.
Table of contents
- Features
- Admin interface
- Screenshots
- Installation
- Settings
- WP-CLI
- Security notes
- Cron reliability
- Tests and development
- What this plugin deliberately does not do
- Architecture
- License
Features
1. Database backups
- Pure-PHP,
$wpdb-based dumps read in chunks, so memory usage stays bounded on large databases. - Chunks are paged by the table's primary key (
WHERE pk > last ORDER BY pk LIMIT n), not byOFFSET. A row inserted or deleted between two chunks can therefore never duplicate or skip a row in the dump. Tables without a single-column primary key (rare) fall back toLIMIT/OFFSET. - gzip-compressed SQL with
CREATE TABLE,INSERT,DROP TABLE IF EXISTSand a standard/*!40101 ... */header. - Tables are ordered parent-first using
information_schemaforeign keys; the dump setsFOREIGN_KEY_CHECKS=0and restores it at the end. - Stored in
wp-content/maintenance-backups/asdb-YYYYmmdd-HHMMSS-{random}.sql.gz. - The directory gets an Apache deny
.htaccessand anindex.phpguard. Nginx ignores.htaccess, so the dashboard warns on Nginx andSECURITY.mdships adenysnippet. - Only one dump runs at a time (a 15-minute transient lock), so cron and the manual button cannot race on retention or fill the disk.
- Retention keeps the newest N files (default 7). Only filenames matching this plugin's strict pattern and containing a valid timestamp are eligible for deletion. At least one file is always kept, even with
keep=0. - Manual "Backup now" button (nonce +
manage_options) plus daily/weekly WP-Cron.
2. Uptime monitoring
- Every 5 minutes,
wp_remote_getpings the site's home URL and optionally one extra URL, with a 5-second timeout. - A single failure never opens an incident: two consecutive failures are required, which filters out shared-host hiccups and one-off network timeouts.
- On confirmed downtime, one email per incident is sent and the start is logged. Further failed checks do not duplicate the incident or the email.
- On recovery, the end time/duration are logged and a recovery email is sent.
- The incident log is stored in an option and pruned to the last 100 entries.
3. Monthly health report
On the first cron run after a new month starts, an HTML email is sent for the previous calendar month:
- uptime percentage, incident count and total downtime
- last successful backup, backup count and total size
- pending plugin/theme/core updates
- WordPress and PHP versions
- free disk space
- the last 10 PHP error-log lines (only from the absolute path configured in settings)
A manual "Send report" button and wp maint report --send are always available. The report is rendered by the pure function ReportBuilder::build(array $data): array.
4. WP-CLI
wp maint backup --keep=7
wp maint status
wp maint report # preview
wp maint report --send # send
wp maint incidents --limit=20
Admin interface
The plugin ships a designed admin experience rather than a default settings form:
- Persian-first, RTL layout in Vazirmatn with a deliberate Arabic-script fallback stack.
- Vazirmatn (variable) is bundled in
assets/fontsunder SIL OFL 1.1 — no Google Fonts or CDN request, so the admin stays fast on filtered networks. - Light-first tokens inside
wp-admin; the dark glass palette applies automatically underprefers-color-scheme: dark. - Glass stat tiles, hairline separators and a single gold accent — no CSS framework, no build step.
- Dense tables use sticky headers, row hover and
data-labelcards below 760px. - Visible
:focus-visiblerings, ≥44px targets,aria-live/role="alert"notices, and labelled fields that keep their hints. - Styles live in
assets/css/admin.css, scoped to.maintkit-wrap/.maintkit-noticeso nothing leaks into other admin screens. - Full token, component and accessibility notes:
docs/design-system.md.
Screenshots
No image files are committed, so the project never ships fabricated or stale screenshots. Suggested shots and a generation guide live in docs/screenshots/README.md:
| File | Subject |
|---|---|
dashboard.png |
Status dashboard with backup, uptime and report cards |
settings.png |
Settings page |
report-email.png |
Monthly report in a mail client |
cli.png |
wp maint status / wp maint incidents output |
Add real captures and update this table; do not create placeholder image binaries.
Installation
Option 1: zip upload
- Zip the plugin folder so the archive contains
wp-maintenance-toolkit/wp-maintenance-toolkit.php(not a nested folder). - In WordPress: Plugins → Add New → Upload Plugin.
- Upload, install and activate.
- Open Maintenance Toolkit in the admin menu and save the settings.
Option 2: FTP / SFTP
- Upload the
wp-maintenance-toolkitdirectory towp-content/plugins/. - Verify
wp-maintenance-toolkit.phpis directly inside that directory. - Activate the plugin from the Plugins screen.
Composer (recommended for development)
cd wp-content/plugins/wp-maintenance-toolkit
COMPOSER_ALLOW_SUPERUSER=1 composer install --no-dev --optimize-autoloader
If vendor/autoload.php is missing, a small built-in PSR-4 fallback keeps the plugin working and an admin notice asks you to run Composer. Production installs should use the optimized autoloader.
Backup directory permissions
The PHP user must be able to write to wp-content/maintenance-backups/. The plugin creates the directory, .htaccess and index.php automatically. On Nginx, .htaccess is ignored; add:
location ^~ /wp-content/maintenance-backups/ {
deny all;
return 403;
}
Settings
Admin → Maintenance Toolkit → Settings
| Setting | Default | Notes |
|---|---|---|
| Scheduled backups | on | Enable/disable automatic dumps |
| Backup schedule | weekly | daily or weekly |
| Backups to keep | 7 | 1–60; only plugin-owned files are deleted |
| Automatic monitoring | on | Check every 5 minutes |
| Extra URL | empty | Optional second probe (e.g. a health endpoint) |
| Alert email | admin email | Empty falls back to admin_email |
| Automatic report | on | First cron run of each month |
| PHP error log path | empty | Local absolute path; only the last 256 KB / 10 lines are read |
Relative paths, stream wrappers (http://, ftp://, …) and non-absolute paths are rejected. An unreadable path produces an empty log section, not an error.
WP-CLI
wp maint backup --keep=10 # backup and keep 10 files
wp maint status # table of backup/monitor/report status
wp maint report # preview last month's report
wp maint report --send # send it
wp maint incidents --limit=20 # recent incidents
wp maint status shows WordPress/PHP versions, backup directory and usage, latest backup, monitor state, 30-day uptime, report recipient and log path.
Security notes
- Every admin page checks
manage_options; every state-changing action uses a nonce and goes throughadmin-post.php. - Output is escaped with
esc_html,esc_attr,esc_url; numeric input is cast/absint. - Every PHP file starts with
defined( 'ABSPATH' ) || exit;. - Backups are protected by
.htaccess+index.php; Nginx needs an explicitdeny all. - Retention validates filenames against a strict regex and a real calendar timestamp. It never deletes directories, symlinks or files it did not create.
- Report HTML escapes site names and every log line, so an injected `