OpenGrok becomes most useful around the third time you ask, “who calls this function across twelve repositories?” The web interface is the visible half; the real product is a reproducible indexing pipeline whose source snapshot, ctags parser, SCM tools, generated configuration, and Lucene data stay in sync. A container packages those moving pieces without pretending operations disappear.
Architecture in one minute
The source root contains checked-out repositories, usually one project per top-level directory.
The indexer reads source, symbols, and optional SCM history and writes index/configuration data.
The data root persists indexes, configuration, suggestions, and related generated state.
The web application reads source and indexes to serve search, definitions, references, history, and navigation.
A sync/reindex schedule makes the searchable snapshot converge with repository state.
Size and security before installation
Initial indexing is CPU, memory, and I/O intensive; the official guide commonly expects an indexer heap around 8 GiB for substantial code bases.
Repository history increases time and storage. Begin without unnecessary history when validating capacity.
Source search can expose secrets, unpublished vulnerabilities, credentials committed by mistake, and proprietary code.
Bind to loopback for a personal instance; use authenticated TLS reverse proxy and network policy before sharing.
Treat Docker daemon access as privileged host access and do not add users to its group casually.
Prepare source and persistent data directories
mkdir -p "$HOME/opengrok/src" "$HOME/opengrok/data"
cd "$HOME/opengrok/src"
git clone --depth=1 https://github.com/githubtraining/hellogitworld.git
find "$HOME/opengrok/src" -maxdepth 2 -type d -name .git -print/home/user/opengrok/src/hellogitworld/.gitThe top-level directory becomes a project
Use repositories you are authorized to index; the public training repository is only a small test fixture.
A shallow clone reduces history work but also limits history browsing.
Keep source and generated data separate so indexes never pollute repositories.
Local source is preferable because indexing is I/O intensive.
Scan repositories for secrets and define access controls before exposing results.
Pin the official image in Compose
services:
opengrok:
image: opengrok/docker:1.14.15
container_name: opengrok
restart: unless-stopped
ports:
- "127.0.0.1:8080:8080"
environment:
NOMIRROR: "true"
SYNC_PERIOD_MINUTES: "60"
volumes:
- ./src:/opengrok/src:ro
- ./data:/opengrok/data:rw
security_opt:
- no-new-privileges:trueEvery mount and port expresses policy
A numbered tag makes upgrades deliberate; verify the current official tag and digest before deployment.
Loopback binding prevents direct remote access, unlike publishing on every interface.
NOMIRRORtells the image that you manage repository checkouts outside the container.The source mount is read-only; generated data remains writable and persistent.
The sync period controls scheduled indexing behavior in this container workflow; validate cadence against repository size.
no-new-privilegesnarrows one escalation path but is not a complete container sandbox.
Start and watch the initial index
docker compose pull
docker compose up -d
docker compose logs -f --tail=200 opengrok... indexing ...
... configuration ...
... Server startup ...Risk level: caution. Review the command before running it.
Do not mistake container health for index readiness
pulldownloads the pinned image; verify provenance/digest in controlled environments.up -dcreates persistent container state and publishes only the declared local port.Logs reveal ctags failures, permissions, out-of-memory events, malformed files, and indexing completion.
Large first indexes can take hours; subsequent indexes are generally incremental.
Press Ctrl-C to stop following logs; it does not stop the detached service.
Verify UI, project, and search
curl -fsS -o /dev/null -w 'HTTP %{http_code}\n' http://127.0.0.1:8080/source/
docker compose -f "$HOME/opengrok/compose.yaml" psHTTP 200
NAME IMAGE STATUS PORTS
opengrok opengrok/docker:1.14.15 Up ... 127.0.0.1:8080->8080/tcpA useful acceptance test goes beyond HTTP 200
Open
http://127.0.0.1:8080/source/and confirm the expected project appears.Search a known symbol and follow its definition and references.
Browse a file and, if enabled, verify history against the checked-out repository.
Confirm a remote machine cannot reach port 8080 directly.
Record index completion time, data size, and peak memory for capacity planning.
Update source without giving OpenGrok write access
git -C "$HOME/opengrok/src/hellogitworld" pull --ff-only
docker compose -f "$HOME/opengrok/compose.yaml" restart opengrok
docker compose -f "$HOME/opengrok/compose.yaml" logs -f --tail=100 opengrokAlready up to date.
... indexing ...Separate synchronization from indexing
--ff-onlyrefuses an unexpected merge in the indexed checkout.Production synchronization should use a locked script/service and per-repository error reporting.
The official image schedules indexing; restart is a simple lab trigger, not the most efficient large-scale reindex API.
Do not update a huge tree throughout indexing without understanding snapshot consistency.
Monitor stale-index age and failures rather than assuming cron-like work succeeded.
Expose it through an authenticated reverse proxy
Keep the container port on loopback.
Terminate HTTPS at a maintained reverse proxy.
Require SSO, mTLS, VPN, or another organization-approved authentication layer.
Preserve the
/sourcecontext path and proxy headers correctly.Limit request sizes/rates and log access without leaking search terms unnecessarily.
Do not place private source search on the public internet merely because no repository write action exists.
Back up what can and cannot be rebuilt
Repositories should have authoritative upstream remotes or separate backups.
Indexes are rebuildable but expensive; snapshotting the data root can shorten recovery if version-compatible.
Preserve Compose configuration, image digest, proxy/auth configuration, sync scripts, exclusions, and operational secrets.
Test restoration and reindexing; a copied live index is not automatically a consistent backup.
Never store credentials inside the indexed source tree.
Upgrade without gambling the index
Read OpenGrok release notes and container documentation.
Snapshot configuration and persistent data, and ensure source can be reconstructed.
Pull the intended numbered tag or immutable digest in staging.
Rebuild/reindex representative repositories and exercise searches.
Update production Compose deliberately, observe logs, and retain rollback artifacts.
Do not use
latestas an unattended production upgrade policy.
Troubleshooting map
No projects: source mount is empty/wrong, permissions prevent reads, or initial indexing failed.
Symbols missing: Universal Ctags failed, language analysis is unsupported, files are excluded, or index is stale.
Permission denied under data: host directory ownership/labels do not allow container writes.
Killed with exit 137: memory limit or host OOM ended Java; size heap/container resources based on evidence.
History absent: checkout is shallow, SCM executable/history option is missing, or repository metadata is unreadable.
UI shows old files: source sync and index completion are separate; inspect timestamps/logs.
Works locally but not through proxy: context path, forwarded headers, websocket/API route, authentication, or timeout policy is wrong.
Manual deployment requirements
Primary OpenGrok references
Official OpenGrok setup defines current requirements, source/data roots, indexing, tokens, and lifecycle.
OpenGrok repository links releases, security policy, container path, and project documentation.
Official OpenGrok container tags provides maintained versioned images and digests.
Large-code-base tuning covers heap and indexing parallelism.
Comments and corrections