How to deploy Charmed OpenSearch¶
This guide walks you through deploying Charmed OpenSearch, covering both the IAAS/VM charm (opensearch) and the
Kubernetes charm (opensearch-k8s).
If you are new to OpenSearch or Juju and are looking for a more comprehensive walkthrough of these steps, see the Tutorial.
For large, multi-application deployments, see the Launch a large deployment guide instead.
Prerequisites¶
Check that you fulfill the hardware requirements in the system requirements page.
Before continuing, decide whether you are going to use a machine (VM)-based or a Kubernetes environment for this deployment. Use the tabs below to switch between the two substrates. The instructions will update accordingly.
To deploy Charmed OpenSearch using Juju in machine/VM environment, you need:
Juju
3.6+(latest LTS) – Canonical’s orchestration engine (see How to install Juju)LXD
v6.1+– Canonical’s lightweight container hypervisor (see LXD tutorial).
To deploy Charmed OpenSearch using Juju in K8s environment, you need:
Juju
3.6+(latest LTS) – Canonical’s orchestration engine (see How to install Juju)Kubernetes
v1.29+cluster, for example:Canonical Kubernetes with the following features enabled:
local-storageload-balancer
MicroK8s with the following add-ons:
hostpath-storagednsmetallb
Prepare the substrate¶
Prepare the environment for Charmed OpenSearch deployment:
Disable IPv6 on LXD
Juju does not support IPv6 addresses with LXD. To set the network bridge to have no IPv6 addresses, run the following command after initializing LXD:
lxc network set lxdbr0 ipv6.address none
See The LXD cloud and Juju for more information.
Check the storage class
Charmed OpenSearch K8s requests two persistent volumes per unit. Confirm that your cluster has a storage class that can satisfy them:
kubectl get storageclass
The controller bootstrap and model-creation commands below assume a suitable storage class
is marked (default). If your cluster has no default, see
Juju’s Kubernetes storage configuration
before continuing.
Bootstrap a Juju controller¶
Make sure your cloud is registered with Juju:
juju list-clouds
Note
See also: How to manage clouds in the Juju documentation.
Bootstrap a new controller:
juju bootstrap <cloud> <controller-name>
Or switch to an existing one:
juju switch <controller-name>
Make sure that the controller’s back-end cloud is not Kubernetes-based.
Make sure that the controller’s back-end cloud is Kubernetes-based.
Create a model¶
Create a model if you haven’t already:
juju add-model <model-name>
If you are reusing an existing model, select it before running the remaining commands:
juju switch <controller-name>:<model-name>
Check that the model is of the expected type:
juju show-model <model-name>
The output includes a model-type field (distinct from the cloud’s type field).
The model-type must not be caas.
The model-type must be caas.
Kernel parameter configuration¶
OpenSearch relies on a number of kernel parameters that are not set to suitable values by default. Configure them before deploying OpenSearch. How and where you apply them depends on the substrate.
Note
To take note of the current values before changing them:
sudo sysctl -a | grep -E 'swappiness|max_map_count|file-max'
The settings below are saved in /etc/sysctl.d/opensearch.conf and persist across
reboots. To restore the previous values, remove or update that file and reset the
parameters manually, or reboot after removing the file.
Configure the required kernel settings on the host machine, then configure the workload model to apply them to new containers. You can do this after bootstrapping a controller, but before deploying OpenSearch.
Configure sysctl on the host machine
On the host machine, run the following command to add the settings to a config file:
sudo tee /etc/sysctl.d/opensearch.conf <<EOF
vm.swappiness = 0
vm.max_map_count = 262144
fs.file-max = 1048576
EOF
Then, apply the new settings:
sudo sysctl -p /etc/sysctl.d/opensearch.conf
Configure sysctl for new containers
Create a cloud-init user data file to set sysctl on new containers:
cat <<EOF > cloudinit-userdata.yaml
cloudinit-userdata: |
postruncmd:
- echo 'vm.max_map_count=262144' >> /etc/sysctl.conf
- echo 'vm.swappiness=0' >> /etc/sysctl.conf
- echo 'fs.file-max=1048576' >> /etc/sysctl.conf
- sysctl -p
EOF
Note
Keep each postruncmd entry as a string. Cloud-init runs string entries through a
shell, so the >> redirection works. Entries written as a YAML list are passed straight to
execve(3) with no shell, so >> would become a literal argument to echo instead of
appending to the file.
Apply it to the existing model before deploying OpenSearch, so the settings are
included when Juju provisions its machines. Changing model configuration does not
retroactively run cloud-init on existing machines:
juju model-config --file=./cloudinit-userdata.yaml --model <model-name>
For models you create in the future, you can instead set this as a default on the
selected controller using
juju model-defaults
before creating those models. Defaults do not change the model created above:
juju model-defaults --file=./cloudinit-userdata.yaml
On Kubernetes, kernel parameters are applied per worker node, not per container:
vm.max_map_count, vm.swappiness, and fs.file-max are node-wide settings that the
workload pods inherit from the host they are scheduled on.
Configure sysctl on each Kubernetes node
On each node that may run OpenSearch pods, run the following command to add the settings to a config file:
sudo tee /etc/sysctl.d/opensearch.conf <<EOF
vm.swappiness = 0
vm.max_map_count = 262144
fs.file-max = 1048576
EOF
Then, apply the new settings:
sudo sysctl -p /etc/sysctl.d/opensearch.conf
Note
If your nodes are managed by a cloud provider, prefer the provider’s node configuration mechanism (for example, a node bootstrap script or a machine image) so the settings survive node replacement.
Configure TCP retries (optional)¶
The VM charm sets net.ipv4.tcp_retries2 automatically; no separate configuration is needed.
The K8s charm does not set the pod-scoped net.ipv4.tcp_retries2. For optional
net.ipv4.tcp_retries2=5 tuning, install the
data-platform-k8s-mutator
before deploying OpenSearch; it does not update existing workloads.
Follow the mutator prerequisites: check admission registration:
kubectl api-versions | grep admissionregistration.k8s.io/v1
Allow the unsafe sysctl on every eligible worker node and restart each kubelet after changing its configuration. On the Canonical Kubernetes
k8ssnap, run the following on each of those nodes:echo '--allowed-unsafe-sysctls=net.ipv4.tcp_retries2' | sudo tee -a /var/snap/k8s/common/args/kubelet sudo snap restart k8s.kubelet
Use your distribution’s method elsewhere.
With
uv,openssl, and cluster-adminkubectlavailable, use the mutator’s bootstrap script from its own repository (not this one):git clone https://github.com/canonical/data-platform-k8s-mutator.git cd data-platform-k8s-mutator uv run python -m scripts.bootstrap_webhook --namespace webhooks \ --image ghcr.io/canonical/data-platform-k8s-mutator:1.0-24.04_edge \ --target-container-names opensearch \ --target-namespaces <model-kubernetes-namespace> --dry-run
Replace
<model-kubernetes-namespace>with the selected Juju model’s Kubernetes namespace (usually its short name; check withkubectl get namespaces).webhookshosts the mutator. The image is an edge example; use a cluster-accessible image. Review the generated YAML indeploy/. Dry-run also writes TLS private keys there; keep them private. Then run the same command without--dry-runto deploy:uv run python -m scripts.bootstrap_webhook --namespace webhooks \ --image ghcr.io/canonical/data-platform-k8s-mutator:1.0-24.04_edge \ --target-container-names opensearch \ --target-namespaces <model-kubernetes-namespace>
Check the webhook before deploying OpenSearch:
kubectl -n webhooks get deployment,pods kubectl get mutatingwebhookconfiguration sysctl-webhook
The webhook has one replica by default and can block controller creation cluster-wide when unavailable; review the HA guidance before production use.
Deploy OpenSearch¶
For a single-host deployment, we recommend the default testing profile, which
sets a 1 GB JVM heap per unit. For production, meet the
profile’s resource and node-role requirements before setting production: three
units can cover both cluster-manager and data roles when the roles are combined, but each needs sufficient resources.
Choose a command for your substrate. All examples deploy three units of an application named opensearch.
Deploy with the default testing profile:
juju deploy opensearch --channel=2/stable -n 3
Or deploy with the production profile:
juju deploy opensearch --channel=2/stable -n 3 --config profile=production
The Kubernetes charm requires the --trust flag to access the model’s cloud credentials
and manage Kubernetes resources such as Services and StatefulSets on your behalf.
Deploy the opensearch-k8s charm with the default testing profile:
juju deploy opensearch-k8s opensearch --channel=2/edge -n 3 --trust
To use a specific Kubernetes storage class for both OpenSearch volumes, first ensure
an existing Juju kubernetes storage pool in this model selects that class and can
provision both volumes. Find available pools with juju storage-pools. Pass the pool
name, not the Kubernetes storage class, to --storage:
juju deploy opensearch-k8s opensearch --channel=2/edge -n 3 --trust \
--storage opensearch-data=<juju-storage-pool>,10G \
--storage opensearch-logs=<juju-storage-pool>,2G
Or deploy with the production profile:
juju deploy opensearch-k8s opensearch --channel=2/edge -n 3 --trust --config profile=production
Note
The charm pulls a pinned OpenSearch workload from a charmed-opensearch-rock OCI image rather than from a snap. The image is published together with the charm revision, so no additional resource needs to be specified at deploy time.
If you installed the optional mutator, inspect a new pod:
kubectl -n <model-kubernetes-namespace> get pod opensearch-0 -o yaml
Under securityContext.sysctls, look for name: net.ipv4.tcp_retries2 and value: "5".
If absent, check the target namespace and kubectl -n webhooks logs deployment/sysctl-webhook.
If the opensearch container is running, check the effective value:
kubectl -n <model-kubernetes-namespace> exec opensearch-0 -c opensearch -- sysctl net.ipv4.tcp_retries2
The container might not run until TLS is configured.
Check the deployment¶
To check the current status of the application:
juju status
Once the units have been provisioned and the other prerequisites are met, the opensearch application should become
blocked with a Missing TLS relation with this cluster message. If provisioning or storage is still pending,
juju status may show a different state first. Charmed OpenSearch requires TLS encryption to start, on both the HTTP
and Transport layers.