This folder is the on-prem deployment: DBSheriff on a Linux server you manage, in your data centre or in your own cloud account. Nothing here calls back to Sun AI and Data LLC; the product is pulled by you, from us.
| Minimum | Notes | |
|---|---|---|
| Host | Linux x86_64: Ubuntu 22.04+, RHEL/Rocky 9+, Debian 12+, Amazon Linux 2023 | one VM or bare-metal host; on GCP and Azure pick an Ubuntu 22.04+ image |
| Sizing | 4 vCPU, 32 GB RAM, 100 GB disk (AWS r6i.xlarge, GCP e2-highmem-4, Azure Standard_E4s_v5) — sustains ~30 scored events/s with headroom (measured ceiling ~100); a 16 GB host (m6i.xlarge, e2-standard-4, Standard_D4s_v7) works if you enable only a few engines: Spark is an opt-in profile and stays off, and the Ollama container always runs but uses memory only once you pull a model into it, so on 16 GB leave the natural-language interface off or point it at an external endpoint in the Admin Console | For 30-100 events/s use 8 vCPU / 32 GB (m6i.2xlarge, e2-standard-8, Standard_D8s_v7); for 100-200 events/s use 64 GB (r6i.2xlarge, e2-highmem-8, Standard_E8s_v5). Disk grows ~54 GB per 1 event/s of sustained rate at the default 90-day retention (images alone ~12 GB) — see sizing guide (inside the package) or installation guide (inside the package) to estimate your rate and size the disk |
| Software | Docker Engine 24+ with the Compose v2 plugin, curl, tar, openssl |
cosign verifies the installer and the release signatures; without it the installer and the image check use cosign's container image, pinned by digest, through Docker (and with neither, the installer stops). Amazon Linux 2023: dnf install docker gives the engine but not the Compose plugin — install it from GitHub, see below |
| Network, connected install | outbound HTTPS to get.dbsheriff.ai, releases.dbsheriff.ai, ghcr.io, Docker Hub (the third-party images, no login) and tuf-repo-cdn.sigstore.dev (Sigstore's trust root) |
none for an air-gapped install |
| From Sun AI and Data | the install command with your download token, registry credentials (user name + read-only token), your .lic file |
all in your release e-mail |
| Root | sudo for the install, or a user in the docker group that can write /opt/dbsheriff |
Inbound: 443 for the bundled HTTPS front door (the default), plus 80
(it redirects to HTTPS, and Let's Encrypt acme certificates need it
reachable from the internet), or, with
internal / proxy access, the two consoles (8501 reporting, 8502
admin) and Grafana 3000, from wherever your users sit. Nothing else is
published beyond the host itself: Kafka, Kafka UI, MLflow, the training
database and Ollama listen on 127.0.0.1 (SIDE_BIND_ADDR). Docker's
published ports bypass ufw and firewalld; see the installation guide,
"Firewall". The host must reach every database you monitor on its normal
port.
Public subnet, private subnet, or a private subnet with no internet, and how your
people reach the consoles in each: the guide install-public-or-private-subnet
(published beside this page, and in customer_docs/ of the package).
docker-ce packages include the Compose plugin), then
sudo systemctl enable --now docker.bash
sudo dnf install -y docker && sudo systemctl enable --now docker
sudo mkdir -p /usr/local/lib/docker/cli-plugins
U=https://github.com/docker/compose/releases/download/v5.5.1/docker-compose-linux-x86_64
curl -fsSL "$U" -o /tmp/docker-compose
# check the download against the checksum Docker publishes next to it; this must print "OK"
curl -fsSL "$U.sha256" | awk '{print $1 " /tmp/docker-compose"}' | sha256sum -c -
sudo install -m 0755 /tmp/docker-compose /usr/local/lib/docker/cli-plugins/docker-compose
docker compose version
Every DBSheriff container carries a memory limit (sizing guide (inside the package), Limits), so a runaway service is killed and restarted instead of taking the host down. Three host settings keep the host reachable, and its logs on disk, when memory does run short. Run them once as root:
# 1. never let the kernel's OOM killer pick sshd (or a cloud agent, where one runs)
for u in ssh sshd amazon-ssm-agent google-guest-agent walinuxagent; do
systemctl cat "$u.service" >/dev/null 2>&1 || continue
mkdir -p "/etc/systemd/system/$u.service.d"
printf '[Service]\nOOMScoreAdjust=-1000\n' > "/etc/systemd/system/$u.service.d/10-dbsheriff.conf"
done
systemctl daemon-reload
# 2. a 4 GB swap file, used only when memory is really short (the containers cannot reach it)
fallocate -l 4G /swapfile && chmod 600 /swapfile && mkswap /swapfile && swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
echo 'vm.swappiness = 10' > /etc/sysctl.d/90-dbsheriff.conf && sysctl -p /etc/sysctl.d/90-dbsheriff.conf
# 3. keep the system journal on disk, bounded — a kernel OOM message then survives a reboot
mkdir -p /var/log/journal /etc/systemd/journald.conf.d
printf '[Journal]\nStorage=persistent\nSystemMaxUse=1G\n' > /etc/systemd/journald.conf.d/10-dbsheriff.conf
systemctl restart systemd-journald
install.sh warns at pre-flight when the swap or the persistent journal is
missing. The container health check (scheduled every minute: see the next
section) mails when a container is at its limit, when one was OOM-killed, or
when the host itself is short of memory (CONTAINER_HEALTH_MEM_PCT, 10 %),
naming the containers and the .env variable that raises a limit. If the
installer ended with "NOT scheduled: container-health" there is no such mail:
check with deploy/onprem/stack.sh health and free -h.
python3-venvinstall.sh schedules six jobs on the host (systemd timers as root, cron
otherwise). Three of them run Python: the container health check (every
minute), the daily report (07:00) and the weekly model retrain (Sunday 02:00).
All three are scheduled wherever the host can run them (the conditions are
below). The customer package carries no application Python
except the health check's: that job runs on the host's own python3 (the next
paragraph), and the daily report and the weekly retrain run in containers of the
stack and need only docker. The installer ends its run with one line for each job it could
not schedule, and says what that job's absence costs.
The container health check (every minute) is scheduled. It reads the Docker
socket and the host's memory and disks, so no container can run it for the host:
the package carries its Python as plain source (scripts/container_health_check.py
and six small modules under common/: the mail sender with its URL and TLS
checks, the log-line cleaner and the audit-chain report), and the installer runs it
with the host's own python3, in the virtual environment described below. Every
minute it fills the System Health tab, and it mails the addresses in
CONTAINER_HEALTH_ALERT_EMAILS (set it in /opt/dbsheriff/state/.env; with none
it logs a warning, sends nothing, and the tab still fills), through the SMTP_*
settings of .env, when a container is down, crash-looping, unhealthy, at its
memory limit or killed for running out of memory, when the host runs short of
memory or disk, when Redis starts evicting, or when the audit-chain check goes
stale. Its log is /opt/dbsheriff/state/logs/container_health.log. What the
host needs: python3 3.9 or later with its venv module (python3-venv on
Debian and Ubuntu: see below); the docker command for the account that runs the
job (root for the systemd timers, the account that ran the installer for the
cron fallback, which must be allowed to use Docker); and, while the installer
builds the virtual environment, a route to a Python package index (PyPI or your
own mirror) for psycopg2-binary, requests and cryptography, installed from
a hash-locked list. When the host cannot run the job the installer does not
schedule one that would fail every minute: it ends with "NOT scheduled:
container-health" and the reason. An air-gapped host has no package index
(the image bundle carries no Python packages): build the environment once from
your own mirror (pip reads your usual pip.conf or PIP_INDEX_URL), then run
the installer's job step again:
sudo python3 -m venv /opt/dbsheriff/venv
sudo /opt/dbsheriff/venv/bin/pip install psycopg2-binary requests cryptography
sudo /opt/dbsheriff/current/deploy/onprem/scripts/install_cron_jobs.sh
On a customer host the log tail of a crashed container that goes into the mail is
cleaned without the console's redaction module: a log line that holds a quote or a
credential word appears as [line withheld: redaction failed] instead of being
redacted, which keeps a literal out of your inbox at the cost of some context.
The daily report (07:00) is scheduled. Its script is not on a customer host, so
scripts/daily_email_report_cron.sh runs it inside the admin console container
(docker exec dbsheriff-<ENV_NAME>-admin python scripts/daily_email_report.py):
the image carries the script and the container already holds the training-database
login (ops_rw) and the SMTP_* settings. Only REPORT_RECIPIENTS, GRAFANA_URL
and REPORT_DATE are read from .env by the wrapper and handed in, by name, so no
secret is on a command line or in the log. A full-package host, where the script is on
disk, runs it on the host with the ops venv as before. The installer schedules the job
on a customer package when docker is on the PATH, and otherwise ends with
"NOT scheduled: daily-email-report". The container uses the SMTP_* values it was
created with, so after editing them in .env run deploy/onprem/stack.sh up. If the
admin console container is not running at 07:00 the wrapper says so in
state/logs/daily_email.log (exit 2) and no report goes out that day.
The weekly retrain (Sunday 02:00) is scheduled. model_training/ is not on a
customer host, so retrain_and_deploy.sh runs each Python step in a throwaway
docker run --rm container of the admin image (the image carries the training
libraries, the compiled common/ and the four files of model_training/ the
retrain needs; deploy/common/host/retrain_runner.sh decides this, RETRAIN_RUNNER
in .env: auto, host, container). The Admin Console's Run Retrain button
goes through the same script, handed over by the watcher job. The container runs as the user
running the job, with every capability dropped, on the training database's network only, with a memory
limit of 4g (RETRAIN_MEMORY_LIMIT; exit 137 in the log means it was hit) and the
ops_rw login of the services; the model integrity key reaches one container, the
signer's, by name. The model registry (state/mlruns, started on the first run from
mlruns_seed.tar.gz, i.e. the model that shipped) and the record of the deployed version
(state/.deployed_model_version) live in the state directory, so an upgrade keeps them.
Step 1 of the run checks the admin container, the database network, a writable registry
and that the image carries the retraining code (an image from before 1.4.0 does not),
before anything is applied. The installer schedules the job when docker is on the PATH
and otherwise ends with "NOT scheduled: weekly-retrain". A full-package host, where
model_training/ is on disk, trains on the host in its virtual environment as before.
Detection, kill and lock, the audit trail and alerts run in the containers and
are unaffected, and the nightly statistics refresh and retention pruning are
scheduled. You can always check the stack yourself, and must when the container
health check shows "NOT scheduled": deploy/onprem/stack.sh health (exit 1 if
anything is not healthy), df -h /var/lib/docker and free -h. The nightly retention pruning runs the PostgreSQL client psql on
the host, so install your distribution's PostgreSQL client package; without it
that job writes "psql not found on PATH" to /opt/dbsheriff/state/logs/prune_training_db.log
each night and prunes nothing.
The virtual environment and python3-venv. The container health check (and,
on a host installed from the full package, the daily report) runs Python on the
host, in a small virtual environment the installer builds next to the releases
(/opt/dbsheriff/venv, from a hash-locked requirements file). Debian and
Ubuntu ship python3 -m venv in a separate package, python3-venv, that
minimal and cloud images lack. When the environment cannot be created, and
the installer runs as root on a host with apt-get, it installs
python3.X-venv (else python3-venv) with apt-get and tries once more,
for at most five minutes in all (DBSHERIFF_OPS_VENV_APT_SECONDS, default
300; an air-gapped or firewalled host would otherwise wait out several
download time-outs). That is the only package the installer installs.
If your change control does not allow that, install the package yourself
before the installer runs (sudo apt-get install -y python3-venv), or switch
the installer's apt-get off: DBSHERIFF_OPS_VENV_APT=false, either in the
environment (sudo env DBSHERIFF_OPS_VENV_APT=false bash install-v1.4.0.sh ...;
a plain sudo drops variables) or as a line in
/opt/dbsheriff/state/host.env (<install dir>/state/host.env when you
installed with --dir; create the file if the host has none), which
every later run reads too. With the switch off and no virtual environment, the
container-health timer stays off (and, on a full-package host, the daily
report), and the container health check is what mails when a container is at its
memory limit, was OOM-killed, or the host is short of memory; the installer
prints that, with the command to run, and nothing else is affected. Re-run
deploy/onprem/scripts/install_cron_jobs.sh once the package is there.
The commands below name the release v1.4.0 (1.4.0 without the v). Use the
tag from your release e-mail; the copy of this page published at
https://get.dbsheriff.ai/docs/<tag>/install.html has it filled in.
set -e
curl -fsSLO https://get.dbsheriff.ai/install-v1.4.0.sh
curl -fsSLO https://get.dbsheriff.ai/install-v1.4.0.sh.sigstore.json
cosign verify-blob --bundle install-v1.4.0.sh.sigstore.json \
--certificate-identity https://github.com/dbsheriffai/dbsheriff/.github/workflows/release.yml@refs/tags/v1.4.0 \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
install-v1.4.0.sh
sudo bash install-v1.4.0.sh --version v1.4.0 --token '<download token>'
The installer runs as root, so it is verified before it runs
(customer_docs/verify-the-installer.md, also published beside this page as
verify-the-installer.html: the identity to expect, how to confirm it through
a second channel, the same check through Docker without cosign, and a host
that cannot reach Sigstore). The one-line form
curl -fsSL https://get.dbsheriff.ai | sudo bash -s -- --version v1.4.0 --token '<download token>'
is for a throw-away evaluation host only: it runs the script without checking it.
The installer then downloads and verifies the release (it stops when a signature
is missing or does not verify, or when there is no cosign and no Docker to check it with;
--skip-signature goes on without the check and says so), unpacks it to
/opt/dbsheriff/releases/v1.4.0, points /opt/dbsheriff/current at it
and starts this folder's install.sh, which asks:
prod);.lic file (blank runs a 30-day demo; without a licence
it also asks how many monitored servers the demo may register, blank = no limit);postgresql,mysql,sqlserver,oracle,mongodb,snowflake,databricks,bigquery,hana,redshift,spanner,synapse) —
a licence that names engines only lets those run: their producers refuse to
start and the Admin Console refuses their servers (contact sales@dbsheriff.ai to add one);caddy (the default: bundled HTTPS on
443 with your certificate, Caddy's own CA, or Let's Encrypt),
internal (plain HTTP on this host, unencrypted: VPN or tunnel users
only) or proxy (your own reverse proxy in front). In caddy mode it
then asks for the name or IP address users will type, the certificate
source (internal, file, acme or acme-dns, each with the details it
needs) and an optional allow-list of client networks. Details:
docs/console-access.md;Then it pulls the images, starts the core services, applies the schema migrations, starts the rest of the stack, creates the console accounts, checks that every service is healthy and prints the URLs. A failed migration stops it before the scorer, the audit consumer and the producers are started.
Unattended install, same thing without questions:
sudo bash install-v1.4.0.sh --version v1.4.0 --token '<token>' -- \
--yes --license /root/acme.lic --engines postgresql,mysql --access-mode caddy --hostname db-guard.acme.internal \
--registry-user acme-pull --registry-token '<ghcr token>' --admin-password '<password>'
install.sh --help lists every option.
On a connected machine, download from the links in your release e-mail:
the customer package, SHA256SUMS, SHA256SUMS.sigstore.json, the image
bundle dbsheriff-v1.4.0-images.tar.gz (about 13 GB) with its signed
checksum dbsheriff-v1.4.0-images.tar.gz.sha256 (+ .sigstore.json), and
the signed image manifest IMAGES-v1.4.0.txt (+ .sigstore.json). Verify
(the full guide with every check is customer_docs/verify-your-download.md):
ID=https://github.com/dbsheriffai/dbsheriff/.github/workflows/release.yml@refs/tags/v1.4.0
ISS=https://token.actions.githubusercontent.com
cosign verify-blob --bundle SHA256SUMS.sigstore.json --certificate-identity "$ID" --certificate-oidc-issuer "$ISS" SHA256SUMS
sha256sum -c --ignore-missing SHA256SUMS
cosign verify-blob --bundle dbsheriff-v1.4.0-images.tar.gz.sha256.sigstore.json --certificate-identity "$ID" --certificate-oidc-issuer "$ISS" dbsheriff-v1.4.0-images.tar.gz.sha256
sha256sum -c dbsheriff-v1.4.0-images.tar.gz.sha256
cosign verify-blob --bundle IMAGES-v1.4.0.txt.sigstore.json --certificate-identity "$ID" --certificate-oidc-issuer "$ISS" IMAGES-v1.4.0.txt
Copy the package, the bundle and IMAGES-v1.4.0.txt (next to the bundle)
to the host, then:
sudo mkdir -p /opt/dbsheriff/releases/v1.4.0
sudo tar --no-same-owner -xzf dbsheriff-1.4.0-v1.4.0-customer.tar.gz -C /opt/dbsheriff/releases/v1.4.0
sudo ln -sfn /opt/dbsheriff/releases/v1.4.0 /opt/dbsheriff/current
sudo /opt/dbsheriff/current/deploy/onprem/install.sh --airgap /path/dbsheriff-v1.4.0-images.tar.gz
The bundle is complete: the six DBSheriff images plus every third-party
image the stack runs (Kafka, ZooKeeper, Redis, PostgreSQL, Grafana, Kafka
UI, Ollama), each at the digest pinned in deploy/common/images.lock at
release time, as one docker save archive per image inside the bundle
(the installer unpacks it next to itself, so keep as much free space again). The installer checks every loaded image's id against the
signed manifest before starting anything, and stops before loading if IMAGES-v1.4.0.txt is not in the bundle's directory.
The host needs no registry access
at all. Only the optional local language models (Ollama, used by the
console's assistant) are not in the bundle; on an air-gapped host, load a
model with ollama pull on a connected machine and copy the ollama-models
volume, or leave the assistant on a hosted model.
| Path | What |
|---|---|
/opt/dbsheriff/current |
link to the running release |
/opt/dbsheriff/releases/<tag> |
one directory per installed release (none are removed for you; delete old ones yourself once you no longer need to roll back to them) |
/opt/dbsheriff/state/.env |
your configuration; the release's .env is a link to it. Mode 600, holds secrets |
/opt/dbsheriff/state/dbsheriff.lic |
your licence (mode 644: the services read it as an unprivileged user; a signed token, not a secret) |
/opt/dbsheriff/state/installation_id |
the id your licence is bound to; keep it with backups |
/opt/dbsheriff/state/install.log |
every install and upgrade run |
Docker volumes dbsheriff-<env>_* |
databases, Kafka, Redis, the model, Grafana. Back these up: docs/backup-and-recovery.md |
The installer creates every secret DBSheriff needs and keeps nearly all of them in one file. This section says where each kind lives, who can read it, and the few things you should do once the install has finished.
| Secret | Where it is kept | Shown to you |
|---|---|---|
| Admin Console and Reporting Console passwords | Not kept in /opt/dbsheriff/state/.env once the installer has created the accounts (only the two user names are). The installer prints a generated password once, at the end of the run; a password you typed is not printed again. On an upgrade of an install whose accounts already exist, or if the seeding failed, INITIAL_ADMIN_PASSWORD and INITIAL_REPORT_PASSWORD stay in .env: blank those lines once you have signed in |
Once, at the end of the install |
| Passwords of the services (Redis, the training database, Grafana, Kafka UI, and with Kafka SASL one per service) | /opt/dbsheriff/state/.env, mode 600, readable by root. Generated by the installer unless you typed one |
Never |
| Session and gatekeeper secrets of the consoles | /opt/dbsheriff/state/.env |
Never |
The encryption keys: SERVER_CREDENTIALS_KEY, CONSOLE_SECRETS_KEY, SCOPE_CREDENTIALS_KEY |
/opt/dbsheriff/state/.env. Each key is 32 random bytes, generated by the installer |
Never |
The audit and evidence keys: AUDIT_ANCHOR_KEY, VAULT_KEK, SUBJECT_HMAC_KEY, EVIDENCE_SIGNING_KEY |
/opt/dbsheriff/state/.env |
Never |
| The credentials of the databases you monitor | The registry in DBSheriff's own database, encrypted with AES-256 under SERVER_CREDENTIALS_KEY |
Never again after you save them |
| Your licence | /opt/dbsheriff/state/dbsheriff.lic: a signed token, not a secret |
Not applicable |
| Key | Encrypts | Held by |
|---|---|---|
SERVER_CREDENTIALS_KEY |
Every credential of every database you monitor: passwords, kill, lock, posture and classification accounts, connection strings, service-account keys | The Admin Console, the services that connect to your databases (producers and collectors), the scorer and the analysers. Not the Reporting UI |
CONSOLE_SECRETS_KEY |
What the consoles use themselves: the single sign-on client secret, the LLM API key, Slack webhook addresses, and the write credential of a cloud scope | Both consoles and a few back-end services (the scorer, the query-performance analyser, the audit archiver, the identity profiler, the SIEM exporter). No producer and no collector holds it. The Reporting UI holds this key and not the first one, so a flaw in the console people reach from outside cannot open a database credential |
SCOPE_CREDENTIALS_KEY |
The read credential you store for a cloud scope (Discovered Databases, the Azure Activity Log) | The Admin Console and the one service that reads the Azure Activity Log, and nothing else |
/opt/dbsheriff/state/.env somewhere only you can reach, a password manager or your secrets vault, and keep it separate from the data backups. It is the only place the keys exist: a restored database without the key that sealed it still holds the credentials, but nobody can open them, and you would enter every monitored server's password again. Keep /opt/dbsheriff/state/installation_id next to it.SERVER_CREDENTIALS_KEY (or the other two) in .env makes everything already stored under the old value unreadable. Changing a key is a procedure that re-encrypts the stored values under the new one (sudo docker exec dbsheriff-<env>-admin python scripts/rotate_credentials_key.py ..., described in docs/runbooks/credential-keys.md in your release package; ask support@dbsheriff.ai if you do not have it). The installer never replaces AUDIT_ANCHOR_KEY, VAULT_KEK or SUBJECT_HMAC_KEY once it has set them, and you must not overwrite one by editing it either: each has its own rotation procedure, which puts the new value in the variable and keeps the old value in the matching _PREVIOUS variable (VAULT_KEK every 30 days, docs/runbooks/vault-keys.md section 3; AUDIT_ANCHOR_KEY, docs/runbooks/audit-integrity.md; SUBJECT_HMAC_KEY the same way, its old value staying in SUBJECT_HMAC_KEY_PREVIOUS for as long as an erasure tombstone names it). Replacing one without keeping the old value leaves what it protected unreadable or unverifiable./opt/dbsheriff/state/.env and running sudo /opt/dbsheriff/current/deploy/onprem/stack.sh up, which recreates only what changed.INITIAL_ADMIN_PASSWORD (and INITIAL_REPORT_PASSWORD) back in .env and run sudo /opt/dbsheriff/current/deploy/onprem/stack.sh seed-users --reset.create_reset_dbuser in reset mode: it sets a new password on the database account and updates the stored credential in one step.stack.sh ps output and the log tail, never .env.cd /opt/dbsheriff/current
sudo deploy/onprem/stack.sh ps # what is running
sudo deploy/onprem/stack.sh health # exit 1 if anything is not healthy (on a customer package no timer runs this for you)
sudo deploy/onprem/stack.sh logs serve # the streaming scorer
sudo deploy/onprem/stack.sh stop # stop everything, keep data
sudo deploy/onprem/stack.sh up # start again
sudo systemctl status dbsheriff # if you chose the boot-time unit
Change a setting: edit /opt/dbsheriff/state/.env, then
sudo deploy/onprem/stack.sh up (it recreates only what changed). Turning on
automatic kill is not an .env setting: tick kill enabled for the
server in the Admin Console's Monitored Servers tab; leave it off for a day or
two first and watch the alerts.
Until the first database is registered, query-perf-scanner restarts
every few seconds; stack.sh health reports it as waiting, not as a
fault. Add databases in the Admin Console, Monitored Servers tab. Each needs a
least-privilege monitoring account: docs/connecting-engines.md.
Download and verify the new release's installer (see Install, above) and run it with the new --version (and the new token from
your e-mail). It unpacks the new release next to the old one, re-enters
install.sh, which keeps your configuration, pulls the new images,
switches current, starts the core services, applies the schema migrations,
starts the rest and checks health. A failed migration stops the installer
before the scorer, the audit consumer, the producers and the consoles are
started or recreated, so those keep running. The core services (ZooKeeper,
Kafka, Redis, the training database) were started from the new release and may
have been recreated, and a container that depends on one can be left down:
the installer prints what is not running (stack.sh health lists it again).
Fix the cause and run the installer again (docs/runbooks/failed-migration-deploy.md).
Roll back (the link alone starts nothing: install.sh --yes does, then check):
sudo ln -sfn /opt/dbsheriff/releases/<previous tag> /opt/dbsheriff/current
sudo /opt/dbsheriff/current/deploy/onprem/install.sh --yes
sudo /opt/dbsheriff/current/deploy/onprem/stack.sh health
Schema migrations are additive, so an older release runs against a newer schema.
cd /opt/dbsheriff/current && deploy/onprem/stack.sh stop
docker compose -f deploy/onprem/docker-compose.yml --project-directory . --profile '*' down -v # -v deletes the data
sudo systemctl disable --now dbsheriff 2>/dev/null; sudo rm -f /etc/systemd/system/dbsheriff.service
sudo rm -rf /opt/dbsheriff
docs/troubleshooting-and-logs.md first; then support@dbsheriff.ai with
the output of deploy/onprem/stack.sh ps and the tail of
/opt/dbsheriff/state/install.log.