Fess is an Enterprise Search Server. This Docker environment provides a Source Code Search Server using Fess.
- Theme: Fess 15.7 static theme system —
theme.default=codesearchinsystem.propertiesselects the codesearch theme. No virtual-host routing is needed for theme activation. - Fess config (
fess_config.properties):setup.shgeneratesdata/fess/opt/fess/fess_config.propertiesfrom the upstream base for the pinned Fess version plus the codesearch overlay (conf/fess_config.overlay.properties) and an optional local override (conf/fess_config.local.properties). It is mounted at/opt/fess, which the image places ahead of its/etc/fessdefault on the classpath, so the generated file takes effect. Only the delta is tracked in git; the base auto-tracks the pinned version. See Configuration. - Version pins (
.env):FESS_VERSION/OPENSEARCH_VERSIONare the single source of truth for the image tags (compose.yaml) and thefess_config.propertiesbase. - system.properties: The live file (
data/fess/opt/fess/system.properties) is generated fromdata/fess/opt/fess/system.properties.templatebysetup.shon first run. The live file is git-ignored. - Theme files: The codesearch static theme is fetched from the fess-themes repository by
setup.sh(re-fetched on every run; setFESS_THEMES_SKIP_FETCH=1to keep the local copy) and stored indata/fess/themes/codesearch/. This directory is mounted into the container at/usr/share/fess/app/themes/codesearch. The theme requires the facet fields configured in the overlay — see Troubleshooting. - index.filetype: Source-code aware mimetype→label map, maintained in
conf/fess_config.overlay.properties(a multi-line value, so it lives in the file rather than a-Dflag). - Management CLI (
fessctl): Repositories are registered and crawls are triggered withfessctl, the official Fess admin-API CLI (see Install fessctl). - Verification (
bin/verify.sh): Checks at runtime that the generated config is in effect and that the live index has the codesearch fields mapped. Run it aftersetup.shand after any version bump (see Verify the Setup).
First, clone the repository and navigate into the directory:
$ git clone https://github.com/codelibs/docker-codesearch.git
$ cd docker-codesearch
$ bash ./bin/setup.shsetup.sh will:
- Create required data directories
- Download the Fess data store plugin (fess-ds-git)
- Fetch the codesearch static theme from fess-themes
- Generate
data/fess/opt/fess/system.propertiesfrom the template (if not already present) - Generate
data/fess/opt/fess/fess_config.propertiesfrom the pinned base + codesearch overlay
setup.sh aborts on the first failure and exits non-zero. Do not start the stack after a failed run: a missing fess_config.properties boots a Fess that looks healthy but returns 0 hits for every search (see Troubleshooting).
To start the server, use Docker Compose:
docker compose -f compose.yaml up -dOnce the server is running, access it at http://localhost:8080/.
The first start initializes the search indices in OpenSearch (this can take a minute or two). The site has no documents until you register a repository and run a crawler (see below).
The first start is also the only chance to get the index mapping right, so check it before crawling:
bash ./bin/verify.shIt confirms that the generated fess_config.properties is in effect (including the facet allowlist the theme depends on) and that the live index really has the codesearch fields mapped. It prints the remedy for whatever it finds and exits non-zero on failure.
Once documents are indexed, it also probes the search API by comparing the theme's faceted request against the same query without facets — a rejected facet field makes only the faceted count collapse to 0. Set VERIFY_QUERY if test matches nothing in your repositories.
fessctl (used in the next steps) authenticates to Fess with an access token. Create one with the {role}admin-api permission on the Admin Access Token page (http://localhost:8080/admin/accesstoken/).
For more details, see the Admin Access Token Guide.
Repositories are registered and crawls are triggered with fessctl, the official CLI for the Fess Admin API:
pipx install fessctl # or: uv tool install fessctlfessctl requires Python 3.13+ (pipx / uv provide it automatically). Point it at the server and the access token created above:
export FESS_ENDPOINT=http://localhost:8080
export FESS_ACCESS_TOKEN=<your-access-token>
export FESS_VERSION=15.7.0
fessctl ping # reports the search engine status (GREEN when ready)
fessctlcan also be run from its container image (ghcr.io/codelibs/fessctl); see the fessctl README for details.
Create a Git data store config for each repository you want to index. The handler-script maps Git metadata to the codesearch fields (organization, repository, filetype, …) that power the search facets. Replace codelibs / fess-suggest / master with your own organization, repository, and default branch:
fessctl dataconfig create \
--name "github.com/codelibs/fess-suggest" \
--handler-name GitDataStore \
--handler-parameter 'uri=https://github.com/codelibs/fess-suggest.git
base_url=https://github.com/codelibs/fess-suggest/blob/master/
extractors=text/.*:textExtractor,application/xml:textExtractor,application/javascript:textExtractor,application/json:textExtractor,application/x-sh:textExtractor,application/x-bat:textExtractor,audio/.*:filenameExtractor,chemical/.*:filenameExtractor,image/.*:filenameExtractor,model/.*:filenameExtractor,video/.*:filenameExtractor,
delete_old_docs=false
repository_path=/home/fess/workspace/fess-suggest' \
--handler-script 'url=url
host="github.com"
site="github.com/codelibs/fess-suggest/" + path
title=name
content=container.getComponent("documentHelper").appendLineNumber("L", content)
digest=author.toExternalString()
content_length=contentLength
last_modified=timestamp
timestamp=timestamp
filename=name
mimetype=mimetype
domain="github.com"
organization="codelibs"
repository="fess-suggest"
path=path
repository_url="https://github.com/codelibs/fess-suggest"
filetype=container.getComponent("fileTypeHelper").get(mimetype)' \
--permission "{role}guest"Review the registered repositories on the DataConfig page.
Trigger the built-in Default Crawler, which crawls every registered data store config:
fessctl scheduler start default_crawlerIt also runs daily on its own schedule, so newly registered repositories are picked up automatically. Follow progress on the Scheduler page (Job Log), or watch results appear on the search page.
You can view search results at http://localhost:8080/.
To stop the server, use the following command:
docker compose -f compose.yaml downCodesearch-specific fess_config.properties settings are maintained as a small delta in conf/fess_config.overlay.properties. setup.sh (via bin/render-fess-config.sh) fetches the upstream base for the pinned FESS_VERSION and overlays this delta to generate data/fess/opt/fess/fess_config.properties. After editing the overlay, re-run setup.sh (or bash ./bin/render-fess-config.sh).
Secrets / per-deployment values (e.g. the cipher key, the initial admin password) must not go in the tracked overlay. Create conf/fess_config.local.properties (git-ignored) — its keys are applied last and win:
app.cipher.key=your-secret-key-here
index.user.initial_password=your-admin-passwordThe cipher key encrypts stored credentials; set it before first boot, because changing it later invalidates already-encrypted data.
To modify system-level Fess settings, edit data/fess/opt/fess/system.properties.template and re-run setup.sh, or edit the live data/fess/opt/fess/system.properties directly. The live file is git-ignored.
To enable AI-powered chat on search results, add the following to conf/fess_config.local.properties (or the overlay) and install an LLM plugin, then re-run setup.sh:
rag.chat.enabled=trueAI chat is disabled by default. See Fess LLM plugins for available LLM integrations.
To update to the latest code, use plain git pull:
git pullLive/generated files (system.properties, fess_config.properties, theme assets) are git-ignored and will not be overwritten by git pull.
To upgrade the Fess / OpenSearch version, edit the pins in .env (FESS_VERSION, OPENSEARCH_VERSION) and re-run setup.sh. The fess_config.properties base is re-fetched for the new version and the codesearch overlay is re-applied automatically:
bash ./bin/setup.sh
bash ./bin/verify.sh
docker compose -f compose.yaml up -dFESS_VERSION is a ghcr.io/codelibs/fess image tag, and render-fess-config.sh maps it to the codelibs/fess git ref that holds the matching base config:
FESS_VERSION |
base ref |
|---|---|
15.7.0, 15.6.1, … |
fess-<version> (the release tag) |
15.7.0-noble, 15.7.0-al2023 |
fess-<version> (the OS suffix is dropped) |
snapshot, snapshot-noble, snapshot-al2023, 15.8.0-SNAPSHOT |
master |
anything else (latest, 15.7, …) |
rejected — pin an explicit release, or set FESS_CONFIG_BASE_REF |
Floating tags are rejected on purpose: latest has no matching source ref, so there is no way to render a base config that is guaranteed to match the running image. For a ref this mapping does not cover — an unreleased version, or a maintenance branch — name it explicitly:
FESS_CONFIG_BASE_REF=15.8.x bash ./bin/render-fess-config.shPlugin versions are not derived from FESS_VERSION — snapshot images have no matching plugin release. Override them per plugin if the pinned one is too old, e.g. FESS_DS_GIT_VERSION=15.8.0 bash ./bin/setup.sh.
data/fess/usr/share/fess/app/WEB-INF/classes/fess_indices/_codesearch/ is a hand-maintained fork of the upstream index schema, selected by search_engine.type=codesearch. It carries genuine codesearch tuning that has no upstream equivalent — the line_number_filter char filter that strips the L<n>: prefix added by the handler script, code-aware operator_filter / dotnum_filter / code_stop_filter tokenization, and the seven codesearch document fields (domain, organization, repository, path, repository_url, owner, homepage).
Unlike fess_config.properties, it is not regenerated per version, so it can drift from upstream. bin/verify.sh reports the dangerous direction (a core field the running Fess expects that the fork lacks) as an advisory WARN; it currently reports none for 15.7.0. Refresh it by hand when upstream adds document fields.
Re-index after a major version bump: a Fess or OpenSearch major upgrade can change the index format. If search returns errors or stops returning results after upgrading, re-crawl your repositories with
fessctl scheduler start default_crawlerto rebuild the index.
Every query comes back empty — from the top page, the result page and the help page alike — while the admin UI works and fess.log only shows:
WARN Main searcher failed to execute search for query: ...
org.codelibs.fess.exception.SearchQueryException: Invalid facet field: repository
Run bash ./bin/verify.sh; it distinguishes the two causes below. Both mean the generated data/fess/opt/fess/fess_config.properties was not in effect (a failed or skipped setup.sh), because that file is what carries the codesearch settings — Fess otherwise falls back to the stock config baked into the image.
-
query.additional.facet.fieldsdoes not list the codesearch fields. The theme facets onrepository,organizationandfilenameon every search, and Fess validates facet fields against that allowlist only — never against the index mapping. One unlisted field aborts the entire search, and older Fess versions report it as an empty result set instead of an error. Fix by re-runningsetup.shand restarting the container. -
The index was created without the codesearch fields.
search_engine.type=codesearchselects the_codesearchindex schema, and it is read only when the index is created. If the very first boot ran on stock defaults, correcting the config later does not repair the index — Fess applies a mapping only to an index that has none yet. Rebuild it:- open http://localhost:8080/admin/maintenance/ and run Reindex
- re-crawl with
fessctl scheduler start default_crawler
On a deployment with no documents worth keeping, deleting the
fess.*indices and restartingfess01is equivalent and faster.