﻿---
title: Add custom bundles and plugins to your deployment
description: ECE allows you to add custom plugins or external files as bundled ZIP files to your Elasticsearch instances. These ZIP files must be referenced through...
url: https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/4399/deploy-manage/deploy/cloud-enterprise/add-custom-bundles-plugins
products:
  - Elastic Cloud Enterprise
---

# Add custom bundles and plugins to your deployment
ECE allows you to add custom plugins or external files as bundled ZIP files to your Elasticsearch instances. These ZIP files must be referenced through an HTTP or HTTPS URL.
<important>
  - When referencing plugins or bundles, URLs using `https` with a certificate signed by an internal Certificate Authority (CA) are **not supported**. Either use a publicly trusted certificate, or fall back to the `http` scheme.
  - Avoid using the same URL to serve newer versions of a plugin or bundle, as this may cause different nodes within the same cluster to run different plugin versions. Whenever you update the content of the bundle or plugin, use a new URL in the deployment configuration as well.
  - If the URL becomes unreachable (if the URL changes at remote end, or connectivity to the remote web server has issues) you might encounter boot loops if Elasticsearch instances are restarted.
</important>

Follow these steps to configure custom bundles and plugins to your Elasticsearch clusters, making them available to all Elasticsearch instances:
- Update your Elasticsearch cluster using the [advanced configuration editor](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/4399/deploy-manage/deploy/cloud-enterprise/advanced-cluster-configuration):
  - For bundles, modify the `resources.elasticsearch.plan.elasticsearch.user_bundles` JSON attribute.
- For plugins, modify the `resources.elasticsearch.plan.elasticsearch.user_plugins` JSON attribute.

Here are some examples of custom bundles you can add:
- [Custom plugin](#ece-add-custom-plugin)
- [LDAP bundles](#ece-add-custom-bundle-example-LDAP)
- [SAML bundles](#ece-add-custom-bundle-example-SAML)
- [JVM truststore cacerts](#ece-add-custom-bundle-example-cacerts)
- [GeoIP database bundle](#ece-add-custom-bundle-example-geoip)
- [Synonym bundles](#ece-add-custom-bundle-example-synonyms)


## Add custom plugins to your deployment

Custom plugins can include the official Elasticsearch plugins not provided with Elastic Cloud Enterprise, any of the community-sourced plugins, or plugins that you write yourself.
1. [Log into the Cloud UI](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/4399/deploy-manage/deploy/cloud-enterprise/log-into-cloud-ui).
2. From the **Deployments** page, select your deployment.
   Narrow the list by name, ID, or choose from several other filters. To further define the list, use a combination of filters.
3. In the left side navigation select **Edit** from your deployment menu, then go to the bottom of the page and select [**Advanced Edit**](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/4399/deploy-manage/deploy/cloud-enterprise/advanced-cluster-configuration).
4. Within the **Deployment configuration** JSON find the section:
   `resources` > `elasticsearch` > `plan` > `elasticsearch`
   If there is an existing `user_plugins` section, then add the new plugin there, otherwise add a `user_plugins` section.
   ```sh
   {
   ...
     "resources": {
       "elasticsearch": [
        ...
           "plan": {
            ...
             "elasticsearch": {
               ...
               "user_bundles": [
               {
                   ....
               } ] ,
               "user_plugins": [
                 {
                   "url" : "<some static non_expirable url>", 
                   "name" : "plugin_name",
                   "elasticsearch_version" : "<es_version>" 
                 },
                 {
                   "url": "<MY_HOST_URL>/my-custom-plugin.zip",
                   "name": "my-custom-plugin",
                   "elasticsearch_version": "7.17.1"
                 }
               ]
             }
   ```
5. Save your changes.
6. To verify that all nodes have the plugins installed, use one of these commands: `GET /_nodes/plugins?filter_path=nodes.*.plugins` or `GET _cat/plugins?v`


## Example: Custom LDAP bundle

This example adds a custom LDAP bundle for deployment level role-based access control (RBAC). To set platform level RBAC, check [Manage users and roles](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/4399/deploy-manage/users-roles/cloud-enterprise-orchestrator/manage-users-roles).
1. Prepare a custom bundle as a ZIP file that contains your keystore file with the private key and certificate inside of a `truststore` folder [in the same way that you would on Elastic Cloud](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/4399/deploy-manage/deploy/elastic-cloud/upload-custom-plugins-bundles). This bundle allows all Elasticsearch containers to access the same keystore file through your `ssl.truststore` settings.
2. In the [advanced configuration editor](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/4399/deploy-manage/deploy/cloud-enterprise/advanced-cluster-configuration), update your new Elasticsearch cluster with the custom bundle you have just created. Modify the `user_bundles` JSON attribute of **each** Elasticsearch instance type as shown in the following example:
   ```sh
   {
   ...
     "resources": {
       "elasticsearch": [
        ...
           "plan": {
            ...
             "elasticsearch": {
               ...
               "user_bundles": [
               {
                 "name": "ldap-cert",
                 "url": "<MY_HOST_URL>/ldapcert.zip", 
                 "elasticsearch_version": "*"
               }
             ]
           }
           ...
   ```
3. Custom bundles are unzipped in `/app/config/BUNDLE_DIRECTORY_STRUCTURE`, where `BUNDLE_DIRECTORY_STRUCTURE` is the directory structure within the bundle ZIP file itself. These file locations are needed in the next step.
   ```sh
   $ tree .
   .
   └── truststore
         └── keystore.ks
   ```
   In this example, the unzipped keystore file gets placed under `/app/config/truststore/keystore.ks`.


## Example: Custom SAML bundle

This example adds a custom SAML bundle for deployment level role-based access control (RBAC). To set platform level RBAC, check [Manage users and roles](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/4399/deploy-manage/users-roles/cloud-enterprise-orchestrator/manage-users-roles).
In this example, we assume the Identity Provider does not publish its SAML metadata at an HTTP URL, so we provide it through a custom bundle.
1. Prepare a ZIP file with a custom bundle that contains your Identity Provider’s metadata (`metadata.xml`). Place the file inside a `saml` folder within the ZIP (`saml/metadata.xml`).
   This bundle will allow all Elasticsearch containers to access the metadata file.
2. In the [advanced configuration editor](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/4399/deploy-manage/deploy/cloud-enterprise/advanced-cluster-configuration), update your Elasticsearch cluster configuration with the bundle you prepared in the previous step. Modify the `user_bundles` JSON attribute of **each** Elasticsearch instance type as shown in the following example:
   ```text
   {
   ...
     "resources": {
       "elasticsearch": [
         ...
           "plan": {
             ...
             "elasticsearch": {
               ...
               "user_bundles": [
               {
                 "name": "saml-metadata",
                 "url": "<MY_HOST_URL>/saml-metadata.zip", 
                 "elasticsearch_version": "*"
               }
             ]
           }
           ...
   ```
   Custom bundles are unzipped in `/app/config/BUNDLE_DIRECTORY_STRUCTURE`, where `BUNDLE_DIRECTORY_STRUCTURE` is the directory structure within the ZIP file itself. These file locations are needed in the next step.
   In this example, the SAML metadata file is located in the path `/app/config/saml/metadata.xml`:
   ```sh
   $ tree .
   .
   └── saml
         └── metadata.xml
   ```
3. Adjust your `saml` realm configuration accordingly through [Edit stack user settings](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/4399/deploy-manage/deploy/cloud-enterprise/edit-stack-settings):
   ```sh
       idp.metadata.path: /app/config/saml/metadata.xml 
   ```
   Refer to [SAML authentication](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/4399/deploy-manage/users-roles/cluster-or-deployment-auth/saml) for more details on SAML authentication.


## Example: Custom JVM trust store bundle

If you are using SSL certificates signed by non-public certificate authorities, Elasticsearch is not able to communicate with the services using those certificates unless you import a custom JVM trust store containing the certificates of your signing authority into your Elastic Cloud Enterprise installation. You’ll need the trust store to access snapshot repositories like MinIO, for your Elastic Cloud Enterprise proxy, or to reindex from remote.
To import a JVM trust store:
1. Prepare the custom JVM trust store:
   1. Pull the certificate from the service you want to make accessible:
   ```sh
     openssl s_client -connect <server using the certificate> -showcerts 
   ```
2. Save it to a file with as a PEM extension.
3. Locate your JRE’s default trust store, and copy it to the current directory:
   ```sh
     cp <default trust store location> cacerts 
   ```
   <tip>
   Default trust store contains certificates of many well known root authorities that are trusted by default. If you only want to include a limited list of CAs to trust, skip this step, and simply import specific certificates you want to trust into an empty store as shown next
   </tip>
4. Use keytool command from your JRE to import certificate(s) into the keystore:
   ```sh
   $JAVA_HOME/bin/keytool -keystore cacerts -storepass changeit -noprompt -importcert -file <certificate>.pem -alias <some alias> 
   ```
   <important>
   We recommend that you keep file name and password for the trust store as JVM defaults (`cacerts` and `changeit` respectively). If you need to use different values, you need to add extra configuration, as detailed later in this document, in addition to adding the bundle.
   </important>
   You can have multiple certificates to the trust store, repeating the same command. There is only one JVM trust store per cluster currently supported. You cannot, for example, add multiple bundles with different JVM trust stores to the same cluster, they will not get merged. Add all certificates to be trusted to the same trust store
2. Create the bundle:
   ```sh
    zip cacerts.zip cacerts 
   ```
   <tip>
   A bundle may contain other contents beyond the trust store if you prefer, but we recommend creating separate bundles for different purposes.
   </tip>
3. In the [advanced configuration editor](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/4399/deploy-manage/deploy/cloud-enterprise/advanced-cluster-configuration), update your Elasticsearch cluster configuration with the bundle you prepared in the previous step. Modify the `user_bundles` JSON attribute of **each** Elasticsearch instance type as shown in the following example:
   ```sh
   {
   ...
     "resources": {
       "elasticsearch": [
        ...
           "plan": {
            ...
             "elasticsearch": {
               ...
               "user_bundles": [
               {
                 "name": "custom-ca-certs",
                 "url": "<MY_HOST_URL>/cacerts.zip", 
                 "elasticsearch_version": "*" 
               }
             ]
           }
       ...
   ```
4. (Optional) If you prefer to use a different file name and/or password for the trust store, you also need to add an additional configuration section to the cluster metadata before adding the bundle. This configuration should be added to the `Elasticsearch cluster data` section of the [advanced configuration](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/4399/deploy-manage/deploy/cloud-enterprise/advanced-cluster-configuration) page:
   ```sh
     "jvm_trust_store": {
       "name": "<filename included into bundle>", 
       "password": "<password used to create keystore>" 
     }
   ```
   <important>
   - Use only alphanumeric characters, dashes, and underscores in both file name and password.
   - You do not need to do this step if you are using default filename and password (`cacerts` and `changeit` respectively) in your bundle.
   </important>


## Example: Custom GeoIP database bundle

1. Prepare a ZIP file with a custom bundle that contains a: [GeoLite2 database](https://dev.maxmind.com/geoip/geoip2/geolite2). The folder has to be named `ingest-geoip`, and the file name can be anything that is appended `-(City|Country|ASN)` with the `mmdb` file extension, and it must have a different name than the original name `GeoLite2-City.mmdb`.
   The file `my-geoip-file.zip` should look like this:
   ```sh
   $ tree .
   .
   └── ingest-geoip
       └── MyGeoLite2-City.mmdb
   ```
2. Copy the ZIP file to a webserver that is reachable from any allocator in your environment.
3. In the [advanced configuration editor](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/4399/deploy-manage/deploy/cloud-enterprise/advanced-cluster-configuration), update your Elasticsearch cluster configuration with the bundle you prepared in the previous step. Modify the `user_bundles` JSON attribute of **each** Elasticsearch instance type as shown in the following example.
   ```sh
   {
   ...
     "resources": {
       "elasticsearch": [
        ...
           "plan": {
            ...
             "elasticsearch": {
               ...
               "user_bundles": [
               {
                 "name": "custom-geoip-db",
                 "url": "<MY_HOST_URL>/my-geoip-file.zip",
                 "elasticsearch_version": "*"
               }
             ]
           }
   ```
4. To use this bundle, you can refer it in the [GeoIP processor](https://docs-v3-preview.elastic.dev/elastic/elasticsearch/tree/main/reference/enrich-processor/geoip-processor) of an ingest pipeline as `MyGeoLite2-City.mmdb` under `database_file` such as:
   ```sh
   ...
   {
     "geoip": {
       "field": ...
       "database_file": "MyGeoLite2-City.mmdb",
       ...
     }
   }
   ...
   ```


## Example: Custom synonyms bundle

1. Prepare a ZIP file with a custom bundle that contains a dictionary of synonyms in a text file.
   The file `synonyms.zip` should look like this:
   ```sh
   $ tree .
   .
   └── dictionaries
       └── synonyms.txt
   ```
2. Copy the ZIP file to a webserver that is reachable from any allocator in your environment.
3. In the [advanced configuration editor](https://docs-v3-preview.elastic.dev/elastic/docs-content/pull/4399/deploy-manage/deploy/cloud-enterprise/advanced-cluster-configuration), update your Elasticsearch cluster configuration with the bundle you prepared in the previous step. Modify the `user_bundles` JSON attribute of **each** Elasticsearch instance type as shown in the following example.
   ```sh
   {
   ...
     "resources": {
       "elasticsearch": [
        ...
           "plan": {
            ...
             "elasticsearch": {
               ...
               "user_bundles": [
               {
                 "name": "custom-synonyms",
                 "url": "<MY_HOST_URL>/synonyms.zip",
                 "elasticsearch_version": "*"
               }
             ]
           }
   ```