Skip to main content

Self-hosted DBtune on Docker Compose

Overview​

DBtune can be self-hosted on a single VM using Docker Compose. This is the simplest deployment option, recommended if you are not running Kubernetes.

If you are running a Kubernetes cluster, see the Kubernetes self-hosted guide instead. For general background on self-hosting, see the self-hosted deployment overview.

How to host​

The self-hosted platform ships as an offline bundle — dbtune-self-hosted-<version>-compose.tar — that already contains every image alongside the compose stack. No container registry access is required, which makes it suitable for air-gapped environments.

Once DBtune has enabled self-hosting for your organization, the bundle is available to download directly from the platform: open your organization page and download the latest Docker Compose self-hosted release. That single archive is everything you need.

  1. Setup requirements
  • dbtune-self-hosted-<version>-compose.tar — the bundle downloaded from your organization page. It contains all images plus docker-compose.yml, .env.self-hosted, load-images.sh and setup-self-hosted.sh.
  • A Linux host (x86_64).
  • docker (Engine 24+ with the Compose v2 docker compose subcommand), or podman with podman-compose — runs the stack and configures volumes and networking.
  1. Download and extract the bundle
  • Download dbtune-self-hosted-<version>-compose.tar from your organization page in the platform, copy it to your server, then unpack it:
    tar -xf dbtune-self-hosted-<version>-compose.tar
    cd dbtune-self-hosted-<version>-compose
  1. Load the bundled images
  • Every image ships inside the bundle, so there is no registry login or docker pull.
    ./load-images.sh docker # or: ./load-images.sh podman
  1. Run the setup helper (recommended)
  • Generates secure values (e.g. DJANGO_SECRET_KEY, DJANGO_SUPERUSER_API_KEY) and prompts for required fields (FRONTEND_URL, superuser email and password).
    chmod +x ./setup-self-hosted.sh
    ./setup-self-hosted.sh
  1. Configure environment variables
  • Open the included .env.self-hosted file.
  • Read the comments in that file and fill in the CHANGEME fields with your actual values.
  • Pay special attention to:
    • Database, password, and secret key settings.
    • SMTP/email settings for password reset and notifications.
  1. Start the platform
  • The bundle already pins every image version, so no manual tag editing is required.
    docker compose --env-file .env.self-hosted up -d
  • Only the main web interface (default port 8000) needs to be reachable from outside the host. Compose also publishes port 8001, which is used internally by the tuning service; you don't need to open it in your firewall. You may optionally expose influxdb and prefect-server to access their UIs.
  • All other components communicate privately inside Docker.
  1. 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).

After a host reboot​

The stack doesn't fully start again on its own after the host reboots. Start it again from the bundle directory:

docker compose --env-file .env.self-hosted up -d

On Podman, use sudo podman-compose --env-file .env.self-hosted up -d. Your data in ./volumes/ is kept.

RHEL / Podman​

On RHEL (8, 9 or 10) with Podman, follow the steps above with these differences. Run every podman command the same way, either all with sudo or all without: rootful and rootless Podman keep separate image stores.

  1. Open port 8000 in firewalld before starting the stack. Reloading firewalld removes the port forwarding rules Podman creates, so a reload after the stack is running makes the platform unreachable. Enabling netavark-firewalld-reload restores them after any later reload:

    sudo firewall-cmd --permanent --add-port=8000/tcp && sudo firewall-cmd --reload
    sudo systemctl enable --now netavark-firewalld-reload.service

    If the platform has already stopped responding after a reload, run sudo podman network reload --all.

  2. Load the images with sudo ./load-images.sh podman, then run the setup helper and configure .env.self-hosted as above.

  3. Label the data directories for SELinux. RHEL runs SELinux in enforcing mode by default, and containers can't write to directories that aren't labeled for them. The directories must exist before they can be labeled:

    mkdir -p volumes/{postgres_data,influxdb-data,influxdb-config,redis-data,prefect-data}
    sudo chcon -Rt container_file_t ./volumes

    You can skip this step if getenforce returns Disabled.

  4. Start the platform:

    sudo podman-compose --env-file .env.self-hosted up -d

    Podman may ask you to select a registry for the postgres, influxdb, redis and prefect images. Any choice works: Podman uses the images already loaded from the bundle. Without internet access, up can take a few minutes, because Podman tries the registry for each image and waits about 30 seconds before using the bundled one.

System Requirements​

The images used are the following:

  • postgres - Primary database for most operations.
  • influx - Timeseries database.
  • redis - KV cache.
  • dbtune-platform - The main platform application.
  • dbtuner - The optimization service.
  • dbtune-indigo - Index recommendation engine (self-contained; runs its own ephemeral PostgreSQL internally, no external database required).
  • prefect - Distributed task scheduling.

System requirements primarily scale according to the number of connected databases, and concurrent tuning sessions. Services gracefully degrade under load spikes, but monitoring is advised.

The figures below assume an air-gapped setup for 5-10 databases with 1-2 concurrent tuning sessions, where a guarantee of low latency under load is not a requirement.

vCPURAMStorage
Minimal28 GB20 GB
Recommended416 GB30 GB

Storage scales mainly with the number of connected databases and how many distinct queries/tables each one has, not database count alone. Metrics storage (InfluxDB) is the main driver; based on real production usage, budget roughly 1-2GB per connected database there.

When scaling for your requirements, please monitor usage and adjust accordingly.

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.

Upgrade​

Data (postgres_data, influxdb-data, influxdb-config, prefect-data, redis-data) lives under ./volumes/ inside each bundle's own folder — it is not a Docker-managed volume shared across versions. Extracting the new bundle into a fresh directory starts that directory's ./volumes/ empty. Skipping step 3 below will bring the new version up with a blank database.

  1. Download the latest self-hosted bundle from your organization page and extract it alongside the old one, then cd into it. Do not run docker compose up yet.
  2. Stop the old stack, so the databases aren't written to while you copy them. Don't add -v:
    (cd ../<old-bundle-dir> && docker compose --env-file .env.self-hosted down)
  3. Copy your data across. The data files are owned by the container users, so copy them as root and keep their ownership and permissions:
    sudo cp -a ../<old-bundle-dir>/volumes ./volumes
  4. Load the new images: ./load-images.sh docker (or podman).
  5. Reconcile your config, don't blind-copy it. Diff your old .env.self-hosted against the new bundle's and carry your values into the new file, keeping any keys the release added. A release can introduce new required variables; compose reads them as ${VAR:?}, so a missing key fails docker compose up immediately rather than starting on a stale value.
  6. Carry over the secrets your data was created with — POSTGRES_PASSWORD, PREFECT_DB_PASSWORD, INFLUXDB_INIT_PASSWORD, INFLUXDB_TOKEN — not just user-facing settings like FRONTEND_URL. If you run ./setup-self-hosted.sh on the new bundle, it regenerates these randomly; overwrite them with the originals afterward, or the new containers can't authenticate against the copied data.
  7. Apply: docker compose --env-file .env.self-hosted up -d. Compose starts the new version against the copied data.

On Podman, use podman-compose in place of docker compose in steps 2 and 7. Once the new version is running and your data is there, you can delete the old bundle directory.

The bundle already pins every image version, so there is nothing to look up manually.

DBtune agent​

The DBtune agent can be set up as it is normally done with the DBtune SaaS platform, where the agent runs outside of the Docker network, communicating via the open 8000 port.

If you wish to host the DBtune agent inside the Docker network via docker compose alongside the platform stack, you will need to update the server_url to point to the web container inside the Docker Compose network.

The 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 Go.

The agent image is not part of the self-hosted bundle. In an air-gapped environment, pull it on a machine with internet access, save it to a file, and load it on the agent host:

docker pull public.ecr.aws/dbtune/dbtune/agent:latest
docker save -o dbtune-agent.tar public.ecr.aws/dbtune/dbtune/agent:latest
# copy dbtune-agent.tar to the agent host, then:
docker load -i dbtune-agent.tar

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.