Pourquoi héberger Elasticsearch sur votre propre VPS
Elasticsearch brille là où une recherche simple ne suffit plus : scoring BM25 configurable, synonymes, analyseurs linguistiques personnalisés (français, arabe ICU), géo-requêtes, et stack ELK complète pour centraliser les logs de vos applications. Les offres Elastic Cloud et OpenSearch Service deviennent rapidement coûteuses dès que les volumes indexés dépassent quelques gigaoctets, avec une facturation à l'egress en prime. Sur votre VPS, vous décidez de la version déployée, des plugins activés, de la durée de rétention et de la politique de snapshot. C'est aussi le seul moyen de garder des données sensibles — logs applicatifs, index clients — strictement dans votre propre infrastructure, sans dépendance à un tier cloud.
Sur le plan de la licence, Elasticsearch est redevenu open source en août 2024 : depuis la version 8.16, Elastic distribue le code sous licence AGPLv3 (approuvée par l'OSI), en complément de l'Elastic License et du SSPL. Ce retour à l'open source change le calcul pour les équipes qui avaient écarté Elasticsearch en 2021 au profit d'OpenSearch. Les versions courantes en production sont 8.19.x (branche 8 en maintenance longue) et 9.x (branche principale depuis 2025).
Ce que vous gagnez en self-hébergeant Elasticsearch
- Recherche full-text avancée : scoring BM25, synonymes, analyseurs linguistiques personnalisés (FR, AR ICU, langue de Molière ou de l'ijtihad)
- Agrégations et facettes complexes pour le e-commerce, la BI ou la centralisation de logs
- Stack ELK complète (Logstash, Beats, Kibana) sans surcoût logiciel
- Contrôle total de la version, des plugins et des index lifecycle policies (ILM)
- Aucune facturation au volume indexé ni frais d'egress réseau
- Snapshots automatiques vers votre propre stockage objet (S3-compatible) via SLM
- Données sensibles conservées dans votre propre infrastructure, sous votre seule juridiction
Prérequis chiffrés avant de lancer le premier conteneur
RAM minimum : 4 Go pour un environnement de test, 8 Go (2-4 vCPU) pour une instance de production légère, 16 Go dès que vous ajoutez Kibana ou indexez plusieurs millions de documents. Le paramètre clé est le heap JVM : fixez -Xms et -Xmx à 50 % de la RAM disponible, sans dépasser 31 Go (au-delà, la JVM bascule en mode de compression de pointeurs moins efficace ; la limite exacte est 31 Go avec G1GC, pas 32 Go). Sur un VPS 8 Go RAM, utilisez donc -Xms4g -Xmx4g. Fixez toujours -Xms égal à -Xmx : un heap sous-alloué au démarrage puis étendu en cours de route provoque des pauses GC longues.
Côté réseau, Elasticsearch utilise deux ports : 9200 (HTTP, API REST) et 9300 (transport inter-nœuds). N'exposez jamais le port 9200 directement sur l'interface publique — c'est la première source de compromission constatée sur des instances non sécurisées. Côté stockage, un SSD NVMe est recommandé : Elasticsearch effectue de nombreuses opérations de lecture aléatoire sur les segments Lucene ; un disque magnétique ou un SSD bas de gamme saturera rapidement sur de gros index. Prévoyez également Docker et Docker Compose, et un sous-domaine es.votredomaine.com pointé sur le VPS.
Déploiement pas à pas
Préparer le noyau Linux
Avant de lancer le conteneur, appliquez deux réglages système obligatoires. D'abord, augmentez la limite de zones de mémoire mappées : sysctl -w vm.max_map_count=262144. Persistez ce réglage en ajoutant vm.max_map_count=262144 dans /etc/sysctl.conf — sans cela, Elasticsearch refuse de démarrer avec une erreur max virtual memory areas vm.max_map_count [65530] is too low. Ensuite, désactivez le swap sur le VPS (swapoff -a et commentez la ligne swap dans /etc/fstab), ou configurez bootstrap.memory_lock=true dans elasticsearch.yml pour que la JVM ne soit jamais paginée sur disque, ce qui dégraderait les performances de façon catastrophique.
Écrire le fichier docker-compose.yml
Créez un répertoire de travail, puis un fichier docker-compose.yml avec le service Elasticsearch : image docker.elastic.co/elasticsearch/elasticsearch:8.19.4, variable d'environnement ES_JAVA_OPTS=-Xms4g -Xmx4g (adaptez au VPS), un volume nommé monté sur /usr/share/elasticsearch/data, et le port 9200 lié à 127.0.0.1 uniquement (127.0.0.1:9200:9200). Ajoutez également discovery.type=single-node pour un déploiement mono-nœud. Ne publiez jamais 0.0.0.0:9200:9200 en production.
Activer xpack.security et démarrer
Dans elasticsearch.yml, vérifiez que xpack.security.enabled: true et xpack.security.http.ssl.enabled: true sont actifs. Depuis la version 8.x, la sécurité est activée par défaut, mais un fichier de configuration hérité peut la désactiver explicitement. Lancez le cluster : docker compose up -d. Au premier démarrage, attendez 2 à 3 minutes — l'initialisation des index système (.security-*, .kibana_*) prend du temps. Consultez les logs : docker compose logs -f elasticsearch.
Créer les utilisateurs et récupérer le mot de passe elastic
Une fois le conteneur démarré, réinitialisez le mot de passe du superutilisateur elastic : docker exec -it elasticsearch bin/elasticsearch-reset-password -u elastic. Conservez ce mot de passe dans un gestionnaire de secrets. Créez ensuite l'utilisateur système kibana_system si vous ajoutez Kibana : docker exec -it elasticsearch bin/elasticsearch-users useradd kibana_system -r kibana_system. Ce compte ne doit jamais être utilisé pour des requêtes applicatives : créez des utilisateurs dédiés par application, avec des rôles au minimum nécessaire.
Vérifier le cluster avec curl
Testez la connexion depuis le VPS (pas depuis l'extérieur) : curl -u elastic:<MOT_DE_PASSE> https://localhost:9200 --cacert /usr/share/elasticsearch/config/certs/http_ca.crt. Une réponse JSON avec cluster_name et status: green ou yellow confirme que le cluster est opérationnel. Un statut yellow sur un cluster mono-nœud est normal : les répliques de shards ne peuvent pas être allouées sans un second nœud.
Exposer via reverse proxy HTTPS avec Nginx
Installez Nginx sur le VPS et configurez un virtual host pour es.votredomaine.com. Le reverse proxy transmet les requêtes vers https://127.0.0.1:9200 et présente un certificat Let's Encrypt au client. Ajoutez une authentification basique Nginx comme couche de protection supplémentaire si l'API doit être accessible depuis l'extérieur. Ne transférez que les routes nécessaires à votre application — évitez d'exposer /_cat/* ou /_cluster/* publiquement.
Sécurité xpack : TLS, rôles et isolation réseau
La sécurité xpack d'Elasticsearch couvre trois couches. TLS inter-nœuds (xpack.security.transport.ssl.enabled: true) chiffre le trafic entre nœuds sur le port 9300 — indispensable dès qu'un second nœud rejoint le cluster. TLS HTTP (xpack.security.http.ssl.enabled: true) chiffre le port 9200 ; sans lui, les mots de passe transitent en clair même sur un réseau privé. Contrôle des rôles : Elasticsearch propose des rôles prédéfinis (read, write, monitor, kibana_system, logstash_writer). Attribuez le rôle minimal requis à chaque application : un service qui ne fait que lire un index n'a pas besoin du rôle superuser. Évitez d'utiliser le compte elastic en production — réservez-le à l'administration initiale. Dernier point : le paramètre network.host dans elasticsearch.yml. Sa valeur par défaut est _local_ (loopback uniquement). Passer à 0.0.0.0 pour écouter sur toutes les interfaces sans avoir configuré la sécurité xpack expose votre cluster à l'internet entier.
Durcir l'accès réseau avec UFW
Après avoir vérifié qu'Elasticsearch écoute uniquement sur 127.0.0.1, verrouillez le pare-feu : ufw deny 9200/tcp et ufw deny 9300/tcp. Seul le reverse proxy Nginx (port 443) doit être accessible. Si plusieurs nœuds communiquent entre eux, autorisez explicitement les IP des nœuds sur le port 9300 (ufw allow from <IP_NOEUD_2> to any port 9300), et bloquez tout le reste. Un ufw status après configuration vous donne la vue exacte de ce qui est ouvert.
Index Lifecycle Management (ILM) : gérer la durée de vie des données
L'ILM automatise le cycle de vie de vos index selon leur âge ou leur taille, en évitant la saturation disque et en gardant les requêtes rapides sur les données récentes. Une politique ILM typique se décompose en quatre phases :
Phase hot : l'index reçoit les nouvelles données. Configurez le rollover automatique pour créer un nouvel index dès qu'il atteint une certaine taille (max_size: 50gb) ou un certain âge (max_age: 7d). Les index hot vivent sur vos SSD NVMe les plus rapides.
Phase warm : l'index n'est plus écrit mais reste consulté. Elasticsearch réduit le nombre de répliques à 1 et effectue un force-merge des segments Lucene (forcemerge: max_num_segments: 1) — cette opération réduit la mémoire utilisée par les segments ouverts et accélère les lectures.
Phase cold : données rarement consultées. Le nombre de répliques passe à 0 et l'index peut être monté en mode read-only depuis un stockage objet (searchable snapshots), éliminant l'overhead de réplication.
Phase delete : suppression automatique après la durée de rétention définie (exemple : 90 jours pour des logs applicatifs).
Créez la politique avec l'API REST puis attachez-la à un index template pour qu'elle s'applique automatiquement à tous les nouveaux index :
PUT _ilm/policy/logs-policy
{
"policy": {
"phases": {
"hot": { "actions": { "rollover": { "max_age": "7d", "max_size": "50gb" } } },
"warm": { "min_age": "7d", "actions": { "forcemerge": { "max_num_segments": 1 }, "shrink": { "number_of_shards": 1 } } },
"cold": { "min_age": "30d", "actions": { "freeze": {} } },
"delete": { "min_age": "90d", "actions": { "delete": {} } }
}
}
}Sur un VPS où l'espace disque est compté, c'est l'ILM qui empêche vos logs de saturer le SSD et maintient les requêtes rapides sur les données chaudes.
Snapshots vers S3 : sauvegarde et reprise après incident
Les snapshots Elasticsearch permettent de sauvegarder l'état complet de vos index vers un stockage objet compatible S3. Combinez-les avec une Snapshot Lifecycle Policy (SLM) pour automatiser les sauvegardes et leur rétention, sans intervention manuelle.
1. Installer le plugin S3 et configurer le repository :
docker exec -it elasticsearch bin/elasticsearch-plugin install repository-s3Puis déclarez les clés d'accès dans le keystore Elasticsearch (jamais en clair dans elasticsearch.yml) :
docker exec -it elasticsearch bin/elasticsearch-keystore add s3.client.default.access_key
docker exec -it elasticsearch bin/elasticsearch-keystore add s3.client.default.secret_keyEnregistrez le repository :
PUT _snapshot/s3-backup
{
"type": "s3",
"settings": {
"bucket": "mon-bucket-elasticsearch",
"region": "eu-west-1",
"base_path": "snapshots/production"
}
}2. Créer la Snapshot Lifecycle Policy :
PUT _slm/policy/daily-snapshots
{
"schedule": "0 30 2 * * ?",
"name": "<daily-snap-{now/d}>",
"repository": "s3-backup",
"config": { "indices": ["*"], "ignore_unavailable": true },
"retention": { "expire_after": "30d", "min_count": 5, "max_count": 50 }
}Cette politique déclenche un snapshot tous les jours à 2h30, nomme automatiquement chaque snapshot avec la date, et purge les snapshots de plus de 30 jours en conservant au minimum 5 versions. Vérifiez l'état des snapshots avec GET _slm/policy/daily-snapshots et testez la restauration depuis un environnement de staging avant d'en avoir besoin.
Monitoring du heap JVM : détecter l'OOM avant qu'il arrive
Sur un VPS, le tueur OOM du noyau Linux peut terminer le processus Elasticsearch sans avertissement. La surveillance proactive du heap JVM évite 80 % des incidents de production.
Métriques à surveiller via l'API _nodes/stats :
- jvm.mem.heap_used_percent : déclenchez une alerte au-dessus de 75 % en moyenne, et une alerte critique au-dessus de 85 % sur 5 minutes. Une saturation prolongée à 90 %+ indique une fuite mémoire ou un heap sous-dimensionné.
- jvm.gc.collectors.old.collection_count et collection_time_in_millis : une augmentation rapide du nombre de GC old-gen (major GC) et des temps > 2 s par collecte signalent que la JVM passe plus de temps à collecter qu'à travailler.
Intégration Prometheus + Grafana (stack Beats) :
Ajoutez metricbeat dans votre Compose pour collecter les métriques Elasticsearch et les pousser vers Prometheus ou directement dans Elasticsearch pour les visualiser dans Kibana. L'image officielle docker.elastic.co/beats/metricbeat:8.19.4 est préconfigurée avec un module Elasticsearch.
Règles opérationnelles :
1. Si heap_used_percent dépasse 75 % en régime stable, augmentez le heap (ou la RAM du VPS) avant d'observer le premier OOM.
2. N'augmentez pas le heap au-delà de 31 Go — passé ce seuil, le garbage collector désactive la compression de pointeurs (compressed oops) et la JVM consomme plus de mémoire par objet, ce qui est contre-productif.
3. Vérifiez régulièrement que bootstrap.memory_lock: true est actif avec GET _nodes?filter_path=**.mlockall : une valeur false indique que le swap est encore actif sur le VPS.
Kibana : exploration des index et dashboards (optionnel)
Kibana est l'interface graphique officielle d'Elasticsearch pour explorer les index, bâtir des dashboards et configurer des alertes. Il n'est pas obligatoire pour une utilisation programmatique (API REST), mais il simplifie la gestion de l'ILM, des SLM et des index en général.
Ajoutez Kibana dans le même fichier Compose, en le connectant au réseau interne Docker :
kibana:
image: docker.elastic.co/kibana/kibana:8.19.4
environment:
- ELASTICSEARCH_HOSTS=https://elasticsearch:9200
- ELASTICSEARCH_USERNAME=kibana_system
- ELASTICSEARCH_PASSWORD=<MOT_DE_PASSE_KIBANA_SYSTEM>
- ELASTICSEARCH_SSL_CERTIFICATEAUTHORITIES=/usr/share/kibana/config/certs/http_ca.crt
volumes:
- /chemin/vers/http_ca.crt:/usr/share/kibana/config/certs/http_ca.crt:ro
depends_on:
- elasticsearchExposez Kibana derrière un reverse proxy Nginx sur kibana.votredomaine.com avec authentification. N'exposez jamais Kibana directement sur l'internet sans authentification : il donne accès à la gestion complète du cluster. Depuis Kibana, accédez à Stack Management → Index Lifecycle Policies et Snapshot and Restore pour gérer visuellement l'ILM et les SLM, sans écrire d'appels API manuellement.
Dépannage : les 5 erreurs courantes au démarrage
1. OOM Killer tue le processus Elasticsearch. Symptôme : le conteneur s'arrête sans message d'erreur dans les logs, dmesg | grep -i killed révèle un Killed process. Cause : le heap -Xmx est trop élevé pour la RAM disponible, ou d'autres processus saturent la mémoire. Correction : réduisez -Xmx à 50 % de la RAM libre réelle (max 31 Go), et surveillez la consommation mémoire avec docker stats.
2. max_map_count trop bas. Symptôme : Elasticsearch refuse de démarrer avec l'erreur max virtual memory areas vm.max_map_count [65530] is too low. Correction : sysctl -w vm.max_map_count=262144 puis ajoutez vm.max_map_count=262144 dans /etc/sysctl.conf.
3. Permission denied sur /usr/share/elasticsearch/data. Symptôme : l'erreur AccessDeniedException apparaît dans les logs au montage du volume. Cause : le répertoire hôte appartient à root mais le conteneur tourne avec l'UID 1000 (utilisateur elasticsearch). Correction : chown -R 1000:1000 <chemin_du_volume_hote> avant de lancer docker compose up.
4. Connexion refusée sur le port 9200. Symptôme : curl localhost:9200 retourne Connection refused. Cause fréquente : network.host est mal configuré dans elasticsearch.yml (valeur _site_ ou une IP qui ne correspond pas à l'interface Docker). Sur un cluster mono-nœud Docker, laissez network.host à sa valeur par défaut (_local_) et accédez via 127.0.0.1:9200 depuis le conteneur ou l'hôte. Vérifiez aussi que le conteneur est bien démarré : docker ps.
5. Démarrage lent : normal à la première initialisation. Symptôme : le cluster met 2 à 3 minutes à répondre au premier lancement. Ce n'est pas une panne. Elasticsearch initialise les index système (.security-7, .kibana_1, mappings par défaut). Attendez que les logs affichent mode [basic], reason [security is enabled] ou Cluster health status changed from [RED] to [GREEN] avant d'envoyer des requêtes.
Et si vous voulez une alternative open source complète ?
OpenSearch est le fork communautaire d'Elasticsearch, né en 2021 lorsqu'Elastic a changé sa licence vers SSPL (non OSI-approuvée). OpenSearch conserve une licence Apache 2.0, propose une API REST largement compatible, et inclut des fonctionnalités de sécurité avancées dans sa distribution gratuite (contrôle d'accès par rôle, audit logging, chiffrement au repos). Depuis qu'Elastic a réintroduit l'AGPLv3 en août 2024 avec la version 8.16, les deux projets sont de nouveau sous licence OSI-approuvée — le choix dépend désormais davantage de l'écosystème (plugins, intégrations, support commercial) que de la contrainte de licence. Le déploiement Docker d'OpenSearch suit le même schéma, avec l'image opensearchproject/opensearch à la place.