Skip to content
Karisto

Self-hosting

Karisto runs as a hosted service at karisto.app. You can also run it on your own server with Docker Compose. A self-hosted install usually serves a single Tenant: your own company.

Licence

Karisto is source-available under the Business Source License 1.1. Testing and other non-production use are free. Running it in production on your own server needs a commercial licence from Erwins Enkel GmbH; contact us. Each version becomes Apache 2.0 four years after its release.

The licence comes with credentials for our image registry, registry.karisto.app, which has every release of Karisto.

Requirements

  • A Linux server with Docker and Docker Compose 2.23.1 or later
  • Credentials for registry.karisto.app (see Licence)
  • A domain name and a reverse proxy for HTTPS (for example Caddy or nginx)
  • Your company's sign-in: Microsoft Entra ID, another OpenID Connect provider or GitHub
  • An Autotask API user (see Autotask)
  • Optional: an SMTP account for email (an EU provider is recommended)

Install

  1. In a new directory, log in to the registry and take the Compose file from the release you install (here 1.4.0):

    docker login registry.karisto.app
    docker run --rm registry.karisto.app/karisto:1.4.0 cat compose.yaml > compose.yaml
    

    The file is also at /docs/self-hosting/compose.yaml, for the version serving these docs.

  2. Create a file .env next to compose.yaml:

    KARISTO_VERSION=1.4.0
    ORIGIN=https://karisto.example.com
    ADDRESS_HEADER=X-Forwarded-For
    

    KARISTO_VERSION is the release you run. ORIGIN is the address your users open; sign-in redirects and email links use it.

  3. Create the secret files in a directory secrets next to it, see Secrets:

    install -d -m 700 secrets && cd secrets && umask 022
    openssl rand -base64 32 | tr -d '\n' > secret_key
    openssl rand -hex 24 | tr -d '\n' > postgres_password
    printf 'postgres://karisto_owner:%s@db:5432/karisto' "$(openssl rand -hex 24)" > database_admin_url
    printf 'postgres://karisto_app:%s@db:5432/karisto' "$(openssl rand -hex 24)" > database_url
    cd ..
    

    secret_key encrypts your sign-in and Autotask secrets in the database: keep it safe, without it they cannot be read.

  4. Start Karisto: docker compose up -d. Compose pulls the image, sets up the database, applies migrations and starts the app on 127.0.0.1:3000.

  5. Create your Tenant and its first Admin, see First Tenant.

First Tenant

docker compose run --rm ops bun build/cli/karisto.js tenant create --name "Example GmbH"
docker compose run --rm ops bun build/cli/karisto.js admin bootstrap <slug> --email you@example.com --name "Your Name"

The first command prints the Tenant's slug, the second a sign-in link for the first Admin, valid once for 24 hours. Open it and continue with Getting started. Once your company's sign-in is set up and an Admin has used it, no more such links are issued.

Set SINGLE_TENANT_SLUG=<slug> in .env and run docker compose up -d again: the start page then leads straight to your Tenant's sign-in.

Reverse proxy

The app listens on 127.0.0.1 only. Put a reverse proxy on the same host in front of it that terminates HTTPS, for example with Caddy:

karisto.example.com {
	reverse_proxy 127.0.0.1:3000
}

Karisto uses the visitor's IP address for rate limits, so it needs to know where it comes from. Set exactly one of:

  • ADDRESS_HEADER=X-Forwarded-For behind a proxy. XFF_DEPTH (default 1) is the number of proxies in front of the app. Only use this while the proxy is the sole way in, since a visitor reaching the app directly could fake the header.
  • DIRECT_CLIENT_ADDRESS=true when visitors connect to the app directly, without a proxy.

The app refuses to start without one of them.

If anything else can reach the app (another container on its network, say), set PROXY_SECRET to a long random value (openssl rand -hex 32) and have the proxy send it in the X-Karisto-Proxy-Secret header. The app then refuses every request without it, except GET /healthz.

Secrets

The four required secrets are files, not entries in .env: Compose mounts them read-only at /run/secrets in the containers that need them, so docker inspect and docker compose config show only their paths. The files are in secrets next to compose.yaml; set SECRETS_DIR in .env to keep them elsewhere. Write them without a trailing newline and readable by the containers (mode 644, in a directory with mode 700).

File Content
secret_key 32 random bytes, base64
postgres_password Password of the database superuser, hex
database_admin_url postgres://karisto_owner:<hex password>@db:5432/karisto: migrations and the command line
database_url postgres://karisto_app:<hex password>@db:5432/karisto: the app

Optional secrets, such as SMTP_URL or PROXY_SECRET, are entries in .env by default, so docker inspect shows them. Each can be a file too: set <name>_FILE to its path in the container, for example with a compose.override.yaml. .env.schema lists them.

Upgrading from a version with secrets in .env: write the four files from your .env (SECRET_KEY, POSTGRES_PASSWORD, and the OWNER_DB_PASSWORD and APP_DB_PASSWORD in the two URLs), remove those entries from .env, then run docker compose up -d.

Changing secret_key: stop the app (docker compose stop app), write the new key to a file secrets/secret_key.new, re-encrypt the stored secrets with docker compose run --rm -v ./secrets/secret_key.new:/new-key:ro ops bun build/cli/karisto.js secret-key rotate --new-key-file /new-key, then move secret_key.new to secret_key and run docker compose up -d.

Options

Variable Effect
SMTP_URL, MAIL_FROM Send email (invitations, alerts), e.g. smtps://user:pass@host:465. Unset, it is only logged
SENTRY_DSN Report server errors and failed jobs to Sentry or a compatible tracker
PORT Host port of the app, default 3000
SINGLE_TENANT_SLUG The start page leads to this Tenant's sign-in

Customer CSAT PDFs are rendered by the pdf service that Compose starts with the app. Self-hosted installs have no billing and no public signup. The full list of settings with explanations is in .env.schema.

Updates and backups

To update, set the new release in .env (KARISTO_VERSION=1.5.0), take its Compose file and restart:

docker run --rm registry.karisto.app/karisto:1.5.0 cat compose.yaml > compose.yaml
docker compose up -d

Database migrations run automatically before the app starts. Running from the source code instead: docker compose -f compose.yaml -f compose.build.yaml up -d --build builds the image from it, with any value for KARISTO_VERSION.

All data lives in the PostgreSQL database in the Docker volume db-data. Back it up regularly with your usual tooling, for example pg_dumpall, and keep .env and the secrets directory (above all secret_key) with it.

Command line

Run commands as docker compose run --rm ops bun build/cli/karisto.js <command>:

Command Effect
tenant create --name <name> Create a Tenant, prints its slug
tenant list List Tenants with status and active Members
tenant suspend <slug>, tenant reactivate <slug> Suspend or reactivate a Tenant
admin bootstrap <slug> --email <e> --name <n> Sign-in link for the first Admin
demo seed <slug> Fill an empty Tenant with six months of demo data
scanner list, scanner add ua|ip <value>, scanner remove ua|ip <value> Link scanners (by user agent or IP range) whose clicks on rating links are ignored
secret-key rotate --new-key-file <path> Re-encrypt the stored secrets with a new secret_key, see Secrets

bun build/cli/karisto.js --help lists every command.