Data Observability platform Help

Deploying SelectZero

Prerequisites

The minimum requirements for the server or cloud instance hosting the application are as follows:

  • CPU: 2 core

  • RAM: 4GB

  • HDD: A minimum of 10GB of free space

If you are doing production deployment, please also follow information about securing deployment for production

Encryption key

SelectZero platform encrypts all user secrets and files at rest. On initial deployment, encryption key will be automatically generated if not provided by environment variables. Generated encryption key will be stored in "database" volume under "encryption.key" with only running user access rights.

Quick start

Fastest way of deploying SelectZero is by using our predefined docker compose file, which takes care of the configuration for you. Make sure you are in the preferred directory on the machine.

Downloading docker compose and launching it:

curl -fsSL https://selectzero.com/deploy/docker-compose.yml -o docker-compose.yml && docker compose up -d
curl -o docker-compose.yml https://selectzero.com/deploy/docker-compose.yml; docker compose up -d

If you need more precise configuration you can simply download the docker-compose:

curl -fsSL https://selectzero.com/deploy/docker-compose.yml -o docker-compose.yml
curl -o docker-compose.yml https://selectzero.com/deploy/docker-compose.yml

Modify it based on your needs and then run it:

docker compose up -d

After running the platform, you can acquire initial admin password (application boot up might take a few seconds):

docker exec -it selectzero cat admin-init.txt

Deployment

Download the latest version of JAR from https://selectzero.com/downloads/

To run JAR application, use command:

java -XX:TieredStopAtLevel=1 -Dspring.jmx.enabled=false -Xmx4g -Dserver.port=8090 -jar SelectZero_2026.7.3.jar

-Xmx4g – Specifies the amount of memory that can be used by Java – in this case 4GB

-Dserver.port=8090 – Defining SelectZero application port (8090 is also default port if not provided)

It is recommended to run the Java file as a service.

Link to Java Service on Ubuntu

Link to Java Service on Windows

For hosting your application on your cloud instance, make sure you have a connection to docker hub.

You can find latest versions here

Deploy with docker

  1. Pull docker image

    docker pull selectzero/selectzero:2026.7.3
  2. Run container

    docker run -d -v {local_path}/logs:/usr/app/logs -v {local_path}/database:/usr/app/database -v {local_path}/drivers:/usr/app/drivers -v {local_path}/reports:/usr/app/reports -v {local_path}/keys:/usr/app/keys -p 8090:8090 -e JAVA_OPTIONS='-Xmx4g' selectzero/selectzero:2026.7.3

    -v {local_path}/logs:/usr/app/logs

    Logs storage

    -v {local_path}/database:/usr/app/database

    DQM local database

    -v {local_path}/drivers:/usr/app/drivers

    JAR connectors storage

    -v {local_path}/reports:/usr/app/reports

    Reports and results

    -v {local_path}/keys:/usr/app/keys

    Key files storage such as BigQuery JSON keys and application certificate keystore

    -p 8090:8090

    Port mapping for exposing DQM application at port 8090

    selectzero/selectzero:2026.7.3

    Image from dockerhub, latest tag is also available

  3. Validate that DQM container is running

    docker ps

Docker compose is the easiest way to deploy/manage DQM with docker containers

Deploy with docker compose

  1. Create docker-compose.yml

    services: selectzero: container_name: selectzero image: selectzero/selectzero:2026.7.3 ports: - 8090:8090 volumes: - ./logs:/usr/app/logs - ./database:/usr/app/database - ./drivers:/usr/app/drivers - ./reports:/usr/app/reports - ./keys:/usr/app/keys environment: JAVA_OPTIONS: -Xmx4g
  2. Run docker compose

    docker compose up -d

Deploying on kubernetes

  1. Create a PersistentVolumeClaim using your cloud provider's storage class. Choose the tab matching your environment:

    apiVersion: v1 kind: PersistentVolumeClaim metadata: name: selectzero-storage-claim spec: storageClassName: gp3 accessModes: - ReadWriteOnce resources: requests: storage: 20Gi

    Uses the Amazon EBS gp3 storage class (default on most EKS clusters). Ensure the EBS CSI driver is installed.

    apiVersion: v1 kind: PersistentVolumeClaim metadata: name: selectzero-storage-claim spec: storageClassName: managed-csi accessModes: - ReadWriteOnce resources: requests: storage: 20Gi

    Uses Azure Managed Disk (CSI) storage class, available by default on AKS clusters.

    apiVersion: v1 kind: PersistentVolumeClaim metadata: name: selectzero-storage-claim spec: storageClassName: standard-rwo accessModes: - ReadWriteOnce resources: requests: storage: 20Gi

    Uses GCE Persistent Disk (CSI) storage class, available by default on GKE clusters.

    apiVersion: v1 kind: PersistentVolumeClaim metadata: name: selectzero-storage-claim spec: accessModes: - ReadWriteOnce resources: requests: storage: 20Gi

    Omitting storageClassName uses the cluster's default storage class. Verify your default class provides persistent block storage (not hostPath) by running: kubectl get storageclass

  2. Create resource file selectzero.yaml

    --- apiVersion: apps/v1 kind: Deployment metadata: name: selectzero-deployment spec: selector: matchLabels: app: selectzero replicas: 1 # SelectZero is limited to one replica due to sessions strategy: type: RollingUpdate template: metadata: labels: app: selectzero spec: volumes: - name: selectzero-storage persistentVolumeClaim: claimName: selectzero-storage-claim containers: - name: selectzero image: selectzero/selectzero:2026.7.3 # Modify version tag resources: limits: memory: 4Gi requests: memory: 4Gi env: - name: JAVA_OPTIONS value: -XX:MaxRAMPercentage=80.0 ports: - containerPort: 8090 volumeMounts: - mountPath: "/usr/app/database" name: selectzero-storage subPath: database - mountPath: "/usr/app/logs" name: selectzero-storage subPath: logs - mountPath: "/usr/app/drivers" name: selectzero-storage subPath: drivers - mountPath: "/usr/app/reports" name: selectzero-storage subPath: reports - mountPath: "/usr/app/keys" name: selectzero-storage subPath: keys - mountPath: "/usr/app/tempFiles" name: selectzero-storage subPath: tempFiles --- apiVersion: v1 kind: Service metadata: name: selectzero labels: app: selectzero spec: type: NodePort ports: - port: 80 targetPort: 8090 selector: app: selectzero
  3. Apply using kubectl

    kubectl apply -f selectzero.yaml

S3 storage mode (stateless Kubernetes)

For Kubernetes environments with a "no persistent volumes" policy, SelectZero can store all durable files (execution results, test suite reports, uploaded data source files, JDBC drivers, custom logo) in an Amazon S3 bucket instead of a persistent volume. Pods become fully stateless: local disk is used only as an ephemeral working copy (emptyDir), and the entire cluster can be rebuilt from manifests without data loss.

By default all files are encrypted by the application before they reach the bucket - S3 only ever holds ciphertext, and the encryption key never leaves your secrets manager.

Alternatively, set DQM_FILE_ENCRYPTION=false to delegate encryption at rest entirely to the storage layer: files are written as-is and the bucket's server-side encryption (SSE-S3 or SSE-KMS) protects them. The main benefit is key management - rotating a KMS key is handled by AWS and never requires re-encrypting stored files, whereas rotating DQM_ENCRYPTION_KEY with application-side file encryption would. DQM_ENCRYPTION_KEY is still required in S3 mode either way, because it also protects secrets stored in database columns (connection passwords, certificates), which S3 encryption cannot cover. File names keep their .enc suffix in both modes.

Requirements

  1. External PostgreSQL as the application database (SQLite is not supported in S3 mode)

  2. DQM_ENCRYPTION_KEY must be provided from your secrets manager - the application refuses to start in S3 mode without it, because a locally generated key would not survive a pod restart and would make the encrypted files in the bucket unreadable

  3. An S3 bucket and IAM permissions: s3:ListBucket on the bucket, s3:GetObject, s3:PutObject, s3:DeleteObject on the bucket contents

At startup in S3 mode the application lists the bucket once to verify the configuration. If the bucket cannot be reached - wrong name or region, missing s3:ListBucket, or credentials that do not resolve - the application refuses to start and logs the reason, rather than running with file storage that would fail later during a scheduled job.

Configuration

Variable name

Explanation

SZ_STORAGE_MODE

local (default, filesystem/volume storage) or s3

SZ_STORAGE_S3_BUCKET

Bucket name for durable file storage

SZ_STORAGE_S3_REGION

AWS region of the bucket, e.g. eu-central-1

SZ_STORAGE_S3_PREFIX

Optional key prefix inside the bucket, e.g. dqm/ - for team-shared buckets

SZ_STORAGE_S3_ACCESS_KEY

Optional. Leave empty to use the AWS default credential chain - on EKS attach an IAM role to the service account (IRSA), no secrets required

SZ_STORAGE_S3_SECRET_KEY

Optional, pairs with the access key

DQM_FILE_ENCRYPTION

true (default): the application encrypts files before upload. false: files are stored as-is and encryption at rest is delegated to bucket SSE. Install-time choice - never flip on existing data

Reading a credential failure

When credentials cannot be resolved, the AWS SDK reports every provider it tried. Only the provider matching your setup matters; the others always fail and can be ignored:

  • Running with an IAM role (IRSA or EKS Pod Identity), the messages about AWS_ACCESS_KEY_ID are expected. A role never produces an access key.

  • Profile file contained no credentials for profile 'default' refers to an AWS credentials profile (~/.aws/credentials), not to a Kubernetes service account. The container has no such file, so this line is normal and unrelated to which service account the pod runs under.

  • With IRSA, the line to check is the web identity one. The pod's service account must carry the eks.amazonaws.com/role-arn annotation, which makes EKS inject AWS_ROLE_ARN and AWS_WEB_IDENTITY_TOKEN_FILE into the container. Verify with kubectl exec <pod> -- env | grep AWS_; the service account itself is selected by spec.serviceAccountName in the pod spec, never by application configuration.

Security model with file encryption disabled

When DQM_FILE_ENCRYPTION=false, encryption at rest is provided by the infrastructure instead of the application. Verify and configure the following:

  1. Bucket encryption. Check what the bucket applies with aws s3api get-bucket-encryption --bucket <bucket>. Every S3 bucket encrypts objects at rest by default (SSE-S3/AES256). For customer-controlled key rotation, configure the bucket with SSE-KMS and a customer-managed KMS key, and enable S3 Bucket Keys to reduce KMS request costs (SelectZero writes many small objects). Rotating the KMS key never requires re-encrypting stored objects.

  2. Per-object proof. aws s3api head-object --bucket <bucket> --key "reports/results/<file>" returns a ServerSideEncryption field (AES256 or aws:kms with the key id) - evidence that a specific object is encrypted at rest.

  3. IAM. With SSE-KMS the application's role additionally needs kms:GenerateDataKey and kms:Decrypt on the key, on top of the S3 permissions listed under Requirements.

  4. Bucket policies. The application sends no explicit encryption headers - bucket default encryption applies to its uploads. A bucket policy that denies PutObject requests lacking an explicit x-amz-server-side-encryption header will therefore reject all uploads; rely on bucket default encryption instead of such deny policies.

  5. Local working copies. The pod's ephemeral volume holds plaintext working copies of uploaded files and cached results while the pod runs (with file encryption enabled it holds plaintext temporary data during query execution as well - only the durable copies differ). At-rest protection for the node comes from volume encryption (e.g. encrypted EBS), which is the same posture as any database workload. For the strictest setup, back the working directories with emptyDir: {medium: Memory} sized for the full uploaded-file set plus cache - plaintext then never touches a disk.

Secrets stored in database columns (connection passwords, certificates, tokens) are encrypted with DQM_ENCRYPTION_KEY in every mode - the flag only affects files.

Kubernetes example

The deployment differs from the persistent volume variant in three ways: no PersistentVolumeClaim (all volumes are emptyDir), the Recreate update strategy (avoids two pods overlapping on the same working files), and the storage/encryption environment variables.

apiVersion: apps/v1 kind: Deployment metadata: name: selectzero-deployment spec: selector: matchLabels: app: selectzero replicas: 1 strategy: type: Recreate template: metadata: labels: app: selectzero spec: volumes: - name: workdir emptyDir: sizeLimit: 20Gi # size for uploaded data source files + caches containers: - name: selectzero image: selectzero/selectzero:2026.7.3 resources: requests: memory: 4Gi cpu: "1" ephemeral-storage: 10Gi limits: memory: 4Gi env: - name: SZ_STORAGE_MODE value: s3 - name: SZ_STORAGE_S3_BUCKET value: my-selectzero-storage - name: SZ_STORAGE_S3_REGION value: eu-central-1 - name: DQM_ENCRYPTION_KEY valueFrom: secretKeyRef: name: selectzero-secrets key: encryption-key - name: SZ_JDBC_URL value: jdbc:postgresql://your-postgres-host:5432/selectzero - name: SZ_DATABASE_USER valueFrom: secretKeyRef: {name: selectzero-secrets, key: db-user} - name: SZ_DATABASE_PASSWORD valueFrom: secretKeyRef: {name: selectzero-secrets, key: db-password} ports: - containerPort: 8090 volumeMounts: - mountPath: "/usr/app/tempFiles" name: workdir subPath: tempFiles - mountPath: "/usr/app/database" name: workdir subPath: database - mountPath: "/usr/app/reports" name: workdir subPath: reports - mountPath: "/usr/app/drivers" name: workdir subPath: drivers - mountPath: "/usr/app/keys" name: workdir subPath: keys - mountPath: "/usr/app/logs" name: workdir subPath: logs

Startup hydration

On every pod start in S3 mode, SelectZero restores its working directories (uploaded data source files, drivers, logo) from the bucket before the application becomes ready. Progress is visible in the system log:

S3 hydration starting: restoring mirrored directories [database/files, database/images, drivers] from bucket storage S3 hydration finished: restored 142 file(s) in 8317ms

Startup time grows with the amount of uploaded data (downloads run in parallel; roughly 1-3 minutes for 15GB of uploaded files in-region). Configure the pod's startupProbe with enough headroom for your data volume so Kubernetes does not restart the pod mid-hydration.

Choosing underlying database

SelectZero supports two underlying database types:

  1. SQLite - default deployment database. Local file based database

  2. Postgres - Separate database instance for better performance and scaling. Can be enabled via UI or directly from environment variables

Supported environment variables

Variable name

Explanation

DQM_ENCRYPTION_KEY

Custom encryption key for encrypting underlying secrets, and files while file encryption is enabled

DQM_FILE_ENCRYPTION

true (default) encrypts stored files application-side; false stores files as-is and leaves encryption at rest to the storage layer (e.g. S3 SSE, disk encryption). Install-time choice - never flip on existing data

SPRING_PROFILES_ACTIVE

Profile to run platform with (default 'on-prem'). Available profiles are on-prem, secure-session

SZ_JDBC_URL

Postgres JDBC url for application database

SZ_DATABASE_USER

Postgres JDBC user for application database

SZ_DATABASE_PASSWORD

Postgres JDBC password for application database

SZ_DATABASE_SSL_MODE

Postgres JDBC SSL Mode for application database (NONE, SSL, SSL_VERIFY).
If CA cert is needed, it is possible to map it with docker keys volume (keys/pg-certs/ca.crt)

SZ_STORAGE_MODE

Durable file storage backend: local (default) or s3. See S3 storage mode for the full variable set

Updating SelectZero version

Update JAR

  1. Download the latest version of JAR from https://selectzero.com/downloads/

  2. Stop the old JAR service

    systemctl stop {service-name}
  3. Run new JAR

    java -XX:TieredStopAtLevel=1 -Dspring.jmx.enabled=false -Xmx4g -Dserver.port=8090 -jar SelectZero_2026.7.3.jar

Update docker container

  1. Pull new docker image

    docker pull selectzero/selectzero:2026.7.3
  2. Look for old container name

    docker ps
  3. Stop old container

    docker stop {container-name}
  4. Run container with new version

    docker run -d -v {local_path}/logs:/usr/app/logs -v {local_path}/database:/usr/app/database -v {local_path}/drivers:/usr/app/drivers -v {local_path}/reports:/usr/app/reports -v {local_path}/keys:/usr/app/keys -p 8090:8090 -e JAVA_OPTIONS='-Xmx4g' selectzero/selectzero:2026.7.3
  5. Validate that SelectZero container is running with new version

    docker ps

Update docker compose

  1. Modify existing docker-compose.yml

    services: selectzero: container_name: selectzero image: selectzero/selectzero:2026.7.3 # Change version ports: - 8090:8090 volumes: - ./logs:/usr/app/logs - ./database:/usr/app/database - ./drivers:/usr/app/drivers - ./reports:/usr/app/reports - ./keys:/usr/app/keys environment: JAVA_OPTIONS: -Xmx4g
  2. Run docker compose

    docker compose up -d

Update kubernetes

  1. Modify resource file selectzero.yaml

    ... containers: - name: selectzero image: selectzero/selectzero:2026.7.3 # Modify version tag resources: limits: memory: 4Gi requests: memory: 4Gi ...
  2. Apply using kubectl

    kubectl apply -f selectzero.yaml

Fixing folder rights for non-root user

From version 2023.10 SelectZero container is running on non-root user "dqm". This is due to security reasons. "dqm" user uses default groups UID:1000 GID:1000. In case SelectZero container startup should give folder access rights, we have prepared a few fix scripts for assigning correct user rights to folders. This is a one time action and will work as expected in the future. It won't affect new installs, unless using direct docker bind mount to host system.

Fix folder rights

  1. Stop existing container

    docker stop {container_name}
  2. Run busybox container to fix folder permissions

    docker run --rm -v {local_path}/logs:/usr/app/logs -v {local_path}/database:/usr/app/database -v {local_path}/drivers:/usr/app/drivers -v {local_path}/reports:/usr/app/reports -v {local_path}/keys:/usr/app/keys busybox:latest sh -c "chown -R 1000:1000 /usr/app"
  3. Run the original container with fixed folder permissions

    docker run -d -v {local_path}/logs:/usr/app/logs -v {local_path}/database:/usr/app/database -v {local_path}/drivers:/usr/app/drivers -v {local_path}/reports:/usr/app/reports -v {local_path}/keys:/usr/app/keys -p 8090:8090 -e JAVA_OPTIONS='-Xmx4g' {image_id}

Fix folder rights

  1. Modify existing docker-compose.yml to include startup service

    version: "3.7" services: busybox: container_name: busybox-folders image: busybox volumes: - ./logs:/usr/app/logs - ./database:/usr/app/database - ./drivers:/usr/app/drivers - ./reports:/usr/app/reports - ./keys:/usr/app/keys command: ["chown", "-R", "1000:1000", "/usr/app/"] selectzero: container_name: selectzero-non-root image: "selectzero/selectzero:2026.7.3" ports: - "8090:8090" volumes: - ./logs:/usr/app/logs - ./database:/usr/app/database - ./drivers:/usr/app/drivers - ./reports:/usr/app/reports - ./keys:/usr/app/keys environment: JAVA_OPTIONS: "-Xmx2g -Djava.security.egd=:/dev/urandom" depends_on: busybox: condition: service_completed_successfully
  2. Re-apply docker compose

    docker compose up -d

Fix folder rights

  1. Modify deployment resource to include init container

    ... spec: volumes: - name: selectzero-storage persistentVolumeClaim: claimName: selectzero-storage-claim initContainers: - name: folder-rights-fix image: busybox:latest securityContext: runAsUser: 0 imagePullPolicy: Always command: ["chown", "-R", "1000:1000", "/usr/app/"] volumeMounts: - mountPath: "/usr/app/database" name: {storage-name} subPath: database - mountPath: "/usr/app/logs" name: {storage-name} subPath: logs - mountPath: "/usr/app/drivers" name: {storage-name} subPath: drivers - mountPath: "/usr/app/reports" name: {storage-name} subPath: reports - mountPath: "/usr/app/keys" name: {storage-name} subPath: keys containers: - name: selectzero ...
  2. Add security context to main deployment spec

    ... apiVersion: apps/v1 kind: Deployment metadata: name: selectzero-deployment spec: securityContext: runAsUser: 1000 fsGroup: 1000 ...
  3. Re-apply kubernetes resource

    kubectl apply -f selectzero.yaml

Securing deployment for production

For production environments we recommend adding extra security options to make the platform more resilient.

  1. Adding host headers

  2. Enabling secure session cookies with secure-session profile (requires https)

18 August 2026