DEV Community

Cover image for 6 Ways to Speed Up Your Laravel Deployments on Deploynix
Deploynix
Deploynix

Posted on • Originally published at deploynix.io

6 Ways to Speed Up Your Laravel Deployments on Deploynix

Deployment speed matters more than most teams realize. A deployment that takes eight minutes is not just slower than one that takes two minutes — it fundamentally changes how your team ships code. Slow deployments discourage small, frequent releases. They make hotfixes stressful. They turn deployment into an event rather than a routine.

On Deploynix, deployments are already zero-downtime, so speed is not about avoiding outages. It is about tightening the feedback loop between writing code and seeing it live. The faster your deployments run, the more confidently and frequently your team can ship.

After analyzing deployment patterns across thousands of Laravel projects on Deploynix, we have identified six optimizations that consistently deliver the biggest time savings. Some are one-line changes. Others require rethinking your deploy script. All of them work.

1. Optimize Your Composer Install Step

Composer is almost always the single largest time sink in a Laravel deployment. A fresh composer install on a moderately sized Laravel application can take 60 to 90 seconds, sometimes longer. On every deployment.

The problem is that many developers use composer install without the flags that tell Composer to skip unnecessary work in production.

Here is what your deploy script should look like:

composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader
Enter fullscreen mode Exit fullscreen mode

Each flag matters:

  • --no-dev skips development dependencies like Pest, Telescope debug bar, and IDE helpers. These packages have their own dependencies, and skipping them can eliminate dozens of packages from the install.
  • --no-interaction prevents Composer from asking questions mid-deployment, which would cause the process to hang.
  • --prefer-dist downloads pre-built distribution archives instead of cloning Git repositories. This is significantly faster for most packages.
  • --optimize-autoloader generates a classmap-based autoloader that converts PSR-4/PSR-0 rules into faster lookups. Your application starts faster on every request.

The combined effect of these flags typically reduces Composer install time by 30 to 50 percent. On large projects with 100+ packages, the savings can be even more dramatic.

Bonus tip: If your project uses private packages, make sure your Composer auth tokens are configured on the server so authentication does not add latency to every install.

2. Use npm ci Instead of npm install

If your Laravel application has a frontend build step — and most do, whether for Vite, Tailwind CSS, Alpine.js, or a full single-page application — the npm step is likely your second-largest time consumer.

The difference between npm install and npm ci is significant in a deployment context:

  • npm install reads package.json, resolves dependency ranges, potentially updates package-lock.json, and installs packages. It is designed for development flexibility.
  • npm ci reads package-lock.json directly, installs exact versions, and skips the dependency resolution step entirely. It is designed for reproducible, fast installs in CI/CD and production environments.
npm ci --no-audit --no-fund
Enter fullscreen mode Exit fullscreen mode

The --no-audit flag skips the vulnerability audit that npm runs by default (you should run audits in CI, not during deployment). The --no-fund flag suppresses funding messages that add noise to deployment logs.

npm ci also starts by deleting the node_modules directory and rebuilding from scratch, which sounds slower but is actually faster because it skips the complex diffing logic that npm install uses to update existing modules.

On a typical Laravel project with Vite and Tailwind, switching from npm install to npm ci saves 10 to 30 seconds per deployment. On larger frontend codebases, the savings multiply.

3. Cache Dependencies Between Deployments

Even with optimized install commands, downloading and resolving dependencies from scratch on every deployment is wasteful. Most deployments change application code, not dependencies. Your composer.lock and package-lock.json files are identical across the vast majority of your deployments.

Deploynix uses a shared directory structure for zero-downtime deployments. Each release gets its own directory, and the live symlink switches between them. This means you can leverage shared storage for dependency caches.

For Composer, configure the COMPOSER_HOME or COMPOSER_CACHE_DIR environment variable to point to a shared location:

export COMPOSER_CACHE_DIR=/home/deploynix/.composer-cache
composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader
Enter fullscreen mode Exit fullscreen mode

This way, packages downloaded in a previous deployment are already cached locally. Composer checks the cache before downloading from Packagist, and cache hits are nearly instantaneous.

For npm, you can leverage the npm cache similarly:

npm config set cache /home/deploynix/.npm-cache
npm ci --no-audit --no-fund
Enter fullscreen mode Exit fullscreen mode

The first deployment after a cache configuration still downloads everything, but subsequent deployments only download packages that have changed. For a project that deploys five times a day but updates dependencies once a week, the cumulative time savings are enormous.

4. Parallelize Build Steps Where Possible

Most deployment scripts run steps sequentially: install PHP dependencies, then install Node dependencies, then build frontend assets, then run migrations, then clear caches. Each step waits for the previous one to finish.

But some of these steps have no dependency on each other. Composer install and npm install are completely independent — they can run simultaneously. In your deploy script, you can use background processes to parallelize:

composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader &
COMPOSER_PID=$!

npm ci --no-audit --no-fund &
NPM_PID=$!

wait $COMPOSER_PID
wait $NPM_PID

npm run build
Enter fullscreen mode Exit fullscreen mode

This runs Composer and npm installs in parallel, then waits for both to complete before running the frontend build (which depends on npm packages being installed). On a server with multiple CPU cores — which is virtually every server in 2026 — this can cut the install phase nearly in half.

A word of caution: be careful about parallelizing steps that depend on each other. The npm run build step must wait for npm ci to complete. Migrations must wait for the application code to be in place. But independent steps like dependency installation are safe to parallelize.

Also, be mindful of server resources. If your server is a small instance with a single core and limited memory, parallelization might actually slow things down due to resource contention. On Deploynix, this works best on servers with at least 2 vCPUs and 2 GB of memory.

5. Run Migrations Selectively

Laravel's php artisan migrate command checks every migration file against the migrations table to determine which ones need to run. On a mature application with hundreds of migrations, this check alone can take several seconds — even when there are no new migrations to run.

For most deployments, you know whether your release includes database changes. If it does not, you can skip the migration step entirely and save time.

In your deploy script, you can make migrations conditional:

php artisan migrate --force --no-interaction
Enter fullscreen mode Exit fullscreen mode

The --force flag is required to run migrations in production. The --no-interaction flag prevents Artisan from prompting for confirmation.

If you want to be smarter about it, you can check whether there are pending migrations before running the command:

if php artisan migrate:status --no-interaction | grep -q "Pending"; then
    php artisan migrate --force --no-interaction
else
    echo "No pending migrations, skipping."
fi
Enter fullscreen mode Exit fullscreen mode

This adds a small overhead for the status check but saves significant time when there are no migrations to run — which is the case for most deployments.

Another approach is to separate your migration step from your deployment entirely. Run migrations as a dedicated step before deploying application code. This gives you more control over the process and keeps your deploy script focused on application-level concerns.

6. Tune Your Deploy Script

Deploynix gives you full control over your deploy script — custom commands that run as part of the deployment process. The most common performance problem is not slow individual commands but unnecessary commands that run on every deployment.

Audit your deploy script and ask these questions:

Are you clearing caches that do not need clearing? Many deployment scripts include php artisan cache:clear out of habit. But if your application cache contains expensive computed values — dashboard statistics, API response caches, configuration lookups — clearing it on every deployment forces your application to rebuild all of that data immediately after going live. Only clear caches that are actually invalidated by your code changes.

Are you rebuilding assets that have not changed? If your deployment includes npm run build but your release only changed backend PHP code, the frontend build is wasted time. Consider making the build step conditional based on whether frontend files have changed.

Are you running multiple cache commands when one would suffice? Instead of running php artisan config:cache, php artisan route:cache, and php artisan view:cache as separate commands, combine them:

php artisan optimize
Enter fullscreen mode Exit fullscreen mode

The optimize command runs config caching, route caching, and event caching in a single invocation with less overhead than running them individually.

Are you running unnecessary queue restarts? If your deployment does not change job classes or queue configuration, restarting queue workers is unnecessary. The php artisan queue:restart command sends a signal to all workers to finish their current job and restart, which is important when job code changes but wasteful otherwise.

Is your deploy script order optimal? On Deploynix, the deploy script runs inside the new release directory before the symlink swap. This means all build steps, migrations, and cache optimizations complete before the new release goes live — ensuring users never see a partially prepared release.

Here is an optimized deploy script sequence:

composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader
npm ci --no-audit --no-fund
npm run build
php artisan migrate --force --no-interaction
php artisan optimize
php artisan queue:restart
Enter fullscreen mode Exit fullscreen mode

After this script completes, Deploynix atomically swaps the symlink and reloads PHP-FPM. The new release goes live fully prepared with all dependencies installed, assets built, and caches warmed.

Measuring Your Improvements

Optimization without measurement is just guessing. After implementing these changes, pay attention to your deployment logs on Deploynix. The platform tracks how long each deployment takes, so you can compare before-and-after times.

A well-optimized Laravel deployment on Deploynix should complete in under two minutes for most applications. Complex applications with large frontend builds might take three to four minutes. If your deployments are consistently taking longer than five minutes, there is almost certainly room for improvement.

The goal is not to reach some arbitrary speed target. The goal is to make deployments fast enough that your team never hesitates to ship. When deploying takes two minutes and carries zero risk of downtime, you stop batching changes into big, scary releases. You ship small changes frequently, catch problems early, and keep your application moving forward.

The Bigger Picture

Fast deployments are a force multiplier. They improve code quality because developers ship smaller, more reviewable changes. They reduce risk because each deployment contains fewer unknowns. They improve team morale because shipping feels good instead of stressful.

Deploynix handles the hard parts — zero-downtime releases, symlink management, rollback capability, and deployment scheduling. These six optimizations handle the parts that depend on your application: how you install dependencies, build assets, run migrations, and configure your hooks.

Top comments (0)