Plesk Webhosting Edition is what I run for hosting PHP applications on the josephcharnin.com stack, and it's what I recommend for developers who want cPanel-level convenience without cPanel's baggage. The setup process for deploying a PHP application properly — with PHP-FPM pools, NGINX as the front-end, Let's Encrypt SSL, and Git-based deployment — has a specific sequence that isn't obvious from the Plesk UI. Here's the exact process I use.

Initial Virtual Host Setup

In Plesk, each domain gets its own subscription, and each subscription corresponds to a virtual host. Navigate to Domains > Add Domain. Enter your domain name and select the hosting type as Website hosting (not forwarding or no hosting). Set the document root — I recommend leaving it at the Plesk default of /var/www/vhosts/yourdomain.com/httpdocs unless you have a specific reason to deviate, because Plesk's automated SSL and deployment tools all assume this path structure.

For PHP applications that need a public/ subdirectory as the web root (Laravel, Symfony, custom MVC frameworks), set the document root to /var/www/vhosts/yourdomain.com/httpdocs/public at this step rather than trying to change it later. Changing document root after SSL is already provisioned requires reissuing the certificate.

Under Hosting Settings for the domain, set:

  • PHP version: your target version (I typically run PHP 8.2 or 8.3 for new projects)
  • PHP handler: FPM application served by nginx — this is the setting that enables the NGINX + PHP-FPM stack
  • Document root: as decided above

PHP-FPM Pool Configuration Per Domain

Each Plesk subscription gets its own PHP-FPM pool, which is one of Plesk's better features compared to traditional shared hosting. The pool runs as the subscription's system user, giving proper file permission isolation between sites on the same server.

To customize the FPM pool settings for a domain, go to Domains > yourdomain.com > PHP Settings. Here you can set:

  • memory_limit — for billing applications I set this to 256M minimum
  • max_execution_time — 120 seconds for pages that may do batch processing
  • upload_max_filesize and post_max_size — set these together if your app accepts file uploads
  • Custom php.ini directives via the "Additional directives" textarea

For production PHP-FPM pool settings that Plesk doesn't expose in the UI, edit the pool config file directly at /opt/plesk/php/8.2/etc/php-fpm.d/yourdomain.com.conf. Key settings for a billing application under load:

[yourdomain.com]
pm = dynamic
pm.max_children = 20
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 8
pm.max_requests = 500          ; recycle workers to prevent memory leaks
request_terminate_timeout = 120s
php_admin_value[error_log] = /var/www/vhosts/yourdomain.com/logs/php-fpm-error.log
php_admin_flag[log_errors] = on

After editing pool configs directly, restart PHP-FPM with plesk bin php_handler --reread or via Tools & Settings > Services Management > PHP-FPM in the Plesk panel.

NGINX + Apache Stack in Plesk

Plesk's default "FPM application served by nginx" mode runs NGINX as the front-end reverse proxy with Apache behind it handling PHP via FPM. For most PHP applications you actually want to bypass Apache entirely and have NGINX serve static files and proxy PHP directly to FPM. Enable this under Domains > yourdomain.com > Apache & nginx Settings.

Under the nginx section, check "Serve static files directly by nginx" and uncheck "Process PHP by Apache" if you want pure NGINX + FPM without Apache in the chain. For applications that need .htaccess support (WordPress, legacy apps), keep Apache in the chain — NGINX doesn't process .htaccess files.

For custom NGINX directives, use the "Additional nginx directives" textarea in Apache & nginx Settings rather than editing the vhost config file directly. Plesk regenerates its managed config files on certain events (domain changes, SSL renewal) and will overwrite manual edits. The additional directives section survives these regenerations. A typical billing app configuration:

# Paste into Additional nginx directives field
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2)$ {
    expires 30d;
    add_header Cache-Control "public, immutable";
}

location /api/ {
    fastcgi_read_timeout 120;
    fastcgi_send_timeout 120;
}

client_max_body_size 20M;

Git Deployment Hooks

Plesk has a built-in Git integration under Domains > yourdomain.com > Git. Click Add Repository, enter your remote repository URL, and configure the branch. Plesk will clone the repository to /var/www/vhosts/yourdomain.com/httpdocs (or a separate repository directory if you configure it differently).

The post-deployment script runs after each git pull. For a PHP application, the deployment script handles Composer dependencies, cache clearing, and migration runs:

#!/bin/bash
# Plesk post-deployment hook
# Location: configure in Plesk Git panel "Actions after pull"

set -e

APP_DIR="/var/www/vhosts/yourdomain.com/httpdocs"

cd "$APP_DIR"

# Install/update Composer dependencies (no dev in production)
/usr/local/bin/composer install --no-dev --optimize-autoloader --no-interaction

# Run database migrations
php bin/migrate.php --run

# Clear application cache
php bin/cache.php --clear

# Set correct permissions
find "$APP_DIR/storage" -type d -exec chmod 775 {} \;
find "$APP_DIR/storage" -type f -exec chmod 664 {} \;

# Gracefully reload PHP-FPM to pick up any new code
kill -USR2 $(cat /var/run/plesk-php82-fpm.pid)

echo "Deployment completed at $(date)"

The kill -USR2 signal triggers a graceful PHP-FPM reload — it waits for in-flight requests to complete before recycling workers. This is zero-downtime deployment for PHP-FPM: new requests start hitting the new code while current requests finish on the old worker processes.

SSL with Let's Encrypt via Plesk

Navigate to Domains > yourdomain.com > SSL/TLS Certificates. Click Get it free under the Let's Encrypt section. Plesk handles the ACME challenge, certificate issuance, NGINX/Apache configuration, and auto-renewal. Check "Secure the webmail" and "Redirect from http to https" at this step.

The redirect from HTTP to HTTPS is implemented in the NGINX front-end config as a 301. If your application also sets its own redirect logic (via PHP header() calls or .htaccess), you can end up with redirect chains. Verify with curl -I http://yourdomain.com that the redirect chain is exactly one hop: HTTP → HTTPS, not HTTP → HTTP → HTTPS.

For wildcard certificates (useful if you're hosting multiple subdomains per client), Plesk's Let's Encrypt integration supports wildcard issuance via DNS-01 challenge. You'll need API access to your DNS provider configured in Plesk — it supports Cloudflare, Route 53, and several others natively.

Environment Variables

Never commit environment-specific configuration (database credentials, API keys, payment gateway secrets) to your Git repository. In Plesk, set environment variables for a domain under Domains > yourdomain.com > PHP Settings > Additional directives:

; Paste into Additional directives
env[APP_ENV] = "production"
env[DB_HOST] = "localhost"
env[DB_NAME] = "billing_db"
env[DB_USER] = "billing_user"
env[DB_PASS] = "your-secure-password"
env[PAYMENT_GATEWAY_KEY] = "your-gateway-api-key"

These are injected into the PHP-FPM environment and accessible via $_ENV['DB_HOST'] or getenv('DB_HOST') in your application. This approach keeps secrets out of your codebase and out of the Git history while still being manageable through the Plesk UI without SSH access.

Deploying Without Downtime

The combination of PHP-FPM graceful reload, Composer's autoloader optimization, and atomic Git pulls gives you a near-zero-downtime deployment workflow in Plesk. The key sequence is: pull new code → install dependencies → run migrations → reload FPM. Database migrations are the highest-risk step — ensure your migrations are backward-compatible (additive only during the deployment window) so the old code continues working on the new schema while FPM workers are recycling.

For deployments that require truly atomic switchovers — where even a partial second of inconsistent state is unacceptable — implement a symlink-based release strategy: deploy to a timestamped directory, then atomically move the symlink that httpdocs points to. Plesk supports this if you set the document root to the symlink path during initial setup. It adds complexity but gives you instant rollback by re-pointing the symlink to the previous release directory.

Plesk Webhosting Edition hits a solid middle ground between raw server administration and fully managed hosting. For PHP developers who want server-level control over FPM pools, NGINX configuration, and deployment automation without writing Ansible playbooks for every domain, it's a well-designed tool once you know which settings live where.