ELK Stack — Aliases

5 minute read

Aliases

An alias is an alternate name that points to one or more indices. Your application queries the alias instead of the real index name, which gives you the flexibility to swap, filter, or restructure indices behind the scenes without any client-side changes.


Why Use Aliases

  • Zero-downtime reindexing — Reindex into a new index, then swap the alias.
  • Filtered views — Create an alias that only exposes a subset of data.
  • Simplified querying — Query logs instead of logs-2026.03.28,logs-2026.03.29,....
  • Write routing — Direct writes to a specific index through a write alias.

Creating an Alias

Using the _aliases API

The _aliases endpoint supports atomic, multi-action operations:

curl -s -X POST "localhost:9200/_aliases" \
  -H "Content-Type: application/json" \
  -d '{
  "actions": [
    {
      "add": {
        "index": "logs-2026.04.01",
        "alias": "logs-today"
      }
    }
  ]
}'
{
  "acknowledged": true
}

Using the Index Alias Endpoint

curl -s -X PUT "localhost:9200/logs-2026.04.01/_alias/logs-today"
{
  "acknowledged": true
}

Listing Aliases

All Aliases

curl -s -X GET "localhost:9200/_aliases?pretty"
{
  "logs-2026.04.01": {
    "aliases": {
      "logs-today": {}
    }
  },
  "logs-2026.03.31": {
    "aliases": {}
  }
}

Aliases for a Specific Index

curl -s -X GET "localhost:9200/logs-2026.04.01/_alias?pretty"
{
  "logs-2026.04.01": {
    "aliases": {
      "logs-today": {}
    }
  }
}

Check If an Alias Exists

curl -s -o /dev/null -w "%{http_code}" -X HEAD "localhost:9200/_alias/logs-today"
# Returns 200 if exists, 404 if not

Deleting an Alias

curl -s -X POST "localhost:9200/_aliases" \
  -H "Content-Type: application/json" \
  -d '{
  "actions": [
    {
      "remove": {
        "index": "logs-2026.04.01",
        "alias": "logs-today"
      }
    }
  ]
}'
{
  "acknowledged": true
}

Alias Pointing to Multiple Indices

An alias can span multiple indices. Searches against the alias will query all of them:

curl -s -X POST "localhost:9200/_aliases" \
  -H "Content-Type: application/json" \
  -d '{
  "actions": [
    { "add": { "index": "logs-2026.03.30", "alias": "logs-last-3-days" } },
    { "add": { "index": "logs-2026.03.31", "alias": "logs-last-3-days" } },
    { "add": { "index": "logs-2026.04.01", "alias": "logs-last-3-days" } }
  ]
}'
{
  "acknowledged": true
}

You can also use a wildcard pattern:

curl -s -X POST "localhost:9200/_aliases" \
  -H "Content-Type: application/json" \
  -d '{
  "actions": [
    {
      "add": {
        "index": "logs-2026.03.*",
        "alias": "logs-march"
      }
    }
  ]
}'
{
  "acknowledged": true
}

Filtered Aliases

A filtered alias only exposes documents that match a given query. This is useful for multi-tenant setups where each tenant should only see their own data.

curl -s -X POST "localhost:9200/_aliases" \
  -H "Content-Type: application/json" \
  -d '{
  "actions": [
    {
      "add": {
        "index": "orders",
        "alias": "orders-us",
        "filter": {
          "term": {
            "country": "US"
          }
        }
      }
    }
  ]
}'
{
  "acknowledged": true
}

Now querying orders-us returns only US orders:

curl -s -X GET "localhost:9200/orders-us/_search?pretty" \
  -H "Content-Type: application/json" \
  -d '{
  "query": { "match_all": {} }
}'
{
  "hits": {
    "total": { "value": 1524, "relation": "eq" },
    "hits": [
      {
        "_index": "orders",
        "_id": "o1001",
        "_source": {
          "order_id": "o1001",
          "country": "US",
          "amount": 129.99
        }
      }
    ]
  }
}

Alias with Routing

Routing controls which shard a document is stored on. You can attach routing to an alias so all reads and writes through that alias go to a specific shard:

curl -s -X POST "localhost:9200/_aliases" \
  -H "Content-Type: application/json" \
  -d '{
  "actions": [
    {
      "add": {
        "index": "orders",
        "alias": "orders-tenant-42",
        "filter": {
          "term": { "tenant_id": 42 }
        },
        "routing": "42"
      }
    }
  ]
}'
{
  "acknowledged": true
}

You can also set search_routing and index_routing independently:

curl -s -X POST "localhost:9200/_aliases" \
  -H "Content-Type: application/json" \
  -d '{
  "actions": [
    {
      "add": {
        "index": "orders",
        "alias": "orders-tenant-99",
        "search_routing": "99",
        "index_routing": "99"
      }
    }
  ]
}'
{
  "acknowledged": true
}

Write Index

When an alias points to multiple indices, you must designate exactly one as the write index. All index (write) requests through the alias go to that index.

curl -s -X POST "localhost:9200/_aliases" \
  -H "Content-Type: application/json" \
  -d '{
  "actions": [
    {
      "add": {
        "index": "logs-000001",
        "alias": "logs-write",
        "is_write_index": true
      }
    },
    {
      "add": {
        "index": "logs-000002",
        "alias": "logs-write"
      }
    }
  ]
}'
{
  "acknowledged": true
}

Searches against logs-write query both indices. Writes go to logs-000001.


Zero-Downtime Reindexing with Alias Swap

This is the most common real-world use of aliases. The pattern:

  1. Your application reads and writes through alias products.
  2. You create a new index products-v2 with updated mappings.
  3. You reindex data from products-v1 to products-v2.
  4. You atomically swap the alias.

Step 1: Setup — Application Uses the Alias

# Create v1 index
curl -s -X PUT "localhost:9200/products-v1" \
  -H "Content-Type: application/json" \
  -d '{
  "mappings": {
    "properties": {
      "name":  { "type": "text" },
      "price": { "type": "float" }
    }
  }
}'

# Point alias to v1
curl -s -X POST "localhost:9200/_aliases" \
  -H "Content-Type: application/json" \
  -d '{
  "actions": [
    { "add": { "index": "products-v1", "alias": "products", "is_write_index": true } }
  ]
}'

Step 2: Create v2 with New Mappings

curl -s -X PUT "localhost:9200/products-v2" \
  -H "Content-Type: application/json" \
  -d '{
  "mappings": {
    "properties": {
      "name":  { "type": "text" },
      "price": { "type": "scaled_float", "scaling_factor": 100 },
      "category": { "type": "keyword" }
    }
  }
}'

Step 3: Reindex

curl -s -X POST "localhost:9200/_reindex" \
  -H "Content-Type: application/json" \
  -d '{
  "source": { "index": "products-v1" },
  "dest":   { "index": "products-v2" }
}'
{
  "took": 1245,
  "total": 50000,
  "updated": 0,
  "created": 50000,
  "deleted": 0,
  "failures": []
}

Step 4: Atomic Alias Swap

curl -s -X POST "localhost:9200/_aliases" \
  -H "Content-Type: application/json" \
  -d '{
  "actions": [
    { "remove": { "index": "products-v1", "alias": "products" } },
    { "add":    { "index": "products-v2", "alias": "products", "is_write_index": true } }
  ]
}'
{
  "acknowledged": true
}

Both actions execute atomically — there is no moment where the alias is missing. Your application experiences zero downtime.

Step 5: Clean Up

curl -s -X DELETE "localhost:9200/products-v1"
{
  "acknowledged": true
}

Aliases vs Data Streams

Feature Aliases Data Streams
Use case General purpose Append-only time-series
Write target Configurable write index Automatic (latest backing index)
Rollover Manual or ILM Automatic with ILM
Update / delete Supported Only via update-by-query on backing index
Setup complexity Low Requires matching index template

Use data streams for logs and metrics. Use aliases when you need update/delete support or custom routing.


Quick Reference

Task Command
Create alias POST _aliases with add action
Delete alias POST _aliases with remove action
List all aliases GET _aliases
Check alias exists HEAD _alias/<alias-name>
Alias with filter add action with filter field
Alias with routing add action with routing field
Set write index add action with "is_write_index": true
Atomic swap POST _aliases with remove + add in one request

Next → Analyzers and Tokenizers