Run laravel queue worker in docker

Stefan Bogdanescu

Founder & Senior Architect · 2026-06-29

Laravel Company

Running Laravel Queue Workers Effectively in Docker: Avoiding the 502 Gateway Error

Running background jobs, especially queue workers, is crucial for any robust application built with Laravel. When you containerize your application using Docker and Docker Compose, a common hurdle developers face is getting these long-running processes—like php artisan queue:work --daemon—to run reliably without disrupting the web server (like Nginx) or causing connection timeouts (the infamous 502 Bad Gateway error).

This post will dive deep into why you are encountering issues, and provide a robust, production-ready architecture for running your Laravel queue workers inside Docker.

The Root Cause: Why You Get a 502 Error

The 502 Bad Gateway error typically occurs when the front-end web server (Nginx) successfully receives a request but cannot establish a connection with the backend application process it delegates the work to.

In your setup, if you try to force php artisan queue:work directly within the main PHP service container or via an entrypoint script that runs concurrently with FPM, you create a conflict. The web server expects the primary PHP process (often PHP-FPM) to handle requests. When a long-running command like a queue worker monopolizes resources or interferes with the expected request handling flow, Nginx loses connection, resulting in the 502 error.

The solution is simple but requires architectural separation: Decouple your concerns. The web server should only handle HTTP requests, and the queue worker should run as an independent, persistent background service.

The Docker Compose Solution: Decoupling Services

Instead of trying to force the queue worker into the same container that serves web traffic, we leverage Docker Compose's ability to run multiple, isolated services communicating over a network. We will create three distinct services: webserver (Nginx), app (PHP-FPM/Application logic), and a dedicated worker.

Step 1: Revising the docker-compose.yml for Separation

We need to modify your existing structure to introduce a separate service specifically for queue processing. This approach ensures that if the worker crashes or restarts, it does not affect the web server's ability to serve requests.

Here is how you should restructure your configuration:

yaml
version: '3'
services:

  # Nginx Service (Handles HTTP traffic)
  webserver:
    image: nginx:alpine
    container_name: LibraryWebserver
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./:/var/www
      - ./nginx/conf.d/:/etc/nginx/conf.d/
    networks:
      - app-network

  # MySQL Service (Database)
  db:
    image: mysql:5.7.22
    container_name: Librarydb
    restart: unless-stopped
    tty: true
    volumes:
      - dbdata:/var/lib/mysql
    environment:
      MYSQL_DATABASE: library
      MYSQL_ROOT_PASSWORD: Library!23
      MYSQL_USER: root
      MYSQL_PASSWORD: Library!23
      SERVICE_TAGS: dev
      SERVICE_NAME: mysql
    networks:
      - app-network

  # PHP Application Service (Handles web requests)
  app:
    build:
      context: .
      dockerfile: Dockerfile
    image: hoseinnormohamadi/lumen:Library
    container_name: LibraryApp
    restart: unless-stopped
    tty: true
    environment:
      SERVICE_NAME: app
      SERVICE_TAGS: dev
    working_dir: /var/www
    volumes:
      - ./:/var/www
    networks:
      - app-network

  # Dedicated Queue Worker Service (Runs background jobs)
  worker:
    build:
      context: .
      dockerfile: Dockerfile # Use the same base image, potentially a smaller one if only PHP is needed
    image: hoseinnormohamadi/lumen:Library
    container_name: LibraryWorker
    restart: always
    volumes:
      - ./:/var/www
    environment:
      SERVICE_NAME: app
      SERVICE_TAGS: dev
    networks:
      - app-network

# Docker Networks and Volumes remain the same
networks:
  app-network:
    driver: bridge
volumes:
  dbdata:
    driver: local

Step 2: Implementing the Queue Worker Command

Now that we have a dedicated worker service, we define its entrypoint to execute the queue command. For long-running processes in Docker, it's best practice to use an explicit shell script or direct execution within the container's entrypoint.

In your application's Dockerfile or by defining a custom entrypoint for the worker service, you will ensure that when this container starts, it immediately runs the queue command and keeps running:

bash
# Example entrypoint modification (in Dockerfile or docker-compose run)
ENTRYPOINT ["/bin/sh", "-c"]
CMD ["php artisan queue:work --daemon"]

By setting restart: always on the worker service, Docker ensures that if the worker process is killed for any reason, it will automatically be restarted, making your queue processing highly resilient. This separation pattern aligns perfectly with the principles of scalable microservices often discussed in the Laravel ecosystem, emphasizing clear boundaries between web responsibilities and background processing.

Conclusion: Building Resilient Laravel Applications

Running queue workers within Docker requires shifting your mindset from running a single monolithic command to designing a multi-service architecture. By decoupling your concerns—separating HTTP serving (Nginx/App) from background processing (Worker)—you eliminate the conflict that causes 502 errors and create an application that is both scalable and resilient.

Remember, leveraging Docker Compose allows you to manage complex dependencies efficiently. As you continue to build sophisticated applications using Laravel, embracing this containerized approach ensures your infrastructure scales alongside your code. For more deep dives into how Laravel integrates with modern infrastructure, always check out resources from laravelcompany.com. Happy coding!