# Self-hosted deployment

Download, verify, run, and upgrade a self-hosted CodeHerder server on your own PostgreSQL database, and confirm which build is live, on the Enterprise plan.

Source: https://codeherder.com/docs/self-hosting/

Download, verify, run, and upgrade a self-hosted CodeHerder server on your own PostgreSQL database, and confirm which build is live, on the Enterprise plan.

The Enterprise plan lets you run CodeHerder on your own infrastructure instead of the hosted service. This page covers downloading a server release and starting it. It doesn’t cover editions or licensing — see [Plans and limits](https://codeherder.com/docs/plans-and-limits/) for that.

## Check you have access

Self-hosted deployment is an Enterprise feature, gated by the `self_host` entitlement. Run `ch workspace plan` and look for `self_host` in the `Entitlements:` line:

```
$ ch workspace plan
...
Entitlements: budgets_spend_caps, capability_routing, cost_dashboards, rest_api_cli, scim, self_host, sso_saml, webhooks, workflow_rigor
```

See [Check your plan from the CLI](https://codeherder.com/docs/plans-and-limits/#check-your-plan-from-the-cli) for the rest of that command’s output. If `self_host` is missing, contact sales from the Plan page to upgrade.

## Read the manifest

Query the manifest to find the current release and the build for your platform:

```
curl -H "Authorization: Bearer $CH_TOKEN" https://api.codeherder.com/v1/self-host/manifest
```

Any signed-in member of an entitled workspace can call this with their own API key — you don’t need to request a separate download key. The response’s `data` object carries the release `version` and a `builds` array. Each build entry has `os`, `arch`, `filename`, `size`, and a `sha256` checksum. Pick the entry that matches your machine, and read the `filename` and `sha256` straight from that response. Don’t hard-code them — they change on every release.

Only the version in the manifest is available to download. An older bundle you already downloaded still runs, but you can’t fetch it again from this endpoint once a new release supersedes it.

## Download a build

Use the `filename` from the manifest in this request:

```
curl -L -H "Authorization: Bearer $CH_TOKEN" \
  https://api.codeherder.com/v1/self-host/artifacts/<VERSION>/<FILENAME> -O
```

`<VERSION>` is the manifest’s `version` value — a leading `v` is optional, so either form works. `-L` is required: this route redirects to a temporary download link rather than streaming the file itself, and the link expires in a few minutes.

### Verify the checksum

Before you extract the archive, confirm it matches the manifest’s `sha256` for that build:

```
sha256sum <FILENAME>
```

Compare the result against the `sha256` field from the manifest response. If they don’t match, don’t extract or run the archive — download it again.

## What’s in the bundle

Each platform’s archive holds the `codeherder` server binary, plus two operator tools for occasional use: one that loads an existing SQLite-backed deployment into PostgreSQL, and one that prints a read-only summary of a database’s schema. A fresh install needs neither — you only reach for them if you’re migrating an older deployment or inspecting a running one.

## Start the server

CodeHerder encrypts secrets at rest by default, so it needs four keys before it will start. Generate each one with `openssl rand -hex 32` and set them as environment variables:

```
export CH_WEBHOOK_SECRET_KEY=$(openssl rand -hex 32)
export CH_INTEGRATIONS_SECRET_KEY=$(openssl rand -hex 32)
export CH_OTP_HMAC_KEY=$(openssl rand -hex 32)
export CH_INTEGRATIONS_OAUTH_STATE_KEY=$(openssl rand -hex 32)
```

Save these somewhere durable. Losing `CH_INTEGRATIONS_SECRET_KEY` or `CH_WEBHOOK_SECRET_KEY` makes everything already encrypted under it unreadable, and every restart needs the same four values.

Then point `--db` at an empty PostgreSQL database and start the binary:

```
./codeherder --db "postgres://user:pass@host:5432/codeherder"
```

CodeHerder applies every pending schema migration to that database before it starts serving requests. PostgreSQL is the only backend a self-hosted server supports. On a fresh database this takes a few seconds. Expect a longer pause the first time you upgrade an existing, heavily used database across several migrations at once.

## Upgrade to a new release

Upgrading follows the same steps as your first install: read the manifest, download the new build, verify it, then restart on it.

1. Query the manifest again to confirm the current release. See [Read the manifest](https://codeherder.com/docs/self-hosting/#read-the-manifest) above; only that release is available to download.
2. Download and verify the new build. See [Download a build](https://codeherder.com/docs/self-hosting/#download-a-build) and [Verify the checksum](https://codeherder.com/docs/self-hosting/#verify-the-checksum) above.
3. Stop the running server, then start the new binary in its place with the same four keys and the same `--db` value. See [Start the server](https://codeherder.com/docs/self-hosting/#start-the-server) above. CodeHerder applies any pending schema migrations before it starts serving requests, so there’s nothing extra to run yourself.

Keep the archive for the version you’re currently running until you’ve confirmed the new one is healthy. The manifest only ever serves the current release. Once a version is superseded you can’t fetch it again from there, though a copy you already downloaded keeps working.

## Confirm which build is live

A version number alone doesn’t prove your restart came up on the new binary. The build reference is what actually confirms it.

Run `ch whoami` and check the `server` row:

```
$ ch whoami
...
server: 2.4.0 (a1b2c3d)
...
```

When the server binary is stamped with a build reference, `server` shows it in parentheses next to the version. Compare that reference before and after a restart: if it changed, the new binary is answering requests; if it didn’t, something is still serving the old one.

Prefer not to sign in? The same information is available with a plain request:

```
curl https://<your-server>/v1/version
```

This returns the version and, when the build is stamped, the same build reference. No token needed.

If your CLI warns you that the server is behind or ahead of it, see [Updating the CLI](https://codeherder.com/docs/updating/) for what each direction means.

## Connect your CLI and devices

Once the server is running, point your CLI and device servers at it with `CH_ADDR`. See [Credentials and profiles](https://codeherder.com/docs/credentials/) for setting that up, and [How do I add a device?](https://codeherder.com/docs/adding-a-device/) for registering a device against a self-hosted server.

If your server doesn’t offer browser sign-in, create the first account with `ch human bootstrap` — see [Bootstrapping the first account (self-hosted)](https://codeherder.com/docs/credentials/#bootstrapping-the-first-account-self-hosted).

## Errors

| Status | Meaning |
| --- | --- |
| `401` | No token, or an invalid or expired one. |
| `403 feature_not_entitled` | Your workspace’s plan doesn’t include `self_host`. |
| `403 forbidden_scope` | A download key was used on a route other than the manifest or artifact endpoints. |
| `404 not_found` | The filename doesn’t match a build in the current manifest, or the version has been superseded. |
| `503 feature_disabled` | This CodeHerder deployment doesn’t have self-host downloads configured. |

Each completed download is recorded on your workspace’s activity feed as “server bundle downloaded” — see [Activity feeds](https://codeherder.com/docs/activity/).

## Related guides

- [Plans and limits](https://codeherder.com/docs/plans-and-limits/) — what the Enterprise plan includes, and how to check your entitlements
- [Credentials and profiles](https://codeherder.com/docs/credentials/) — sign in, configure `CH_ADDR`, and bootstrap the first account
- [How do I add a device?](https://codeherder.com/docs/adding-a-device/) — register a device against your self-hosted server
- [Activity feeds](https://codeherder.com/docs/activity/) — where a completed download shows up
- [Updating the CLI](https://codeherder.com/docs/updating/) — what the behind and ahead version-skew notices mean
