Skip to content

Search and analytics engines

When your app uses a search engine, an analytics database or a graph database, OpsCrew can run it as one node next to the app: OpenSearch, Elasticsearch, Meilisearch, Typesense, ClickHouse or Neo4j Community. Each gets a memory limit, its engine’s own memory settings below that limit, a generated key or password where the engine has a login, and no open port. These engines are heavy for a small server: each has a server size it is comfortable on. On Hetzner Cloud and DigitalOcean, design picks a server at least that big; on a smaller server you already have, it warns you. Like every component, an engine costs nothing beyond the server memory it takes, which design shows with its share of the server’s price (see Components).

Engine Version Memory limit (lowest) Smallest server Licence Login
OpenSearch 3.9 1024 MB (768 MB) 4 GB Apache 2.0 none: only the app’s containers reach it
Elasticsearch 9.5 1024 MB (768 MB) 4 GB Elastic License 2.0 generated password
Meilisearch 1.55 512 MB (256 MB) 2 GB MIT generated master key
Typesense 30.2 512 MB (256 MB) 2 GB GPL 3.0 generated API key
ClickHouse 26.3, a long-term release 1024 MB (768 MB) 2 GB Apache 2.0 user app, generated password
Neo4j Community 2026.09 1536 MB (1024 MB) 4 GB GPL 3.0 user neo4j, generated password

OpsCrew adds an engine when it finds your app’s client library for it. Turn one off with enabled: false on the service in opscrew/plan.yaml, and give the app a hosted engine’s address instead. Cassandra is never run on the server: it is built for a ring of several nodes, so OpsCrew keeps it where the app reaches it now (a hosted Cassandra such as DataStax Astra DB works with the app’s driver).

Elasticsearch’s official image is under the Elastic License 2.0, and Typesense and Neo4j Community under the GPL 3.0: all allow running them for your own app; their conditions are about offering or sharing the engine itself.

Engine Variables
OpenSearch OPENSEARCH_URL (http://opensearch:9200)
Elasticsearch ELASTICSEARCH_URL (http://elastic:<password>@elasticsearch:9200)
Meilisearch MEILISEARCH_HOST (http://meilisearch:7700), MEILISEARCH_KEY
Typesense TYPESENSE_HOST, TYPESENSE_PORT, TYPESENSE_PROTOCOL, TYPESENSE_API_KEY
ClickHouse CLICKHOUSE_URL, CLICKHOUSE_USER, CLICKHOUSE_PASSWORD
Neo4j NEO4J_URI (bolt://neo4j:7687), NEO4J_USERNAME, NEO4J_PASSWORD

When your app reads its own variable for the address, that one gets the address too. Each key and password is generated by OpsCrew and kept on the computer that ran npx opscrew provision, never in your repository.

  • OpenSearch runs with its security plugin off: it has no login, and only the app’s own containers can reach it, on the app’s internal network. Its Java heap is half the memory limit.
  • Elasticsearch runs only for apps that use an Elasticsearch client: since version 7.14, Elasticsearch clients refuse OpenSearch, and OpenSearch clients refuse Elasticsearch. Security is on, with the elastic user’s password, over plain HTTP on the app’s internal network. Machine learning is off (it needs 6 GB of memory on its own). Its Java heap is half the memory limit.
  • Meilisearch runs in production mode, which requires its master key. The app gets the master key itself as MEILISEARCH_KEY. To search from the browser, create a search-only key with it and give the browser that one, never the master key. Indexing uses at most half the memory limit, and Meilisearch sends no usage data. Before you merge an update of Meilisearch, read its release notes: some versions need the data dumped and imported again.
  • Typesense keeps its index in memory (about 165 MB for a million short documents), so give it a memory limit to match your data.
  • ClickHouse gets a low-memory profile, since its own documentation is written for much larger servers: it refuses more work once it uses three quarters of its memory limit, keeps small caches, runs at most 20 queries at once and two threads per query, and leaves out the system log tables that grow on their own. Only the user app can log in from the network. ClickHouse’s long-term releases are the ones numbered YY.3 and YY.8 (26.3, 26.8 …); Dependabot also proposes the releases between them, which are supported for a shorter time, so prefer merging the YY.3 and YY.8 updates.
  • Neo4j Community has one database, the neo4j user and its password. Its heap and its page cache each get a third of the memory limit. Its versions are named after the year and month, so Dependabot also proposes the next year’s releases.

The kernel setting of OpenSearch and Elasticsearch

Section titled “The kernel setting of OpenSearch and Elasticsearch”

OpenSearch and Elasticsearch map their indexes into memory, and the server’s kernel must allow that many maps: vm.max_map_count must be at least 262144. That is a setting of the host, not of a container:

  • On Hetzner Cloud and DigitalOcean, the app’s Compose project has a small one-off container that raises the setting before the engine starts on every deploy, and writes it to /etc/sysctl.d/90-opscrew.conf so a restart of the server keeps it. A value that is already higher stays.
  • On a server you already have, the host plan sets it in /etc/sysctl.d/90-opscrew.conf, as a step you see and approve with the rest of the plan. npx opscrew drift reports it when it is lowered later.
  • In your Portainer, OpsCrew never changes the Docker host, so it does not run OpenSearch or Elasticsearch there. Design keeps the engine the app uses now (for example Amazon OpenSearch Service, Aiven for OpenSearch or Elastic Cloud). To run one next to your Portainer yourself, set it on the Docker host as root (sysctl -w vm.max_map_count=262144, and the line vm.max_map_count = 262144 in a file in /etc/sysctl.d/ so a restart keeps it), and give the app its address.

The two engines also get an open-file limit of 65536.

On a server, Meilisearch, Typesense, ClickHouse and Neo4j keep their data in /srv/data/<app>/<name>; OpenSearch and Elasticsearch, whose images run as their own users, in a Docker volume of the app’s Compose project (<app>_opensearch_data, <app>_elasticsearch_data). In your Portainer, the data is in the named volumes of the app’s stack. npx opscrew destroy keeps the data unless you add --delete-data and type the app’s name.