Shipping with Laravel


Software Engineer
Shipping with Laravel has never been easier.
With the recent release of Laravel Cloud, the Laravel team has redefined the way you deploy and manage your apps. But while the deployment process has been streamlined, there’s still a lot you need to consider when shipping your application.
As developers, we need to understand what happens behind the scenes, what might break, and how to respond when it does. Whether you’re using Laravel Cloud or another hosting provider, it’s essential to know which platforms are out there and what trade-offs they bring to the table.
In this post, I’ll share some lessons I’ve learned from my years working with Laravel—from deploying tiny side projects to managing commercial-scale apps—and hopefully help you avoid a few pitfalls along the way.
TLDR; Scroll to the bottom for summarised checklists.
My biggest lesson learned from working with Laravel day-to-day
What I learned: Things fail all the time. You need to spot these failures quickly and resolve them without breaking anything else.
Sometimes the cause of failure is obvious. Other times, it’s buried deep.
500
Service Unavailable
Here are a few lessons that might help you, regardless of your experience level:
1. Did I cache that?
When you’re about to ship your product or deploy code changes, caching in Laravel can seriously cause some weird side effects. Common examples include:
-
Environment Variables (.env): If they’re cached, your app may be reading outdated values.
-
Never use Env(): Always use config rather than
env()
in your code. If your .env is cached then you will not see any results from env(). See: Laravel Docs -
Routes and Config Files: Running php artisan route:cache or php artisan config:cache is great for performance, but it also means you might miss new or updated routes or config values.
-
Queue Workers: Laravel queue workers can cache and serialise code or environment variables. If something changes, your queue worker may still be referencing old data or serializations. Be careful.
-
Supervisord processes: When using Supervisord to manage processes (such as a process listening to an SQS queue), be aware that these laravel might cache your code. This means that running php artisan queue:restart may not fully restart the Supervisord-managed process like it would the queue workers, leaving it to run outdated code until you manually restart it.
https://didyoucacheyourconfig.com
Even if you’re using Laravel Cloud or Laravel Forge, which does a lot of this automatically, you should still keep caching in mind. If your app behaves oddly, think: Could it be the cache? You’d be surprised how often that’s the culprit.
2. I’m (and My Users Are) Seeing a 500 Error
When fatal (5**) errors strike, you need visibility into why it’s happening. Without an error integrated solution, you’re stuck reading logs by hand—usually in storage/logs/laravel.log. That’s doable, but not exactly fun. Here’s what I recommend:
Without a real-time exception monitoring system in place, you risk finding out about critical issues directly from your users—something you definitely want to avoid. While you can always fall back on log files or set up custom automation, these methods lack the depth and context that Sentry or another alternative service provides, such as comprehensive backtraces.
You can also use slack or something else but I wouldn’t recommend it as Sentry will include a backtrace etc.
3. Database: The Maybe Silent Killer
One often overlooked aspect when shipping Laravel apps is a deep technical understanding of your database. In local environments or during small-scale tests, you might not notice performance issues. However, as real traffic or a large user base hits your application, a lack of database expertise can turn your database into a significant bottleneck. To address this, make sure you focus on:
- Indexing: Ensure your most frequently queried columns are properly indexed.
- Query Optimisation: Use tools like Laravel Pulse or the Laravel Telescope to identify slow or repetitive queries.
(Recommended): For a deeper dive into database fundamentals and best practices, consider these resources:
4. Observability: How Do I Know What’s Happening?
Observability is a fancy word for “understanding the health and status of your application.” While I won’t dive too deep here (saving that for another post), keep in mind you want:
- Logs: Are you logging what’s happening with your application? If not, you should be. It should look like this:
[2025-02-17 09:56:08] local.INFO: ScopeImportFileChange status updated from import_scheduled to import_in_progress for file 2025/distribution/AWS-Sheet-Data-Exchange/distribution_latest.csv {"scope_import_file_id":5,"entity_type":"scope_import_file_change"}
[2025-02-17 10:00:51] local.ERROR: [App\Drivers\ScopeManager\AbstractDriver:3] Failed SCOPE_IMPORT_FILE_CHANGES - Failed to import scope file {"file":"2025/file/AWS-Sheet-Data-Exchange/distribution_latest.csv","error":"App\\Jobs\\UpdateAssetsInScopeJob has been attempted too many times."}
-
Application Metrics: CPU usage, memory usage, and response times.
-
Request Tracing: Tools like Laravel Pulse can help you see long running queries and more.
-
Health: Monitor the health of the application simply with Packages like Spatie Health.
5. Pre-release or Staging?
Having a pre-release or staging environment that mirrors your production setup can be crucial.
This environment allows you to:
- Spot Issues Early: By catching bugs or performance bottlenecks in an environment that closely resembles production, you avoid unleashing them on real users.
- Test with Real Data (Safely): While you might not be able to use full production data due to privacy or cost constraints, using a recent, anonymised snapshot can help you detect edge cases early.
- Validate Deploy Scripts: If you use automated deployment pipelines, staging is the place to confirm that your build, cache clearing, and migrations all run smoothly before you hit production.
It’s tempting to skip staging to “move fast,” but it’s a huge risk to rely solely on local environments. You should weigh up the pros and cons of this for your workflow.
6. Backout Plan
No matter how confident you are, deployments can (and do) fail. Having a backout plan (or rollback plan) is essential. Consider the following:
- Rollback or Rolling Forward Migrations: If your new migration breaks something or triggers a performance issue, how do you revert it? Ensure your migrations are written in a way that allows for a graceful rollback without data loss (where possible). Alternatively, you may consider using rolling forward migrations if that better suits your strategy. Laravel’s built-in migrate:rollback works fine for most cases, but be sure to test both rollback and rolling forward options locally and in staging.
- Automated Backups: Before deploying, taking a database snapshot or backup can save your day. If something catastrophic happens, you can restore from the snapshot.
- Deployment Strategy: Things like Blue-Green Deployments and Zero-Downtime Deployments can help you avoid downtime or issues during deployment. Laravel Cloud already provides this (powered by K8s).
7. Enforce Code Quality
To prevent simple mistakes from reaching production and to maintain code quality as your project grows, automation is essential:
-
Static Analysis: Use tools like Larastan to catch bugs and enforce best practices automatically.
-
Code Formatting & Style: Apply automated formatters such as Laravel Pint/PHP CS Fixer to keep your codebase consistent.
-
Code Reviews: Complement automation with peer reviews to catch subtle issues and maintain high standards. You can also try CodeRabbit AI.
8. PHP Type Safety
Be careful when building Laravel applications or any PHP application that you keep in mind that type-safety is important. You should never neglect it and always ensure you are using the correct most visible types.
- Static Type Analysis: Use PHPStan at the maximum level that makes sense for you, to detect type mismatches and potential errors early.
- Type Hints: Use type hints in your code to make it more readable and enforce type safety.
Note: More on this coming soon.
Platforms for Shipping Your Laravel App
You need to consider how you’ll deploy your app.
The Laravel team and ecosystem support a few first-party services which you may use, but there’s also many other options. To keep this simple, here’s a table that represents the services/deployments available to you and also the complexity involved.
With whatever you choose, you should have knowledge in Linux systems and Docker if possible. This will help you miles along the time run.
Platform | Type | Ideal For | Complexity | Cost |
---|---|---|---|---|
Laravel Cloud | Fully managed PaaS | Developers wanting a fully managed solution | Very low | £££ |
Laravel Forge | VPS Management | Those seeking self-hosted VPS deployment with moderate control | Low/Medium | ££ |
Laravel Vapor | Serverless (AWS) | Serverless with minimal server management | High/Medium | ££ |
Bref | Serverless AWS | Experienced devs in AWS & Lambdas trying to save cost | High | £ |
Hetzner | Traditional VPS/Dedicated | Cost-sensitive projects needing full control over servers | High | £ |
Digital Ocean | Traditional VPS/Dedicated | Similar to Hetzner—affordable self-managed deployments | High | £ |
Note: Laravel Cloud cost dependent on useage
If you are new to Systems then I would recommend you start with Laravel Cloud.
When you Ship Checklist
1. Validate Environment Variables & Caching
2. Set Up Error Monitoring
3. Optimise Your Database
4. Add Observability
5. Test in a Staging/Pre-Release Environment
6. Deployment Platform Setup
7. Rollback/Recovery Plan
8. Storage
9. File uploads
10. Code Quality
11. Pull Requests
Bonus+ Checklist:
If you are working with small-teams or of varying sizes and want to keep releasing new features less problematicly, then you can consider these additional bonus things: