Cleanly Connecting Redis, RabbitMQ, OpenSearch, and MySQL in Docker Stacks
AI generated
Docker · Redis · RabbitMQ · OpenSearch · MySQL
Redis, RabbitMQ, OpenSearch, and MySQL
connected cleanly in Docker stacks

A Magento Docker stack consists of at least five services: PHP-FPM, Nginx, MySQL, Redis, and OpenSearch. Add RabbitMQ for message queues and Varnish for full page caching. How these services are correctly networked, persisted, and started in the right order determines stability and maintainability.

14 min read Redis · RabbitMQ · OpenSearch · MySQL · Health Checks Magento 2.4 · Docker Compose · Persistent Volumes

1. The Service Architecture of a Magento Docker Stack

A complete Magento Docker stack consists of a set of services that need to work together despite having very different characteristics. MySQL is stateful and needs persistent volumes, warm up time on first start, and health checks before other services connect to it. Redis acts as both cache and session store and can run persistently or ephemerally, depending on configuration. OpenSearch requires specific kernel parameters (vm.max_map_count) and significantly more RAM than the other services. RabbitMQ manages message queues for asynchronous Magento operations such as inventory updates and newsletter dispatch.

The central problem when connecting these services in a Magento Docker stack is startup order. Docker Compose starts all services in parallel by default. PHP-FPM tries to connect to MySQL before MySQL has finished initializing, which causes connection errors on the very first start. Health checks combined with depends_on: condition: service_healthy solve the problem: PHP-FPM only starts once MySQL reports as healthy. This startup sequence needs to be explicitly configured for every service in the stack.

2. MySQL in Docker: Volumes, Init Scripts, and Health Checks

MySQL in a Magento Docker stack absolutely needs a persistent named volume for its database files. Without a volume, all data is lost on every docker compose down. The volume must be properly populated during the very first initialization: MySQL waits for initialization on startup, which can take several seconds. The health check using mysqladmin ping ensures that other services only connect once MySQL is truly ready, not merely once the container has started.

Init scripts for MySQL in a Magento Docker stack live in the /docker-entrypoint-initdb.d/ directory. All SQL and shell files placed there run on the very first start, while the data volume is still empty. That's the right place to create the Magento database, set the character set, and create database users. On a later docker compose down && docker compose up with an existing volume, these init scripts no longer run, which is the correct behavior.


# compose.yaml: MySQL, Redis, OpenSearch, RabbitMQ for Magento stack
services:
  mysql:
    image: mysql:8.4
    environment:
      MYSQL_ROOT_PASSWORD: "${MYSQL_ROOT_PASSWORD}"
      MYSQL_DATABASE: magento
      MYSQL_USER: magento
      MYSQL_PASSWORD: "${MYSQL_PASSWORD}"
    volumes:
      - mysql-data:/var/lib/mysql
      - ./env/mysql/conf.d:/etc/mysql/conf.d:ro
      - ./env/mysql/init:/docker-entrypoint-initdb.d:ro
    networks:
      - db
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "--password=${MYSQL_ROOT_PASSWORD}"]
      interval: 10s
      timeout: 5s
      retries: 10
      start_period: 30s   # Give MySQL time to initialize on first start
    restart: unless-stopped

  redis:
    image: redis:7.4-alpine
    command: >
      redis-server
      --maxmemory 512mb
      --maxmemory-policy allkeys-lru
      --save 60 1000
      --appendonly no
    volumes:
      - redis-data:/data
    networks:
      - db
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5
    restart: unless-stopped

3. Redis in Docker: Cache, Sessions, and Persistent Mode

Redis in a Magento Docker stack takes on two roles: full page cache backend and session storage. Magento can configure Redis for both purposes, but the configuration parameters should differ between the two roles. The cache store can run with an aggressive eviction policy (allkeys-lru) and no persistence at all, since the cache can be rebuilt from the backend at any time. The session store, on the other hand, needs persistence: sessions must survive a container restart, otherwise every logged in customer gets signed out.

The cleanest solution for a Magento Docker stack is two Redis instances: one for cache with --maxmemory-policy allkeys-lru and no RDB persistence, and one for sessions with --maxmemory-policy noeviction and append only file (AOF) persistence enabled. This slightly increases resource requirements but eliminates the risk of cache eviction wiping out a session or session writes degrading cache performance. Both Redis instances are then registered as separate backends in env.php.

4. OpenSearch in Docker: Memory Limits and Indexing

OpenSearch in a Magento Docker stack is the most resource intensive service. It needs at least 1 GB of heap memory for a small Magento installation, and the kernel parameter vm.max_map_count must be set to at least 262144. Without this parameter, OpenSearch won't start. On Linux hosts the parameter is set with sysctl -w vm.max_map_count=262144 and made permanent in /etc/sysctl.conf. On Docker Desktop systems (macOS, Windows), the VM already sets this parameter, which is why OpenSearch starts there even without explicit configuration.

In a Magento Docker stack, OpenSearch should be configured with explicit memory limits: ES_JAVA_OPTS: "-Xms512m -Xmx512m" for the JVM heap size. These two values should be identical, so OpenSearch fully allocates the heap at startup instead of scaling it dynamically. In development mode OpenSearch runs as a single node cluster (discovery.type: single-node), and security features can be disabled for local development. In production, security is mandatory.


# OpenSearch and RabbitMQ service definitions for Magento Docker stack
services:
  opensearch:
    image: opensearchproject/opensearch:2.18
    environment:
      - discovery.type=single-node
      - bootstrap.memory_lock=true
      - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m"
      # Disable security for local development only
      - DISABLE_SECURITY_PLUGIN=true
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536
        hard: 65536
    volumes:
      - opensearch-data:/usr/share/opensearch/data
    networks:
      - db
    healthcheck:
      test: ["CMD-SHELL", "curl -sf http://localhost:9200/_cluster/health | grep -qv '\"status\":\"red\"'"]
      interval: 15s
      timeout: 10s
      retries: 12
      start_period: 60s   # OpenSearch needs significant startup time
    restart: unless-stopped

  rabbitmq:
    image: rabbitmq:3.13-management-alpine
    environment:
      RABBITMQ_DEFAULT_USER: "${RABBITMQ_USER:-magento}"
      RABBITMQ_DEFAULT_PASS: "${RABBITMQ_PASSWORD}"
      RABBITMQ_DEFAULT_VHOST: "magento"
    volumes:
      - rabbitmq-data:/var/lib/rabbitmq
    networks:
      - db
    healthcheck:
      test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
      interval: 10s
      timeout: 5s
      retries: 10
      start_period: 30s
    restart: unless-stopped
    # Expose management UI only in development
    ports:
      - "15672:15672"  # Management UI, remove in production

5. RabbitMQ in Docker: Virtual Hosts and the Management UI

RabbitMQ in a Magento Docker stack manages message queues for asynchronous Magento processing. Magento uses RabbitMQ for inventory reservations on orders, asynchronous product updates via the Bulk API, and newsletter dispatch. Without RabbitMQ, these operations run synchronously, which causes timeouts under heavy traffic. Like MySQL, RabbitMQ needs a persistent volume so queue messages survive a container restart.

The RabbitMQ configuration for a Magento Docker stack includes a dedicated virtual host for Magento (RABBITMQ_DEFAULT_VHOST=magento), its own credentials, and a persistent data volume. The management UI on port 15672 is a useful tool during development for watching queue depths, unprocessed messages, and consumer status. In production, the management UI port should not be exposed to the host, or it should be protected by an additional authentication layer.

6. Controlling Startup Order with Health Checks

Startup order in a Magento Docker stack is one of the most common sources of errors in new Docker setups. depends_on: mysql only waits until the MySQL container has started, not until MySQL is ready to accept connections. The correct solution is depends_on: condition: service_healthy, which only proceeds once the dependent service's health check reports healthy. That requires every service in the stack to have a health check configured.

In a Magento Docker stack, the optimal startup order looks like this: first start MySQL, Redis, and OpenSearch and wait for them to become healthy. Then start RabbitMQ. Then PHP-FPM, which depends on all the database services. Finally Nginx, which depends on PHP-FPM. The start_period in the health check gives services extra time on their very first start, since OpenSearch needs considerably longer than Redis. This configuration prevents connection errors during stack startup and makes the first docker compose up reliable.


# Startup order using health checks: PHP-FPM waits for all backends
services:
  phpfpm:
    build:
      context: .
      target: phpfpm
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_healthy
      opensearch:
        condition: service_healthy
      rabbitmq:
        condition: service_healthy
    networks:
      - backend
      - db
    healthcheck:
      test: ["CMD-SHELL", "php-fpm-healthcheck || exit 1"]
      interval: 10s
      timeout: 5s
      retries: 5

  nginx:
    image: nginx:1.27-alpine
    depends_on:
      phpfpm:
        condition: service_healthy
    networks:
      - frontend
      - backend
    ports:
      - "80:80"
      - "443:443"

volumes:
  mysql-data:
  redis-data:
  redis-session-data:   # Separate volume for session Redis instance
  opensearch-data:
  rabbitmq-data:

7. Network Segmentation in a Multi-Service Stack

In a Magento Docker stack with five or more services, network segmentation isn't optional, it's a necessary practice. MySQL, Redis, OpenSearch, and RabbitMQ belong on an internal database network with internal: true. That prevents these services from opening outbound connections to the internet and blocks direct access from the host or the frontend network. PHP-FPM sits on both the backend network (reachable from Nginx) and the database network (reachable from every database service). Nginx sits on the frontend network with a host port and on the backend network for PHP-FPM.

One important exception in a Magento Docker stack: the RabbitMQ management UI, and sometimes MySQL, need direct host port mappings during development so developers can use database tools like TablePlus or the RabbitMQ browser. These ports are only defined in compose.dev.yaml, never in the production compose file. The pattern is simple: base infrastructure without host ports, with a development override that adds the ports that are actually needed.

8. Connection Configuration in Magento's env.php

The connection configuration for every service in a Magento Docker stack lives in app/etc/env.php. All service hostnames match the Compose service names, because Docker's built in DNS resolves those names inside the network. MySQL is referenced as mysql, Redis as redis, OpenSearch as opensearch, RabbitMQ as rabbitmq. No fixed IP addresses, no port forwarding to the host, just direct container to container communication over the internal network.

For a Magento Docker stack in development, it makes sense not to version env.php in the Git repository, but instead to provide an env.php.sample using the Docker service names as a template. A setup script copies the sample file and fills in the credentials from environment variables. That makes onboarding new developers simple: start docker compose up, run the setup script, done. Every service connection is configured correctly and automatically.

9. Service Configurations Compared

Getting the configuration right for each service in a Magento Docker stack has a direct impact on stability and performance. The table below shows the most critical parameters for each service.

Service Persistence Health Check Critical Parameter
MySQL 8.4 Mandatory mysqladmin ping start_period: 30s
Redis (Cache) Optional redis-cli ping allkeys-lru eviction
Redis (Sessions) Mandatory redis-cli ping noeviction + AOF
OpenSearch 2.x Recommended cluster/health API vm.max_map_count, 1GB heap
RabbitMQ 3.13 Mandatory rabbitmq-diagnostics ping Virtual host for Magento

Every service in a Magento Docker stack needs persistent volumes in production. In development, Redis cache volumes can be skipped, because the cache rebuilds quickly after a restart. MySQL, RabbitMQ, and session Redis volumes must be persistent in every environment. OpenSearch volumes are recommended, because reindexing takes time, especially with large product catalogs.

Mironsoft

Magento Docker stacks, service configuration, and production ready infrastructure

Need a clean Magento Docker stack?

We design and build your complete Magento Docker stack with Redis, RabbitMQ, OpenSearch, and MySQL, including health checks, network segmentation, and persistent volumes.

Stack Design

Complete Compose configuration with all Magento services and the correct startup order

Health Checks

Correct health check configuration for every service with service_healthy depends_on

Production Hardening

Network segmentation, secrets management, and resource limits for production operations

10. Summary

Cleanly connecting a Magento Docker stack with Redis, RabbitMQ, OpenSearch, and MySQL requires three things: persistent volumes for every stateful service, health checks for every database service instance, and correctly configured depends_on: condition: service_healthy for startup order. Network segmentation with internal: true for the database network prevents unnecessary exposure while documenting the allowed communication paths at the same time.

Running Redis as two instances, one for cache and one for sessions, prevents the two roles from interfering with each other. OpenSearch needs vm.max_map_count and enough heap memory. RabbitMQ needs its own virtual host for Magento. MySQL needs a start_period buffer in its health check to allow for initialization. All service hostnames in env.php match the Compose service names, since Docker's built in DNS handles the resolution. The result is a Magento Docker stack that starts reliably on the very first docker compose up and stays stable in operation.

Redis, RabbitMQ, OpenSearch, MySQL in Docker: the essentials at a glance

Startup Order

depends_on: condition: service_healthy instead of depends_on without a condition. Every service needs a health check. start_period gives time for initialization.

Persistent Volumes

MySQL, RabbitMQ, and session Redis absolutely need persistent volumes. Cache Redis is optional. An OpenSearch volume saves reindexing time.

Redis Separation

Two Redis instances: cache with allkeys-lru, sessions with noeviction plus AOF persistence. Prevents cache pressure from evicting sessions.

OpenSearch Requirements

vm.max_map_count=262144 on the host. At least 512 MB heap. discovery.type=single-node for development. start_period: 60s in the health check.

11. FAQ: Redis, RabbitMQ, OpenSearch, and MySQL in Docker Stacks

1PHP-FPM won't connect to MySQL on first start?
MySQL needs time to initialize. depends_on without a condition only waits for the container to start. depends_on: condition: service_healthy plus a mysqladmin ping health check fixes it.
2OpenSearch won't start in Docker?
vm.max_map_count too low, at least 262144 is required. sysctl -w vm.max_map_count=262144 on Linux. Docker Desktop sets the value automatically.
3Redis for cache and sessions on one instance?
Not recommended. allkeys-lru evicts sessions. Two separate instances: cache with lru, sessions with noeviction plus AOF.
4Does RabbitMQ need a persistent volume?
Yes, always. Without a volume: lost queue messages on restart. /var/lib/rabbitmq must be persistent, otherwise order data can be lost.
5Service names in Magento's env.php?
Compose service name equals the hostname on the network. mysql becomes host: mysql, redis becomes host: redis. Docker DNS resolves these names automatically.
6What is start_period in a health check?
Extra time on startup before failures count as unhealthy. MySQL 30s, OpenSearch 60s. Prevents marking a container unhealthy too early.
7Checking OpenSearch status?
curl http://localhost:9200/_cluster/health. Status green/yellow means ok, red means a problem. Inside the container: curl -s http://localhost:9200/_cat/indices for the index list.
8Expose MySQL port in the production stack?
No. Security risk. MySQL stays on the internal network. SSH tunnel for external access. Dev port only in compose.dev.yaml.
9Migrating MySQL data between volumes?
mysqldump inside the container for a SQL dump. An alpine container with both volumes mounted for a direct volume copy with cp -av. Then transfer the volume to the new host.
10RAM needed for a complete Magento Docker stack?
Minimum about 3 GB: MySQL 512MB, OpenSearch 1GB, Redis 512MB, RabbitMQ 256MB, PHP-FPM 512MB. Recommended: 8 GB for comfortable work on macOS.