DevOps & Cloud

Dockerizing Laravel Applications: A Production-Ready Dockerfile

SC

Sarah Chen

DevOps Lead · May 10, 2026 · 4 min read

Dockerizing Laravel Applications: A Production-Ready Dockerfile

Dockerizing Laravel for Consistent Deploys

"Works on my machine" dies when staging runs PHP 8.2 and production runs 8.1 with different extensions. Docker gives you reproducible builds—if your Dockerfile is not copying vendor/ from your laptop and calling it done.

Production Dockerfile

FROM php:8.3-fpm-alpine AS base
RUN apk add --no-cache nginx supervisor mysql-client \
    && docker-php-ext-install pdo_mysql opcache pcntl

WORKDIR /var/www/html
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader --no-scripts

COPY . .
RUN php artisan config:cache && php artisan route:cache && php artisan view:cache

FROM base AS production
RUN chown -R www-data:www-data storage bootstrap/cache
EXPOSE 9000
CMD ["php-fpm"]

Compose for Local Dev

# docker-compose.yml excerpt
services:
  app:
    build: .
    volumes:
      - .:/var/www/html
    depends_on:
      - mysql
      - redis
  queue:
    build: .
    command: php artisan queue:work --sleep=3 --tries=3
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: laravel
      MYSQL_ROOT_PASSWORD: secret
  redis:
    image: redis:7-alpine

Deployment Tips

  • Multi-stage builds: separate dev dependencies from production image
  • Run migrations in init container or deploy hook, not on every pod start racing
  • Store .env in secrets—inject at runtime, never bake into image
  • Health check endpoint: /health hitting DB and Redis

Image size matters for pull times—Alpine-based images often land under 200MB vs 800MB Ubuntu monsters.

CI Image Builds

Build images in GitHub Actions, push to ECR or GHCR, deploy by tag—not git pull on server. Immutable artifacts mean rollback is retagging previous image digest, not guessing which commit was stable.

Scan images with Trivy or Grype in CI. Base image CVEs patch weekly; rebuild pipeline should be automated on schedule, not panic-driven after a blog post about Log4Shell-style headlines.

Document local dev onboarding: docker compose up should get new hires running in under 15 minutes. Mount source for hot reload in dev; never mount production secrets into dev containers shared on Slack screenshots.

Pin base image digests in production Dockerfile, not floating tags like php:8.3-fpm-alpine latest—surprise breaking upstream changes on rebuild Friday afternoon are avoidable. Document why each installed extension exists; orphaned extensions widen attack surface and image size.

Runtime Configuration

PHP-FPM pm.max_children must match container memory limit—each child consumes 40–80MB typical Laravel app. Formula: available memory divided by average child RSS with 20% headroom. OOM kills during traffic spike mean pm settings too aggressive for Kubernetes memory limits.

Run scheduler as separate container or Kubernetes CronJob, not inside web container cron—double scheduling causes duplicate invoice emails and angry customers every month boundary.

Use .dockerignore to exclude node_modules and .git from build context. Order Dockerfile layers so composer install caches when only application code changes—context upload and rebuild time drops dramatically on large repos when layer order is correct.

Local Parity

Match PHP extensions and versions exactly between dev containers and production image—missing ext-intl in prod only manifests when formatting dates in French during demo. Compose override files for Xdebug stay out of production builds via separate compose profiles developers enable explicitly.

Run php artisan optimize in build stage, not at container start—faster cold starts on autoscaling events. Verify storage and bootstrap/cache permissions in entrypoint script idempotently; Kubernetes pods restarting should not require manual chmod on NFS mounts.

Scan images in registry for CVEs on schedule; base image updates are cheaper than emergency fire drills when compliance audit finds critical OpenSSL vulnerability two days before deadline.

Document docker compose commands in README for day-one onboarding—standard commands reduce Slack questions and eliminate PHP version mismatches across macOS and Windows machines before containers unify the environment.

Local vs Production Parity

Match PHP extensions and php.ini settings between Docker and production—a missing intl or redis extension causes deploy surprises that pass CI because nobody runs the prod image locally. Pin base image digests in production Dockerfiles so upstream tag moves do not change behavior silently.

Frequently Asked Questions

Docker vs Laravel Sail?

Sail is excellent for local dev. Production usually needs custom Dockerfile tuned for opcache, nginx, and queue workers.

One container or many?

Separate web, queue worker, and scheduler containers. Scale workers independently from HTTP traffic.

How do we handle storage/uploads?

Mount S3 via Flysystem—do not persist uploads in container filesystem.

Leave a comment

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Quick Inquiry

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Wait — don't leave yet!

Get a free project consultation. Leave your email and we'll reach out within 24 hours.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Stay ahead in AI & tech

Weekly insights on AI, software, and growth — no spam.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Book a Consultation

Pick a preferred time — we'll confirm by email.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Inquire about

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Request a directory listing

Submit your company details. Our team will review your application and publish your listing after approval.

Contact person

Company details

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Listings are reviewed manually before going live on the directory.