If you build with a modern Laravel stack — Inertia, React or Vue, Tailwind, a Vite build step — and your client insists on their existing cPanel shared hosting, you've probably hit the wall this post is about. The framework assumes a build toolchain and often SSH. The host gives you neither. Every just run `composer install` on the server
tutorial quietly assumes access you don't have.
You don't actually need that access. The trick is to stop treating the shared host as a place where building happens, and start treating it as a place where finished files land.
Why the usual advice fails on shared hosting
The standard deploy recipe for a Laravel VPS is: SSH in, git pull, composer install, npm run build, php artisan migrate. On cPanel shared hosting, that chain breaks at almost every link:
- No SSH. Many shared plans expose only FTP/SFTP and the cPanel UI. There's no shell to run commands in.
- Composer won't install (or won't run). Memory limits, missing CLI PHP, no write access outside your home directory, or no way to invoke it at all. This is one of the most common shared-hosting complaints, and it's usually not fixable from your side. Running Composer where the build actually belongs — off the shared host — is the approach in deploying a PHP site with Composer off-host.
- No Node build. Vite/webpack need a build environment the host doesn't provide, so your compiled CSS/JS never gets generated where the site actually runs.
- Connecting Git is fiddly. cPanel's built-in Git Version Control ties awkwardly to a single deploy key, which is why people hit walls trying to connect multiple private repositories to one cPanel account.
The instinct is to fight each of these on the server. The better move is to not run any of them on the server at all.
Build off-host, deploy the artifact
The model that works on constrained hosting separates the two jobs the shared host can't do from the one it can:
- Build somewhere with a real toolchain — install Composer dependencies, run the Vite/npm build, cache config, generate the optimized autoloader.
- Ship only the finished result to the host over FTP/SFTP.
The shared host never sees Composer or Node. It receives a directory of ready-to-serve files, which is the one thing every cPanel plan can accept. This is the whole idea behind a managed build pipeline: your build commands run in an isolated environment on the deployment side, and only the compiled output is transferred to the FTP/SFTP or cPanel-style target you configure.
Compared to wiring cPanel's own Git tooling to your repositories, this sidesteps the single-deploy-key limitation entirely — the platform connects to your repository centrally, so multiple private repos and multiple targets stop being a plumbing exercise. If you're weighing the two approaches directly, the DeployHQ vs cPanel deployment comparison lays out where each fits. For the full sequence of build steps and release hygiene on this stack specifically, see Laravel build-pipeline deployment best practices.
A no-SSH Laravel deployment, end to end
Here's the shape of it, assuming a Laravel app with an Inertia/React front end on a cPanel shared plan that only offers FTP/SFTP.
1. Point deployment at your repository. Connect the Git repo once. Every push to your deploy branch becomes a candidate release — no cPanel Git key juggling.
2. Define the build commands. These run off-host, not on cPanel:
composer install --no-dev --optimize-autoloader
npm ci
npm run build
php artisan config:cache
php artisan route:cache
The output — vendor/, the built public/build/ assets, cached config — becomes the artifact.
3. Configure the FTP/SFTP target. Add the host's FTP or SFTP credentials as a server. The shared-hosting deployment setup covers the target configuration, and the step-by-step FTP-from-Git walkthrough shows the mechanics if FTP is all you've got. Prefer SFTP where the host offers it — the same setup over SSH/SFTP is worth the small extra effort for the encrypted transport.
4. Handle Laravel's public-directory quirk. On shared hosting the web root is usually public_html, not your app's public/. Map the deploy so the built public/ contents land in public_html, and keep the framework files above the web root. This is a mapping decision, not a code change.
5. Deploy. Push to your branch. The build runs off-host, the finished files transfer to public_html, and the site updates — with a deploy log for each run, so a failed transfer tells you what happened instead of leaving you guessing.
If that sequence is where you're stuck right now, connecting a repository and a shared-hosting target is the fastest way to see it work against your own setup.
The same pattern isn't Laravel-specific
Everything above is really about any build-step framework meeting a host that can't build. The same off-host-build approach applies to a Drupal site where Composer won't run on shared hosting (see the Laravel deployment guide for framework specifics), a static-site generator, or a Vue SPA. It works just as well when you deploy WordPress from Git without FTP uploads. The constraint — the host serves files but can't assemble them
— is the same, and so is the fix.
Is shared hosting still the right home?
Sometimes the honest answer is that the constraint is worth removing rather than working around. If you're regularly fighting the host's limits, the signs it's time to move off shared hosting to automated deployments is a useful gut-check. And if you've been told FTP is obsolete, the reality is more nuanced — FTP's continued, legitimate role in deployment covers where it still earns its place. But when the client's cPanel plan is a fixed requirement, building off-host and deploying the artifact is how you ship a modern app onto it without asking the host for access it will never give you.
Stuck on a specific no-SSH deployment? Email support@deployhq.com or ask us on X/Twitter @deployhq.