Self-hosted DBtune
Background
DBtune offers a self-hosted deployment option for enterprise customers. Self-hosting is particularly valuable in two scenarios:
- For organizations requiring an air-gapped environment due to regulatory compliance and information security requirements. If you are not sure if you need an air-gapped environment, you don’t need an air-gapped environment.
- For cases needing specific customizations or integrations that are only possible in a self-managed environment. For example, if you are a software provider you may want to integrate your service more tightly with the DBtune optimizer. This is common for partners who adopt a value added reseller (VAR) or original equipment manufacturer (OEM) commercial model.
The self-hosted deployment delivers the complete feature set of the DBtune SaaS while giving you the added benefits of enhanced security, customization options, and infrastructure control. However, your team will be responsible for maintenance, updates, and infrastructure management.
How to host
-
Clone or Download the Release
- Unpack/copy the
self-hosteddirectory to your server.
- Unpack/copy the
-
GHCR Authentication (Required)
- Before pulling images, log in to GitHub Container Registry using the Personal Access Token (PAT) provided by DBtune:
docker login ghcr.io -u dbtuneai -p <personal-access-token> - This is required to download the necessary images.
- Before pulling images, log in to GitHub Container Registry using the Personal Access Token (PAT) provided by DBtune:
-
Run the setup helper (recommended)
- This script will generate secure values (e.g.,
DJANGO_SECRET_KEY,DJANGO_SUPERUSER_API_KEY) and prompt you for required fields (superuser email, password etc.).cd self-hosted
chmod +x ./setup-self-hosted.sh
./setup-self-hosted.sh
- This script will generate secure values (e.g.,
-
Configure Environment Variables
- Open the included
.env.self-hostedfile. - Read the instructions/comments in that file and fill in the
CHANGEMEfields with your actual values. - Pay special attention to:
- Database, password, and secret key settings.
- SMTP/Email settings for password reset and notifications.
- Save and do not commit secrets to public repositories!
- Open the included
-
Start the Platform
- Run:
docker compose --env-file .env.self-hosted up -d - You can see an overview at the bottom of this page, here
- Only the main web interface (default port 8000) is open externally.
- All other components communicate privately inside Docker.
- Run:
-
Access the Application
- Visit http://your-server:8000 (replace with your server address or port).
- Log in with your configured superuser (see
.env.self-hosted).
Email/SMTP Setup
- Real email (SMTP) is required for password resets and notifications.
- Enter your SMTP provider's details in the environment file. Example for Gmail:
EMAIL_HOST=smtp.gmail.com
EMAIL_PORT=587
EMAIL_USE_TLS=True
EMAIL_FROM=your@email.com
EMAIL_HOST_PASSWORD=your-app-password - If your organization uses another provider (Exchange, Office365, SES, etc.) fill in those corresponding details.
- We do NOT recommend using test relays or Mailpit in production.
Other Notes
- Ensure required ports are open (default: 8000 for web).
- For advanced features, SSO, or customization, contact support or see extended documentation.
DBtune agent (part 2)
The DBtune agent requires to be installed and connected to your database. The agent will then communicate with the hosted DBtune container using the internal IP address you assigned to the DBtune container. Please note that this agent is the same as the agent in the SaaS solution — More information can be found in the DBtune documentation. The agent is open-source and written in GoLang.
Downloading the container image
The DBtune team provides access to the container registry credentials that are needed to authenticate and download the artifacts.
If your infrastructure is in AWS, the DBtune team will give you the possibility of whitelisting your AWS Account ID. This allows you to fetch the container images without needing any specific additional authentication.
Programmatic access
The DBtune REST API enables you to programmatically retrieve database information and recommendations. The API uses resource-oriented URLs, returns request responses in JSON format, and uses standard HTTP status codes.
To get started, please refer to the DBtune API documentation.
Security
The self-hosted deployment of DBtune works in an air-gapped environment, requiring no internet access. This makes it ideal for Enterprise offline deployments.
Docker compose overview
x-platform: &platform
platform: linux/amd64
restart: unless-stopped
x-shared-env: &shared-env
DJANGO_SETTINGS_MODULE: ${DJANGO_SETTINGS_MODULE:?}
DJANGO_SECRET_KEY: ${DJANGO_SECRET_KEY:?}
EMAIL_BACKEND: ${EMAIL_BACKEND:?}
EMAIL_USE_TLS: ${EMAIL_USE_TLS:?}
EMAIL_HOST: ${EMAIL_HOST:?}
EMAIL_FROM: ${EMAIL_FROM:?}
EMAIL_HOST_PASSWORD: ${EMAIL_HOST_PASSWORD:?}
EMAIL_PORT: ${EMAIL_PORT:?}
PG_URL: ${PG_URL:?}
CELERY_BROKER_URL: ${CELERY_BROKER_URL:?}
INFLUXDB_URL: ${INFLUXDB_URL:?}
INFLUXDB_ORG: ${INFLUXDB_ORG:?}
INFLUXDB_BUCKET: ${INFLUXDB_BUCKET:?}
INFLUXDB_USERNAME: ${INFLUXDB_USERNAME:?}
INFLUXDB_PASSWORD: ${INFLUXDB_PASSWORD:?}
APP_LOGGING_SETTINGS: ${APP_LOGGING_SETTINGS:?}
IMPERSONATE_MODE: ${IMPERSONATE_MODE:?}
CUSTOMER_SUPPORT_EMAIL: ${CUSTOMER_SUPPORT_EMAIL:?}
FRONTEND_URL: ${FRONTEND_URL:?}
SOCIAL_AUTH_REDIRECT_IS_HTTPS: ${SOCIAL_AUTH_REDIRECT_IS_HTTPS:?}
ACCOUNT_DEFAULT_HTTP_PROTOCOL: ${ACCOUNT_DEFAULT_HTTP_PROTOCOL:?}
DEBUG: ${DEBUG:?}
SUPERUSER: ${SUPERUSER:?}
DAPHNE_PORT: ${WS_PORT:?}
DJANGO_SUPERUSER_USERNAME: ${DJANGO_SUPERUSER_USERNAME:?}
DJANGO_SUPERUSER_PASSWORD: ${DJANGO_SUPERUSER_PASSWORD:?}
DJANGO_SUPERUSER_EMAIL: ${DJANGO_SUPERUSER_EMAIL:?}
DJANGO_SUPERUSER_API_KEY: ${DJANGO_SUPERUSER_API_KEY:?}
DBTUNE_WS_URL: ${DBTUNE_WS_URL:?}
GOOGLE_OAUTH2_CLIENT_ID: ${GOOGLE_OAUTH2_CLIENT_ID:-}
GOOGLE_OAUTH2_CLIENT_SECRET: ${GOOGLE_OAUTH2_CLIENT_SECRET:-}
GRACE_PERIOD_ZOMBIE_SECONDS: ${GRACE_PERIOD_ZOMBIE_SECONDS:?}
# Prefect variables (app references Prefect API URL)
PREFECT_API_URL: ${PREFECT_API_URL:?}
PREFECT_SERVER_ALLOW_EPHEMERAL_MODE: ${PREFECT_SERVER_ALLOW_EPHEMERAL_MODE:?}
PREFECT_LOGGING_LEVEL: ${PREFECT_LOGGING_LEVEL:?}
DJANGO_ENABLE_SILK_PROFILING: ${DJANGO_ENABLE_SILK_PROFILING:?}
services:
db:
platform: linux/amd64
image: postgres:16-bookworm
environment:
- POSTGRES_USER=${POSTGRES_USER:?}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?}
- POSTGRES_DB=${POSTGRES_DB:?}
- POSTGRES_PORT=${POSTGRES_PORT:?}
- PREFECT_DB_USER=${PREFECT_DB_USER:?}
- PREFECT_DB_PASSWORD=${PREFECT_DB_PASSWORD:?}
- PREFECT_DB_NAME=${PREFECT_DB_NAME:?}
command: |
bash -c "
docker-entrypoint.sh postgres -c shared_preload_libraries=pg_stat_statements -c pg_stat_statements.track=all
until pg_isready -U $$POSTGRES_USER; do sleep 0.1; done
psql -U $$POSTGRES_USER -d $$POSTGRES_DB -c \"CREATE USER $$PREFECT_DB_USER WITH PASSWORD '$$PREFECT_DB_PASSWORD';\"
psql -U $$POSTGRES_USER -d $$POSTGRES_DB -c \"CREATE DATABASE $$PREFECT_DB_NAME OWNER $$PREFECT_DB_USER;\"
"
volumes:
- postgres_data:/var/lib/postgresql/data/
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
interval: 2s
timeout: 5s
retries: 20
influxdb:
platform: linux/amd64
image: influxdb:2.7
volumes:
- influxdb-data:/var/lib/influxdb2
- influxdb-config:/etc/influxdb2
environment:
- DOCKER_INFLUXDB_INIT_MODE=${DOCKER_INFLUXDB_INIT_MODE:?}
- DOCKER_INFLUXDB_INIT_USERNAME=${INFLUXDB_USERNAME:?}
- DOCKER_INFLUXDB_INIT_PASSWORD=${INFLUXDB_PASSWORD:?}
- DOCKER_INFLUXDB_INIT_ORG=${INFLUXDB_ORG:?}
- DOCKER_INFLUXDB_INIT_BUCKET=${INFLUXDB_BUCKET:?}
# The main web service for responding to HTTP requests
web:
<<: *platform
image: ghcr.io/dbtuneai/dbtune-platform-self-hosted:3.0.3
environment:
<<: *shared-env
DJANGO_CONN_USE_POOL: ${WEB_DJANGO_CONN_USE_POOL:?}
entrypoint: /usr/local/bin/entrypoint.sh
ports:
- "${WEB_PORT:?}:${WEB_PORT:?}"
healthcheck:
test: ["CMD-SHELL", "curl --fail localhost:${WEB_PORT:?}/api/ping || exit 1"]
start_period: 120s
interval: 10s
timeout: 60s
retries: 5
depends_on:
influxdb:
condition: service_started
db:
condition: service_healthy
redis:
condition: service_healthy
# The websocket service for responding to websocket requests
ws:
<<: *platform
image: ghcr.io/dbtuneai/dbtune-platform-self-hosted:3.0.3
environment:
<<: *shared-env
DJANGO_CONN_USE_POOL: ${WS_DJANGO_CONN_USE_POOL:?}
entrypoint: /usr/local/bin/ws-entrypoint.sh
healthcheck:
test: ["CMD-SHELL", "curl --fail localhost:${WS_PORT:?}/api/ping || exit 1"]
start_period: 120s
interval: 10s
timeout: 60s
retries: 5
depends_on:
influxdb:
condition: service_started
db:
condition: service_healthy
redis:
condition: service_healthy
web:
condition: service_healthy
# Celery workers for handling background tasks
worker:
<<: *platform
image: ghcr.io/dbtuneai/dbtune-platform-self-hosted:3.0.3
environment:
<<: *shared-env
DJANGO_CONN_USE_POOL: ${WORKER_DJANGO_CONN_USE_POOL:?}
entrypoint: /usr/local/bin/worker-entrypoint.sh
healthcheck:
test: ["CMD-SHELL", "celery -A cloud inspect ping || exit 1"]
interval: 10s
timeout: 10s
retries: 5
depends_on:
web:
condition: service_healthy
redis:
condition: service_healthy
redis:
platform: linux/amd64
restart: unless-stopped
image: redis:7.0.15-alpine
volumes:
- redis-data:/data
healthcheck:
test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
interval: 10s
timeout: 10s
retries: 5
# Prefect server (mandatory)
prefect-server:
platform: linux/amd64
image: prefecthq/prefect:3.4.22-python3.13
command: prefect server start
environment:
- PREFECT_DB_USER=${PREFECT_DB_USER:?}
- PREFECT_DB_PASSWORD=${PREFECT_DB_PASSWORD:?}
- PREFECT_DB_NAME=${PREFECT_DB_NAME:?}
- PREFECT_SERVER_API_HOST=${PREFECT_SERVER_API_HOST:?}
- PREFECT_DATABASE_CONNECTION_URL=${PREFECT_DATABASE_CONNECTION_URL:?}
- PREFECT_MESSAGING_BROKER=${PREFECT_MESSAGING_BROKER:?}
- PREFECT_MESSAGING_CACHE=${PREFECT_MESSAGING_CACHE:?}
- PREFECT_REDIS_MESSAGING_HOST=${PREFECT_REDIS_MESSAGING_HOST:?}
- PREFECT_REDIS_MESSAGING_PORT=${PREFECT_REDIS_MESSAGING_PORT:?}
- PREFECT_REDIS_MESSAGING_DB=${PREFECT_REDIS_MESSAGING_DB:?}
depends_on:
db:
condition: service_healthy
volumes:
- prefect-data:/root/.prefect
healthcheck:
test: ["CMD-SHELL", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:${PREFECT_SERVER_PORT}/api/health')"]
interval: 10s
timeout: 5s
retries: 5
dbtuner:
platform: linux/amd64
image: ghcr.io/dbtuneai/dbtuner-self-hosted:0.4.3
working_dir: /app
environment:
- PREFECT_API_URL=${PREFECT_API_URL:?}
- PREFECT_SERVER_ALLOW_EPHEMERAL_MODE=${PREFECT_SERVER_ALLOW_EPHEMERAL_MODE:?}
- WORKER_POOL_NAME=${WORKER_POOL_NAME:?}
- WORKER_NAME=${WORKER_NAME:?}
- FLOW_RUN_EXPECTED_MEMORY_MB=${FLOW_RUN_EXPECTED_MEMORY_MB:?}
depends_on:
prefect-server:
condition: service_healthy
# SMTP relay example for production; remove or customize for your environment
# smtp-relay:
# image: bytemark/smtp
# restart: always
# environment:
# - MAILNAME=yourdomain.com
# - RELAY_HOST=smtp.your-provider.example.com
# - RELAY_PORT=587
# - RELAY_USER=your-email@example.com
# - RELAY_PASS=your-email-password
# ports:
# - "25:25"
volumes:
postgres_data:
influxdb-data:
influxdb-config:
prefect-data:
redis-data: