Quick Start Guide
Zero to a running instance with an activated license and a live connection. Two paths, Compose for single-host and evaluation, Helm for clusters. The delivery bundle ships docker-compose.yml, .env.example, and helm/; your license key arrives separately by email.
Running the image constitutes acceptance of the EULA (LICENSE.txt in the bundle): no reverse engineering of the image or its contents, no redistribution beyond licensed seat count, no use outside your own infrastructure. Commercial terms: contactus@simchasolutions.com.
Secrets (both paths)
Three secrets are mandatory; the app fails closed at startup if any is missing.
JWT_SECRET, ≥32 hex chars (64 recommended). Signs session JWTs; rotating it invalidates all live sessions.ENCRYPTION_KEY, ≥32 hex chars. AES-256-GCM key wrapping stored connection secrets and other sensitive fields. Back it up and treat it as unrotatable in place, without the original key, previously encrypted rows are unrecoverable.DB_PASSWORD, password for the metadata Postgres.
Path A: Docker Compose
cp .env.example .env
echo "JWT_SECRET=$(openssl rand -hex 32)" >> .env
echo "ENCRYPTION_KEY=$(openssl rand -hex 32)" >> .env
echo "DB_PASSWORD=$(openssl rand -hex 16)" >> .env
# strip the placeholder lines from .env, keep only the appended values
docker compose up -d
Compose pulls the image, starts Postgres, applies pending migrations idempotently, and launches the app. First boot is 30-60s; health returns STARTING until the metadata DB is reachable (startup retries the connection before reporting ready).
curl http://localhost:5002/api/health
# {"status":"ok","database":"ready",...}
docker compose logs -f app
Open the UI, complete license activation on first access (paste the SDB-... key; verified locally against the bundled RSA public key, no network call), then open the IDE and add a connection, test, save, and query it. Pin an image tag for anything beyond evaluation:
echo "SIMCHA_IMAGE=simchasolutions/db-studio:1.0.2" >> .env
docker compose pull app && docker compose up -d app
Path B: Kubernetes (Helm)
helm install dbstudio ./helm \\
--namespace dbstudio --create-namespace \\
--set secrets.jwtSecret=$(openssl rand -hex 32) \\
--set secrets.encryptionKey=$(openssl rand -hex 32) \\
--set secrets.dbPassword=$(openssl rand -hex 16) \\
--set ingress.enabled=true \\
--set ingress.hosts[0].host=dbstudio.example.com
The chart renders the app Deployment, a single-node Postgres StatefulSet, a Service, an optional Ingress, and a pre-install/pre-upgrade migration Job that gates the rollout, migrations complete before any new pod serves traffic.
kubectl -n dbstudio get pods
kubectl -n dbstudio port-forward svc/dbstudio 3000:3000 5002:5002
The chart defaults (in-namespace Postgres, chart-generated Secret) are evaluation-grade. For production, override:
postgres.enabled: falseand point at managed/HA Postgres.secrets.existingSecretsourced from your secret manager (External Secrets Operator, sealed-secrets) rather than a chart-generated Secret.- Real Ingress host + TLS;
replicaCount > 1for HA (the app is stateless, all state is in Postgres).
Full overrides template: Kubernetes Deployment.
Bringing up ChangeOps (Professional / Enterprise)
The first registered user becomes admin. From there the standing order is: register repository/storage sources (branch, path, *.sql pattern) under Repository & Storage Sources; register the target database connections executions will run against under Connections; then populate Scripts by upload or repo sync. Scripts carry engine, environment, version, content hash, and dependencies from creation, which is what makes downstream dependency ordering and tamper detection work.
Startup troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Container exits, "JWT_SECRET is not set" (or ENCRYPTION_KEY / DB_PASSWORD) | Missing mandatory secret, fail-closed startup | Set the var (≥32 chars where required), restart |
| Bind failure on the UI/API port | Host port already bound | Free the port or remap host ports in .env |
| Dashboard reads all zeros | CORS origin mismatch, CLIENT_URL ≠ the browser origin | Set CLIENT_URL to the exact origin (scheme+host+port, no trailing slash), restart |
| Target connection fails | Credentials/host/network path to the target | Confirm reachability from inside the container's network before blaming the app |
| License activation rejected | Malformed or expired key | Re-copy the full key; confirm it is within its validity window |
| Migrations fail on boot | Metadata Postgres role lacks DDL rights | Grant the role ownership/DDL on the metadata database and restart |