User Guide

Exact page and CLI steps for operating ZePanel.

# ZePanel End User Guide

This guide explains exactly what to do in the web panel and with each CLI command.
Email hosting and DNS management are intentionally not included.

## Before You Start

1. Point your domain's DNS records to this server outside ZePanel.
2. Install the server stack and initialize ZePanel:

```bash
sudo /opt/zepanel/scripts/install-production.sh panel.example.com
```

3. Save the admin password printed by `initialize`.
4. Open the panel at `https://panel.example.com`.
5. Log in as `admin` with the generated password.
6. Open `/health` and fix any failed checks before creating production sites.

## Web Panel Pages

### Login: `/login`

Use this page to start an admin session.

1. Enter the admin username.
2. Enter the admin password.
3. Click `Login`.

Expected result: the dashboard opens.

If login fails:

1. Confirm `/etc/zepanel/zepanel.conf` contains `[panel] admin_user` and either `admin_password_hash` or `admin_password`.
2. Wait 15 minutes if too many failed attempts were made.
3. Check panel logs with `journalctl -u zepanel.service -f`.

### Dashboard: `/`

Use this page as the main navigation hub.

Click:

- `Sites` to create, disable, enable, delete, or repair site permissions.
- `Databases` to create or delete managed databases.
- `CMS Installer` to install, update, or repair WordPress.
- `Backups` to view and restore backups.
- `SSL` to issue or renew certificates.
- `PHP Versions` to change a site's PHP handler.
- `Services` to view job status and retry or cancel jobs.
- `Health Check` to validate system readiness.
- `Audit Log` to review admin actions.
- `Settings` to change the panel admin password.

### Sites: `/sites`

Use this page to manage Apache sites.

Create a site:

1. Enter `Domain`, for example `example.com`.
2. Enter `Document Root`, for example `/var/www/example.com/public`.
3. Click `Create Site`.
4. Go to `/services` and wait for the `site.create` job to complete.

Disable a site:

1. Find the site in `Managed Sites`.
2. Click `Disable`.
3. Go to `/services` and wait for the `site.disable` job to complete.

Enable a disabled site:

1. Find the disabled site.
2. Click `Enable`.
3. Go to `/services` and wait for the `site.enable` job to complete.

Repair file permissions:

1. Find the site.
2. Click `Repair Permissions`.
3. Go to `/services` and wait for the `site.permissions.repair` job to complete.

Delete a site:

1. Find the site.
2. Type the exact domain into the confirmation box.
3. Check `Remove files` only if you want the document root deleted after backup.
4. Click `Delete`.
5. Go to `/services` and wait for the `site.delete` job to complete.
6. Confirm a backup exists on `/backups`.

Important: deleting a site removes the Apache vhost. If `Remove files` is checked, files are deleted after a site backup is created.

### Databases: `/databases`

Use this page to create and delete managed MariaDB/MySQL databases.

Create a database:

1. Enter `Database`, for example `example_wp`.
2. Enter `User`, for example `example_wp_user`. Leave blank only if you want the username to match the database name.
3. Enter a strong database password.
4. Click `Create Database`.
5. Go to `/services` and wait for the `database.create` job to complete.

Delete a database:

1. Find the database.
2. Type the exact database name into the confirmation box.
3. Click `Delete`.
4. Go to `/services` and wait for the `database.delete` job to complete.
5. Confirm a database backup exists on `/backups`.

Retry a failed database delete:

1. Find the database with status `delete_failed`.
2. Type the exact database name into the confirmation box.
3. Click `Retry Delete`.
4. Go to `/services` and wait for the new delete job to complete.

### CMS Installer: `/cms`

Use this page for WordPress installation and maintenance.

Install WordPress:

1. Create the site first from `/sites`.
2. Select the site from the `Site` list. ZePanel automatically uses that site's saved document root.
3. Leave `Database Mode` set to `Automatic` for the normal install path.
4. Click `Install WordPress`.
5. Go to `/services` and wait for the `cms.wordpress.install` job to complete.
6. Open the domain in a browser and finish WordPress setup.

Automatic database mode creates a new database, database user, and strong random
database password for the WordPress install. The password is stored only in the
queued job payload and written into `wp-config.php`; it is not shown in the UI.
ZePanel also writes Redis object cache constants into `wp-config.php` during
install, using `[redis]` settings or the `ZEPANEL_REDIS_*` environment variables
when present.

Use `Manual` database mode when you want ZePanel to create a database and user
with names you choose:

1. Select `Manual - create database and user entered below`.
2. Enter the database name to create.
3. Enter the database user to create.
4. Enter the database password.
5. Click `Install WordPress`.

Use `Existing` database mode only when you already created the database and user
outside this installer:

1. Select `Existing - use database credentials entered below`.
2. Enter the existing database name.
3. Enter the existing database user.
4. Enter that user's database password.
5. Click `Install WordPress`.

Update WordPress:

1. Enter the domain of an active WordPress site.
2. Click `Update WordPress`.
3. Go to `/services` and wait for the `cms.wordpress.update` job to complete.
4. Review the site in a browser.

Repair WordPress:

1. Enter the domain of an active WordPress site.
2. Click `Repair WordPress`.
3. Go to `/services` and wait for the `cms.wordpress.repair` job to complete.

Clean Redis cache:

1. Select the site whose Redis object cache should be cleaned.
2. Click `Clean Redis Cache`.
2. Go to `/services` and wait for the `cache.redis.clean` job to complete.

Redis cleanup is prefix-based and targets the selected site's generated
`WP_REDIS_PREFIX`. It does not run `FLUSHDB`.

Clean Nginx cache:

1. Click `Clean Nginx Cache`.
2. Go to `/services` and wait for the `cache.nginx.clean` job to complete.

Requirements:

- `wp` or the configured `ZEPANEL_WP_CLI` command must be installed for update and repair.
- `redis-cli` must be installed for Redis cache cleanup.
- `nginx` must be installed and `[nginx] cache_path` or `ZEPANEL_NGINX_CACHE_PATH`
  must point at the intended cache directory for Nginx cache cleanup.
- The site must be active in ZePanel.

### Backups: `/backups`

Use this page to create, review, and restore backups.

Create a site backup:

1. Select the site domain in `Create Site Backup`.
2. Click `Create Site Backup`.
3. Go to `/services` and wait for the `backup.site.create` job to complete.
4. Return to `/backups` and confirm a new `site` backup record appears with status `complete`.

Create a database backup:

1. Select the database name in `Create Database Backup`.
2. Click `Create Database Backup`.
3. Go to `/services` and wait for the `backup.database.create` job to complete.
4. Return to `/backups` and confirm a new `database` backup record appears with status `complete`.

Restore a site backup:

1. Find a backup with `Type` set to `site` and `Status` set to `complete`.
2. Use the backup row's `Restore` action, or copy its `ID`.
3. Select or enter the destination domain.
4. Click `Restore`.
5. Go to `/services` and wait for the `backup.site.restore` job to complete.

Restore a database backup:

1. Find a backup with `Type` set to `database` and `Status` set to `complete`.
2. Use the backup row's `Restore` action, or copy its `ID`.
3. Select or enter the destination database name.
4. Click `Restore`.
5. Go to `/services` and wait for the `backup.database.restore` job to complete.

Important: restore jobs create a pre-restore backup first. Site restores replace the destination document root contents with the backup contents, so files not present in the backup are removed.

### SSL: `/ssl`

Use this page to issue and renew Let's Encrypt certificates. DNS is not managed by ZePanel.

Before issuing SSL:

1. Confirm the domain points to this server.
2. Confirm the site is active in `/sites`.
3. Confirm Apache is reachable on port 80.
4. Configure `[ssl] email` in `/etc/zepanel/zepanel.conf` or set `ZEPANEL_SSL_EMAIL`.

Issue SSL:

1. Select the active site domain.
2. Click `Issue SSL`.
3. Go to `/services` and wait for the `ssl.issue` job to complete.
4. Visit `https://DOMAIN` and confirm the certificate works.

Renew all certificates:

1. Click `Renew All`.
2. Go to `/services` and wait for the `ssl.renew` job to complete.

### PHP Versions: `/php`

Use this page to change the PHP handler for an active site.

1. Select the domain.
2. Select the PHP version.
3. Click `Set PHP Version`.
4. Go to `/services` and wait for the `php.set` job to complete.
5. Test the site.

If the job fails:

1. Open `/health`.
2. Confirm the selected PHP-FPM socket exists for PHP 8.1, 8.2, or 8.3.
3. Confirm Apache config validation passes in the job logs.

### Services: `/services`

Use this page to inspect services and manage jobs.

1. Review Apache, MariaDB, and the versioned PHP-FPM services.
2. Review `Recent Jobs`.
3. Click a job ID to open detailed logs.
4. For queued jobs, click `Cancel` if the job should not run.
5. For failed retryable jobs, click `Retry`.
6. Use the status buttons to show only queued, running, complete, failed, or canceled jobs.
7. Use `Next` and `Previous` when older jobs are available.

Job statuses:

- `queued`: waiting for the worker.
- `running`: claimed by the worker.
- `complete`: finished successfully.
- `failed`: stopped with an error.
- `canceled`: canceled before execution.

If jobs do not move:

1. Check worker status: `systemctl status zepanel-worker.service`.
2. Watch worker logs: `journalctl -u zepanel-worker.service -f`.
3. Run one job manually: `cd /opt/zepanel && sudo ./venv/bin/python cli/zepanel.py work-once`.

### Job Detail: `/jobs/{job_id}`

Use this page to troubleshoot one job.

1. Open `/services`.
2. Click the job ID.
3. Read logs from top to bottom.
4. If the job is queued and should not run, click `Cancel Job`.
5. If the job failed and is retryable, click `Retry Job`.

For non-retryable failed jobs, create a new operation after fixing the root cause.

### Health Check: `/health`

Use this page before production use and after system changes.

1. Open `/health`.
2. Review the passing and failing counts.
3. Fix failed config checks in `/etc/zepanel/zepanel.conf`.
4. Fix failed service checks with `systemctl status SERVICE`.
5. Fix missing PHP socket checks by installing/enabling the relevant PHP 8.1, 8.2, or 8.3 FPM service.
6. Reopen `/health` after changes.

### Audit Log: `/audit`

Use this page to review admin actions.

1. Open `/audit`.
2. Review `Actor`, `Action`, `Target`, and `Message`.
3. Use the job ID in `Message` to cross-check `/services` or `/jobs/{job_id}`.

### Settings: `/settings`

Use this page to change the panel admin password and check production cookie status.

Change the admin password:

1. Enter the current admin password.
2. Enter a new password with at least 12 characters.
3. Enter the new password again.
4. Click `Update Password`.
5. Log out and log back in with the new password.

Production status:

1. Confirm `Secure Cookies` is enabled after HTTPS is configured for the panel.
2. If secure cookies are disabled, run `enable-secure-cookies` or rerun the panel proxy installer after TLS certificate files exist.

## CLI Commands

Run all commands from `/opt/zepanel` unless the command path is absolute.

### `initialize`

Creates `/etc/zepanel/zepanel.conf`, creates the OS service user, and prints the panel admin password.

```bash
sudo ./venv/bin/python cli/zepanel.py initialize
```

After running:

1. Save the printed admin password.
2. Confirm `/etc/zepanel/zepanel.conf` exists.
3. Run `configure-database-admin`.
4. Run `migrate`.
5. Start the panel and worker services.

### `configure-database-admin`

Creates or updates the configured panel database account and grants it the privileges needed to create databases, create users, and grant database access.

```bash
sudo ./venv/bin/python cli/zepanel.py configure-database-admin
```

### Production Install Script

Creates the Python virtual environment, installs Python requirements, installs
the LAMP stack, installs Remi PHP-FPM runtimes for supported PHP versions,
installs WP-CLI when available, applies migrations, installs services,
scheduled backups, and the panel reverse proxy. On AlmaLinux/RHEL-like systems
it also installs Apache SSL support and certbot when `dnf` is available.

```bash
sudo /opt/zepanel/scripts/install-production.sh panel.example.com
```

After running:

1. Open `https://panel.example.com`.
2. If TLS was not added, confirm DNS points at the server, install certbot, and rerun `install-panel-proxy.sh`.
3. Open `/settings` and confirm secure cookies and HTTPS enforcement are enabled.

### `install-lamp`

Installs Apache, MariaDB, Nginx, Redis, ModSecurity, and the supported Remi
PHP-FPM runtimes using the project install helpers.

```bash
sudo ./venv/bin/python cli/zepanel.py install-lamp
```

After running:

1. Check Apache: `systemctl status httpd`.
2. Check MariaDB: `systemctl status mariadb`.
3. Check Nginx: `systemctl status nginx`.
4. Check Redis: `systemctl status redis`.
5. Check PHP-FPM: `systemctl status php81-php-fpm php82-php-fpm php83-php-fpm`.

### `install-nginx`

Installs and starts Nginx.

```bash
sudo ./venv/bin/python cli/zepanel.py install-nginx
```

### `install-redis`

Installs and starts Redis.

```bash
sudo ./venv/bin/python cli/zepanel.py install-redis
```

### Sudo helper

Installs the root-owned sudo helper and sudoers allowlist used by the planned
least-privilege worker model.

```bash
sudo /opt/zepanel/scripts/install-sudo-helper.sh
```

Read `SUDO_DESIGN.md` before changing the worker service user.

### `install-modsecurity`

Installs ModSecurity and CRS packages when available from the OS repositories.

```bash
sudo ./venv/bin/python cli/zepanel.py install-modsecurity
```

### Nginx panel proxy

Writes an Nginx panel proxy and creates the default Nginx cache directory.

```bash
sudo /opt/zepanel/scripts/install-nginx-panel-proxy.sh panel.example.com
```

When Apache is already serving public `80/443`, set a non-conflicting listen
address:

```bash
sudo ZEPANEL_NGINX_LISTEN=127.0.0.1:18081 /opt/zepanel/scripts/install-nginx-panel-proxy.sh panel.example.com
```

### `install-php-fpm`

Installs or repairs the supported Remi PHP-FPM runtimes and ZePanel pool files.

```bash
sudo ./venv/bin/python cli/zepanel.py install-php-fpm
```

After running, open `/health` and confirm the PHP-FPM service and socket checks pass.

### `install-profile PROFILE`

Installs a CMS dependency profile.

```bash
sudo ./venv/bin/python cli/zepanel.py install-profile wordpress
```

Use this before installing WordPress if the server does not already have the needed PHP extensions.

### `migrate`

Creates or updates ZePanel SQLite tables.

```bash
./venv/bin/python cli/zepanel.py migrate
```

Run this after code updates and before starting services.

### `create-site DOMAIN DOCROOT`

Queues a site creation job.

```bash
./venv/bin/python cli/zepanel.py create-site example.com /var/www/example.com/public
```

After running:

1. Copy the printed job ID.
2. Run `work-once` or wait for the worker.
3. Check the job in `/services`.

### `disable-site DOMAIN`

Queues a site disable job.

```bash
./venv/bin/python cli/zepanel.py disable-site example.com
```

Use this when you want Apache to stop serving the site without deleting state.

### `enable-site DOMAIN`

Queues a site enable job.

```bash
./venv/bin/python cli/zepanel.py enable-site example.com
```

Use this only for sites currently marked disabled.

### `delete-site DOMAIN [--remove-docroot]`

Queues a site delete job.

```bash
./venv/bin/python cli/zepanel.py delete-site example.com
```

To delete files after backup:

```bash
./venv/bin/python cli/zepanel.py delete-site example.com --remove-docroot
```

After running:

1. Confirm a site backup appears in `/backups`.
2. Confirm the job completes in `/services`.

### `repair-site-permissions DOMAIN`

Queues a permissions repair job.

```bash
sudo ./venv/bin/python cli/zepanel.py repair-site-permissions example.com
```

Use this after manual file uploads, restores, or WordPress updates.

### `create-db DBNAME`

Queues database and database-user creation. The username matches the database name.

```bash
./venv/bin/python cli/zepanel.py create-db example_wp
```

After running:

1. Save the printed database password.
2. Wait for the job to complete.
3. Use the credentials in the CMS installer or application config.

### `delete-db DBNAME`

Queues a database delete job.

```bash
./venv/bin/python cli/zepanel.py delete-db example_wp
```

After running:

1. Confirm a database backup appears in `/backups`.
2. Confirm the job completes in `/services`.

### `create-db-user USER PASSWORD`

Creates a database user directly.

```bash
sudo ./venv/bin/python cli/zepanel.py create-db-user example_user 'STRONG_PASSWORD'
```

Use this only for manual recovery or special cases. Normal database creation should use `create-db` or the web panel.

### `install-wordpress DOMAIN DOCROOT DBNAME USERNAME`

Queues a WordPress install job and prompts for the database password.

```bash
./venv/bin/python cli/zepanel.py install-wordpress example.com /var/www/example.com/public example_wp example_wp_user
```

After running:

1. Enter a strong database password when prompted.
2. Wait for the job to complete.
3. Open the domain in a browser.
4. Complete the WordPress setup screen.

### `issue-ssl DOMAIN`

Queues a Let's Encrypt certificate issue job.

```bash
sudo ./venv/bin/python cli/zepanel.py issue-ssl example.com
```

Before running:

1. Confirm DNS points to the server.
2. Confirm Apache serves the domain on port 80.
3. Confirm `[ssl] email` or `ZEPANEL_SSL_EMAIL` is configured.

### `renew-ssl`

Queues certificate renewal for all managed Certbot certificates.

```bash
sudo ./venv/bin/python cli/zepanel.py renew-ssl
```

Run manually when testing renewals. Production renewal can also be handled by system timers if installed.

### `backup-site DOMAIN`

Queues a manual site files backup for a managed site.

```bash
sudo ./venv/bin/python cli/zepanel.py backup-site example.com
```

After running:

1. Copy the printed job ID.
2. Run `work-once` or confirm `zepanel-worker.service` is running.
3. Open `/jobs/JOB_ID` or `/services` and wait for `backup.site.create` to complete.
4. Open `/backups` and confirm the backup record exists.

### `backup-database DBNAME`

Queues a manual SQL dump backup for a managed database.

```bash
sudo ./venv/bin/python cli/zepanel.py backup-database example_wp
```

After running:

1. Copy the printed job ID.
2. Run `work-once` or confirm `zepanel-worker.service` is running.
3. Open `/jobs/JOB_ID` or `/services` and wait for `backup.database.create` to complete.
4. Open `/backups` and confirm the backup record exists.

### `backup-all`

Queues backups for every visible managed site and database.

```bash
sudo ./venv/bin/python cli/zepanel.py backup-all
```

After running:

1. Confirm the worker is running.
2. Open `/services` and wait for queued backup jobs to complete.
3. Open `/backups` and confirm new records exist.

### `prune-backups`

Deletes backup records and files older than the retention window.

```bash
sudo ./venv/bin/python cli/zepanel.py prune-backups --days 30
```

Use the default retention by omitting `--days`.

### `change-admin-password`

Changes the panel admin password from the CLI.

```bash
sudo ./venv/bin/python cli/zepanel.py change-admin-password
```

After running:

1. Enter and confirm the new password.
2. Log in to the panel with the new password.

### `enable-secure-cookies`

Enables secure cookies after the panel is served only over HTTPS.

```bash
sudo ./venv/bin/python cli/zepanel.py enable-secure-cookies
```

### `restore-site BACKUP_ID DOMAIN`

Queues a site-file restore job.

```bash
sudo ./venv/bin/python cli/zepanel.py restore-site 12 example.com
```

Before running:

1. Confirm backup ID `12` is a complete site backup in `/backups`.
2. Confirm `example.com` exists in ZePanel.
3. Confirm you want files overwritten.

### `restore-database BACKUP_ID DBNAME`

Queues a database restore job.

```bash
sudo ./venv/bin/python cli/zepanel.py restore-database 13 example_wp
```

Before running:

1. Confirm backup ID `13` is a complete database backup in `/backups`.
2. Confirm `example_wp` exists in ZePanel.
3. Confirm you want the SQL imported into that database.

### `work-once`

Claims and runs one queued job.

```bash
sudo ./venv/bin/python cli/zepanel.py work-once
```

Use this for testing or recovery when the worker service is stopped.

### `worker [--poll-interval SECONDS]`

Runs the durable job worker in the foreground.

```bash
sudo ./venv/bin/python cli/zepanel.py worker --poll-interval 2
```

Use systemd for normal production operation. Use this foreground command for troubleshooting.

### `recover-jobs`

Requeues stale retryable jobs and fails stale non-retryable jobs.

```bash
sudo ./venv/bin/python cli/zepanel.py recover-jobs
```

Use this after a worker crash or server reboot if jobs remain stuck in `running`.

### `help-page`

Prints a short CLI help page.

```bash
./venv/bin/python cli/zepanel.py help-page
```

Use `--help` for the full command list:

```bash
./venv/bin/python cli/zepanel.py --help
```

## Common Recovery Steps

If a web action fails:

1. Open `/services`.
2. Click the failed job ID.
3. Read the error log.
4. Fix the missing service, permission, config, or package.
5. Retry only if the job shows a retry button; otherwise create a new operation.

If the panel will not load:

```bash
systemctl status zepanel.service
journalctl -u zepanel.service -n 100 --no-pager
```

If jobs do not run:

```bash
systemctl status zepanel-worker.service
journalctl -u zepanel-worker.service -n 100 --no-pager
```

If SSL fails:

1. Confirm DNS points to this server.
2. Confirm port 80 is reachable.
3. Confirm the site vhost exists and is active.
4. Confirm `certbot` is installed.
5. Confirm `[ssl] email` is configured.

If WordPress update or repair fails:

1. Confirm `wp` is installed or `ZEPANEL_WP_CLI` points to WP-CLI.
2. Confirm the site has `wp-config.php`.
3. Repair permissions.
4. Review the job log.