Upgrades
An upgrade is: back up one file, replace the binary or image, restart. There is no migration command, no online component and no upgrade wizard. This page says what happens to each kind of state when you do, what you will see while a new release settles in, and how to go back.
What an upgrade touches
Everything the product keeps lives under instance.dataDir, and only one thing
there is durable in the sense that it cannot be rebuilt.
| What | Where | On upgrade |
|---|---|---|
| Users, local accounts, API keys, OAuth refresh tokens, connection records | subjects.db |
Kept. The schema is brought up to date at startup, additively. |
| Clones | repos/ |
Kept. Nothing is re-cloned; the scheduler carries on fetching. |
| Deep-analysis results | analysis/ |
Kept, or recomputed if the release changed what analysis produces. |
| Search indexes | memory | Rebuilt after every restart, upgrade or not. |
| Your configuration | searchcode.yaml, conf.d/ |
Untouched. Validated at startup. |
| Audit records written to files | audit/ |
Untouched. |
subjects.db is the one to protect. Everything else is a clone of your code hosts
or derived from one, and comes back on its own.
Before you start
-
Back up
subjects.dbwith SQLite's online backup, or stop the service and copy it. Do notcpit while the process is running. See Backup and restore. -
Validate your configuration against the new binary before you restart onto it:
searchcode --config /etc/searchcode/searchcode.yaml config validateIt prints every problem rather than the first, exits non-zero on any, and makes no network call, so it is safe to run anywhere the new binary is.
-
Keep the old release's
codes.json. Every log line carries a code that identifies where it was emitted, and a code from an old log resolves against the build that wrote it. See Log codes.
Configuration compatibility
The configuration file is strict: an unknown key is a startup failure, naming
the key and the line. A file that silently dropped a block it did not recognise —
an authz block, say — would be far worse than one that refuses to start.
That strictness cuts both ways across releases. Running config validate with the
new binary tells you before the restart whether anything in your file needs to
change. Going back to an older release with a file that uses a key the older
one does not know makes it refuse to start, naming the key: remove the key, or keep
the newer release.
The database schema
subjects.db is SQLite. Its schema is applied at every startup, one statement at a
time, and every change is additive: new tables are created if they do not exist,
and new columns are added to existing tables. There is no separate migration step
and nothing to run by hand.
Re-analysis after an upgrade
Each release stamps the deep-analysis results it writes with an analysis version. When a release changes what analysis produces — a security rule's meaning or severity, what a technology detector matches, a new kind of symbol a parser extracts — the version moves. Stored results with the old stamp are then discarded, and each repository's deep pass runs again in the background.
Not every release moves it. When it has not moved, an upgrade re-analyses nothing.
While a re-analysis runs:
- Search keeps working throughout, across every repository. Search reads the clones, and the fast inventory pass that makes a repository searchable and filterable runs ahead of the deep pass.
- Analysis-backed answers come back repository by repository as each deep pass finishes: findings, technology and dependency information, and parsed symbol outlines. Until a repository's deep pass is done, an answer that depends on it says the data is incomplete rather than reporting nothing found, and a file's outline falls back to a line-based heuristic.
- Progress is visible.
analysis.coverageon/api/v1/statusand the admin page show analysed against total,searchcode_analysis_repositories{state="pending"}counts what is left, andsearchcode_analysis_jobswithstate="queued"falls to zero when it is finished.
How long it takes depends on the estate and on analysis.deepConcurrency; see
Sizing. It is the same work as the first analysis after install.
A change to the bundled security rule set alone is handled more gently: the stored results stay in service, and a fresh deep pass is queued behind them.
Search indexes after a restart
The search indexes are held in memory and are never written to disk, so they are
rebuilt after every restart. A repository whose index is not built yet is searched
by reading its files, which gives the same results more slowly. Nothing is reported
as missing. searchcode_search_index_warming is 1 while builds are running.
Upgrading, by install method
Tarball with systemd
searchcode --config /etc/searchcode/searchcode.yaml config validate # with the NEW binary
sudo systemctl stop searchcode
sudo install -m0755 bin/searchcode /usr/local/bin/searchcode
sudo systemctl start searchcode
State stays in /var/lib/searchcode. Nothing else in the tarball needs to be
re-run. The shipped unit gives the process 45 seconds to drain on stop. See
Install on a VM with systemd.
Helm
helm upgrade searchcode ./deploy/helm/searchcode \
--namespace searchcode --values my-values.yaml
Leave image.tag empty in your values: it resolves to the chart's app version, so
upgrading the chart is upgrading the product, and an install never drifts onto a
different build when a node is replaced. Never set it to latest.
The chart's Deployment uses the Recreate strategy. The data volume is
ReadWriteOnce, so a rolling update would deadlock: the new pod cannot attach the
volume until the old one lets go. The cost is a few seconds without a pod during
the upgrade. Take a volume snapshot of the PVC first; it is the simplest correct
backup. See Install with Helm.
Container image
Pull or load the new image, stop the old container, and start the new one with the
same dataDir volume and the same configuration mounts. For an air-gapped
registry, mirror the new image first. See
Air-gapped install.
After the restart
- The three listeners announce themselves on stderr, and
/readyzreturns 200 as soon as the instance can serve. It does not wait for the estate. searchcode versionprints the build and the set of languages it parses; check it is the build you meant to deploy.- Sign in, run a search, and call one MCP tool with an existing key. OAuth
refresh tokens and API keys are in
subjects.db, so agents stay signed in. - Watch
analysis.coverageif the release moved the analysis version.
searchcode doctor runs without a running instance and is the first thing to try
if something looks wrong. See searchcode doctor.
Rolling back
Going back to the previous release is the same procedure in reverse, with two things to know:
- The database is fine. An older binary ignores tables and columns it does not
know. If you want to be certain, restore the
subjects.dbbackup you took before upgrading; you lose only what changed since (new keys, new sign-ins). - Expect a re-analysis if the two releases have different analysis versions. The older binary does not recognise the newer stamp, discards those results and analyses again. Search works throughout, as above.
And the configuration point from above: a key added for the newer release must come out before the older one will start.
Licences and versions
A licence can name the last version it covers. Upgrading past it does not break
anything: indexing pauses, and search, the API, MCP, the audit log and authorisation
keep working. Check before upgrading with searchcode license show; see
Licensing.