Skip to main content
This guide is the supported command-line installation path for an on-premises CaseBender deployment. It does not require public internet access after the container images have been made available in your environment.
Prefer a graphical workflow? The Desktop Installer uses the same first-run activation model.

Prerequisites

  • Linux, macOS, or Windows with WSL 2
  • Docker Engine 20.10+ and Docker Compose v2+
  • On Apple Silicon or other arm64 hosts, Docker must emulate linux/amd64 (Docker Desktop Rosetta or QEMU). CaseBender application images and the bundled ClamAV scanner are amd64-only. First boot can take several minutes while the web container runs database migrations and seed data.
  • Node.js 20+ on the host (./casebender setup, preflight, up, upgrade, and the canary check invoke Node; init alone does not)
  • OpenSSL
  • Cosign (bundle signature verification only; not required to run init or up)
  • 8 GB RAM minimum, 16 GB recommended (web, API, workers, PostgreSQL, Redis, and the bundled ClamAV scanner)
  • Ports 80 and 443 free on the host
  • For production: a DNS name and trusted TLS certificate for the CaseBender host
  • Internet access to download the public Community bundle and container images, or an approved process for mirroring the release’s exact image digests
The bundle does not include container images. ./casebender up pulls the pinned tags from the registry in release.env. If that registry requires authentication, run docker login for it before up. Do not copy credentials from this guide into an installation. CaseBender generates installation-specific secrets.

1. Download and verify the Community bundle

The CaseBender source repository is private and is not part of the customer installation process. The public, source-free bundle contains the production Compose definition, the casebender management command, preflight and canary checks, Nginx configuration, and pinned release metadata.

Download the latest Community on-premises bundle

Source-free Docker Compose installation bundle
Download the archive, checksum, and Sigstore verification bundle:
On macOS, use shasum -a 256 -c instead of each sha256sum -c. The signed archive contains the same release-manifest.json; compare it with the separately downloaded manifest and retain it with your deployment records. It records the exact source commit and signed digest for every container image in the release. The Community download does not currently include an offline image archive. For an air-gapped installation, mirror or export all seven exact image digests from release-manifest.json through your approved software-import process and load them into Docker or your internal registry before running ./casebender up --offline.

2. Initialize the installation

On a terminal, one command prompts for the public HTTPS URL, writes .env, starts the stack, and prints a boxed one-time setup code:
Use --yes or CASEBENDER_NONINTERACTIVE=1 to skip prompts. Air-gapped hosts that already have images loaded use ./casebender setup --offline.

Default local volume

The first install uses local file storage. License (community or enterprise) does not change that. Non-interactive initialize with no storage variables:
This writes STORAGE_PROVIDER=local, stores files on a named Docker volume at /data, and starts a bundled ClamAV scanner on the internal Compose network. An in-app banner notes that files live on this host. You can keep this after applying an enterprise license, or switch later to object storage.

Optional object storage

GCS uses GCS_BUCKET/GCS_PROJECT_ID with ADC. Azure uses AZURE_STORAGE_ACCOUNT/AZURE_CONTAINER with Managed Identity. A mounted STORAGE_CONFIG_FILE or inline STORAGE_CONFIG_JSON can define separate quarantine, records, and ephemeral profiles. MinIO is not a supported provider and is not embedded in Compose. Initialization:
  • reads the pinned image version and registry from the bundle’s release.env;
  • creates .env with unique authentication, database, Redis, and OpenSearch secrets while preserving the supplied storage configuration;
  • keeps secrets out of command output;
  • prepares the one-time local activation state; and
  • refuses to overwrite an existing .env or reset an installation.
Back up .env in your approved secret store. Never commit it or send it through email or chat. Then set the public HTTPS origin if you did not use ./casebender setup. Non-interactive init writes https://localhost, which is enough only for a local browser override. Sign-in cookies and later OAuth integrations need the real URL:
If web logs show Failed to connect to database while waiting for host db, this release is older than the Postgres service rename. Add the following to .env and to the web service environment in docker-compose.prod.yml, then run ./casebender up again:

3. Install TLS certificates

./casebender init already writes a temporary self-signed certificate at:
Replace those files with a certificate trusted by client devices before production use. Keep the self-signed pair only for isolated evaluation; browsers will warn until you install a trusted chain. A certificate or “connection is not private” warning on https://localhost after ./casebender init is expected. Proceed for evaluation, then replace the certificate before production.

4. Run production preflight

Preflight blocks startup when installation secrets are missing or weak, Redis is unauthenticated, TLS files are absent, mutable image tags are used, the demo profile is selected, storage is missing or insecure, malware scanning is not configured, or a non-proxy service port is published. Local volume storage is the default (STORAGE_PROVIDER=local). Object-storage profiles must use canonical variables and HTTPS endpoints. Mounted storage profile files must be absolute paths, valid JSON, and inaccessible to group/other users (mode 0600).

5. Start CaseBender

Skip this if you already ran ./casebender setup. This pulls only the pinned images identified by release.env; it does not clone or build the private source repository. First up prints numbered steps while the web container runs migrations and seed (often several minutes on ARM under amd64 emulation) and then prints a boxed one-time setup code with the full /setup URL. If Compose exits before that, the /setup page shows Setup code required until you run ./casebender activation-code against a healthy web container. Nginx starts only after the web process answers GET /api/health. Next.js printing Ready is not the same as storage /api/health/ready. If web logs already show the server started but Compose still shows casebender-web as Waiting (and nginx never starts), start the proxy:
Then continue with activation. In the extracted Compose file, the web healthcheck URL must be /api/health, not /api/health/ready. If ./casebender activation-code fails with Cannot find package 'zod', Node ESM cannot see pnpm’s nested copy. Create a top-level symlink, then issue the code. NODE_PATH does not work for this ESM CLI.
If the zod@ version differs, pick the path from:
If the matching images were already loaded from the air-gapped archive, run:
Only Nginx ports 80 and 443 are published. PostgreSQL, Redis, OpenSearch, API, ingestion, worker, and processor services remain on the internal Compose network. Default local storage uses a named volume and bundled ClamAV on that same network. Optional object storage is customer-owned and external to the Compose stack. Monitor startup with the management command so local-storage Compose overlays are included:

6. Complete one-time activation

After ./casebender up or ./casebender setup prints the boxed setup code, open the URL shown next to Open: (from NEXTAUTH_URL) on the same host. To reissue a code:
The terminal prints the code as a single copy-paste line and a CODE= line for logs. The code is at least 32 characters, expires after 30 minutes, allows five failed attempts, and cannot be reused after activation.
The setup page is hosted by the on-premises instance. It does not contact a CaseBender public activation service and works in an air-gapped network. Enter the code, administrator email, display name, and a unique administrator password (at least 12 characters, with uppercase, lowercase, a digit, and a symbol). See First-run setup. If the code expires, run ./casebender activation-code again; the previous code is invalidated.
CaseBender does not create a shared production password. The legacy bootstrap mode is for temporary compatibility only and requires an explicit risk acknowledgement. Do not enable it for a new installation.

7. Verify the deployment

Then verify:
  • the administrator can sign in;
  • another organization cannot access its cases, alerts, tasks, attachments, or audit records;
  • API keys cannot request scopes beyond the owner’s permissions;
  • integration destinations use HTTPS and approved egress;
  • backup and restore procedures work.

Clean start on a new host

Use this sequence when replacing a legacy install with an empty product. It creates a new database, new encryption keys, and a new first administrator. Existing cases, users, and integrations are not imported.
  1. Leave the legacy directory untouched. Extract this bundle into a new directory so Docker Compose does not reuse the old project name or volumes.
  2. Confirm Node.js 20+ (node -v) and that ports 80 and 443 are free.
  3. ./casebender init (no storage variables for local volume + bundled ClamAV).
  4. Edit .env and set NEXTAUTH_URL, NEXTAPP_URL, and OAUTH_ALLOWED_ORIGINS to https://<your-dns>.
  5. Replace deploy/nginx/ssl/ with a trusted certificate, or accept a browser warning for evaluation only.
  6. ./casebender preflight then ./casebender up.
  7. ./casebender activation-code and open https://<your-dns>/setup.
Do not unpack over the legacy docker-compose.yml directory, and do not run ./casebender init where an existing .env or named volumes already belong to a live install. init refuses to overwrite .env; deleting that file to force init would generate new database passwords against leftover pgdata. Postgres only reads POSTGRES_PASSWORD on first initialization, so the new .env cannot log in. init now refuses when pgdata already exists. If you already generated a new .env against leftover volumes, keep that .env and reset data:
If you still need the legacy data, stop and follow Migrate a Legacy Docker Compose Installation instead of this clean start.

Existing installations

When an existing CaseBender database is upgraded, the compatibility migration marks it ACTIVE. Existing users, passwords, roles, organizations, cases, and API keys remain unchanged. The setup page does not appear and seeding does not create or reset an administrator. Take a verified backup and follow Upgrading CaseBender. Connected hosts run ./casebender upgrade --version 1.0.9. Air-gapped hosts apply a local archive with ./casebender upgrade --offline ./casebender-onprem-v1.0.9.tar.gz. Do not copy release.env or Compose files by hand. If the current deployment uses a legacy docker-compose.yml with app, db, or embedded MinIO services, use Migrate a Legacy Docker Compose Installation instead of treating it as a routine upgrade. Preserve the existing .env, Compose project name, named volumes, attachment storage, and cryptographic keys. Never run ./casebender init against an existing installation.

On-premises private integrations

Outbound integrations require HTTPS and reject loopback, link-local, metadata, and private destinations by default. To permit a specific internal integration, add only its exact hostname to:
This setting is an allowlist, not a switch to permit all private networks.

Useful commands

Never use docker compose down -v on an installation that contains data. A clean reinstall that should wipe Postgres uses ./casebender down --purge-volumes --confirm.

Next steps