Deploy Laravel on DigitalOcean — then monitor it with Reflex
DigitalOcean Droplets are the default home for thousands of Laravel apps — cheap, fast to provision, well documented. Launch day feels great: Ubuntu LTS, PHP 8.3, nginx, Redis, MySQL, Forge or a manual deploy script, TLS via Lets Encrypt. Then week three hits: disk is 88% full from logs, a CSV import saturates PHP-FPM, and nobody notices Horizon stopped until the client asks why invoices are not sending.
Deploying Laravel on DigitalOcean is solved problem. Keeping it healthy is where teams bleed time. This guide walks through a production checklist and what to monitor after go-live.
Provision the Droplet
Start with Ubuntu 22.04 or 24.04 LTS — 2GB RAM minimum for small apps, 4GB+ when running queues and Redis on the same box. Enable backups and monitoring in the DO control panel, but treat DO metrics as host-level only — they will not tell you FPM is saturated while CPU looks fine.
Create a non-root sudo user, disable password SSH, and firewall:
ufw allow OpenSSH
ufw allow 'Nginx Full'
ufw enable
Stack installation
Install nginx, PHP-FPM 8.3 with extensions Laravel needs (mbstring, xml, curl, mysql, redis, bcmath, intl), Composer, Node for asset builds, Redis, and MySQL 8 or use DO Managed Database for production.
PHP-FPM pool defaults on a 4GB Droplet often need tuning — use the PHP-FPM calculator before launch:
pm = dynamic
pm.max_children = 15
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 5
pm.max_requests = 500
Configure nginx with real client timeouts and adequate fastcgi_buffers for larger Laravel responses.
Laravel application setup
Clone your repo to /var/www/example or Forge's default path. Set permissions:
chown -R deploy:www-data /var/www/example
chmod -R 775 storage bootstrap/cache
Environment:
cp .env.example .env
php artisan key:generate
Point .env at Redis for cache and sessions, configure QUEUE_CONNECTION=redis, mail driver, and APP_URL with https.
Run migrations on first deploy:
php artisan migrate --force
Cache for production:
php artisan config:cache
php artisan route:cache
php artisan view:cache
Queues and scheduler
Use supervisor for workers:
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/example/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=deploy
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/example/storage/logs/worker.log
Horizon replaces multiple workers with one supervised process — prefer Horizon when you already depend on it locally.
Cron for scheduler — single entry:
* * * * * cd /var/www/example && php artisan schedule:run >> /dev/null 2>&1
TLS and deploy workflow
Certbot with nginx plugin for Lets Encrypt. Automate renewal — expired staging certs still ruin client QA.
Deploy via Forge, GitHub Actions, or Reflex Pipeline. Every deploy should:
- Enable maintenance mode optionally
- Pull code,
composer install --no-dev - Run migrations
- Rebuild caches
php artisan queue:restart- Run health gates before opening traffic — see health-gated deployments
Smoke test:
curl -sf https://example.com/health
curl -sf https://example.com/login
After go-live: what to monitor
HTTP 200 on / is insufficient. Watch:
- PHP-FPM — active processes, max children warnings, slow log
- Queues — Horizon status, backlog depth, failed jobs
- Disk and inode —
/var/log,storage/logs, release directories - Deploy markers — correlate error spikes with releases
- Memory — OOM kills precede hard outages; see PHP-FPM OOM in production and the OOM analyser
DigitalOcean alerts on CPU and disk are useful supplements, not replacements.
Common DigitalOcean Laravel failures
Undersized Droplet after marketing launch — scale vertically or split queue workers to a second Droplet before FPM collapses.
Log disk fill — configure logrotate for nginx, PHP, and Laravel daily logs.
Managed DB connection limits — Laravel persistent connections exhaust small MySQL tiers; tune DB_PERSISTENT and pool sizes.
Forgotten queue restart after deploy — workers run old code until bounce.
Snapshot before major migrations — DO snapshots are cheap insurance. RDS-style managed DBs have their own backup windows; verify restore procedure once per year.
Consider a separate worker Droplet when CPU is idle but queues lag — same guidance as PHP-FPM OOM for memory-bound Laravel apps.
Enable DO monitoring alerts for CPU and memory, but treat them as supplementary — they will not replace FPM pool metrics. Document runbook links in Reflex so junior devs on rotation know when to scale the Droplet versus tune PHP.
After the first week in production, schedule a thirty-minute review of FPM slow logs and Horizon metrics — baseline normal before alerts fire on the wrong thresholds.
DigitalOcean makes it easy to launch — Reflex makes it survivable to stay launched without hiring a full-time SRE per app.
How Reflex helps post-deploy
Install reflexd on the Droplet — one command, no Docker required. Reflex ingests FPM, queue, disk, and deploy telemetry. Brain playbooks restart workers, relieve pool pressure, and address safe disk cleanup when policy allows.
Reflex Pipeline integrates with your deploy webhook for health-gated releases. Laravel monitoring guide covers ongoing signals.
Start a trial. Reflex vs Datadog if you considered observability SaaS per Droplet. Reflex for agencies managing many client DO servers. How the Brain repair cycle works.
Ready to stop firefighting your servers?
Try Reflex free for 14 days.