SCA

Deploy OWASP Dependency-Track 5 with PostgreSQL using Docker Compose

A production-minded Docker Compose setup for Dependency-Track 5 on your own infrastructure, from first login to SBOM uploads from CI/CD.

What Dependency-Track does

OWASP Dependency-Track keeps track of the third-party components in everything you ship. Your build pipeline uploads a software bill of materials (SBOM) after each build. Dependency-Track stores it, checks every component against vulnerability databases such as the NVD, GitHub Advisories and OSV, and keeps checking after the build is long finished. When a new vulnerability is published for a component you use, your team gets a notification by email, Slack, Microsoft Teams, Mattermost or a webhook.

That last part is what makes it more than a scanner. A scan tells you what was vulnerable on the day you built. Dependency-Track tells you what became vulnerable since, across every product and version you still support.

For manufacturers preparing for the EU Cyber Resilience Act, that’s directly useful. The CRA requires an SBOM covering at least your top-level dependencies, monitoring for known vulnerabilities in third-party components, and fixes without delay throughout the support period. Dependency-Track covers the inventory and monitoring parts of that. Our CRA guide for SMBs explains the obligations in more detail.

What changed in Dependency-Track 5

Dependency-Track 5 became generally available in June 2026. If you ran version 4, these are the changes that affect deployment:

  • v5 runs on PostgreSQL only. H2, MySQL and Microsoft SQL Server support is gone, including the embedded H2 database that made v4 easy to try out.
  • Images now come from GitHub’s registry, ghcr.io/dependencytrack/. The bundled single-container image and the WAR file are no longer published.
  • Database settings moved from ALPINE_DATABASE_* to DT_DATASOURCE_*.
  • Health checks and Prometheus metrics have their own port, 9000, separate from the REST API on port 8080.
  • Several API server instances can share one database and coordinate through PostgreSQL. A single instance is fine for most small and medium teams.

Dependency-Track 4.14 receives security fixes until 9 December 2026. Plan your upgrade before then; the project’s migration guide covers moving your data.

What you need

  • A Linux host with Docker Engine and the Docker Compose plugin. See Install Docker Engine.
  • For the API server: at least 2 GB of memory and ideally 4 CPU cores. Below 1 GB it won’t keep up with real load.
  • For PostgreSQL: the project recommends 8 GB of memory and 4 cores on a dedicated host with SSD or NVMe storage, and no less than 4 GB and 2 cores even for evaluation. Running it on the same host is fine to start with; move it out, or to a managed PostgreSQL service, when your portfolio grows.
  • Two host names if you expose it beyond localhost, for example dtrack.example.com for the frontend and dtrack-api.example.com for the API server.

The Docker Compose file

The file below runs three services: PostgreSQL, the API server and the frontend. Compared with the project’s quick-start file, it reads the database password from a Docker secret, pins image versions and limits the API server’s resources.

Download compose.yaml

name: dependency-track

services:
  postgres:
    image: postgres:18-alpine
    environment:
      POSTGRES_DB: dtrack
      POSTGRES_USER: dtrack
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
      interval: 5s
      timeout: 3s
      retries: 5
    volumes:
      - postgres-data:/var/lib/postgresql
    restart: unless-stopped

  apiserver:
    image: ghcr.io/dependencytrack/apiserver:5.1.0
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      DT_DATASOURCE_URL: jdbc:postgresql://postgres:5432/dtrack
      DT_DATASOURCE_USERNAME: dtrack
      DT_DATASOURCE_PASSWORD: "$${file::/run/secrets/db_password}"
    secrets:
      - db_password
    deploy:
      resources:
        limits:
          memory: 2g
          cpus: "4"
    ports:
      - "127.0.0.1:8080:8080" # REST API
      - "127.0.0.1:9000:9000" # health checks and metrics
    volumes:
      - apiserver-data:/data
    restart: unless-stopped

  frontend:
    image: ghcr.io/dependencytrack/frontend:5.1.0
    environment:
      API_BASE_URL: http://localhost:8080
    ports:
      - "127.0.0.1:8081:8080"
    restart: unless-stopped

secrets:
  db_password:
    file: ./secrets/db_password.txt

volumes:
  apiserver-data: {}
  postgres-data: {}

A few choices in this file are deliberate:

  • The database has no published port. Only the API server talks to PostgreSQL, over the internal Compose network. Publishing port 5432 or adding a web admin tool such as Adminer only adds attack surface.
  • All ports are bound to 127.0.0.1, so nothing is reachable from the network until you put a reverse proxy in front.
  • The database password lives in a file. PostgreSQL reads it through POSTGRES_PASSWORD_FILE, Dependency-Track through the ${file::...} reference. The double $$ stops Compose from treating it as one of its own variables.
  • Image versions are pinned. With latest, the next docker compose pull could upgrade Dependency-Track or PostgreSQL without anyone deciding to. Check the releases page for the newest 5.x patch, and read its upgrade notes before you change the tag.
  • The frontend only serves static files, so API_BASE_URL must be the address your users’ browsers use to reach the API server, not an internal container name.

Start it

Create the password file, without a trailing newline, and start the stack:

mkdir -p secrets
printf '%s' "$(openssl rand -base64 32)" > secrets/db_password.txt
chmod 600 secrets/db_password.txt

docker compose up -d
docker compose logs -f apiserver

The first start creates the database schema and default teams, which takes a minute. Check that the API server is ready:

curl -s http://localhost:9000/health/ready

Then open http://localhost:8081 and sign in with admin / admin. Dependency-Track asks you to set a new password straight away.

Local accounts are fine for evaluation. For production, the project recommends connecting OpenID Connect or LDAP/Active Directory so people sign in with their company account.

Put it behind TLS

Terminate TLS at a reverse proxy and leave the containers on plain HTTP. The proxy must forward the X-Forwarded-Proto and X-Forwarded-For headers so audit logs and links in notifications show the public URL. With Caddy, which handles certificates and those headers for you, the configuration is four lines per host:

dtrack.example.com {
	reverse_proxy 127.0.0.1:8081
}

dtrack-api.example.com {
	reverse_proxy 127.0.0.1:8080
}

Then set API_BASE_URL: https://dtrack-api.example.com in the Compose file and run docker compose up -d again. Nginx, Traefik or your existing load balancer work the same way.

Turn on the vulnerability sources

Go to Administration › Vulnerability Sources and enable the sources you want:

  • The NVD needs no credentials. Its first mirror downloads the whole dataset and takes a while.
  • GitHub Advisories needs a personal access token or a GitHub App. It’s worth setting up, because its advisories often appear sooner and map to package names more precisely than NVD entries.
  • OSV needs no credentials. Enable the ecosystems you actually use.

Since version 5.1, Dependency-Track also mirrors the CISA and ENISA catalogs of known exploited vulnerabilities by default. You can use that status in policies and notification filters, so an exploited vulnerability doesn’t drown in a list of theoretical ones.

Create a project and upload an SBOM

A project in Dependency-Track represents one product, library or service, usually with one version per release you support. Create one under Projects › Create Project, then upload a CycloneDX SBOM on the project’s Components tab to see the result.

You need a CycloneDX SBOM first. Most build ecosystems have a generator: the CycloneDX plugins for Maven and Gradle, cdxgen for many languages, or Syft for container images. The CycloneDX Tool Center lists them all. Dependency-Track 5.1 accepts CycloneDX up to version 1.7.

Uploading by hand is fine to try it out. In practice your pipeline should do it after every build.

Upload SBOMs from your CI/CD pipeline

Your pipeline needs an API key with the BOM_UPLOAD permission. The default Automation team has it, together with PROJECT_CREATION_UPLOAD, which lets an upload create the project if it doesn’t exist yet. Generate a key for that team under Administration › Access Management › Teams, or create a separate team per pipeline so you can revoke keys one at a time.

Store the key as a secret in your CI system. Never put it in the pipeline file or the repository.

Then upload the SBOM after each build:

curl -sSf -X POST "https://dtrack-api.example.com/api/v1/bom" \
  -H "X-Api-Key: ${DT_API_KEY}" \
  -F "autoCreate=true" \
  -F "projectName=my-product" \
  -F "projectVersion=${VERSION}" \
  -F "[email protected]"

With autoCreate=true, Dependency-Track creates the project and version on the first upload. If you prefer to manage projects by hand, leave it out and identify the project by its UUID instead, with -F "project=<uuid>". You’ll find the UUID under the project’s View Details.

Upload one version per release you support, not only the main branch. When a new vulnerability comes out, you want to know which shipped versions it affects, because those are the ones your customers run and the ones the CRA expects you to fix.

Back up and upgrade

All persistent data lives in PostgreSQL, so a database backup is a full backup:

docker compose exec -T postgres pg_dump -U dtrack -Fc dtrack > dtrack-$(date +%F).dump

Schedule it, copy the dumps off the host, and test a restore at least once. If you need to inspect the database, use docker compose exec postgres psql -U dtrack dtrack rather than exposing a web admin tool.

To upgrade, read the release’s upgrade notes, take a backup, change the image tags in compose.yaml, and run:

docker compose pull
docker compose up -d

Next steps

Once SBOMs flow in, the value comes from what you do with the findings. Set up notifications for new critical and known-exploited vulnerabilities, create component policies for licenses and outdated components, and agree who triages findings for each product. The Dependency-Track documentation covers each of these.

If you want help fitting Dependency-Track into your secure development lifecycle or your CRA preparation, book a free call.