Build the mental model
Every lesson since Lesson 3 has run the dev container with `xpack.security.enabled=false`, but production must never disable security entirely — beyond username/password authentication, API keys give granular access control. An API key is a token, unlike a long-lived user credential, that can be short-lived (with an expiry) and scoped narrowly to a permission subset, letting you issue a dedicated key per application service so credentials aren't shared across services as a single point of failure. Role-based access control (RBAC) assigns each user or API key a role (say, "search-readonly"), and each role can define index-level permissions (which indices can be read or written) and even fine-grained document/field-level security (row-level or column-level restrictions) — this mechanism is key to implementing the least-privilege principle, giving each application only the permissions it actually needs. TLS encrypts client-to-cluster and node-to-node communication, protecting data from network eavesdropping — just as the Redis course's security lesson explained that a password alone is not enough and must combine with network restrictions and ACL/TLS, Elasticsearch similarly needs authentication, authorization, and encryption layers combined, not one layer (password only) in isolation. Think of it like an office building's security system: badge access is authentication, badge-level room access is authorization/RBAC, and locked doors with CCTV are encryption and network controls.
Connect it to a real scenario
Since the Tutorial Platform's production Node.js search backend service only ever needs read-only permission on the `tutorials` index, create a "search-readonly" role and generate an API key scoped to that role — store it in an environment variable and pass it into the `esClient` config (Lesson 15). The data-sync worker service (the logic writing from PostgreSQL into Elasticsearch) needs write permission, so issue it a separate API key scoped to a "search-writer" role — sharing the search backend's read-only key there would break the security boundary. Enable TLS on the production cluster — configure the client library's (`esClient`) `node` URL as `https://` with certificate validation also enabled.
Try the working example
POST /_security/role/search-readonly
{
"indices": [
{ "names": ["tutorials"], "privileges": ["read"] }
]
}
POST /_security/api_key
{
"name": "tutorial-platform-search-backend",
"role_descriptors": {
"search-readonly": {
"indices": [
{ "names": ["tutorials"], "privileges": ["read"] }
]
}
},
"expiration": "90d"
}You get a role with read-only permission on the `tutorials` index and a scoped API key that expires within 90 days.5-minute try-it
Write a role and API key request for the PostgreSQL-to-Elasticsearch sync worker service, granting write-only permission on the `tutorials` index — note how it differs from the search backend service's role.
One important caution
Carrying the dev container habit of `xpack.security.enabled=false` into a production deployment — the whole cluster ends up exposed on a public network with no authentication at all.
Sharing a single admin-level API key across every service — a compromise of one service then hands an attacker every index and permission in the cluster; issue a scoped key per service instead.