Bringing Search Index, Queue Lag and Cache Warmup Together for Zero-Downtime Deployments
AI generated
CI/CD
.yml
GitLab · Magento · Search Index · Queue · Cache Warmup
Zero-Downtime: Search Index, Queue Lag
and Cache Warmup Together

The symlink switch is only the beginning. Search index reindex, queue consumer restart and cache warmup are the three hidden waiting periods after every Magento deployment, and they need to be coordinated within the pipeline.

14 min read OpenSearch · RabbitMQ · Redis · Cache Warmup · Queue Consumers Magento 2.4 · GitLab CI/CD

1. The Three Hidden Sources of Downtime After a Deploy

The symlink switch in Magento deployments is atomic and takes milliseconds. Anyone who assumes the deployment is finished at that point overlooks three layers that make the difference between a good user experience and visible degradation. The first layer is the search index: after a deployment with schema changes in OpenSearch or Elasticsearch, the index has to be rebuilt. Until that is complete, search results can be incomplete or inaccurate.

The second layer is the queue consumers. Magento uses message queues through RabbitMQ or the MySQL based fallback implementation for asynchronous processing: order confirmations, indexer updates, newsletter dispatch, price calculations. Queue consumers that are running during the deployment keep working with the old code. After the symlink switch they need to be restarted so they pick up the new code. Without a controlled restart, queue consumers can keep running on the old code for minutes or even hours, or crash outright if the old code is incompatible with the new database schema.

The third layer is cache warmup. After a cache flush, Magento responds more slowly to the first requests because every cache is cold. Under heavy load this can cause noticeable latency spikes that show up in monitoring systems as an incident, even though the deployment itself was flawless. A coordinated cache warmup after the deployment prevents these latency spikes by requesting and caching the important pages ahead of time.

2. Search Index: Why an Immediate Reindex Is Not Always Necessary

Not every reindex has to run immediately after a deployment. Magento distinguishes between several indexer types with different impacts. Reindexing the product catalog is often time consuming: for large catalogs it can take hours. If the deployment does not include any changes to the catalog schema or the search configuration, a full reindex is not necessary.

The important distinction: partial indexer updates (indexer:reindex catalogsearch_fulltext) are usually much faster than a full reindex of every indexer. For deployments without schema changes it is enough to let the indexers run in Update by Schedule mode, which processes changes asynchronously. The decision about which indexers need to be rebuilt after which deployment should be documented in the deployment process rather than decided ad hoc.

3. Adding a Controlled Reindex to the Pipeline

A reindex job in the GitLab pipeline ideally runs as an asynchronous step after the symlink switch but before the verify job. It should be controlled through a variable that can be set per deployment. That way, releases without schema changes can skip the reindex job while releases with Elasticsearch mapping changes trigger it automatically.

# Controlled search index rebuild after deployment
reindex:search:
  stage: verify
  variables:
    # Set FORCE_REINDEX=1 for deployments with catalog schema changes
    FORCE_REINDEX: "0"
  script:
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
    - echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
    - |
      if [ "$FORCE_REINDEX" = "1" ]; then
        ssh "$DEPLOY_USER@$DEPLOY_HOST" bash -s << 'SSH'
        set -euo pipefail
        cd "$DEPLOY_PATH/current"
        # Rebuild only the search index, not all indexers
        bin/magento indexer:reindex catalogsearch_fulltext
        echo "Search index rebuild complete"
        # Verify index status
        bin/magento indexer:status catalogsearch_fulltext
        SSH
      else
        echo "FORCE_REINDEX not set, skipping full reindex"
        echo "Invalidating search cache only"
        ssh "$DEPLOY_USER@$DEPLOY_HOST" \
          "cd $DEPLOY_PATH/current && bin/magento cache:clean full_page"
      fi
  needs:
    - deploy:production
  when: on_success
  only:
    - tags

4. Queue Consumers: Pause, Drain and a Safe Restart

Queue consumers need to be handled deliberately during a deployment. The naive approach, simply letting them keep running, means that after the symlink switch consumers work against the new current directory while PHP processes that were already started still access the old code. This is especially critical for deployments with database migrations: an old queue consumer can try to write data based on the old schema state that the new schema does not expect.

The safe pattern: stop or pause the queue consumers before the symlink switch (bin/magento queue:consumers:stop) and restart them after the switch. The drain step makes sure that all messages currently being processed finish before the consumers are stopped, which avoids lost messages. In RabbitMQ based setups the queue can keep receiving messages while the consumers are stopped; they get processed once the consumers restart, which creates queue lag but no lost messages.

5. Managing Queue Consumers in the GitLab Pipeline

Controlling the queue consumers belongs in the deploy job, not in a manual operations runbook. A deploy job that does not handle queue consumers is incomplete for deployments that include database migrations. The following YAML block shows the safe pattern for managing queue consumers as part of the deployment.

# Queue consumer management integrated into the deploy job
deploy:production:
  stage: deploy
  script:
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
    - echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
    - RELEASE_ID=$(date +%Y%m%d-%H%M%S)
    - RELEASE_PATH="$DEPLOY_PATH/releases/$RELEASE_ID"
    # Transfer artifact to new release directory
    - ssh "$DEPLOY_USER@$DEPLOY_HOST" "mkdir -p $RELEASE_PATH"
    - rsync -az --delete ./ "$DEPLOY_USER@$DEPLOY_HOST:$RELEASE_PATH/"
    - |
      ssh "$DEPLOY_USER@$DEPLOY_HOST" bash -s << 'SSH'
      set -euo pipefail
      # Stop queue consumers before symlink switch (prevents old-code processing)
      # This command requires Magento 2.4+ with queue:consumers:stop support
      cd "$DEPLOY_PATH/current"
      bin/magento queue:consumers:stop --wait-for-running-processes || true
      # Link shared resources and switch symlink atomically
      RELEASE_PATH="$DEPLOY_PATH/releases/$RELEASE_ID"
      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"
      ln -sfn "$RELEASE_PATH" "$DEPLOY_PATH/current"
      # Post-switch: flush cache and restart consumers with new code
      cd "$DEPLOY_PATH/current"
      bin/magento cache:flush
      # Restart queue consumers: systemd or supervisor depending on setup
      systemctl restart magento-queue-consumers || supervisorctl restart magento:*
      SSH
  when: manual
  only:
    - tags

6. Cache Warmup: A Strategy for the Most Important Pages

A cache flush without a subsequent warmup leaves a Magento system with a completely cold cache. The first requests after the deployment hit unconditioned PHP processes that have to rebuild every cache: block cache, layout cache, config cache, full page cache. For a mid sized shop this state typically lasts several minutes. During that time, page load times are noticeably higher and can cause users to abandon the site.

The warmup strategy prioritizes pages by importance: the homepage, the most important category pages, the most visited product pages. A simple warmup script requests these pages with curl, forcing the caches to be generated. More advanced implementations use Magento native tools such as bin/magento cache:warm or external cache warming services such as Varnish warming scripts. The warmup should run in the verify stage after the deploy job, once the health check confirms that the new release is responding.

7. Automating Cache Warmup in the Verify Stage

Cache warmup in the GitLab pipeline is a verify stage job that requests the most important URLs after a successful deployment. The URL list should be managed as a variable or as a file in the repository so it can be updated easily whenever the shop's page structure changes. A warmup job that hits the wrong pages is useless; one that hits the right pages measurably reduces post deployment latency.

# Cache warmup job: runs after successful deployment verification
warmup:cache:
  stage: verify
  variables:
    SHOP_BASE_URL: "https://shop.mironsoft.de"
    WARMUP_TIMEOUT: "30"
  script:
    # Health check first: only warm cache if the site responds
    - curl -f --max-time 10 "$SHOP_BASE_URL/health" || exit 1
    # Warm the most important pages sequentially
    - |
      PAGES=(
        "/"
        "/sale.html"
        "/men.html"
        "/women.html"
        "/new-arrivals.html"
        "/customer/account/login"
        "/checkout/cart"
      )
      for page in "${PAGES[@]}"; do
        echo "Warming: $SHOP_BASE_URL$page"
        curl -s -o /dev/null -w "%{http_code} %{time_total}s" \
          --max-time "$WARMUP_TIMEOUT" \
          -H "X-Cache-Warmup: 1" \
          "$SHOP_BASE_URL$page" || true
        echo ""
      done
    # Verify full page cache is now active
    - |
      CACHE_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
        --max-time 10 "$SHOP_BASE_URL/")
      echo "Homepage status after warmup: $CACHE_STATUS"
      [ "$CACHE_STATUS" = "200" ] || exit 1
  needs:
    - deploy:production
  when: on_success
  only:
    - tags

8. Timing: The Order of Post-Deploy Steps

The order of the post deploy steps is not arbitrary. Queue consumers have to be stopped before the symlink switch so they do not process old messages with the new code or new messages with the old code. The symlink switch itself is the atomic core of the deployment. Immediately afterward comes the cache flush, which clears every Magento cache. Then the queue consumers restart with the new code. Only once the consumers are running and the health check is green can the cache warmup begin.

The reindex should run after the cache warmup and after the queue consumer restart, because it is I/O intensive and should not compete with the warmup traffic. Depending on catalog size, a full reindex can take anywhere from minutes to hours, so it should be scheduled outside peak hours or as a separate, manually triggered job. A clear order for these steps is the difference between a coordinated and an improvised post deploy process.

9. Coordinated vs. Uncoordinated: A Direct Comparison

The difference between a coordinated and an uncoordinated post deploy process does not show up immediately, but in the hours after a deployment. Uncoordinated processes leave visible gaps: search results show outdated data, queue consumers process messages with the wrong code, and users notice clearly longer load times. Coordinated processes close these gaps systematically.

Post-Deploy Aspect Uncoordinated Coordinated (GitLab Pipeline) Impact
Search Index Manual or forgotten Pipeline job with a variable Clean search results from minute one
Queue Consumers Keep running on old code Stopped before switch, started after No schema incompatibilities
Cache Warmup Users warm the cache themselves Automatic after verify No latency spikes after deploy
Reindex Timing Simultaneous with warmup After warmup, outside peak hours Lower server load
Error Diagnosis No log structure Pipeline logs per step Fast root cause analysis

The investment required for coordinated post deploy steps is manageable. The three jobs, reindex, queue consumer restart and cache warmup, can be integrated into an existing pipeline within a few hours. The savings in avoided incidents, shorter latency spikes and faster error diagnosis outweigh the effort from the very first production use.

10. Summary

Bringing search index, queue lag and cache warmup together for zero downtime means thinking about the deployment process beyond the symlink switch. The switch itself is the fastest step; the three post deploy layers are the real challenge. Search index reindexing has to be a deliberate decision for every deployment: not always necessary, but coordinated whenever it is. Queue consumers must be stopped before the switch and restarted with the new code afterward. Cache warmup prevents latency spikes after the deploy and protects the user experience in the first minutes after the release.

In the GitLab pipeline, these three steps are implemented as jobs in the verify stage that run after the successful deploy job. Each job can be configured, observed and debugged individually. That is the difference between a deployment process that ends at the symlink switch and one that is only complete once the shop responds fully and performs well.

Zero Downtime: Search Index, Queue and Cache, the Essentials at a Glance

Search Index

Not necessary after every deploy. The FORCE_REINDEX variable controls whether catalogsearch_fulltext gets rebuilt.

Queue Consumers

Stop before the symlink switch, restart afterward. Prevents schema incompatibilities during database migrations.

Cache Warmup

Request the most important pages ahead of time after the verify health check. Prevents latency spikes in the first minutes after the deploy.

Order

Stop consumers → symlink switch → cache flush → start consumers → health check → warmup → reindex (if needed).

11. FAQ: Zero Downtime for Search Index, Queue and Cache Warmup

1Does a reindex have to run after every deploy?
No. Only for schema changes, Elasticsearch/OpenSearch mapping changes, or changed search configurations. For pure code deploys, "Update by Schedule" is enough.
2What happens if consumers are running during a deploy?
After the symlink switch they can end up working with the new code in the old context. With database migrations this leads to schema incompatibilities. Stop consumers before the switch, restart them afterward.
3How long does a cache warmup take?
For 10 to 20 important pages, 30 to 120 seconds. Limit the warmup to the most critical pages: the homepage, top categories, and the most visited products.
4Reindex and warmup in parallel?
Not recommended. The reindex puts load on I/O and CPU, which slows down warmup requests. Run the warmup first, then start the reindex.
5What is queue lag?
The buildup of unprocessed messages while the consumers are paused. It is temporary and gets worked off after the restart. No messages are lost.
6Is maintenance mode required to stop consumers?
No. bin/magento queue:consumers:stop stops the consumers without maintenance mode. This is the preferred approach for zero downtime deployments.
7How do I prioritize pages for the warmup?
By traffic volume from analytics: the homepage, top level categories, the most visited products. Maintain the list as a file in the repository.
8How do I restart consumers in Magento 2.4?
Through systemd, Supervisor, or bin/magento queue:consumers:start. The method depends on the server setup and must be explicitly defined in the deployment script.
9catalogsearch_fulltext vs. other indexers?
catalogsearch_fulltext rebuilds the full text search index in OpenSearch and is I/O intensive. Other indexers such as catalog_product_price are often faster.
10How do I know the warmup worked?
Through HTTP response headers: Varnish sets X-Cache: HIT, Magento FPC sets X-Magento-Cache-Control: public. Check with curl -I against the warmed URLs.