Koha Solutions

Configuration & operations

Rebuilding the Zebra index when Koha search results go stale

A record you can open by its number but cannot find by searching means the index is behind, not that the record is missing. Here is how to rebuild it properly.

Updated 2026-08-09 · Tested against Koha 26.05

If a record opens fine from its own URL but never appears in a search, Koha's Zebra index is out of date — the catalogue is intact and the search index is behind it. Rebuild with sudo koha-rebuild-zebra -f -v <instance>, then make sure the indexer daemon is running so it does not drift again.

A full rebuild is not instant

It re-exports and re-indexes every record, which is disk-heavy and takes real time — tens of minutes on a few hundred thousand records.
  • Search is degraded while it runs. On a live catalogue, run it outside opening hours.
  • Check free space first: the index is rebuilt, not patched, so both copies exist briefly.
  • Circulation is unaffected — checkouts and returns do not go through Zebra.

Step 1Rebuild the index

Terminal
sudo koha-rebuild-zebra -f -v library exporting bibliorecords exported: 218430zebraidx: 218430 recordsexporting authorityrecords exported: 41277

Replace library with the instance name (sudo koha-list). The flags, from the script's own help:

FlagWhat it does
<code>-f</code>, <code>--full</code>Full reindex of the whole collection. Without it the run is incremental and will not repair an index that has already drifted.
<code>-v</code>Verbose. Use it — a silent run tells you nothing about whether it worked.
<code>-b</code>Bibliographic records only. Much faster when authorities are fine.
<code>-a</code>Authority records only.
<code>--force</code>Run an incremental index even when the indexer daemon is enabled.
<code>-q</code>Quiet — for scripts and cron, not for diagnosis.

Narrow it before you widen it

Missing titles but working author searches? -b reindexes bibliographic records alone and finishes in a fraction of the time. Reach for the full rebuild when the targeted one does not fix it.

Step 2Find out why it fell behind

A rebuild fixes today. It does not fix the reason, and if you stop here you will be running it again next month. Koha keeps the index current in one of two ways, and exactly one of them should be active.

The indexer daemon watches the queue and indexes changes as they happen. This is the normal arrangement:

Terminal
sudo koha-indexer --status librarysystemctl status koha-indexer@library

Not running? Start it and enable it so it survives a reboot:

Terminal
sudo koha-indexer --start librarysudo systemctl enable koha-indexer@library

The cron fallback is a scheduled incremental index every five minutes, used on installs that do not run the daemon. It ships commented out in /etc/cron.d/koha-common and looks like this:

/etc/cron.d/koha-common
*/5 * * * * root test -x /usr/sbin/koha-rebuild-zebra && \    koha-rebuild-zebra -q $(koha-list --enabled)

One or the other, never both

The daemon and the cron line do the same job. Running both makes two processes write the same index, which produces intermittent corruption that looks exactly like the stale-results problem you started with.

Step 3Verify it actually worked

Do not verify by searching for something you already believe is indexed. Compare the catalogue against the index directly:

Terminal
sudo koha-mysql library -e "SELECT COUNT(*) FROM biblio;" 218430

Then run a blank search in the OPAC — an empty query returns everything indexed. The two numbers should be close. A large gap means the rebuild did not complete, and the reason will be in the log:

Terminal
sudo tail -50 /var/log/koha/library/zebra-error.log

Why there is a separate index at all

A MARC record is not a row of columns; it is a nested structure with repeatable fields and subfields, and searching it the way a catalogue user expects — a phrase across several fields, ranked, with truncation and diacritic folding — is not something a relational query does well or quickly. Zebra is a purpose-built index for exactly that, and it is why Koha can search a million records in the time a LIKE query would take on a hundred thousand.

The cost of any separate index is that it can disagree with the data. Koha keeps them together with a queue: a saved record enqueues an index job, and something has to drain that queue. When the drain stops, the catalogue keeps working perfectly while search quietly goes stale — which is exactly why this failure is so often reported as "Koha lost my records".

It is the same failure shape as the message queue behind notices, and worth recognising as a pattern — see Koha cron jobs.

Troubleshooting

What you seeWhat it usually is
New records never appear in searchThe indexer daemon is stopped, and the cron fallback is commented out. Nothing is draining the queue.
Rebuild runs, search is still emptyThe Zebra server itself is not running: sudo koha-zebra --start library.
Some records index, others do notThose records fail to export — usually invalid MARC. The record numbers are named in zebra-error.log.
Search works, then breaks again in daysBoth the daemon and the cron line are active and fighting. Disable one.
Rebuild fails part-way with no messageThe disk filled during the rebuild. Check df -h and the log.
Everything indexes but relevance is wrongNot an index problem — the record structure or the search field mapping. Reindexing will not change it.

Share this article

Would rather not do this yourself? We do it as a service — and if you would rather it were already done, it is on Koha Cloud before you log in.