Zum Inhalt springen
A server tower of glowing low-poly layers with light paths running out to two highlighted container nodes — as a glowing low-poly illustration.
All posts
Series: NextSearchPart 2 of 4
·Self-HostingUnraidNextcloud

NextSearch on Unraid

Two containers for NextSearch; everything else comes from what the Unraid server already runs. One compose stack, and search is up.

NextSearch needs two containers of its own on Unraid. Most people already run the rest.

Part one covered what NextSearch does: make the contents of existing Nextcloud folders searchable, strictly read-only, without a cluster alongside. This is the other half — how it lands on an Unraid server without turning into an afternoon.

Unraid is a grateful host for it. Postgres and Redis have been running for other applications on many boxes for a long time. Meilisearch, Tika and Gotenberg are the same services Paperless-ngx uses, so they are well-trodden on Unraid. The plan follows from that: use what is there, and keep the own footprint small.

The full guide in the repository

docs/unraid.md has the complete stack to copy, including the variants for external S3 and for going without Compose Manager.


Why it stays at two containers

The app image ships an all-in-one role: FrankenPHP, the queue worker and the scheduler run under one supervisor in the same container. That leaves NextSearch itself as:

  • app — the backend with serve, worker and scheduler in one
  • web — the Nuxt UI, the only container with a published port

Everything else is a service you point at over the network. All of them exist on Unraid for linux/amd64:

ServiceImageOn Unraid
Postgrespostgres:18-alpineCommunity Applications template, often already running
Redisredis:8-alpineCommunity Applications template, often already running
Meilisearchgetmeili/meilisearchCommunity Applications template
Tikaapache/tika:3.3.1.0-fullknown from the official Unraid guide for Paperless-ngx
Gotenberggotenberg/gotenberg:8same
S3minio/minioCommunity Applications template — or external S3

Tika's -full tag carries Tesseract and its language packs, so OCR needs no container of its own. Gotenberg is used only for previews of Office files; turn those off with PREVIEW_OFFICE_ENABLED=false and you don't need it at all.

Object storage is the one exception to the reuse rule. The stack below runs a small MinIO that writes to Unraid storage — previews and extracted text stay on the array, and external S3 is needed nowhere.


The stack

The least fiddly path is the Compose Manager plugin (Apps → search for "Compose Manager"). It runs a compose stack straight on Unraid, and the whole wiring lives in one file instead of spread across a dozen container templates.

Create a new stack, paste the following in, fill in the hosts:

name: nextsearch

services:
  app:
    image: mirkohaaser/nextsearch-app:0.2.4
    # All-in-one: FrankenPHP + queue worker + scheduler in one container.
    command: ["app-entrypoint", "all"]
    environment:
      APP_KEY: base64:PASTE_A_GENERATED_KEY_HERE
      APP_URL: http://YOUR_UNRAID_IP:3000
      ADMIN_EMAIL: admin@example.com
      ADMIN_PASSWORD: change-me-please

      # Reuse the existing Unraid Postgres and Redis.
      DB_HOST: 192.168.1.10
      DB_PORT: "5432"
      DB_DATABASE: nextsearch
      DB_USERNAME: nextsearch
      DB_PASSWORD: your-postgres-password

      REDIS_HOST: 192.168.1.10
      REDIS_PORT: "6379"
      REDIS_PASSWORD: your-redis-password-or-remove-this-line

      # Search and extraction.
      MEILISEARCH_HOST: http://192.168.1.10:7700
      MEILI_MASTER_KEY: your-meili-master-key
      TIKA_URL: http://192.168.1.10:9998
      GOTENBERG_URL: http://192.168.1.10:3000

      # Object storage: the bundled MinIO on Unraid storage.
      FILESYSTEM_DISK: s3
      AWS_ENDPOINT: http://minio:9000
      AWS_ACCESS_KEY_ID: nextsearch
      AWS_SECRET_ACCESS_KEY: nextsearch-secret
      AWS_BUCKET: nextsearch
      AWS_USE_PATH_STYLE_ENDPOINT: "true"
    volumes:
      - /mnt/user/appdata/nextsearch/storage:/app/storage
    depends_on:
      minio:
        condition: service_healthy
    restart: unless-stopped

  minio:
    image: minio/minio:RELEASE.2025-09-07T16-13-09Z
    command: ["server", "/data", "--console-address", ":9001"]
    environment:
      MINIO_ROOT_USER: nextsearch
      MINIO_ROOT_PASSWORD: nextsearch-secret
    volumes:
      - /mnt/user/appdata/nextsearch/minio:/data
    healthcheck:
      test: ["CMD", "mc", "ready", "local"]
      interval: 10s
      timeout: 5s
      retries: 20
    restart: unless-stopped

  web:
    image: mirkohaaser/nextsearch-web:0.2.4
    environment:
      NUXT_BACKEND_URL: http://app:8080
      NITRO_PORT: "3000"
      NITRO_HOST: 0.0.0.0
    ports:
      - "3000:3000"
    depends_on:
      - app
    restart: unless-stopped

Four values are worth a second look:

APP_KEY encrypts the stored Nextcloud credentials and has to be a real key. Generate one on any machine with Docker:

docker run --rm mirkohaaser/nextsearch-app:0.2.4 php artisan key:generate --show

Paste the whole base64:… string — and leave it alone. Swap it later and the saved Nextcloud passwords can no longer be decrypted.

MEILI_MASTER_KEY has to be the same value that is set on the Meilisearch container.

The hosts. Replace 192.168.1.10 with the address the service actually runs on. If a service is a container on the same Unraid Docker network, its container name works too.

The image tag should be pinned to a released version — 0.2.4 here rather than latest. Otherwise a "force update" in Unraid moves the stack to a build nobody asked for.

Only web publishes a port. Behind a reverse proxy (Nginx Proxy Manager, SWAG, Traefik) you point at the web container on 3000 and set APP_URL to the external address.


Search, extraction, object storage

Meilisearch, Tika and Gotenberg are the three services that are new for most people. Two ways from here.

As their own Unraid containers via Community Applications: Meilisearch with MEILI_MASTER_KEY set and MEILI_ENV=production on port 7700, Tika from apache/tika:3.3.1.0-full on 9998, Gotenberg from gotenberg/gotenberg:8 on 3000. Then MEILISEARCH_HOST, TIKA_URL and GOTENBERG_URL point there. Anyone running Paperless-ngx can use its Tika and Gotenberg directly — two URLs, done.

One stumbling block on the side: Gotenberg listens on 3000 itself, and that host port is already taken by the web container. So pick a different host port for a Gotenberg container of your own.

Or into the same stack, if you'd rather not maintain them separately. Then the three services below come along and the URLs above point at the service names (http://meilisearch:7700, http://tika:9998, http://gotenberg:3000):

  meilisearch:
    image: getmeili/meilisearch:v1.50.0
    environment:
      MEILI_MASTER_KEY: your-meili-master-key
      MEILI_ENV: production
      MEILI_NO_ANALYTICS: "true"
    volumes:
      - /mnt/user/appdata/nextsearch/meili:/meili_data
    restart: unless-stopped

  tika:
    image: apache/tika:3.3.1.0-full
    restart: unless-stopped

  gotenberg:
    image: gotenberg/gotenberg:8.34
    command:
      - gotenberg
      - --api-timeout=120s
      - --chromium-disable-javascript=true
      - --chromium-allow-list=file:///tmp/.*
    restart: unless-stopped

Postgres and Redis stay outside even then — those are the two where reuse pays off most.

For object storage there is nothing to set up: NextSearch creates the bucket itself on first boot. The MinIO volume sits at …/appdata/nextsearch/minio. For a large archive it belongs on a dedicated share rather than in appdata — the blobs grow with the index, and appdata usually lives on the cache pool. Only the path changes.

If you already run a MinIO, drop the minio service along with its depends_on and point AWS_ENDPOINT at the existing instance. For real S3 (AWS, Backblaze B2, Wasabi) AWS_ENDPOINT goes away as well and AWS_USE_PATH_STYLE_ENDPOINT becomes false; the bucket has to exist beforehand there, because a restricted key usually can't create one.


First run, later updates

On first boot app runs the migrations, creates the administrator from ADMIN_EMAIL and ADMIN_PASSWORD, and sets up the Meilisearch index. The container log then reads starting all-in-one. After that, open http://YOUR_UNRAID_IP:3000, sign in, add a Nextcloud instance, run the connection test, pick a folder. Progress shows under Admin → Status.

For an update it is enough to bump the image tags in the stack and redeploy (Compose Manager → Update). The app container re-runs the migrations on start. An image swap touches neither Postgres nor object storage, and the search index rebuilds itself anyway if you ever clear it.

A note on the all-in-one role: it is meant for a single node and bootstraps on start. Exactly one app container may run against a given database. For a distributed setup, use the separate serve, worker and scheduler roles from the regular docker-compose.yml. For an Unraid box at home, all-in-one is the shorter path.


Conclusion

With this setup the effort is not in getting NextSearch up, it's in filling in the hosts that already run. Two containers, one compose stack, one key that has to stay put — after that the scheduler indexes on its own.


Call to action

The stack to copy is in docs/unraid.md, NextSearch itself at https://github.com/McGo/NextSearch.

If you want this set up for a team or a whole organisation — with your own S3, behind a reverse proxy, connected to several instances — feel free to get in touch.