Redis, RabbitMQ, and OpenSearch in Zero-Downtime Deployments
AI generated
CI/CD
.yml
GitLab · Redis · RabbitMQ · OpenSearch · Zero Downtime
Redis, RabbitMQ, and OpenSearch
in Zero-Downtime Deployments

The symlink switch is not the end of a zero-downtime deployment: it is the start of a critical phase in which Redis, RabbitMQ, and OpenSearch must be compatible with the new code base. These three services are too often treated as peripheral details, even though they are frequently the root cause of post-deploy failures.

13 min read Redis · cache:flush · RabbitMQ · OpenSearch · Index Compatibility GitLab 16+ · Magento 2.4 · PHP 8.4

1. Why These Three Services Need Special Attention

Redis, RabbitMQ, and OpenSearch are not simple data stores that can be ignored during a deployment. They hold state that is specific to a particular code version: Redis cache entries are encoded with the data structure of the old release, RabbitMQ messages were produced by consumer code that may have changed, and OpenSearch indices reflect a product catalog structure that may need new fields or changed mapping types.

The problem does not show up with simple deployments, but with deployments that change data structures. A new attribute on a product model, a changed cache key format, a new message format in a queue: these changes are clear in the code base, but their impact on running services is frequently left out of the deployment process. The result is inconsistent state that only manifests as errors after the symlink switch.

The right approach is not to shut these services down during the deployment, since that would defeat the purpose of zero downtime. Instead, it comes down to precise timing: flush Redis only after the symlink switch, briefly pause queue consumers during the critical release transition, and treat the OpenSearch reindex as a scheduled follow-up step using an alias swap. These patterns make it possible to achieve zero downtime while still guaranteeing a clean state transition.

2. Redis in Magento: Cache, Sessions, and Full Page Cache

Magento typically uses Redis for three separate databases: cache (db 0), full page cache (db 1), and sessions (db 2). This separation matters for deployments because the three areas need different flush strategies. The regular cache holds serialized PHP objects and configuration data that are often incompatible after a code change. The full page cache holds fully rendered HTML pages that become stale after a template change. Sessions hold user session data that usually stays compatible after a deployment, but must be cleared when session data structures change.

The most common source of errors: the cache is flushed before the symlink switch, but the new code only starts running after the switch, so the old release refills the cache with entries in the old structure. When the symlink then switches and the new code has to work with entries in the old structure, deserialization errors follow. The correct sequence is to flush the cache after the symlink switch, not before it.

# Deploy job with correct Redis flush timing, after symlink switch
deploy:production:
  stage: deploy
  script:
    - RELEASE_ID="$(date +%Y%m%d-%H%M%S)-${CI_COMMIT_SHORT_SHA}"
    - RELEASE_PATH="${DEPLOY_PATH}/releases/${RELEASE_ID}"

    # Step 1: Transfer artifact to new release directory
    - rsync -az ./ "${DEPLOY_USER}@${DEPLOY_HOST}:${RELEASE_PATH}/"

    # Step 2: Link shared files (env.php, media)
    - ssh "${DEPLOY_USER}@${DEPLOY_HOST}" "
        ln -sfn ${DEPLOY_PATH}/shared/app/etc/env.php ${RELEASE_PATH}/app/etc/env.php &&
        ln -sfn ${DEPLOY_PATH}/shared/pub/media ${RELEASE_PATH}/pub/media"

    # Step 3: Run setup:upgrade in new release (before symlink!)
    - ssh "${DEPLOY_USER}@${DEPLOY_HOST}"
        "cd ${RELEASE_PATH} && php bin/magento setup:upgrade --keep-generated"

    # Step 4: Atomic symlink switch
    - ssh "${DEPLOY_USER}@${DEPLOY_HOST}"
        "ln -sfn ${RELEASE_PATH} ${DEPLOY_PATH}/current"

    # Step 5: Flush Redis AFTER symlink, new code now handles cache rebuilding
    - ssh "${DEPLOY_USER}@${DEPLOY_HOST}"
        "cd ${DEPLOY_PATH}/current && php bin/magento cache:flush"

    # Step 6: Verify Redis is still connected
    - ssh "${DEPLOY_USER}@${DEPLOY_HOST}"
        "redis-cli -h ${REDIS_HOST} ping | grep -q PONG || exit 1"

3. Redis Cache Invalidation in the Deployment Process

The strategy for Redis cache invalidation depends on the type of deployment. For pure code changes without database migrations, a full cache flush after the symlink switch is sufficient and safe. For deployments with database migrations, new configuration options, or changed serialization structures, the cache must be flushed before the symlink switch, and no stale entry may be allowed to land in the cache between the old and new code.

A frequently overlooked detail: bin/magento cache:flush and bin/magento cache:clean have different effects. cache:flush completely deletes all keys in the configured Redis backend. cache:clean only marks Magento specific cache entries as invalid, leaving other backend data untouched. For deployments, cache:flush is the safer choice because it makes no assumptions about which keys might be incompatible.

4. RabbitMQ: Queue Consumers and Message Persistence

RabbitMQ in Magento handles asynchronous operations through queue consumers: sending emails, indexer updates, order processing, and other background tasks. These consumers run as separate processes that continuously pull messages from the queues. The problem during deployment: a consumer process still running the old code processes messages with the old consumer logic while the new code is already active. Depending on the type of message, this can lead to incompatibilities.

The situation is especially critical when a deployment changes the message format of a queue. Messages that were produced by the old code and have not yet been processed still have the old format. Once the consumer is upgraded to the new format, it may no longer be able to correctly deserialize the old messages. In most Magento deployments this is not an issue, because the message format rarely changes, but when it does, the consumer process must be paused during the critical time window.

5. Safely Pausing and Reactivating Queue Consumers

Pausing and reactivating RabbitMQ consumer processes in Magento can be implemented through Supervisor or systemd. The safest point to pause them is immediately before the symlink switch. The consumer processes are stopped, the symlink switch happens, setup:upgrade and the cache flush run, and then the consumers are restarted with the new code. The window is typically under 30 seconds, during which messages accumulate in the queue but are never lost.

Starting with version 2.4, Magento also offers the option to manage consumers through a PID file. bin/magento queue:consumers:start ConsumerName --pid-file-path=/tmp/consumer.pid starts a consumer and stores the PID. This allows a clean stop via kill -SIGTERM $(cat /tmp/consumer.pid), which lets the consumer finish processing the current message before it shuts down. This graceful shutdown prevents a message from being interrupted mid processing.

# Deployment with RabbitMQ consumer management and OpenSearch reindex
deploy:with-services:
  stage: deploy
  script:
    - RELEASE_PATH="${DEPLOY_PATH}/releases/$(date +%Y%m%d-%H%M%S)"

    # Step 1: Stop queue consumers gracefully before symlink switch
    - ssh "${DEPLOY_USER}@${DEPLOY_HOST}" "
        supervisorctl stop magento-consumers:* 2>/dev/null || true &&
        echo 'Waiting for consumers to finish current messages...' &&
        sleep 5"

    # Step 2: Transfer artifact and link shared files
    - rsync -az ./ "${DEPLOY_USER}@${DEPLOY_HOST}:${RELEASE_PATH}/"
    - ssh "${DEPLOY_USER}@${DEPLOY_HOST}" "
        ln -sfn ${DEPLOY_PATH}/shared/app/etc/env.php ${RELEASE_PATH}/app/etc/env.php &&
        ln -sfn ${DEPLOY_PATH}/shared/pub/media ${RELEASE_PATH}/pub/media"

    # Step 3: Run setup:upgrade in new release
    - ssh "${DEPLOY_USER}@${DEPLOY_HOST}"
        "cd ${RELEASE_PATH} && php bin/magento setup:upgrade --keep-generated"

    # Step 4: Atomic symlink switch
    - ssh "${DEPLOY_USER}@${DEPLOY_HOST}"
        "ln -sfn ${RELEASE_PATH} ${DEPLOY_PATH}/current"

    # Step 5: Flush Redis cache with new code active
    - ssh "${DEPLOY_USER}@${DEPLOY_HOST}"
        "cd ${DEPLOY_PATH}/current && php bin/magento cache:flush"

    # Step 6: Restart consumers with new code
    - ssh "${DEPLOY_USER}@${DEPLOY_HOST}"
        "supervisorctl start magento-consumers:* 2>/dev/null || true"

    # Step 7: Trigger OpenSearch reindex asynchronously (non-blocking)
    - ssh "${DEPLOY_USER}@${DEPLOY_HOST}" "
        cd ${DEPLOY_PATH}/current &&
        nohup php bin/magento indexer:reindex catalogsearch_fulltext \
          > ${DEPLOY_PATH}/shared/var/log/reindex.log 2>&1 &
        echo 'OpenSearch reindex started in background'"

6. OpenSearch: Index Structure and Mapping Compatibility

OpenSearch in Magento holds the product catalog in an index with a defined mapping. When a deployment adds new product attributes, changes existing attributes, or modifies the mapping schema, the index has to be updated. The problem: a full reindex for a large catalog can take anywhere from minutes to hours. During the reindex, search results can be incomplete or incorrect.

The professional solution for zero-downtime index updates is the alias swap pattern: a new index is built in the background (for example magento_catalog_product_v2) while the active alias magento_catalog_product still points at the old index. Only once the new index is fully built does the alias get switched over to the new index. The switch is atomic and invisible to the application. Magento supports this pattern natively starting with version 2.4.6 through the OpenSearch adapter.

7. Index Updates in a Zero-Downtime Context

For deployments without mapping changes, an incremental reindex after the deployment is sufficient. Magento marks products that have changed since the last full index as "require reindex", and an incremental reindex only processes those products, not the entire catalog. This process can start asynchronously after the deployment and blocks neither the symlink switch nor the cache flush phase.

For deployments with mapping changes, meaning cases where new fields need to appear in the index, the alias swap pattern is required. In the GitLab pipeline, that means an additional job after the deploy job that starts the reindex and either waits for it to finish or runs asynchronously and is watched through separate monitoring. In that case, the verify job should include a check confirming that search on the site returns correct results.

8. GitLab Pipeline Integration for All Three Services

Redis, RabbitMQ, and OpenSearch are integrated into the GitLab pipeline through dedicated steps inside the deploy job, not through separate jobs. The reason: the order of the steps is critical and has to be carried out atomically. A separate job would leave a gap between the end of the deploy job and the start of a service management job, during which consumers could keep running with the wrong code version.

The verify stage should explicitly check all three services: Redis reachability via redis-cli ping, queue consumer status via supervisorctl status or bin/magento queue:consumers:list, and OpenSearch reachability via a simple HTTP check against the cluster endpoint. Together, these checks take under 10 seconds and give a team the confidence that all services are working correctly after the deployment.

9. Comparing Services and Their Deployment Risks

The three services have different risk profiles during deployment. Redis is affected most often because Magento keeps all critical cache structures in Redis. RabbitMQ is rarely critical, except when message formats change. OpenSearch reindex delays are the most common cause of "search shows outdated data" reports after deployments.

Service Main Risk Deployment Action Timing
Redis (Cache) Incompatible serialized objects cache:flush After symlink switch
Redis (FPC) Stale HTML pages cache:flush (FPC) After symlink switch
RabbitMQ Consumer running old code Stop/Start consumers Before/After symlink
OpenSearch Stale or incompatible indices Async reindex After deployment
Redis (Sessions) Incompatible session structure Flush only on structure change Before symlink (rare)

The table shows that most deployment actions for these services happen after the symlink switch, with the exception of the queue consumer pause, which begins shortly before the switch. This sequence is not arbitrary: it ensures that the new code is the first to work with the new system state, without the old code having filled the data structures with incompatible data.

10. Summary

Redis, RabbitMQ, and OpenSearch are not passive services that simply survive a deployment unscathed. They hold version specific state that must be handled explicitly after a code change. The core principles: flush the Redis cache after the symlink switch, briefly pause queue consumers and restart them with the new code, trigger the OpenSearch reindex asynchronously after the deployment, and check all three services for reachability and correct state in the verify stage.

The good news: these steps can be fully integrated into a GitLab pipeline without creating any downtime. The critical window, from consumer pause to cache flush, lasts under 30 seconds. During that window, messages accumulate in the queue and users may still be served cached pages, but the application never goes down. That is the practical meaning of zero downtime: no outages, just a short window with controlled limitations.

Redis, RabbitMQ, and OpenSearch in Deployments: The Key Points at a Glance

Redis Flush Timing

Run cache:flush after the symlink switch, not before it. The new code fills the cache with compatible structures.

Queue Consumers

Graceful stop before the symlink switch, restart afterward. Maximum pause window: 30 seconds. Messages accumulate, they are never lost.

OpenSearch Reindex

Start it asynchronously after the deployment. For mapping changes, use the alias swap pattern. The verify job checks search after the deployment.

Verify Stage

Redis ping, consumer status check, and OpenSearch HTTP check as part of the verify stage after every deployment touching these services.

11. FAQ: Redis, RabbitMQ, and OpenSearch in Deployments

1Why should the Redis cache be flushed after the symlink switch?
Before the switch, the old code refills the cache with incompatible entries. After the switch, the new code fills it with compatible structures: that is the safe sequence.
2What is the difference between cache:flush and cache:clean?
cache:flush deletes all keys completely. cache:clean only marks Magento entries as invalid. For deployments, cache:flush is safer.
3Are messages lost in RabbitMQ when consumers are paused?
No. RabbitMQ is a persistent broker. Messages accumulate in the queue and get processed after the consumers restart.
4How long should the consumer pause window last at most?
Ideally under 30 seconds. Stop consumers, switch the symlink, run setup:upgrade, flush the cache, start consumers. Anything longer points to a problem in the process.
5What is the alias swap pattern for OpenSearch?
Build a new index in the background, then atomically point the alias at the new index. Invisible to the application, with no interruption to search results.
6Does the OpenSearch index need to be rebuilt with every deployment?
No, only for mapping changes. For pure code deployments, an incremental reindex of the changed products is enough.
7How do I check in the verify stage that search is working correctly?
With curl against a search request: curl -f 'https://shop.example.com/catalogsearch/result/?q=test'. HTTP 200 confirms reachability. Use OpenSearch cluster health for a deeper check.
8Can Redis session data be lost during a deployment?
Not during a normal deployment without a session structure change. Sessions remain intact. Sessions only need to be cleared when the session data structure changes.
9How can I tell that a consumer is running the wrong code version?
Check supervisorctl status or the process start time. A consumer that started before the symlink switch and was not restarted is running the old code.
10What happens if Redis is unreachable during the deployment?
Magento falls back to file based caching, with a significant performance hit. The verify job should check redis-cli ping and fail if it does not get PONG back.