Upgrade Procedure

This page describes the procedures for upgrading Fess from a previous version to the latest release.

Warning

Important Notes Before Upgrade

  • Always create a backup before upgrading

  • It is strongly recommended to validate the upgrade in a test environment first

  • Services will be stopped during the upgrade, so schedule appropriate maintenance time

  • Configuration file formats may have changed depending on the version

Supported Versions

This upgrade procedure supports upgrades between the following versions:

  • Fess 14.x → Fess 15.9

  • Fess 15.x → Fess 15.9

Important

Fess 14.x supports the OpenSearch 2.x series, while Fess 15.9 supports OpenSearch 3.8.0. Because the OpenSearch plugins for Fess must exactly match the OpenSearch version, upgrading from 14.x also requires a major version upgrade of OpenSearch. See Step 4: Upgrade OpenSearch for details.

Note

When upgrading from older versions (13.x or earlier), a phased upgrade may be necessary. For details, check the release notes.

Pre-Upgrade Preparation

Verify Version Compatibility

Verify the compatibility between the upgrade target version and the current version.

Plan Downtime

The upgrade process requires system shutdown. Plan downtime considering the following:

  • Backup time: 10 minutes to several hours (depending on data volume)

  • Upgrade time: 10 to 30 minutes

  • Verification time: 30 minutes to 1 hour

  • Reserve time: 30 minutes

Recommended Maintenance Time: Total 2 to 4 hours

Step 1: Data Backup

Back up all data before upgrading.

Configuration Data Backup

  1. Backup from Admin Screen

    Log in to the admin screen and click “System Info” → “Backup”.

    The backup page lists the following configuration data as individual items. Click each row to download it (these are individual files per item, not a single ZIP; there is no bulk-download feature, so download the items you need one at a time).

    • fess_basic_config.bulk - Configuration indices (19 indices covering crawl settings, scheduler, labels, key matches, roles, web/file authentication, and so on)

    • fess_config.bulk - The same 19 indices plus runtime data such as crawling information, failure URLs, job logs, and the thumbnail queue (25 indices in total)

    • fess_user.bulk - Users, roles, and groups

    • system.properties - System settings, including general configuration

    • fess.json - Index settings (shard count, index.knn, and so on)

    • doc.json - Document mapping (field definitions)

    Note

    fess_config.bulk already includes everything in fess_basic_config.bulk. For a configuration backup before upgrading, fess_basic_config.bulk, fess_user.bulk, and system.properties are sufficient.

    Note

    Log data such as search logs and click logs (search_log.ndjson, click_log.ndjson, favorite_log.ndjson, user_info.ndjson) can also be downloaded from the same page. They are not needed if you only want to back up the configuration. Note that these *.ndjson files cannot be restored by uploading them on the backup page (see “Rollback Procedure”).

  2. Configuration File Backup

    ZIP version:

    $ cp /path/to/fess/app/WEB-INF/conf/system.properties /backup/
    $ cp /path/to/fess/app/WEB-INF/classes/fess_config.properties /backup/
    $ cp /path/to/fess/bin/fess.in.sh /backup/
    

    RPM version:

    $ sudo cp /etc/fess/system.properties /backup/
    $ sudo cp /etc/fess/fess_config.properties /backup/
    $ sudo cp /etc/sysconfig/fess /backup/
    

    DEB version:

    $ sudo cp /etc/fess/system.properties /backup/
    $ sudo cp /etc/fess/fess_config.properties /backup/
    $ sudo cp /etc/default/fess /backup/
    

    Note

    /etc/sysconfig/fess (RPM version) and /etc/default/fess (DEB version) are environment variable files that set values such as FESS_PORT, FESS_HEAP_SIZE, SEARCH_ENGINE_HTTP_URL, and FESS_DICTIONARY_PATH. For the ZIP version, the equivalent settings are in bin/fess.in.sh.

  3. Customized Configuration Files

    If you have customized configuration files, back up those as well:

    $ cp /path/to/fess/app/WEB-INF/classes/log4j2.xml /backup/
    

    Note

    app/WEB-INF/classes/log4j2.xml is the log configuration for the Fess main (web) process. Child processes such as the crawler use separate files (for example, app/WEB-INF/env/crawler/resources/log4j2.xml, one each for crawler, suggest, thumbnail, and chunk — four in total), so back those up too if you have customized them.

Index Data Backup

Back up OpenSearch index data.

Method 2: Backup Entire Directory

Stop OpenSearch and back up the data directory.

$ sudo systemctl stop opensearch
$ sudo tar czf /backup/opensearch-data-$(date +%Y%m%d).tar.gz /var/lib/opensearch/data
$ sudo systemctl start opensearch

Docker Version Backup

OpenSearch data is stored in Docker volumes. compose-opensearch3.yaml defines two volumes: search01_data for index data and search01_dictionary for dictionary files.

Note

The actual volume names are prefixed with the Compose project name (by default, the name of the directory containing the Compose files). Check the exact names with:

$ docker volume ls

Stop the containers, then back up the volumes. Specify the actual volume name, including the prefix, for -v in docker run:

$ docker compose -f compose.yaml -f compose-opensearch3.yaml stop
$ PROJECT=$(basename "$(pwd)")
$ docker run --rm -v ${PROJECT}_search01_data:/data -v $(pwd):/backup ubuntu tar czf /backup/search01-data-backup.tar.gz /data
$ docker run --rm -v ${PROJECT}_search01_dictionary:/data -v $(pwd):/backup ubuntu tar czf /backup/search01-dictionary-backup.tar.gz /data
$ docker compose -f compose.yaml -f compose-opensearch3.yaml start

Warning

If you specify -v with the unprefixed name search01_data, Docker does not reference the existing volume — it creates a new, empty volume with the same name instead. The command does not report an error, and an archive with empty contents is created, so it can look as though the backup succeeded.

Note

The Fess main container (fess01) has no dedicated volume of its own, so the two volumes above are the only backup targets. However, general settings changed from the admin UI and plugins installed from the admin UI are stored only inside the container and are lost when the container is recreated. Persist these instead by specifying them via FESS_JAVA_OPTS or FESS_PLUGINS in the Compose file.

Step 2: Stop Current Version

Stop Fess and OpenSearch.

The ZIP version does not include a stop script. If you started bin/fess with the -p option, stop it using the PID file:

$ kill $(cat /path/to/fess/fess.pid)
$ kill <opensearch_pid>

If you started it without -p, find the process ID and kill it manually (-d alone does not create a PID file).

RPM/DEB version (systemd):

$ sudo systemctl stop fess.service
$ sudo systemctl stop opensearch.service

Docker version:

$ docker compose -f compose.yaml -f compose-opensearch3.yaml down

Step 3: Install New Version

The procedure varies depending on the installation method.

ZIP Version

  1. Download and extract the new version:

    $ wget https://github.com/codelibs/fess/releases/download/fess-15.9.0/fess-15.9.0.zip
    $ unzip fess-15.9.0.zip
    

    Note

    Fess archives are distributed only in ZIP format (fess-15.9.0.tar.gz is not provided).

  2. Copy configuration from the old version:

    $ cp /path/to/old-fess/app/WEB-INF/conf/system.properties /path/to/fess-15.9.0/app/WEB-INF/conf/
    $ cp /path/to/old-fess/app/WEB-INF/classes/fess_config.properties /path/to/fess-15.9.0/app/WEB-INF/classes/
    $ cp /path/to/old-fess/bin/fess.in.sh /path/to/fess-15.9.0/bin/
    

    Warning

    Copied whole, fess_config.properties and fess.in.sh keep their old values, including values whose default changed in 15.9: jobs created afterwards default to Groovy, for example. Before running the last two commands, compare each file with the one in fess-15.9.0 and carry over only the values you changed yourself. See Configuration Files Carried Over from 15.8 for what to check.

  3. If you have customizations, also copy the following:

    # Log configuration
    $ cp /path/to/old-fess/app/WEB-INF/classes/log4j2.xml /path/to/fess-15.9.0/app/WEB-INF/classes/
    # Installed plugins
    $ cp -r /path/to/old-fess/app/WEB-INF/plugin/. /path/to/fess-15.9.0/app/WEB-INF/plugin/
    # Theme
    $ cp -r /path/to/old-fess/app/themes/. /path/to/fess-15.9.0/app/themes/
    

    Warning

    Do not copy JSPs (app/WEB-INF/view/) edited via “Design” in the admin UI as-is. If their structure differs from the JSPs in the new version, pages may not render correctly. Reapply your changes to the new version’s JSPs instead.

    Note

    Plugins copied from app/WEB-INF/plugin/ were built for the old version. After copying them, run bin/fess-setup upgrade plugins in fess-15.9.0 to replace each one with the version built for 15.9; see Updating Plugin Versions.

  4. Verify configuration differences and adjust as necessary

RPM/DEB Version

Install the new version package:

# RPM
$ sudo rpm -Uvh fess-15.9.0.rpm

# DEB
$ sudo dpkg -i fess-15.9.0.deb

Note

For the RPM version, the configuration files under /etc/fess/* are registered as %config(noreplace), so they are retained across upgrades (the new default files are placed alongside them with a .rpmnew suffix). If new configuration options have been added, manual adjustment may be necessary. A /etc/fess/fess_config.properties that you modified keeps its old values in 15.9, just as a file copied in the ZIP procedure does; see Configuration Files Carried Over from 15.8.

Warning

For the DEB version, /etc/fess/* is not registered as a conffile (the only conffiles are /etc/default/fess, /etc/init.d/fess, and /usr/lib/systemd/system/fess.service). As a result, running dpkg -i overwrites files such as /etc/fess/fess_config.properties with the new version’s files, without asking and without keeping a copy of the old ones, so back them up first (Step 1). After upgrading, reapply your changes to the new files rather than restoring the old files whole (see Configuration Files Carried Over from 15.8). Note that /etc/fess/system.properties is a runtime-generated file not included in the package, so it is not overwritten.

Docker Version

  1. Obtain Compose files for the new version:

    $ wget https://raw.githubusercontent.com/codelibs/docker-fess/v15.9.0/compose/compose.yaml
    $ wget https://raw.githubusercontent.com/codelibs/docker-fess/v15.9.0/compose/compose-opensearch3.yaml
    
  2. Pull new images:

    $ docker compose -f compose.yaml -f compose-opensearch3.yaml pull
    

Step 4: Upgrade OpenSearch

Fess 15.9 supports OpenSearch 3.8.0. If the OpenSearch you connect to is older than that, upgrade it using the following procedure.

Note

This procedure applies when you are managing OpenSearch manually on a ZIP or RPM/DEB installation. For the Docker version, pulling the new image in Step 3 updates OpenSearch and its plugins together, so this step is not required.

Important

Regardless of whether you use chunk-vector search (semantic search), Fess 15.9 always includes index.knn in the search index settings and content_chunk_vector (a knn_vector type) in the mapping. Because of this, the OpenSearch you connect to must have the k-NN plugin installed.

  • It is bundled with the standard OpenSearch distribution and the Docker version’s image.

  • It is not included in the minimal distribution, so creating a new index fails and |Fess| cannot start.

  • The index settings also always send knn.derived_source.enabled. An older OpenSearch that does not recognize this setting fails to create the index regardless of whether the k-NN plugin is present.

See “Prerequisites” in Semantic Search (Content Chunking + Vector Search) for details.

Warning

Be careful when performing major version upgrades of OpenSearch. Index compatibility issues may occur. Fess 14.x uses the OpenSearch 2.x series, so upgrading from 14.x always falls into this case.

  1. Install the new version of OpenSearch

  2. Reinstall plugins:

    $ sudo /usr/share/opensearch/bin/opensearch-plugin install org.codelibs.opensearch:opensearch-analysis-fess:3.8.0
    $ sudo /usr/share/opensearch/bin/opensearch-plugin install org.codelibs.opensearch:opensearch-analysis-extension:3.8.0
    $ sudo /usr/share/opensearch/bin/opensearch-plugin install org.codelibs.opensearch:opensearch-minhash:3.8.0
    $ sudo /usr/share/opensearch/bin/opensearch-plugin install org.codelibs.opensearch:opensearch-configsync:3.8.0
    

    Note

    The version of these plugins must match the version of OpenSearch you use. Fess 15.9 supports OpenSearch 3.8.0. Installation fails if the versions do not match.

  3. Start OpenSearch:

    $ sudo systemctl start opensearch.service
    

Step 5: Start New Version

ZIP version:

$ cd /path/to/fess-15.9.0
$ ./bin/fess -d -p /path/to/fess-15.9.0/fess.pid

Note

Specifying -p creates a PID file, which lets you stop Fess the next time with kill $(cat /path/to/fess-15.9.0/fess.pid).

RPM/DEB version:

$ sudo systemctl start opensearch.service
$ sudo systemctl start fess.service

Docker version:

$ docker compose -f compose.yaml -f compose-opensearch3.yaml up -d

Step 6: Verify Operation

  1. Check Logs

    Verify there are no errors.

    ZIP version:

    $ tail -f /path/to/fess/logs/fess.log
    

    RPM/DEB version:

    $ sudo tail -f /var/log/fess/fess.log
    

    Docker version:

    $ docker compose -f compose.yaml -f compose-opensearch3.yaml logs -f fess01
    

    Note

    The same log directory also contains fess-crawler.log for crawl processing, audit.log for authentication and admin operations, and searchlog.log for search requests.

  2. Access Web Interface

    Access http://localhost:8080/ in a browser.

  3. Log in to Admin Screen

    Access http://localhost:8080/admin and log in with the administrator account.

  4. Check the Version

    In the admin UI, click “System Info” → “Config Info” and confirm that fess.version shown under “System Properties” reflects the new version.

  5. Verify Search Operation

    Execute a search on the search screen and verify results are returned normally.

Upgrading from 15.8 to 15.9

If you are upgrading from 15.8, the following changes are not backward compatible.

Removal of the embedded OpenSearch

Up to 15.8, starting bin/fess without setting SEARCH_ENGINE_HTTP_URL made Fess run an OpenSearch node inside its own JVM. That configuration is gone in 15.9: the search engine is always a separate server.

bin/fess.in.sh now sets SEARCH_ENGINE_HTTP_URL=http://localhost:9200 by default. Fess refuses to start when no OpenSearch is reachable. bin/fess-setup install opensearch installs one (Linux and Windows only; OpenSearch publishes no macOS build, so use Homebrew or Docker there).

Fess also needs FESS_DICTIONARY_PATH to match configsync.config_path in the opensearch.yml of that OpenSearch; without it, Fess cannot create its indices. The 15.9 bin/fess.in.sh (bin\fess.in.bat on Windows) sets it on its own when opensearch/ in the Fess directory holds exactly one OpenSearch installed by bin/fess-setup install opensearch. For any other OpenSearch, set it as described in Linux Installation (Detailed Procedure) or Windows Installation (Detailed Procedure).

Removed along with it:

  • the es/ directory (es/modules, es/plugins and es/data)

  • -Dfess.es.dir and SEARCH_ENGINE_HOME

  • bin/module.xml and bin/plugin.xml

  • the fallbacks to the legacy elasticsearch.* configuration keys

An engine older than OpenSearch 3 also stops startup now, where 15.8 logged an error and carried on. Those versions do not implement the _shard_doc sort that every whole-result-set operation relies on, and over HTTP such a request hangs rather than failing.

Warning

Index data from an embedded installation cannot be carried over. Build a new external OpenSearch server, move the settings with the admin Backup page, and re-crawl. The backup covers crawl settings, users and logs; it does not contain the crawled documents.

Playwright Crawler Moved to a Plugin

The Playwright crawler, and the Node.js executables it runs, are no longer part of the distribution. In fess-15.8.0.zip (457.1 MiB), the Playwright driver bundle that carried the Node.js executables took up 204.3 MiB.

If a crawl configuration names the Playwright client, for example with client.crawlerClients=playwright:http://.* in its configuration parameters, install both the plugin and Node.js. The plugin is also available from the System > Plugin page in the administration screen. bin/fess.in.sh discovers Node.js and sets PLAYWRIGHT_NODEJS_PATH.

$ bin/fess-setup install plugin fess-crawler-playwright
$ bin/fess-setup install nodejs

Without the plugin, such a configuration is still crawled, but with the plain HTTP client, so text that only JavaScript produces is not indexed. The crawl job still ends successfully and no failure URL is recorded. For each crawl, fess-crawler.log records one warning per crawling configuration that names the plugin and the two commands above.

Nothing is needed if you do not use the Playwright crawler.

Google Cloud Storage Moved to a Plugin

The Google Cloud Storage SDK is no longer part of the distribution, so gcs:// crawling and the gcs storage type now come from the fess-storage-gcs plugin. Install it from the System > Plugin page in the administration screen, or with the command below.

$ bin/fess-setup install plugin fess-storage-gcs

gcs also left the shipped crawler.file.protocols, which is now file,smb,smb1,ftp; the plugin adds it back when it is installed. Until then a file crawl configuration whose path begins with gcs: is logged as a warning and crawls nothing. An installation that upgrades keeps its own crawler.file.protocols, so the path is still accepted but no crawler client handles it; on a new installation gcs: is not a configured protocol, so the administration screen refuses to save the path and a path saved earlier is read as a local file path. The storage page logs a warning as well and shows the failure as an error naming the plugin to install, where before it rendered an empty file list.

Nothing is needed if you do not use Google Cloud Storage. Amazon S3 and S3-compatible storage such as MinIO moved to a plugin in the same way; see the next section.

Amazon S3 Moved to a Plugin

The AWS SDK is no longer part of the distribution, so s3:// crawling and the s3 and s3_compat storage types now come from the fess-storage-s3 plugin. Install it from the System > Plugin page in the administration screen, or with the command below.

$ bin/fess-setup install plugin fess-storage-s3

s3 also left the shipped crawler.file.protocols, which is now file,smb,smb1,ftp; the plugin adds it back when it is installed. storage.type still defaults to auto, which resolves to S3 when no endpoint is set, so the storage page in the administration screen does not work until the plugin is installed even if you never named s3 yourself. Your storage.* values are kept, because they live in WEB-INF/conf/system.properties, and an existing crawler.file.protocols is not replaced by the upgrade either.

Nothing is needed if you do not use Amazon S3 or S3-compatible storage such as MinIO.

SSO Authentication Moved to Plugins

None of the four SSO authenticators is part of the distribution any more. Each sso.type value now comes from its own plugin, which carries the identity library it needs: saml from fess-sso-saml, spnego from fess-sso-spnego, entraid (and the legacy aad) from fess-sso-entraid, and oic from fess-sso-oidc. Note the last pair: the plugin is named fess-sso-oidc while the sso.type value stays oic, the one place where the two differ. Install the one you use from the System > Plugin page in the administration screen, or with the command below.

$ bin/fess-setup install plugin fess-sso-saml

Your settings are kept, because sso.type and the saml.*, spnego.*, entraid.*, aad.* and oic.* keys live in WEB-INF/conf/system.properties. “System” → “General” in the admin UI also still offers all four types and still shows their settings, because a plugin cannot supply a JSP, so nothing on that screen reports a missing plugin.

Until the plugin is installed, a request to /sso/ is redirected back to the login page, which reports that the SSO login failed, and no one can log in through SSO. 15.9 records a warning in fess.log naming the component it looked for and the plugin that provides it, where up to 15.8 nothing was logged at any level.

Nothing is needed if you do not use SSO, that is if sso.type is none or unset.

The Built-in Script Engine Changed from Groovy to JavaScript

Up to 15.8 the built-in script engine was Groovy and job.default.script defaulted to groovy. In 15.9 the built-in engine is JavaScript and the default is javascript. Groovy is no longer built in: it is provided by the fess-script-groovy plugin, which has to be installed before a scriptType of groovy can be resolved.

An upgrade does not change the engine stored with a setting, and a setting saved before 15.9 that records no engine counts as groovy. Without the plugin, the following stop working:

  • Scheduled jobs saved as groovy. This includes the jobs that 15.8 created itself: Default Crawler, Suggest Indexer, Config Reloader, Log Aggregator, Doc Purger and the other bundled jobs are all saved as groovy, and at startup 15.9 only adds the bundled jobs that do not exist yet, so it leaves them as they are. Each of them fails whenever its schedule fires, so Default Crawler no longer crawls. Most bundled jobs have “Logging” turned off, so their failures do not appear in the job log, only as Failed to execute job warnings in fess.log.

  • Web and file crawling configurations with field scripts (field.script.<field name>) in “Config Parameters”. Every document of such a configuration fails with a ScriptEngineException, recorded as a failure URL, while the crawl job itself still ends successfully.

  • Data store configurations with a “Script”. Values other than a plain parameter name cannot be evaluated; see Data Store Connector Overview.

  • Document boost rules. Such a rule boosts nothing.

  • Path mappings whose “Replacement” begins with groovy:. Such a mapping is not applied, and URLs are left unchanged.

After the first start, look in fess.log for a warning that begins with Settings use the script engine groovy, which is not registered. Fess checks the settings above once at startup and lists how many of each kind use an engine that no plugin provides. It also names job.default.script when a fess_config.properties carried over from 15.8 still sets groovy, in which case jobs created after the upgrade use Groovy too (see Configuration Files Carried Over from 15.8). Resolve it in one of two ways:

  • Install the plugin and restart Fess. The stored Groovy scripts then run unchanged, and the warning is no longer logged. The plugin can also be installed from “System” → “Plugins” in the admin UI.

    $ bin/fess-setup install plugin fess-script-groovy
    
  • Move each setting to JavaScript. First rewrite any syntax that only Groovy accepts, then select JavaScript:

    • Scheduled jobs: under “System” → “Scheduler”, set “Execution Method” to javascript. The scripts of the bundled jobs are valid JavaScript as they are, with two exceptions: Thumbnail Purger uses the Groovy long literal 1000L, which JavaScript rejects (write 1000), and Index Exporter needs the change described in The Index Exporter Job Names a Removed Package. A JavaScript array literal is converted to a Java String[] automatically, so the as String[] casts of the Groovy form are dropped:

      return container.getComponent("crawlJob").logLevel("info").webConfigIds(["1", "2"]).fileConfigIds(["1"]).dataConfigIds([]).execute(executor);
      
    • Web and file crawling configurations: add config.script.type=javascript to “Config Parameters”.

    • Data store configurations: add script_type=javascript to “Parameters”.

    • Document boost rules: set “Script Type” to javascript.

    • Path mappings: begin the “Replacement” with javascript: instead of groovy:.

    • job.default.script: set it to javascript in a fess_config.properties carried over from 15.8.

crawler.default.script Was Removed

crawler.default.script no longer exists in fess_config.properties. Remove it from your configuration; a value left under that name has no effect.

The storage Crawl Protocol Was Removed

storage is no longer accepted in crawler.file.protocols; the shipped value is file,smb,smb1,ftp. Use s3 instead, and change any file crawling configuration whose path begins with storage: to an s3: path. s3 needs the fess-storage-s3 plugin.

The Index Exporter Job Names a Removed Package

15.9 no longer ships the org.opensearch classes; the query builders that job scripts use now live under org.codelibs.fesen.opensearch. The script that 15.8 stored for the Index Exporter job names org.opensearch.index.query.QueryBuilders, and the upgrade does not replace it, so the job fails even with fess-script-groovy installed. The job ships disabled and without a schedule, so this only matters if you run it. Open it under “System” → “Scheduler” and change the package in its script to the one 15.9 uses:

return new org.codelibs.fess.job.IndexExportJob().query(org.codelibs.fesen.opensearch.index.query.QueryBuilders.matchAllQuery()).execute()

Change any script of your own that names org.opensearch.index.query in the same way. See Index Export Feature for more query examples.

Configuration Files Carried Over from 15.8

The ZIP procedure in Step 3 copies fess_config.properties and bin/fess.in.sh from the old installation, and an RPM upgrade keeps a /etc/fess/fess_config.properties that you modified (the 15.9 file is placed next to it as fess_config.properties.rpmnew). Either way, 15.9 then runs with the 15.8 values, including those whose shipped value changed in 15.9. Review at least the following keys.

Key 15.8.0 15.9 Effect of keeping the 15.8 value
job.default.script groovy javascript

Jobs created under “System” → “Scheduler” default to groovy and fail without the fess-script-groovy plugin.

job.template.script Groovy form with as String[] JavaScript form A job created from a crawling configuration gets a Groovy script.
crawler.file.protocols file,smb,smb1,ftp,storage,s3,gcs file,smb,smb1,ftp

A path beginning with s3: or gcs: is still accepted without its plugin and is skipped with a warning when crawled. storage is no longer supported.

search_engine.http.url http://localhost:9201 http://localhost:9200

Used when SEARCH_ENGINE_HTTP_URL is not set, as in a bin/fess.in.sh copied from 15.8 in which you did not set it. Fess then looks for OpenSearch on port 9201, the port of the embedded OpenSearch that 15.9 removed.

jvm.crawler.options, jvm.thumbnail.options -Djcifs.smb.client.*, -Djcifs.smb1.smb.client.* -Djcifs.client.* The SMB timeouts stay at the jcifs defaults; see SMB Timeouts Use the jcifs 3 Property Names.

crawler.default.script, theme.allowed.archive.extensions, theme.assets.cache.max.age, theme.assets.precompressed, rag.chat.message.max.length, supported.uploaded.js.extentions, supported.uploaded.css.extentions, supported.uploaded.media.extentions, supported.uploaded.files, online.help.name.design

Present Removed No effect; remove them.

A bin/fess.in.sh copied from 15.8 also lacks two things that the 15.9 file has. It leaves SEARCH_ENGINE_HTTP_URL unset unless you set it yourself, where 15.9 sets http://localhost:9200, and it does not look for the Node.js installed by bin/fess-setup install nodejs, so the Playwright crawler cannot find Node.js unless you set PLAYWRIGHT_NODEJS_PATH.

Rather than copying either file whole, start from the file shipped with 15.9 and reapply the values you changed. diff shows them:

$ diff /path/to/old-fess/app/WEB-INF/classes/fess_config.properties /path/to/fess-15.9.0/app/WEB-INF/classes/fess_config.properties
$ diff /path/to/old-fess/bin/fess.in.sh /path/to/fess-15.9.0/bin/fess.in.sh

For an RPM upgrade, compare /etc/fess/fess_config.properties with /etc/fess/fess_config.properties.rpmnew in the same way. A DEB upgrade overwrites /etc/fess/fess_config.properties instead (see Step 3), so it starts from the 15.9 values and only your own changes need to be reapplied.

SMB Timeouts Use the jcifs 3 Property Names

jcifs, the library Fess uses to crawl SMB file servers, renamed its properties in version 3: jcifs.smb.client.* became jcifs.client.*, and the separate jcifs.smb1.smb.client.* names for SMB1 were folded into the same properties. Up to 15.8, jvm.crawler.options and jvm.thumbnail.options still passed the old names, which jcifs ignores, so SMB crawls ran with the jcifs defaults. 15.9 passes the new names:

-Djcifs.client.responseTimeout=30000
-Djcifs.client.soTimeout=35000
-Djcifs.client.connTimeout=60000
-Djcifs.client.sessionTimeout=60000

The connect and session timeouts therefore take effect for the first time and grow from the jcifs default of 35 seconds to 60 seconds: a crawl now waits up to 60 seconds for an SMB server that does not respond. The response and socket timeouts equal the jcifs defaults, so they do not change.

If you changed these timeouts, rename them in both options. Under the old names they had no effect in 15.8 either. A fess_config.properties carried over from 15.8 keeps the old names, and with them the jcifs defaults.

Four Properties Without Effect Were Removed

The following keys no longer exist in fess_config.properties. Fess never read them from that file, so a value left under these names has no effect, as before.

  • theme.allowed.archive.extensions

  • theme.assets.cache.max.age

  • theme.assets.precompressed

  • rag.chat.message.max.length

The limit that rag.chat.message.max.length sets still works, but it is read as a system property: set it in app/WEB-INF/conf/system.properties or with -Dfess.system.rag.chat.message.max.length, as described in AI Mode Configuration.

The Page Design Editor Was Removed

[System > Page Design] is no longer in the admin UI, so the JSP, CSS and image files of the search screen can no longer be edited there. Use a static theme to change how the search screen looks (see Theme Development Guide).

The following keys were removed from fess_config.properties as well. A value left under these names is not used.

  • supported.uploaded.js.extentions

  • supported.uploaded.css.extentions

  • supported.uploaded.media.extentions

  • supported.uploaded.files

  • online.help.name.design

The admin-design and admin-design-view roles no longer grant anything. A user who has only these roles is taken to the search screen instead of the admin UI after logging in.

15.9-Specific Migration Tasks

If you are upgrading from 15.7 or earlier to 15.9, the following tasks may be required depending on which features you use.

If You Were Using AI Search Mode (RAG)

Starting with 15.9, AI search mode (RAG) functionality has been split out into plugins such as fess-llm-ollama, fess-llm-openai, and fess-llm-gemini. Install the plugin that corresponds to the provider you use from “System” → “Plugins” in the admin UI.

If You Were Using SPNEGO (Windows Integrated Authentication)

Starting with 15.9, a SPNEGO login is rejected when the Kerberos realm of the client principal differs from the server realm. If your users log in from a child domain of an AD domain tree or from a trusted forest, list those realms in spnego.allowed.realms, separated by commas, from “System” → “General” in the admin UI or in app/WEB-INF/conf/system.properties. Otherwise users who could log in up to 15.7 are rejected with Kerberos realm is not allowed. For details, see SSO Configuration with Windows Integrated Auth.

The coded defaults of spnego.allow.unsecure.basic and spnego.allow.localhost also changed from true to false in 15.9. An installation where these keys are absent from app/WEB-INF/conf/system.properties inherits the stricter behavior on upgrade. In particular, with spnego.allow.unsecure.basic=false the SPNEGO library only offers Basic authentication for requests where HttpServletRequest#isSecure() returns true, so behind a reverse proxy that terminates TLS and forwards plain HTTP, clients that used to fall back to Basic authentication can no longer log in. Set tomcat.secure=true in tomcat_config.properties in that case; see SSO Configuration with Windows Integrated Auth for details.

Warning

A coded default only applies while the key is absent, and “System” → “General” in the admin UI writes every spnego.* key each time you save. An installation that ever pressed Update on that screen under 15.7 therefore still has spnego.allow.unsecure.basic=true and spnego.allow.localhost=true stored, so upgrading to 15.9 does not harden it: the permissive behavior is kept silently, and 15.9 only records a warning in fess.log when SPNEGO is initialized. Open “System” → “General” in the admin UI (or edit system.properties) and turn both off deliberately. spnego.allow.localhost=true is the more dangerous of the two, because the SPNEGO library then authenticates same-host requests as the server OS user with no Kerberos verification at all, which is unsafe behind a same-host reverse proxy.

If You Were Using SAML Authentication (SSO)

Starting with 15.9, Fess binds every SAML response to the ID of the AuthnRequest it sent, so IdP-initiated (unsolicited) SSO no longer works. A login started from a Fess tile in an IdP portal, such as the Okta dashboard or the Microsoft Entra ID “My Apps” portal, has no AuthnRequest to match against and is rejected. It worked up to 15.7 because Fess bounced the unmatched response back to the IdP and the IdP immediately returned a solicited assertion. If you place a tile on the IdP side, point it at the Fess /sso/ endpoint so that the login is SP-initiated.

The IdP also returns the assertion as a cross-site POST, so tomcat.sameSiteCookies in tomcat_config.properties must be set to none. With the shipped default lax the session cookie is not sent on that request and the SAML login cannot complete. This file is located in lib/classes/ for the ZIP package and in /etc/fess/ for the DEB/RPM packages, and Fess must be restarted after the change. Browsers only accept none on a cookie that also carries the Secure attribute, so Fess must be served over HTTPS. Up to 15.7 the same misconfiguration did not produce a clean error but an endless redirect loop back to the IdP, so check the setting even on a site that appeared to work; 15.9 fails once instead of looping. For details, see SAML Authentication SSO Setup.

If You Were Using Microsoft Entra ID (Azure AD)

Starting with 15.9, the response mode requested from the authorization endpoint defaults to query instead of form_post. Up to 15.7 the callback was returned as a cross-site POST, and the Fess default tomcat.sameSiteCookies = lax does not send the session cookie with such a request, so tomcat.sameSiteCookies = none was required. If you set none only for that reason, you can restore the default. To keep the previous behaviour, set entraid.response.mode=form_post and leave tomcat.sameSiteCookies = none in place. Browsers only accept none on a cookie that also carries the Secure attribute, so that path requires Fess to be served over HTTPS as well.

Starting with 15.9, Fess also resolves the user’s group and role membership in the background after login completes, instead of blocking the login on Microsoft Graph. Until resolution finishes — or if it does not fully succeed — the user holds only their own user-level permission and whatever entraid.default.groups and entraid.default.roles provide. With neither of those set, which is the shipped default, a search made in that window returns no documents at all, because a crawling configuration created with the shipped defaults grants {role}guest and a logged-in user does not hold that role. The search screen says so while resolution is in progress, and says something different if it did not fully succeed — the resolution counts as failed unless both the direct membership lookup and the nested group walk succeeded. Resolution is retried whenever the access token is renewed, and a later success clears the message, so a failure is not final for a session that outlives the token; to retry straight away, log out and log in again. For details, see SSO Configuration with Entra ID.

One consequence of resolving in the background: until it lands, the user’s resolved roles are not yet known. An administrator is therefore redirected to the search screen instead of the admin dashboard, and opening an administration page during that window returns them to the search screen. The window is up to about a second of scheduling delay plus the Microsoft Graph calls themselves — one for the direct memberships, then one more for each of those groups to walk the nested groups, issued one after another on a cold cache — so it grows with the number of groups the user belongs to. Access is only ever refused, never granted, in that window, and it needs no configuration to get past: authorization is evaluated again on every request of the same session, so once resolution has landed the administration screens open normally, without logging in again.

Warning

Do not shorten that window by putting the Fess administrator role in entraid.default.roles. That property is a single global value that Fess applies to every Entra ID user at login and re-applies on every later resolution, so it would give every user in the tenant permanent Fess administrator rights.

If You Use LDAP / Active Directory

From 15.9, the permission name of a group or role is the value of the entry’s RDN rather than a slice of the DN text. A group whose CN contains a character the DN escapes – a comma is the common one – therefore gets a different permission name than it did in 15.7.

Group entry DN Permission name up to 15.7 Permission name in 15.9
CN=Sales\, EMEA,CN=Users,... 2Sales 2Sales, EMEA
CN=Sales\, APAC,CN=Users,... 2Sales 2Sales, APAC

Up to 15.7 several groups agreeing up to the comma collapsed onto one permission name, so members of Sales, EMEA and Sales, APAC could read each other’s documents and those of the Sales group. In 15.9 each gets its own permission name and that cross-group access does not happen.

In exchange, documents indexed under the old permission name are no longer visible to members of that group. If a crawling configuration’s permission setting holds an old permission name, update it to the new one and crawl (or reindex) again. If you use no group whose CN contains a comma or another escaped character, no permission name changes.

Change to ldap.role.search.user.enabled

Up to 15.7 the permission derived from the user name (role.search.user.prefix followed by the user name) was granted even with ldap.role.search.user.enabled=false. From 15.9 the setting takes effect and the permission is not granted when it is off.

In a deployment that sets it to false, users lose the permission named after themselves after the upgrade, so documents permissioned to an individual user stop matching for that user. To keep the previous behaviour, restore the shipped default of true.

If You Changed the /api/v2 Configuration Keys

In 15.8.0, four configuration keys lost their api.v2. prefix. Their values, defaults and behaviour are unchanged, but no backward-compatible alias is kept: a setting left under its old name is ignored without any warning, and the shipped default takes effect instead.

Up to 15.7 15.8.0 and later Default
api.v2.chat.stream.keepalive.interval.ms api.chat.stream.keepalive.interval.ms 15000
api.v2.param.max.length api.param.max.length 1000
api.v2.param.max.array.size api.param.max.array.size 100
api.v2.click.max.rt api.click.max.timestamp 9999999999999

If you set any of these in fess_config.properties or as a -Dfess.config.<key> JVM argument, rename them. Only an explicit setting is affected; an installation that never changed them needs no action.

The first key controls how often a keep-alive frame is sent while POST /api/v2/chat/stream is waiting for the model, so AI search mode is where a lost setting becomes visible. The last key was renamed for accuracy as well: it bounds the rt value a click log may carry, which is a timestamp rather than a response time.

Updating Plugin Versions

Plugins installed under app/WEB-INF/plugin/ need to be replaced with versions matching your Fess version. bin/fess-setup upgrade plugins does this for every installed plugin: it installs the version built for this Fess and deletes the old one. Restart Fess afterwards. bin/fess-setup check then reports on OpenSearch, its plugins and the installed Fess plugins, and exits with code 1 when something is wrong.

$ bin/fess-setup upgrade plugins
$ bin/fess-setup check

upgrade plugins only handles plugins that are already installed. Install the plugins that replace parts removed from the distribution in 15.9, such as fess-script-groovy, with bin/fess-setup install plugin as described in the sections above.

If you specify FESS_PLUGINS in the Docker version, update the version part, for example to fess-ds-wikipedia:15.9.0.

Rollback Procedure

If the upgrade fails, you can rollback with the following procedure.

Step 1: Stop New Version

$ sudo systemctl stop fess.service
$ sudo systemctl stop opensearch.service

Step 2: Restore Old Version

Restore configuration files and data from backup.

For RPM/DEB version:

$ sudo rpm -Uvh --oldpackage fess-<old-version>.rpm

Or:

$ sudo dpkg -i fess-<old-version>.deb

Step 3: Restore Data

Restore from snapshot:

$ curl -X POST "http://localhost:9200/_snapshot/fess_backup/snapshot_1/_restore?wait_for_completion=true"

Or restore directory from backup:

$ sudo systemctl stop opensearch
$ sudo rm -rf /var/lib/opensearch/data/*
$ sudo tar xzf /backup/opensearch-data-backup.tar.gz -C /
$ sudo systemctl start opensearch

For the Docker version, revert to the old version’s Compose files, then restore the volume contents:

$ docker compose -f compose.yaml -f compose-opensearch3.yaml down
$ PROJECT=$(basename "$(pwd)")
$ docker run --rm -v ${PROJECT}_search01_data:/data -v $(pwd):/backup ubuntu \
    sh -c "rm -rf /data/* && tar xzf /backup/search01-data-backup.tar.gz -C /"
$ docker compose -f compose.yaml -f compose-opensearch3.yaml up -d

Note

Configuration data downloaded from the admin screen can be re-imported after starting Fess via the upload feature on the “System Info” → “Backup” page. You can upload *.bulk files, *.properties files starting with system, *.xml files starting with gsa, *.json files starting with fess, and *.json files starting with doc — one file per operation. *.ndjson files such as search logs are not accepted and result in an error.

Warning

Uploading fess.json or doc.json overwrites the index definition files bundled with Fess itself. If you upload the fess.json or doc.json from an older version after upgrading, you lose the new version’s index settings and mapping. Do not upload these files except for rollback purposes.

Note

The uploaded system.properties is loaded into memory only and is not written back to a file, so its contents are lost when Fess is restarted. To restore it reliably, place the backed-up file directly in its proper location before starting Fess (app/WEB-INF/conf/ for the ZIP version, /etc/fess/ for the RPM/DEB version).

Note

The import runs asynchronously; the screen only shows that it has started. Check fess.log to confirm whether it actually succeeded.

Step 4: Start and Verify Service

$ sudo systemctl start opensearch.service
$ sudo systemctl start fess.service

Verify operation and confirm it has returned to normal.

Frequently Asked Questions

Q: Can I upgrade without downtime?

A: Upgrading Fess requires service shutdown. To minimize downtime, consider the following:

  • Verify procedures in a test environment first

  • Create backups in advance

  • Secure sufficient maintenance time

Q: Do I need to upgrade OpenSearch too?

A: Each version of Fess requires a specific version of OpenSearch. Fess 15.9 requires OpenSearch 3.8.0. The Fess OpenSearch plugins such as opensearch-analysis-fess must exactly match the OpenSearch version, so if you upgrade OpenSearch, also update the plugins to the corresponding version (3.8.0).

Also, Fess 15.9 requires the k-NN plugin and always sends knn.derived_source.enabled in the index settings. With an older OpenSearch, creating a new index fails, so upgrading OpenSearch is effectively required. See Step 4 for details.

Q: Do I need to recreate the index?

A: For a Fess minor version upgrade (15.x → 15.9) where you do not use chunk-vector search, it is usually not necessary. The existing index can continue to be used as-is, and settings such as content_chunker.enabled remain disabled by default, so behavior does not change.

Recreation and re-indexing are required in the following cases:

Warning

Operations that create a new index (including re-indexing) fail on an OpenSearch without the k-NN plugin. Review the notes in Step 4.

Q: Search results are not displayed after upgrade

A: Verify the following:

  1. Verify OpenSearch is running

  2. Verify indexes exist (curl http://localhost:9200/_cat/indices)

  3. Re-run crawl

Next Steps

After the upgrade is complete: