Updating the CLI and the server
Update ch manually, plan a self-hosted server upgrade and its maintenance window, see how ch and the device server stay current, and fix a stuck update.
There are two ch binaries to keep in mind: the one installed on your own machine (used for the CLI), and the one inside each running device server. Both can keep themselves current, and this page covers how. Not installed yet? See Quickstart for the one-line install command.
Check your current version
ch --version
ch version and ch -v do the same thing. This prints the installed version (and build commit for stamped releases).
Update manually
Run ch self-update to download and install the latest release in one step:
ch self-update
Note:
ch updateis not a valid command. If you type it by mistake, the CLI will suggestch self-updatefor you.
CodeHerder downloads the new binary for your platform, verifies it by SHA-256, and atomically replaces your existing ch binary. If you are already on the latest version, the command says so and exits.
Updating your machine’s ch does not update running device servers. Each device server manages its own binary independently. After a successful update, ch prints a reminder: “Any running ch device-server will auto-restart on its next update check.”
Check for an update without applying it
ch self-update --check
Reports the installed version versus the latest available, then exits with code 0 if you are up to date or non-zero if a newer version is available. No changes are made to your binary. Unlike a plain ch self-update, --check reports this even from an unstamped dev build — it never needs --force.
This exit-code contract makes it straightforward to use in CI or a cron job:
# Example: daily cron that alerts when ch is behind
# 0 9 * * * /usr/local/bin/ch self-update --check || echo "ch update available — run ch self-update"
ch self-update --check
if [ $? -ne 0 ]; then
echo "ch is behind the latest release."
fi
Add --json to get the same result as a machine-readable current/available/needsUpdate/updated object instead of exit-code-only — see Using the ch CLI for the JSON conventions every command shares.
Update a build made from source
Building ch from source gives you one of two binaries, and they behave differently:
-
An unstamped dev build carries no version at all.
ch self-updaterefuses to replace it by default — pass--forceto override:ch self-update --force -
A local build carries a version like
2.3.1+7. It doesn’t need--force: the moment a newer release ships,ch self-updatereplaces it andch self-update --checkreports it as behind. What neither will do is call it behind a release still sitting at the same version number — a local build is always treated as caught up with its own version, never behind it.
Your ch keeps itself current too
Auto-update is on by default for the ch you run on your own machine, the same way it is for the device server, when a release source is configured (CH_CLI_MANIFEST_URL, or the manifest URL stamped into your ch build; with neither it stays off). It works in two parts: a quick stderr notice when your version and the server’s fall out of step, in either direction, and a preflight that updates before certain commands run.
The notice. Any time the CLI talks to the server, it compares versions and prints one line to stderr when they differ. Which direction you’re off in changes the wording:
ch (build 2.3.0) is behind the server (build 2.3.1). Run: ch self-update
ch (build 2.3.1) is ahead of the server (build 2.3.0). The server may be running a stale deploy.
The numbers are build versions, which decide updates. On a hosted install the build is the hosted release, not the CodeHerder version. When your ch knows its CodeHerder version and it differs from the build, the line names it too, for example ch (build 11.1.274, CodeHerder 12.19.11). ch self-update messages add the CodeHerder version of the new build, and say unchanged when only the hosted wrapper changed.
Behind is the common case: your ch hasn’t picked up a release yet, and ch self-update fixes it. Ahead usually means a self-hosted server needs restarting on the release it’s meant to be running — see Confirm which build is live for how to check. The ahead line specifically never appears against a server that hasn’t been assigned a real release version, so it can’t mistake an unreleased build for a stale deploy.
Either line goes to stderr, not stdout, so it never breaks a script that pipes --json output, and by default each repeats at most once an hour.
The preflight. ch start and ch session attach check for a newer release before they do anything else. If one is available, ch downloads it, replaces itself, and restarts the same command on the new version:
ch: updated build 2.3.0 → 2.3.1, restarting `ch start`.
No other command runs this preflight — ch self-update is still there whenever you want to check or update by hand. If the preflight’s check fails — no network, no build for your platform, a failed download — ch just keeps running the version you already have. You’ll never see an error from it.
The preflight needs a real terminal on standard input, so it stays out of the way of scripts and CI by default. To opt a non-interactive caller in, set CH_AUTO_UPDATE_NONINTERACTIVE=1. A build made from source, dev or local, never runs the preflight and never prints the notice — see Update a build made from source above.
Tuning or disabling your CLI’s auto-update
| Setting | Default | Description |
|---|---|---|
--no-auto-update |
on | Disable the preflight for this one command. |
CH_AUTO_UPDATE=0 |
on | Disable the preflight for every command. |
CH_AUTO_UPDATE_NONINTERACTIVE=1 |
off | Let the preflight run without a terminal on standard input — for scripts and CI. |
CH_AUTO_UPDATE_INTERVAL=<duration> |
1h |
How often the preflight checks for a newer release, and how often the notice can repeat. |
CH_UPDATE_NOTICE=0 |
on | Silence the stderr notice, in either direction. |
The device server keeps itself current automatically
When you run ch device-server, auto-update is on by default once a release source is configured (CH_CLI_MANIFEST_URL, or the build stamp; with neither it stays off). Roughly once an hour, the server checks whether a newer ch release is available. When it finds one, it downloads and verifies the binary and then restarts itself with the new version.
By default, in-flight agent sessions are not interrupted. The device server detaches any running agent processes before it restarts, and the new process adopts them automatically. Tasks continue without any action from you. Start the device server with --kill-agents-on-shutdown (or set CH_DS_KILL_ON_STOP=1) if you’d rather it stop running agents on any shutdown or restart — including one triggered by auto-update — instead of carrying them across.
Scope: auto-update keeps only the device server’s own ch binary current — it does not change the agent harness (for example, the Claude Code installation the agent uses to do its work).
If a device is stuck on an old version
A few situations commonly strand a device behind the latest release. None of them need you to watch closely — each is either self-healing or a one-line fix:
- A
chbuilt from source never auto-updates. This covers both shapes: an unstamped dev build, and a local build carrying a version like2.3.1+7. Either way, the device server’s auto-updater skips its check entirely, and the Version current readiness check (see Managing your devices) reports healthy rather than warning — there’s no separate signal telling you it’s behind. Install a releasedchbinary on that machine, or runch self-update(add--forceonly on a dev build), then restart the device server. - Restarting the device server does not, by itself, pull a newer version in immediately. The auto-updater’s first check happens a full interval after startup, not right away — so on the default hourly cadence, a fresh restart can be up to an hour away from checking. To move a device onto a new version right now: run
ch self-update(add--forceon a dev build) to replace the binary on disk, then restartch device-server— it starts up already on the new version. - The update needs write access to the directory the
chbinary lives in, since both manual and automatic updates replace it in place. Ifchis installed somewhere only another account can write to, a manualch self-updatereports the failure directly; the device server’s background updater instead logs a warning and quietly retries at the next check. Installchsomewhere your own user owns, or run the update as the account that owns the install directory. - A failed check is not something to worry about. A transient failure — a network blip, or a brief mismatch right after a new release goes out — is retried automatically, several times, within the same check window. If every retry in that window still fails, the device server keeps running its current binary and tries again at the next interval; no session is lost and nothing needs your attention.
- A laptop that’s asleep through a check doesn’t miss its turn. The device server tracks wall-clock time rather than a fixed timer, so it picks the check back up within about a minute of the machine waking.
- On a proxied network, both
ch self-updateand the device server’s auto-updater fetch releases over HTTPS and honor the standardHTTPS_PROXYandNO_PROXYenvironment variables in whatever environment the command runs in.HTTP_PROXYhas no effect here, since the download itself is always HTTPS.
Tuning or disabling the device server’s auto-update
Pass these flags when starting the device server:
| Flag | Default | Description |
|---|---|---|
--auto-update=false |
on | Disable automatic updates entirely. |
--auto-update-interval <duration> |
1h |
How often to check for a newer version. |
The same settings are available as environment variables — convenient when the device server runs as a background service:
| Variable | Description |
|---|---|
CH_DS_AUTO_UPDATE=0 |
Disable automatic updates. |
CH_DS_AUTO_UPDATE_INTERVAL=<duration> |
Override the check cadence (e.g. 6h). |
Examples:
ch device-server --auto-update-interval 6h # check every six hours
ch device-server --auto-update=false # never auto-update
For the full ch device-server reference, see Managing your devices.
Upgrade the server and plan maintenance
This section is for the person who runs a self-hosted server. The steps to upgrade are in Upgrade to a new release. This section covers when to do it and what to tell your users.
A new release applies its schema migrations when the server starts. The server answers 503 migrating until they finish. Most migrations take well under a second. A migration can lock a table or run for minutes on a large database. Each release tells you which.
Read the notice before you upgrade.
-
Read the
migrationsobject in the release manifest. It names each migration the release adds since the previous release. It says whether any may lock a table (mayLock) or run long (mayRunLong). See Read the manifest. -
Run the preflight with the new binary against your own database. It writes nothing:
codeherder migration-notice -db "$DSN"It lists each migration your database has not applied. It flags each one that may lock or run long. For each flagged table it prints the size, so you can judge the duration. Add
-strictto make the command exit 1 when anything is flagged. -
If you skip releases, the manifest of the newest release is not the full list. The preflight is the full answer. It reads your own ledger.
Choose the maintenance window.
- If nothing is flagged, upgrade at a time you accept a short restart.
- If anything is flagged, schedule a maintenance window and tell your users before it starts. Take a database backup first. Plan for the migration time on your own data, not on a small test database.
- While the server reports
migratingat/v1/health, wait. Do not stop the server with SIGTERM or restart it. A restart abandons the migration and starts it again from the beginning. - A failed upgrade rolls back by restoring the backup. That loses the data written after the backup. See Roll back a failed upgrade.
You choose the window and you tell your users. CodeHerder sets no deadline for an upgrade, except the upgrade-by date on a security release.
Rule text for approval
This text is a template. Adapt it to your policy.
Migration notice rule.
- Every server release manifest states its migrations and whether any may lock a table or run long. The release pipeline generates the statement. No person writes it.
- When a release adds a migration that may lock or run long, the support contact tells the customer communication owner, through ______, within ______ of the release. The email names the migration, the table, the expected effect and any measured duration.
- A migration that would hold a lock for minutes on a large table ships with a separate operator step. It never runs as a startup statement.
- The customer chooses the maintenance window and tells its own users. CodeHerder sets no deadline, except the upgrade-by date in the security release notice rule.
Related guides
- Managing your devices — device health and troubleshooting an Offline device
- Running the device server as a service — tune auto-update under a service manager
- Monitoring your agents — watch fleet health after an update
- Self-hosted deployment — upgrade a self-hosted server, and confirm which build is live
Last updated