Laravel Framework

Laravel on cPanel and DirectAdmin — What Works, What Doesn't, When to Move

By Domain India Team · DomainIndia SupportPublished 21 min read
Knowledge base article
Contents (13 sections)

Verdict at the top: A vanilla Laravel application — controllers, Eloquent, Blade, Livewire, file-based cache and sessions, a cron-driven scheduler — runs perfectly well on Domain India shared cPanel, DirectAdmin or Webuzo hosting. Where it breaks is the Laravel features that need long-running processes: Octane, queue workers, Horizon, Reverb websockets and Redis. Those need a VPS. This article shows you how to ship the parts that work, and tells you straight when you have crossed into VPS territory.

Key takeaways

Vanilla Laravel with Blade, MySQL and a cron-driven scheduler runs fine on shared hosting. Octane, queue:work daemons, Horizon, Reverb and Redis do not: they need persistent processes that the CloudLinux sandbox stops. Jailed SSH is available on every Domain India shared plan on request, but Composer cannot run on the server because shared hosting disables the PHP functions it needs, so build on your computer and upload. Move to a VPS the moment your app needs real queue workers, sub-minute jobs, websockets or Redis.

1. What works and what doesn't — the honest table

FeatureShared (cPanel / DirectAdmin / Webuzo)VPS
Vanilla Laravel app (web routes, controllers, Eloquent)YesYes
Blade templates, Livewire, InertiaYesYes
MySQL from the control panelYesYes (you install it)
File-based cache and session drivers, sync queueYesYes
Mail through PHP mail() with an envelope senderYesYes
Laravel's SMTP or sendmail mail transportUsually no (socket and process functions are disabled)Yes
Cron-based scheduler ( *)Yes, for closures and queued jobsYes, for everything
SSH accessJailed shell on every plan, off by default (ask support)Yes (root)
Composer on the serverNo (build locally and upload vendor/)Yes
Queue: php artisan queue:work as a daemonNo (long-running PHP is stopped)Yes
Laravel HorizonNo (needs queue workers and Redis)Yes
Redis cache, session or queueNo (no Redis daemon on shared)Yes
Laravel Octane (Swoole / RoadRunner / FrankenPHP)No (long-running daemon)Yes
Laravel Reverb (websockets)No (long-running daemon)Yes
Sub-minute scheduled tasksNo (the shortest cron interval is 4 minutes)Yes
Remote Database Access from your desktop on port 3306No (firewalled; use an SSH tunnel)Yes (you configure it)
Custom system libraries (FFmpeg, custom ImageMagick builds)No (only what the panel offers)Yes

If everything you need is in the shared column, stop reading here, deploy with Path A or Path B below, and ship. If two or more of the "No" rows matter to your app, get a VPS. The intermediate "I'll work around it on shared" path almost never ends well, and the next two sections explain why.

2. Why long-running processes don't work on shared hosting

This is the part most Laravel tutorials skip. Shared cPanel and DirectAdmin accounts run inside a CloudLinux LVE (Lightweight Virtual Environment): a kernel-enforced sandbox that limits each account's CPU, memory and process count, and reaps processes sitting around outside the web server. Your plan's CPU and RAM allowance is on its plan page.

A worker like php artisan queue:work is, by design, an infinite loop: it waits for jobs, processes them, then waits again, forever. The sandbox treats that as abandoned and stops it. Connect over SSH, start a worker, and watch it disappear a few minutes later. The same applies to Reverb's websocket daemon, Octane's Swoole, RoadRunner and FrankenPHP servers, and any custom long-lived script.

There is a second limit, and it catches more people than the first. Shared hosting also disables a set of PHP functions for security, including the process functions (proc_open, popen, pcntl_fork) and the socket functions (fsockopen, stream_socket_client). Laravel needs them in more places than you would expect: starting queue workers, running scheduled artisan commands, sending mail over SMTP, and inside Composer. The full list is in PHP disabled functions on shared hosting.

Cron jobs do run, because they exit quickly. The Laravel scheduler line everyone uses is:

cron
* * * * * cd /home/youruser/laravel-app && php artisan schedule:run >> /dev/null 2>&1

That works because schedule:run exits within a fraction of a second after dispatching what is due. It is the workers — schedule:work, queue:work, Horizon supervisors — that do not survive. Section 5 covers a further catch in what schedule:run is then allowed to start.

3. What about queue:work --once from cron?

People ask this every month. Yes, * * * * * php artisan queue:work --once in cron works as a stopgap: it processes one job per cron run, which is every 4 minutes on Domain India shared servers. For a side project with five jobs a day that is fine. For anything needing concurrency or any real-time response it is a trap, and on some servers the command itself fails with a "disabled for security reasons" error, in which case set QUEUE_CONNECTION=sync and let jobs run inside the request.

Where it falls over: a one-time password or password reset that arrives a minute late; a failed-payment retry your customer will not wait for; an order that feels a minute slower because the capture and fulfilment jobs are queued; a webhook that fans out to four jobs and therefore takes four minutes. If any of those describe your app, you have already crossed into VPS territory. Do not fight the cron-once pattern.

4. Deploying Laravel: two paths

If you are new to Laravel on shared hosting, start with the beginner guide, How to install Laravel using Softaculous, which walks through the one-click install and the upload route in more detail. The two paths below are the production versions of the same idea.

Path A — build on your computer, upload the artifact (works on every plan)

This is the most reliable route, because all the Composer work happens on your own machine and the server only has to run PHP.

  1. Build the production artifact on your development machine:
    bash
    composer install --no-dev --optimize-autoloader
    npm run build
    php artisan config:cache
    php artisan route:cache
    php artisan view:cache
    php artisan event:cache
  2. Zip the project, including vendor/ and excluding .git/, node_modules/, tests/, your development .env, and the contents of storage/logs/ and bootstrap/cache/ (keep those folders, clear what is in them).
  3. Upload with File Manager. Upload the zip to /home/YOURUSER/laravel-app/, deliberately outside public_html, and use Extract. Move only the contents of laravel-app/public/ into public_html/, then edit public_html/index.php and change the two require __DIR__.'/../...' lines to point at ../laravel-app/bootstrap/app.php and ../laravel-app/vendor/autoload.php. The point of this layout: your app code, .env, vendor/, models and routes all live outside the web root. Only public/ is reachable from the internet.
  4. Set permissions.
    text
    storage/            755  (or 775 if your server needs group write)
    bootstrap/cache/    755
Never chmod 777

Setting a folder to 777 makes it writable by every account process on the server, and it is one of the most common ways a shared-hosting site gets defaced. Use 755 for folders and 644 for files. If Laravel still cannot write, open a ticket and we will check the ownership rather than loosening the permissions.

  1. Create the production .env in /home/YOURUSER/laravel-app/, not in public_html/: The cache, session and queue drivers are file-based, which is what shared hosting supports. QUEUE_CONNECTION=sync runs jobs inside the request that dispatched them — the right default for a low-traffic app. (Older Laravel releases use CACHE_DRIVER; follow your version's .env.example.) Mail settings are in section 6.
    ini
    APP_ENV=production
    APP_DEBUG=false
    APP_URL=https://yourdomain.com
    
    DB_CONNECTION=mysql
    DB_HOST=localhost
    DB_DATABASE=youruser_dbname
    DB_USERNAME=youruser_dbuser
    DB_PASSWORD=yourdbpassword
    
    CACHE_STORE=file
    SESSION_DRIVER=file
    QUEUE_CONNECTION=sync
  2. Generate the key, migrate and link storage. Over SSH, in the project folder: Run these only if jailed SSH is enabled on your account; otherwise use the fallback below. --force is required when APP_ENV=production. Use ln -s rather than php artisan storage:link, because the artisan command relies on a PHP function that shared hosting disables. If php artisan will not run at all on your server, generate the key locally with php artisan key:generate --show and paste it into the server's .env, run the migrations against a local database, and import the resulting .sql file with phpMyAdmin.
    bash
    php artisan key:generate
    php artisan migrate --force
    ln -s ~/laravel-app/storage/app/public ~/laravel-app/public/storage

Path B — Git over SSH, with vendor/ built on your computer

Jailed SSH access is available on every Domain India shared hosting plan (cPanel, DirectAdmin, Webuzo), including the entry-level Starter plans. It is off by default; ask support to enable it for your account: see How to set up SSH access on shared hosting. Login is with an SSH key on port 22; password login is switched off. Git is available inside the jail on our cPanel servers; on other servers, ask support which tools your jail has. Composer cannot run on shared hosting, so the vendor/ folder still comes from your computer.

On the server:

bash
ssh [email protected]
cd ~
git clone https://github.com/you/yourapp.git laravel-app
cd laravel-app
cp .env.example .env
nano .env           # fill in production values (see Path A, step 5)

On your computer, in the same project, build the dependencies and copy them up over SFTP (or zip vendor/ and extract it with File Manager):

bash
composer install --no-dev --optimize-autoloader
scp -r vendor [email protected]:laravel-app/

Back on the server:

bash
cd ~/laravel-app
php artisan key:generate
php artisan migrate --force
ln -s ~/laravel-app/storage/app/public ~/laravel-app/public/storage
php artisan optimize

Then, in your control panel's domain settings, point the document root for your domain at /home/youruser/laravel-app/public/. Or rename public_html and put a symbolic link in its place:

bash
mv ~/public_html ~/public_html.old
ln -s ~/laravel-app/public ~/public_html

Redeploys become git pull on the server, a fresh upload of vendor/ from your computer whenever composer.lock has changed, and then:

bash
cd ~/laravel-app
git pull
php artisan migrate --force
php artisan optimize
Why Composer can't run on the server

Composer needs proc_open and other process functions to unpack packages and run post-install scripts. Shared hosting disables those functions on both cPanel and DirectAdmin, for PHP on the command line as well as the web, so composer install fails with a "has been disabled for security reasons" error. Run Composer on your computer and upload the finished vendor folder. Nothing is wrong with your account; this is how the server is hardened.

Database, remote access and backups

Create the database and its user in your control panel, then use that name and password in .env. Two practical points:

  • MySQL port 3306 is closed from the internet on every Domain India shared server. A desktop client such as TablePlus, DBeaver or MySQL Workbench cannot reach it directly. Connect through an SSH tunnel on port 22 instead: see Securing MySQL access with SSH tunnels. Some panels also offer a remote-MySQL allow list, but the server firewall may still block the connection.
  • Backups included with shared hosting are weekly. That is a safety net, not a deployment strategy. Keep your code in Git, and take your own database export before every migration that changes an existing table.

5. The scheduler, and the two things that break it

The Laravel scheduler is the one almost-long-running thing that does work on shared hosting, because cron invokes it and it exits immediately. Add one cron job in your control panel:

cron
* * * * * cd /home/youruser/laravel-app && php artisan schedule:run >> /dev/null 2>&1
cPanel Cron Jobs page with the Cron Email setting, the Add New Cron Job form with Common Settings and time fields, and Current Cron Jobs
Add the schedule:run line in the Command box of Cron Jobs.

Two caveats:

One-minute granularity. ->everyTenSeconds() and ->everyThirtySeconds() look like they should work, but they depend on the schedule:work daemon, which is a long-running process and gets stopped. On Domain India shared hosting the shortest cron interval is 4 minutes: a * * * * * line is changed to */4 * * * * automatically, so schedule:run runs every four minutes and a task set to ->everyMinute() runs at that pace.

Scheduled artisan commands may not start. Schedule::command(...) launches each command as a separate process, which needs proc_open. Where that function is disabled, those tasks silently fail to run. There are two workarounds: use Schedule::call(...) closures and Schedule::job(...), which run inside the same process; or give each command its own cron line, since cron starts the PHP binary directly and does not need proc_open.

6. Mail: use PHP mail() with an envelope sender

This is where a lot of Laravel deployments on shared hosting quietly break, and the usual advice — "just configure SMTP" — is the wrong advice here.

Modern Laravel sends mail through Symfony Mailer. Its SMTP transport opens a socket (stream_socket_client) and its sendmail transport starts a process (proc_open). Both function families are disabled on shared hosting, so both transports fail with a message such as stream_socket_client() has been disabled for security reasons. PHP's own mail() function is not affected and keeps working.

Two routes actually deliver:

A small custom transport that calls mail()
Write a short transport class extending Symfony's AbstractTransport: it splits the finished message into headers and body and calls PHP mail() with the -f envelope sender. Register it as a custom mailer in Laravel's mail config. It works wherever mail() works.
A transactional email API over HTTPS
Amazon SES, Postmark, Mailgun and Brevo accept mail over an HTTPS API, which PHP reaches with cURL rather than a raw socket. Laravel has ready-made API transports for most of them, and this is the right answer above a few hundred messages a day. On our DirectAdmin servers curl_exec is disabled on most sites, so test the transport on your plan before you rely on it.

The envelope sender matters as much as the transport: pass -f with an address on your own domain, so SPF is checked against your domain and not the server's hostname. The full explanation, with working code for both routes, is in PHP sendmail settings — read it before you write your mailer. Then publish SPF, DKIM and DMARC records: Email deliverability: SPF, DKIM, DMARC and BIMI.

Do not send as your visitor's address

In a contact form, put your own address (for example [email protected]) in From and the visitor's address in Reply-To. Sending as a gmail.com or outlook.com address you do not own fails SPF and DMARC, and the message is rejected or filed as spam.

On a VPS or on the App Platform, where nothing is disabled, Laravel's normal SMTP transport works exactly as the documentation describes.

7. Performance on shared hosting — what to expect

Our shared cPanel, DirectAdmin and Webuzo servers run the Apache web server on NVMe storage, with a choice of PHP versions you select in the control panel. A Laravel app whose caches have been warmed by php artisan optimize is comfortably fast enough for a brochure site, a small SaaS dashboard or an internal tool.

What actually makes Laravel feel slow on shared hosting, in the order we see it:

  1. Forgetting php artisan optimize after a deploy. Without the config, route and view caches you pay a bootstrap penalty on every request.
  2. N+1 query bloat. Eloquent's lazy loading makes this trivial to introduce. Install Laravel Debugbar in development, watch the query count on your heaviest page, and eager-load with with().
  3. APP_DEBUG=true in production. It builds a stack trace on every error, disables several caches, and shows your configuration to anyone who can trigger an error. Set it to false, always.
  4. Shipping the development asset build. npm run build produces fingerprinted, minified files; npm run dev produces hot-reload assets that expect a Vite server. Make sure the deploy carries the build output.

For page caching, use a framework-level full-page cache or a PHP caching package. Do not follow tutorials that tell you to install LiteSpeed Cache: our shared servers run Apache, not LiteSpeed.

If you have done all four and still see multi-second responses, your app is doing too much per request, or you have outgrown shared hosting.

8. When to move up: VPS or App Platform

Concrete signals. If two or more are true, upgrade now:

  • You need real queue workers — Horizon, or anything beyond --once cron polling.
  • You need Redis for cache, session or queue.
  • You need Octane for performance, or Reverb for websockets.
  • You are seeing "resource limit reached" notices, or your resource graphs sit near the ceiling.
  • You need scheduled jobs more often than every 4 minutes, or scheduled artisan commands that will not start.
  • You need system libraries the control panel does not offer.
  • Your team uses a Forge or Envoyer style deploy workflow and wants to keep it.

A VPS gives you root, so all of that simply works. A sensible Laravel VPS is Nginx with a current PHP 8.x FPM pool (or Caddy if you want automatic certificates), MySQL or PostgreSQL, Redis for cache, session and queue, Supervisor watching php artisan queue:work or Horizon, and one cron line running schedule:run every minute — still the right pattern even here. If you go the Redis route, read Laravel Redis cache tags and stampede prevention before you put it in front of a busy page.

If you would rather not become a system administrator, the App Platform is Domain India's container hosting: connect a GitHub repository and click Deploy Now, or upload with a deploy token from your CI. Pushing to GitHub does not deploy by itself. Node.js apps are detected automatically; a Laravel app needs a Dockerfile. Every plan includes automatic SSL, custom domains, logs and a PostgreSQL database (not MySQL), and each app gets 512 MB of RAM, so check that your app fits before you move a heavy one across. See App Platform: getting started.

9. Common errors and what they actually mean

file_put_contents(...): failed to open stream: Permission denied — almost always storage/logs/laravel.log or bootstrap/cache/. Set storage/ and bootstrap/cache/ to 755 (or 775) recursively and check the files are owned by your hosting user. The longer could not be opened in append mode variant is the same problem.

No application encryption key has been specified — you skipped php artisan key:generate, or .env is missing entirely. Never regenerate the key on a live site: it logs every user out and makes anything encrypted with the old key unreadable.

SQLSTATE[42000]: Specified key was too long — an older MySQL with utf8mb4 and a migration indexing a long string column. In app/Providers/AppServiceProvider.php, inside boot():

php
use Illuminate\Support\Facades\Schema;

Schema::defaultStringLength(191);

Then re-run the migrations.

HTTP 500 with a blank page — APP_DEBUG=false is hiding the cause, which is correct for production. Read storage/logs/laravel.log, then the error log in your control panel. If both are empty, set APP_DEBUG=true, reload once, read the trace, and set it straight back to false.

stream_socket_client() has been disabled for security reasons — Laravel's SMTP mail transport on a server that disables socket functions. See section 6.

proc_open() has been disabled for security reasons — Composer, a scheduled artisan command, or the sendmail mail transport. See sections 4, 5 and 6.

pcntl_fork() has been disabled for security reasons — Horizon or a queue worker tried to fork a child process. This workload needs a VPS.

symlink() has been disabled for security reasons — php artisan storage:link. Create the link over SSH with ln -s instead.

Class "Redis" not found — your .env points cache or session at Redis, but there is no Redis server and no PHP Redis extension. On shared hosting, switch those settings back to file.

Maximum execution time exceeded and Allowed memory size of ... bytes exhausted — the request needs more time or memory than PHP allows. You can often raise both in your control panel's PHP settings, within what your plan allows, but there is a server-side ceiling. The better fix is usually to stop loading a whole table into memory, or to move the heavy work into a queued job.

Class 'finfo' not found and similar — a PHP extension is not switched on. On cPanel the extensions are a server-wide set you can't switch on yourself: to check, upload a file containing <?php print_r(get_loaded_extensions());, open it, then delete it, and if the one you need is missing, open a ticket naming the extension and the PHP version. DirectAdmin and Webuzo have their own extension settings. The hosting compatible technologies list shows what is available.

10. Where Domain India fits

Every Domain India shared hosting plan — cPanel, DirectAdmin and Webuzo — includes a choice of PHP versions, MySQL databases with phpMyAdmin, cron jobs, free SSL and backups (weekly on cPanel and DirectAdmin, nightly on Webuzo). Jailed SSH access is available on every plan, including the entry-level Starter plans; it is off by default, so ask support to enable it. Composer does not run on the server on any plan, so build your vendor/ folder locally. Prices below exclude 18% GST and are Domain India list prices on 20 September 2026.

Shared hosting is the right home for a Laravel app that serves pages, talks to MySQL and sends a reasonable amount of mail:

cPanel Starter
₹125/mo + GST
  • 25 GB NVMe SSD Storage
  • 50 GB Monthly Bandwidth
  • 1 Website
  • 10 Email Accounts
See plan details

For container deploys from GitHub or CI with automatic SSL, without running a server yourself, the App Platform sits between shared hosting and a VPS:

App Starter
₹100/mo + GST
  • 512 MB RAM per app
  • 1 vCPU
  • 5 GB NVMe SSD
  • PostgreSQL Database
See plan details

And if your app needs queue workers, Redis, Octane or Reverb, a VPS is the honest answer:

VPS Starter
₹552.65/mo + GST
  • 1 vCPU
  • 2 GB DDR4 RAM
  • 64 GB NVMe SSD Storage
  • 2 TB Monthly Bandwidth
See plan details

Not sure which side of the line your app is on? Open a ticket at /support/ticket with your composer.json and a short description of the features you rely on, and we will tell you straight whether shared hosting is enough. Live chat is available 24/7. We do not offer phone support.

Do I need a higher hosting plan to get SSH for Laravel?

No. Jailed SSH access is available on every Domain India shared hosting plan, including the entry-level Starter plans on cPanel, DirectAdmin and Webuzo. It is off by default; ask support by live chat or a ticket to enable it, and log in with an SSH key. Composer cannot run on shared hosting on any plan, because it needs PHP functions the server disables, so run Composer on your own computer and upload the project with its vendor folder.

Why does Laravel mail fail with "has been disabled for security reasons"?

Laravel sends through Symfony Mailer, whose SMTP transport needs socket functions and whose sendmail transport needs process functions. Shared hosting disables both families for security. Use a small custom transport that calls PHP's mail() function with the -f envelope sender, or a transactional email API over HTTPS. PHP mail() itself is not disabled and keeps working.

Does Laravel Octane really not work at all on shared hosting?

Correct. Swoole, RoadRunner and FrankenPHP all run as long-lived daemons that bind a port, and none of that is permitted inside the CloudLinux sandbox that shared accounts run in. Octane is a VPS feature.

Can I run Horizon if my queue volume is tiny?

No, regardless of volume. Horizon is a supervisor process managing queue workers, and the supervisor itself is the long-running thing that gets stopped. It also needs Redis, which shared hosting does not provide. If a few minutes of delay is acceptable, use a cron-driven queue:work --once, or set QUEUE_CONNECTION=sync so jobs run during the request.

Can I connect to my hosting database from MySQL Workbench or TablePlus?

Not directly. MySQL port 3306 is firewalled on every Domain India shared server. Open an SSH tunnel on port 22 and point your client at the local end of the tunnel. Jailed SSH is available on every shared plan; ask support to enable it.

Can I use Laravel Vapor with Domain India?

Vapor deploys to AWS Lambda, so it is a serverless setup rather than hosting on our servers. You can keep your domain and your email with us and run the app on Vapor; we simply will not be the hosting provider for that application.

I'm on Laravel 11 or 12. Does that change anything?

No. The threshold is the workload, not the Laravel version: anything needing a persistent process, Redis or a disabled PHP function needs a VPS, and everything else runs on shared hosting. Check your control panel for the PHP versions available and match your composer.json to one of them.

Do Livewire and Inertia work on shared hosting?

Yes. Livewire is ordinary request and response traffic over AJAX with no long-lived server process, and its polling features work too. Inertia is a thin protocol over standard Laravel responses, with the heavy lifting done in the browser. The exception is Inertia server-side rendering, which runs a persistent Node process and needs a VPS.

Forge, or a do-it-yourself VPS?

Forge is a paid monthly subscription that provisions and deploys to a server you own, including one of ours. If more than one person touches the deploy it usually pays for itself in saved system-administration time; if you work alone and like Linux, a plain VPS costs less. Both work on a Domain India VPS.

Ready to deploy? Start with the beginner walkthrough in How to install Laravel using Softaculous, compare cPanel, DirectAdmin and Webuzo hosting for a standard Laravel app, look at the App Platform for container deploys, or move up to a VPS if you need queue workers, Redis or websockets. If you are not sure which fits, open a ticket and ask.

Need root for queue workers, Octane or Reverb?

A Domain India VPS gives you full root access, KVM virtualisation, NVMe storage and your choice of Linux, so Redis, Supervisor-managed queue workers and websockets all run exactly as the Laravel documentation describes.

See VPS plans

Was this article helpful?

Your answer helps us decide what to improve next.

Still need help? Open a support ticket and our team will reply.

Prefer an app? Add this site to your home screen.Get the app