Installing on CloudPanel and nginx
Oct 15, 2026
WHBMP runs well on a VPS. This page uses CloudPanel (Ubuntu 24.04, nginx, PHP-FPM, MariaDB), which is built for Laravel and also runs on ARM servers. The same nginx rules apply to any nginx setup.
1. Create the site
- In CloudPanel choose Sites > Add Site > Create a PHP Site.
- Pick the Laravel application type if it is offered, PHP 8.3 or newer, and enter the domain name.
- CloudPanel creates a site user and the folder
/home/SITEUSER/htdocs/your-domain.com. The document root is thepublicsubfolder. - Create the database under the site's Databases tab and note the user and password.
2. Upload the files
Upload and extract the WHBMP package into /home/SITEUSER/htdocs/your-domain.com as the site user, so ownership is right. Then:
chown -R SITEUSER:SITEUSER /home/SITEUSER/htdocs/your-domain.com
chmod -R ug+rwX storage bootstrap/cache
3. Install
Open https://your-domain.com/install and follow Installing WHBMP. Use 127.0.0.1 as the database host.
4. SSL
In the site's SSL/TLS tab create a Let's Encrypt certificate. The domain's A record must already point to the server and port 80 must be open.
5. Cron
Add the cron job in the site's Cron Jobs tab with the PHP version you chose, for example php8.3 /home/SITEUSER/htdocs/your-domain.com/artisan schedule:run.
6. The nginx rule that makes menus and dropdowns work
CloudPanel's vhost has a rule for static files (css, js, images and so on) that returns a 404 for any such URL that is not a real file on disk. WHBMP uses Livewire, which serves its JavaScript from a route, /livewire-xxxxxxxx/livewire.min.js, not from a file. With the default vhost that script is a 404, and because Alpine.js ships inside it, nothing interactive works: the sidebar dropdowns all stay open, tabs do not switch and buttons do nothing.
You can confirm it in your browser's developer tools: the request to livewire.min.js shows 404.
The fix is to let those requests fall through to Laravel. In CloudPanel open the site's Vhost tab. There are two location ~* ^.+\.(css|js|jpg|...)$ blocks.
In the first server block (the HTTPS one on port 443) change the static location so a missing file is passed to the backend:
location ~* ^.+\.(css|js|jpg|jpeg|gif|png|ico|gz|svg|svgz|ttf|otf|woff|woff2|eot|mp4|ogg|ogv|webm|webp|zip|swf|map)$ {
try_files $uri @backend_dynamic;
add_header Access-Control-Allow-Origin "*";
expires max;
access_log off;
}
location @backend_dynamic {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
}
In the second server block (the one that listens on port 8080 and runs PHP) add the same fallback to the static location:
location ~* ^.+\.(css|js|jpg|jpeg|gif|png|ico|gz|svg|svgz|ttf|otf|woff|woff2|eot|mp4|ogg|ogv|webm|webp|zip|swf|map)$ {
try_files $uri /index.php?$args;
add_header Access-Control-Allow-Origin "*";
expires max;
access_log off;
}
Save the vhost (CloudPanel tests the config and reloads nginx). Reload the page with Ctrl+F5. The Livewire script now loads and the dropdowns collapse.
If you use plain nginx without CloudPanel's Varnish layer, you only need the single rule try_files $uri /index.php?$query_string; in the static location, or the usual Laravel block:
location / {
try_files $uri $uri/ /index.php?$query_string;
}
Alternative without editing nginx: run php artisan livewire:publish --assets. That copies the script to a real file under public. Repeat it after each Livewire update.
If you edit a vhost by hand on the server, CloudPanel may overwrite it the next time you change the site in the panel, so make the change in the Vhost tab.
7. Check the result
https://your-domain.com/loads with styling.- The browser console shows no 404 for
livewire.min.js. - Sidebar menus open and close, and System > System Health has no red items.
See also Common server problems.