Skip to content
ExponaBLOG
ELASTICSEARCH & SEARCH ENGINEERING · DAY 032 / 100

Index aliases: concepts, trade-offs and production lessons

Change indexes without changing code.

Expona Admin6 min read

When you're running Elasticsearch in production, the moment you need to reindex—whether to add a field, change an analyzer, or migrate to a new cluster—you face a hard choice: take downtime, or build complex client-side logic to handle both old and new indexes. Index aliases solve this problem by decoupling your application code from the physical index names. In this article, we explore index aliases: concepts, trade-offs and production lessons learned across high-scale e-commerce, SaaS, and ride-hailing systems that must reindex without dropping a single request.

Apps read and write through an alias

An alias is a virtual name that points to one or more physical indexes. Instead of your application connecting directly to products_v1, it connects to products—which the cluster maps to the actual index behind the scenes. This indirection is powerful because it lets you change the underlying index without redeploying code.

Consider a typical setup. You have a live index, and you create an alias pointing to it:

json
{
  "actions": [
    {
      "add": {
        "index": "products_v1",
        "alias": "products"
      }
    }
  ]
}

Your application code stays simple—it always queries and indexes through products:

json
{
  "index": "products",
  "id": "sku-12345",
  "body": {
    "name": "Wireless Headphones",
    "category": "Electronics",
    "price": 79.99
  }
}

From the application's perspective, products is the index. The alias layer handles routing to the physical index. This single-level indirection is your foundation for zero-downtime operations.

One critical point: an alias can point to multiple indexes for reads, but should point to exactly one index for writes. If you try to write through an alias pointing to multiple indexes, Elasticsearch will reject the request. For read operations, querying through a multi-index alias broadcasts the search across all of them—useful for testing and gradual rollouts, which we'll cover next.

Swap aliases atomically

The real power of aliases emerges during a reindex. You create a new index with your updated mapping or settings, populate it with data, and then atomically swap the alias in a single operation—no downtime, no split-brain scenarios.

Here's the workflow:

  1. Create a new index: products_v2 with the new mapping.
  2. Reindex data from products_v1 to products_v2.
  3. Run a swap operation that removes the alias from v1 and adds it to v2 in a single atomic call.
  4. Delete products_v1 when confident.

The swap operation looks like this:

json
{
  "actions": [
    {
      "remove": {
        "index": "products_v1",
        "alias": "products"
      }
    },
    {
      "add": {
        "index": "products_v2",
        "alias": "products"
      }
    }
  ]
}

Elasticsearch processes both actions as a transaction. Reads and writes in-flight complete against products_v1 before the swap; new requests immediately go to products_v2. There's a brief window of consistency, but no downtime. In high-traffic systems we've worked with—handling 10k+ req/s—this swap takes milliseconds.

A production lesson: always test the swap in staging first. Verify the new index has the correct record count and sample documents match. Run a few queries against both indexes side-by-side. We once missed a silent mapping incompatibility that caused numeric fields to be indexed as strings; catching it pre-swap saved a rollback.

Zero-downtime reindexing

Reindexing is expensive. If your index has millions of documents and you need to add a new field or change an analyzer, you can't simply modify the mapping in-place—Elasticsearch locks mappings once data is written. Your only option is to create a new index and repopulate it.

With aliases, you can run this in the background while the old index stays live:

json
{
  "source": {
    "index": "products_v1"
  },
  "dest": {
    "index": "products_v2"
  },
  "script": {
    "source": "ctx._source.updated_at = params.now",
    "params": {
      "now": "2024-01-15T10:00:00Z"
    }
  }
}

This reindex operation can take hours for a large dataset. During that time, your application keeps writing to products_v1 through the alias. When the reindex finishes, you swap the alias. Any documents written after the reindex started are missing from products_v2, so you need to handle them—either with a second, incremental reindex that only copies documents newer than a timestamp, or by allowing a small window of dual-writes during the swap.

In a real production scenario at a mid-scale e-commerce platform, we reindexed a 500-million-document catalog by:

  1. Creating products_v2 with the new mapping.
  2. Running an incremental reindex from products_v1 (filtered by updated_at > cutoff_time).
  3. Enabling dual-writes for 10 seconds: the application writes to both products_v1 and products_v2.
  4. Swapping the alias to products_v2.
  5. Monitoring the alias for 24 hours before deleting products_v1.

The dual-write window was brief enough that conflict resolution was trivial—the second write simply overwrote the first. Combined with the alias abstraction, this approach deleted the need for any application downtime.

Filtered aliases for tenants

In multi-tenant systems, aliases can act as a security and organizational boundary. A filtered alias restricts queries to a subset of documents based on a filter condition. Each tenant gets their own logical alias pointing to the same physical index, but queries are automatically scoped.

Imagine a SaaS platform with customer documents in a single index. You create a filtered alias per tenant:

json
{
  "actions": [
    {
      "add": {
        "index": "documents",
        "alias": "documents_tenant_acme",
        "filter": {
          "term": {
            "tenant_id": "acme-corp"
          }
        }
      }
    },
    {
      "add": {
        "index": "documents",
        "alias": "documents_tenant_globex",
        "filter": {
          "term": {
            "tenant_id": "globex-inc"
          }
        }
      }
    }
  ]
}

When the ACME tenant queries documents_tenant_acme, Elasticsearch transparently adds a tenant_id: "acme-corp" filter to every query. This eliminates the risk of a code bug exposing another tenant's data. Writes are also scoped—you'd typically write with the tenant context in your middleware, but the filtered alias provides a safety layer.

Operationally, this means you can house hundreds or thousands of tenants in a single index and alias structure, reducing cluster sprawl. However, there are trade-offs: filtering happens at query time, so high-cardinality tenant IDs can impact performance. For very large deployments, we've seen teams use a hybrid approach—sharded indexes (one per tenant) for the largest customers, and shared indexes with filtered aliases for smaller ones.

Common mistakes

Writing to a multi-index alias. If you forget that an alias points to multiple indexes and try to write through it, Elasticsearch returns an error. Always ensure write aliases point to exactly one index. Read aliases can point to many.

Not monitoring the swap. Even though the swap itself is atomic, the application's perception of the index changes instantly. If the new index is missing data or has wrong mappings, queries will fail or return stale results. Run validation queries before and after the swap.

Deleting the old index too soon. If the alias swap doesn't go as planned and you need to roll back, you'll want the old index around. We typically keep it for 48 hours, then delete. One team deleted within 5 minutes and had to restore from backup when a query pattern broke on the new index.

Ignoring filtering overhead. Filtered aliases are powerful for multi-tenancy, but each filter adds CPU cost. If you have thousands of filtered aliases on the same index, consider whether a sharded approach would be more efficient. Benchmark with your query patterns.

Forgetting about writes during reindex. If your application keeps writing to the old index during reindex, those writes won't automatically appear in the new index. Plan for dual-writes or incremental reindexing to capture the delta.

Key takeaways

  • Use aliases to decouple your application code from physical index names, enabling seamless index swaps without code changes.
  • Design your alias strategy early: plan how you'll handle reindexing, tenant isolation, and rollbacks before you hit production at scale.
  • Swapping an alias is atomic and fast, but validate the new index thoroughly before and after the swap to catch mapping or data mismatches.
  • For zero-downtime reindexing, use incremental reindexing or brief dual-write windows combined with alias swaps to avoid data loss.
  • Filtered aliases provide tenant isolation and reduce cluster overhead, but monitor query performance as the number of aliases grows.
  • Keep the old index around for 24–48 hours after a swap to simplify rollbacks if unexpected issues arise.
ShareXLinkedInReddit

Found this useful? Support the writing.

Buy me a coffee
Written by
Expona Admin
Site administrator

Comments

Loading comments…

Leave a comment

Comments are reviewed before they appear. Be kind and stay on topic.

Related articles